TYPO3 API  SVNRelease
class.tx_simulatestatic.php
Go to the documentation of this file.
00001 <?php
00002 /***************************************************************
00003 *  Copyright notice
00004 *
00005 *  (c) 1999-2011 Kasper Skårhøj <kasperYYYY@typo3.com>
00006 *  (c) 2008-2011 Benjamin Mack <benni . typo3 . o)rg>
00007 *  All rights reserved
00008 *
00009 *  This script is part of the TYPO3 project. The TYPO3 project is
00010 *  free software; you can redistribute it and/or modify
00011 *  it under the terms of the GNU General Public License as published by
00012 *  the Free Software Foundation; either version 2 of the License, or
00013 *  (at your option) any later version.
00014 *
00015 *  The GNU General Public License can be found at
00016 *  http://www.gnu.org/copyleft/gpl.html.
00017 *  A copy is found in the textfile GPL.txt and important notices to the license
00018 *  from the author is found in LICENSE.txt distributed with these scripts.
00019 *
00020 *
00021 *  This script is distributed in the hope that it will be useful,
00022 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00023 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00024 *  GNU General Public License for more details.
00025 *
00026 *  This copyright notice MUST APPEAR in all copies of the script!
00027 ***************************************************************/
00028 /**
00029  * Class that does the simulatestatic feature (Speaking URLs)
00030  * Was extracted for TYPO3 4.3 from the core
00031  *
00032  * $Id: class.tx_simulatestatic.php 10120 2011-01-18 20:03:36Z ohader $
00033  *
00034  * @author  Kasper Skårhøj <kasperYYYY@typo3.com>
00035  * @author  Benjamin Mack <benni . typo3 . o)rg>
00036  */
00037 class tx_simulatestatic {
00038     public $enabled = false;
00039     public $replacementChar = '';
00040     public $conf = array();
00041     public $pEncodingAllowedParamNames = array();
00042 
00043     /**
00044      * Initializes the extension, sets some configuration options and does some basic checks
00045      *
00046      * @param   array       holds all the information about the link that is about to be created
00047      * @param   tslib_fe    is a reference to the parent object that calls the hook
00048      * @return  void
00049      */
00050     public function hookInitConfig(array &$parameters, tslib_fe &$parentObject) {
00051         $TSconf = &$parameters['config'];
00052 
00053         // if .simulateStaticDocuments was not present, the installation-wide default value will be used
00054         if (!isset($TSconf['simulateStaticDocuments'])) {
00055             $TSconf['simulateStaticDocuments'] = trim($parentObject->TYPO3_CONF_VARS['FE']['simulateStaticDocuments']);
00056         }
00057 
00058         // simulateStatic was not activated
00059         if (!$TSconf['simulateStaticDocuments']) {
00060             return;
00061         }
00062 
00063         $this->enabled = true;
00064 
00065         // setting configuration options
00066         $this->conf = array(
00067             'mode' => $TSconf['simulateStaticDocuments'],
00068             'dontRedirectPathInfoError' => ($TSconf['simulateStaticDocuments_dontRedirectPathInfoError'] ? $TSconf['simulateStaticDocuments_dontRedirectPathInfoError'] : $TSconf['simulateStaticDocuments.']['dontRedirectPathInfoError']),
00069             'pEncoding' => ($TSconf['simulateStaticDocuments_pEnc'] ? $TSconf['simulateStaticDocuments_pEnc'] : $TSconf['simulateStaticDocuments.']['pEncoding']),
00070             'pEncodingOnlyP' => ($TSconf['simulateStaticDocuments_pEnc_onlyP'] ? $TSconf['simulateStaticDocuments_pEnc_onlyP'] : $TSconf['simulateStaticDocuments.']['pEncoding_onlyP']),
00071             'addTitle'  => ($TSconf['simulateStaticDocuments_addTitle'] ? $TSconf['simulateStaticDocuments_addTitle'] : $TSconf['simulateStaticDocuments.']['addTitle']),
00072             'noTypeIfNoTitle' => ($TSconf['simulateStaticDocuments_noTypeIfNoTitle'] ? $TSconf['simulateStaticDocuments_noTypeIfNoTitle'] : $TSconf['simulateStaticDocuments.']['noTypeIfNoTitle']),
00073             'replacementChar' => (t3lib_div::compat_version('4.0') ? '-' : '_')
00074         );
00075 
00076         if ($this->conf['pEncodingOnlyP']) {
00077             $tempParts = t3lib_div::trimExplode(',', $this->conf['pEncodingOnlyP'], 1);
00078             foreach ($tempParts as $tempPart) {
00079                 $this->pEncodingAllowedParamNames[$tempPart] = 1;
00080             }
00081         }
00082 
00083 
00084         // Checks and sets replacement character for simulateStaticDocuments.
00085         $replacement = trim($TSconf['simulateStaticDocuments_replacementChar'] ? $TSconf['simulateStaticDocuments_replacementChar'] : $TSconf['simulateStaticDocuments.']['replacementChar']);
00086         if ($replacement && (urlencode($replacement) == $replacement)) {
00087             $this->conf['replacementChar'] = $replacement;
00088         }
00089 
00090         // Force absRefPrefix to this value is PATH_INFO is used.
00091         $absRefPrefix = $TSconf['absRefPrefix'];
00092         $absRefPrefix = trim($absRefPrefix);
00093         if ((!strcmp($this->conf['mode'], 'PATH_INFO') || $parentObject->absRefPrefix_force) && !$absRefPrefix) {
00094             $absRefPrefix = t3lib_div::dirname(t3lib_div::getIndpEnv('SCRIPT_NAME')) . '/';
00095         }
00096         $parentObject->absRefPrefix = $absRefPrefix;
00097         $parentObject->config['config']['absRefPrefix'] = $absRefPrefix;
00098 
00099 
00100         // Check PATH_INFO url
00101         if ($parentObject->absRefPrefix_force && strcmp($this->conf['mode'], 'PATH_INFO')) {
00102             $redirectUrl = t3lib_div::getIndpEnv('TYPO3_REQUEST_DIR') . 'index.php?id=' . $parentObject->id . '&type='.$parentObject->type;
00103             if ($this->conf['dontRedirectPathInfoError']) {
00104                 if ($parentObject->checkPageUnavailableHandler()) {
00105                     $parentObject->pageUnavailableAndExit('PATH_INFO was not configured for this website, and the URL tries to find the page by PATH_INFO!');
00106                 } else {
00107                     $message = 'PATH_INFO was not configured for this website, and the URL tries to find the page by PATH_INFO!';
00108                     header(t3lib_utility_Http::HTTP_STATUS_503);
00109                     t3lib_div::sysLog($message, 'cms', t3lib_div::SYSLOG_SEVERITY_ERROR);
00110                     $message = 'Error: PATH_INFO not configured: ' . $message . '<br /><br /><a href="' . htmlspecialchars($redirectUrl) . '">Click here to get to the right page.</a>';
00111                     throw new RuntimeException($message);
00112                 }
00113             } else {
00114                 t3lib_utility_Http::redirect($redirectUrl);
00115             }
00116             exit;
00117             // Set no_cache if PATH_INFO is NOT used as simulateStaticDoc.
00118             // and if absRefPrefix_force shows that such an URL has been passed along.
00119             // $this->set_no_cache();
00120         }
00121     }
00122 
00123 
00124     /**
00125      * Hook for creating a speaking URL when using the generic linkData function
00126      *
00127      * @param   array               holds all the information about the link that is about to be created
00128      * @param   t3lib_TStemplate    is a reference to the parent object that calls the hook
00129      * @return  void
00130      */
00131     public function hookLinkDataPostProc(array &$parameters, t3lib_TStemplate &$parentObject) {
00132         if (!$this->enabled) {
00133             return;
00134         }
00135 
00136         $LD = &$parameters['LD'];
00137         $page = &$parameters['args']['page'];
00138         $LD['type'] = '';
00139 
00140         // MD5/base64 method limitation
00141         $remainLinkVars = '';
00142         $flag_pEncoding = (t3lib_div::inList('md5,base64', $this->conf['pEncoding']) && !$LD['no_cache']);
00143         if ($flag_pEncoding) {
00144             list($LD['linkVars'], $remainLinkVars) = $this->processEncodedQueryString($LD['linkVars']);
00145         }
00146 
00147         $url = $this->makeSimulatedFileName(
00148             $page['title'],
00149             ($page['alias'] ? $page['alias'] : $page['uid']),
00150             intval($parameters['typeNum']),
00151             $LD['linkVars'],
00152             ($LD['no_cache'] ? true : false)
00153         );
00154         if ($this->conf['mode'] == 'PATH_INFO') {
00155             $url = 'index.php/' . str_replace('.', '/', $url) . '/';
00156         } else {
00157             $url .= '.html';
00158         }
00159         $LD['url'] = $GLOBALS['TSFE']->absRefPrefix . $url . '?';
00160 
00161         if ($flag_pEncoding) {
00162             $LD['linkVars'] = $remainLinkVars;
00163         }
00164 
00165         // If the special key 'sectionIndex_uid' (added 'manually' in tslib/menu.php to the page-record) is set,
00166         // then the link jumps directly to a section on the page.
00167         $LD['sectionIndex'] = ($page['sectionIndex_uid'] ? '#c'.$page['sectionIndex_uid'] : '');
00168 
00169             // Compile the normal total url
00170         $LD['totalURL'] = $parentObject->removeQueryString($LD['url'] . $LD['type'] . $LD['no_cache'] . $LD['linkVars'] . $GLOBALS['TSFE']->getMethodUrlIdToken) . $LD['sectionIndex'];
00171     }
00172 
00173 
00174     /**
00175      * Hook for checking to see if the URL is a speaking URL
00176      *
00177      * Here a .htaccess file maps all .html-files to index.php and
00178      *  then we extract the id and type from the name of that HTML-file. (AKA "simulateStaticDocuments")
00179      * Support for RewriteRule to generate   (simulateStaticDocuments)
00180      * With the mod_rewrite compiled into apache, put these lines into a .htaccess in this directory:
00181      * RewriteEngine On
00182      * RewriteRule   ^[^/]*\.html$  index.php
00183      * The url must end with '.html' and the format must comply with either of these:
00184      * 1:      '[title].[id].[type].html'  - title is just for easy recognition in the
00185      *                                       logfile!; no practical use of the title for TYPO3.
00186      * 2:      '[id].[type].html'          - above, but title is omitted; no practical use of
00187      *                                       the title for TYPO3.
00188      * 3:      '[id].html'                 - only id, type is set to the default, zero!
00189      * NOTE: In all case 'id' may be the uid-number OR the page alias (if any)
00190      *
00191      * @param   array       includes a reference to the parent Object (which is the global TSFE)
00192      * @param   tslib_fe    is a reference to the global TSFE
00193      * @return  void
00194      */
00195     public function hookCheckAlternativeIDMethods(array &$parameters, tslib_fe &$parentObject) {
00196         // If there has been a redirect (basically; we arrived here otherwise
00197         // than via "index.php" in the URL)
00198         // this can happend either due to a CGI-script or because of reWrite rule.
00199         // Earlier we used $_SERVER['REDIRECT_URL'] to check
00200         if ($parentObject->siteScript && substr($parentObject->siteScript, 0, 9) != 'index.php') {
00201             $uParts = parse_url($parentObject->siteScript);
00202             $fI = t3lib_div::split_fileref($uParts['path']);
00203 
00204             if (!$fI['path'] && $fI['file'] && substr($fI['file'], -5) == '.html') {
00205                 $parts = explode('.', $fI['file']);
00206                 $pCount = count($parts);
00207                 if ($pCount > 2) {
00208                     $parentObject->type = intval($parts[$pCount-2]);
00209                     $parentObject->id = $parts[$pCount-3];
00210                 } else {
00211                     $parentObject->type = 0;
00212                     $parentObject->id = $parts[0];
00213                 }
00214             }
00215         }
00216 
00217         // If PATH_INFO is defined as simulateStaticDocuments mode and has information:
00218         if (t3lib_div::getIndpEnv('PATH_INFO') && strpos(t3lib_div::getIndpEnv('TYPO3_SITE_SCRIPT'), 'index.php/') === 0) {
00219             $parts = t3lib_div::trimExplode('/', t3lib_div::getIndpEnv('PATH_INFO'), true);
00220             $pCount = count($parts);
00221             if ($pCount > 1) {
00222                 $parentObject->type = intval($parts[$pCount-1]);
00223                 $parentObject->id = $parts[$pCount-2];
00224             } else {
00225                 $parentObject->type = 0;
00226                 $parentObject->id = $parts[0];
00227             }
00228             $parentObject->absRefPrefix_force = 1;
00229         }
00230     }
00231 
00232 
00233     /**
00234      * Analyzes the second part of a id-string (after the "+"), looking for B6 or M5 encoding
00235      * and if found it will resolve it and restore the variables in global $_GET.
00236      * If values for ->cHash, ->no_cache, ->jumpurl and ->MP is found,
00237      * they are also loaded into the internal vars of this class.
00238      * => Not yet used, could be ported from tslib_fe as well
00239      *
00240      * @param   string      String to analyze
00241      * @return  void
00242      */
00243     protected function idPartsAnalyze($string) {
00244         $getVars = '';
00245         switch (substr($string, 0, 2)) {
00246             case 'B6':
00247                 $addParams = base64_decode(str_replace('_', '=', str_replace('-', '/', substr($string, 2))));
00248                 parse_str($addParams, $getVars);
00249             break;
00250             case 'M5':
00251                 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('params', 'cache_md5params', 'md5hash=' . $GLOBALS['TYPO3_DB']->fullQuoteStr(substr($string, 2), 'cache_md5params'));
00252                 $row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
00253 
00254                 $GLOBALS['TSFE']->updateMD5paramsRecord(substr($string, 2));
00255                 parse_str($row['params'], $getVars);
00256             break;
00257         }
00258         $GLOBALS['TSFE']->mergingWithGetVars($getVars);
00259     }
00260 
00261 
00262 
00263 
00264     /********************************************
00265      *
00266      * Various internal API functions
00267      *
00268      *******************************************/
00269 
00270     /**
00271      * This is just a wrapper function to use the params from the array split up. Can be deleted once the function in class.t3lib_fe.php is deleted
00272      *
00273      * @param   array       Parameter array delivered from tslib_fe::makeSimulFileName
00274      * @param   tslib_fe    Reference to the calling TSFE instance
00275      * @return  string      The body of the filename.
00276      * @see makeSimulatedFileName()
00277      * @deprecated since TYPO3 4.3, will be deleted in TYPO3 4.6
00278      */
00279     public function makeSimulatedFileNameCompat(array &$parameters, tslib_fe &$parentObject) {
00280         t3lib_div::logDeprecatedFunction();
00281 
00282         return $this->makeSimulatedFileName(
00283             $parameters['inTitle'],
00284             $parameters['page'],
00285             $parameters['type'],
00286             $parameters['addParams'],
00287             $parameters['no_cache']
00288         );
00289     }
00290 
00291 
00292     /**
00293      * Make simulation filename (without the ".html" ending, only body of filename)
00294      *
00295      * @param   string      The page title to use
00296      * @param   mixed       The page id (integer) or alias (string)
00297      * @param   integer     The type number
00298      * @param   string      Query-parameters to encode (will be done only if caching is enabled and TypoScript configured for it. I don't know it this makes much sense in fact...)
00299      * @param   boolean     The "no_cache" status of the link.
00300      * @return  string      The body of the filename.
00301      * @see getSimulFileName(), t3lib_tstemplate::linkData(), tslib_frameset::frameParams()
00302      */
00303     public function makeSimulatedFileName($inTitle, $page, $type, $addParams = '', $no_cache = false) {
00304             // Default value is 30 but values > 1 will be override this
00305         $titleChars = intval($this->conf['addTitle']);
00306         if ($titleChars == 1) {
00307             $titleChars = 30;
00308         }
00309 
00310         $out = ($titleChars ? $this->fileNameASCIIPrefix($inTitle, $titleChars) : '');
00311         $enc = '';
00312 
00313         if (strcmp($addParams, '') && !$no_cache) {
00314             switch ((string)$this->conf['pEncoding']) {
00315                 case 'md5':
00316                     $md5 = substr(md5($addParams), 0, 10);
00317                     $enc = '+M5'.$md5;
00318 
00319                     $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
00320                         'md5hash',
00321                         'cache_md5params',
00322                         'md5hash=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($md5, 'cache_md5params')
00323                     );
00324                     if (!$GLOBALS['TYPO3_DB']->sql_num_rows($res)) {
00325                         $insertFields = array(
00326                             'md5hash' => $md5,
00327                             'tstamp'  => $GLOBALS['EXEC_TIME'],
00328                             'type'    => 1,
00329                             'params'  => $addParams
00330                         );
00331 
00332                         $GLOBALS['TYPO3_DB']->exec_INSERTquery('cache_md5params', $insertFields);
00333                     }
00334                     $GLOBALS['TYPO3_DB']->sql_free_result($res);
00335                 break;
00336                 case 'base64':
00337                     $enc = '+B6' . str_replace('=', '_', str_replace('/', '-', base64_encode($addParams)));
00338                 break;
00339             }
00340         }
00341             // Setting page and type number:
00342         return $out . $page . $enc . (($type || $out || !$this->conf['noTypeIfNoTitle']) ? '.' . $type : '');
00343     }
00344 
00345 
00346     /**
00347      * Returns the simulated static file name (*.html) for the current page (using the page record in $this->page)
00348      *
00349      * @return  string      The filename (without path)
00350      * @see makeSimulatedFileName(), publish.php
00351      */
00352     public function getSimulatedFileName() {
00353         return $this->makeSimulatedFileName(
00354             $GLOBALS['TSFE']->page['title'],
00355             ($GLOBALS['TSFE']->page['alias'] ? $GLOBALS['TSFE']->page['alias'] : $GLOBALS['TSFE']->id),
00356             $GLOBALS['TSFE']->type
00357         ) . '.html';
00358     }
00359 
00360 
00361     /**
00362      * Processes a query-string with GET-parameters and returns two strings, one with the parameters that CAN be encoded and one array with those which can't be encoded (encoded by the M5 or B6 methods)
00363      *
00364      * @param   string      Query string to analyse
00365      * @return  array       Two num keys returned, first is the parameters that MAY be encoded, second is the non-encodable parameters.
00366      * @see makeSimulatedFileName(), t3lib_tstemplate::linkData()
00367      */
00368     public function processEncodedQueryString($linkVars) {
00369         $remainingLinkVars = '';
00370         if (strcmp($linkVars, '')) {
00371             $parts = t3lib_div::trimExplode('&', $linkVars);
00372             // This sorts the parameters - and may not be needed and further
00373             // it will generate new MD5 hashes in many cases. Maybe not so smart. Hmm?
00374             sort($parts);
00375             $remainingParts = array();
00376             foreach ($parts as $index => $value) {
00377                 if (strlen($value)) {
00378                     list($parameterName) = explode('=', $value, 2);
00379                     $parameterName = rawurldecode($parameterName);
00380                     if (!$this->pEncodingAllowedParamNames[$parameterName]) {
00381                         unset($parts[$index]);
00382                         $remainingParts[] = $value;
00383                     }
00384                 } else {
00385                     unset($parts[$index]);
00386                 }
00387             }
00388             $linkVars = (count($parts) ? '&' . implode('&', $parts) : '');
00389             $remainingLinkVars = (count($remainingParts) ? '&' . implode('&', $remainingParts) : '');
00390         }
00391         return array($linkVars, $remainingLinkVars);
00392     }
00393 
00394 
00395     /**
00396      * Converts input string to an ASCII based file name prefix
00397      *
00398      * @param   string      String to base output on
00399      * @param   integer     Number of characters in the string
00400      * @param   string      Character to put in the end of string to merge it with the next value.
00401      * @return  string      Converted string
00402      */
00403     public function fileNameASCIIPrefix($inTitle, $maxTitleChars, $mergeChar = '.') {
00404         $out = $GLOBALS['TSFE']->csConvObj->specCharsToASCII($GLOBALS['TSFE']->renderCharset, $inTitle);
00405 
00406         // Get replacement character
00407         $replacementChar = $this->conf['replacementChar'];
00408         $replacementChars = '_\-' . ($replacementChar != '_' && $replacementChar != '-' ? $replacementChar : '');
00409         $out = preg_replace('/[^A-Za-z0-9_-]/', $replacementChar, trim(substr($out, 0, $maxTitleChars)));
00410         $out = preg_replace('/([' . $replacementChars . ']){2,}/', '\1', $out);
00411         $out = preg_replace('/['  . $replacementChars . ']?$/', '', $out);
00412         $out = preg_replace('/^[' . $replacementChars . ']?/', '', $out);
00413 
00414         return (strlen($out) ? $out . $mergeChar : '');
00415     }
00416 }
00417 
00418 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/simulatestatic/class.tx_simulatestatic.php'])) {
00419     include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/simulatestatic/class.tx_simulatestatic.php']);
00420 }
00421 ?>