[BUGFIX] Mirror selection is not respected
[Packages/TYPO3.CMS.git] / typo3 / sysext / simulatestatic / class.tx_simulatestatic.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 1999-2011 Kasper Skårhøj <kasperYYYY@typo3.com>
6 * (c) 2008-2011 Benjamin Mack <benni . typo3 . o)rg>
7 * All rights reserved
8 *
9 * This script is part of the TYPO3 project. The TYPO3 project is
10 * free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * The GNU General Public License can be found at
16 * http://www.gnu.org/copyleft/gpl.html.
17 * A copy is found in the textfile GPL.txt and important notices to the license
18 * from the author is found in LICENSE.txt distributed with these scripts.
19 *
20 *
21 * This script is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * This copyright notice MUST APPEAR in all copies of the script!
27 ***************************************************************/
28 /**
29 * Class that does the simulatestatic feature (Speaking URLs)
30 * Was extracted for TYPO3 4.3 from the core
31 *
32 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
33 * @author Benjamin Mack <benni . typo3 . o)rg>
34 */
35 class tx_simulatestatic {
36 public $enabled = FALSE;
37 public $replacementChar = '';
38 public $conf = array();
39 public $pEncodingAllowedParamNames = array();
40
41 /**
42 * Initializes the extension, sets some configuration options and does some basic checks
43 *
44 * @param array holds all the information about the link that is about to be created
45 * @param tslib_fe is a reference to the parent object that calls the hook
46 * @return void
47 */
48 public function hookInitConfig(array &$parameters, tslib_fe &$parentObject) {
49 $TSconf = &$parameters['config'];
50
51 // if .simulateStaticDocuments was not present, the installation-wide default value will be used
52 if (!isset($TSconf['simulateStaticDocuments'])) {
53 $TSconf['simulateStaticDocuments'] = trim($parentObject->TYPO3_CONF_VARS['FE']['simulateStaticDocuments']);
54 }
55
56 // simulateStatic was not activated
57 if (!$TSconf['simulateStaticDocuments']) {
58 return;
59 }
60
61 $this->enabled = TRUE;
62
63 // setting configuration options
64 $this->conf = array(
65 'mode' => $TSconf['simulateStaticDocuments'],
66 'dontRedirectPathInfoError' => ($TSconf['simulateStaticDocuments_dontRedirectPathInfoError'] ? $TSconf['simulateStaticDocuments_dontRedirectPathInfoError'] : $TSconf['simulateStaticDocuments.']['dontRedirectPathInfoError']),
67 'pEncoding' => ($TSconf['simulateStaticDocuments_pEnc'] ? $TSconf['simulateStaticDocuments_pEnc'] : $TSconf['simulateStaticDocuments.']['pEncoding']),
68 'pEncodingOnlyP' => ($TSconf['simulateStaticDocuments_pEnc_onlyP'] ? $TSconf['simulateStaticDocuments_pEnc_onlyP'] : $TSconf['simulateStaticDocuments.']['pEncoding_onlyP']),
69 'addTitle' => ($TSconf['simulateStaticDocuments_addTitle'] ? $TSconf['simulateStaticDocuments_addTitle'] : $TSconf['simulateStaticDocuments.']['addTitle']),
70 'noTypeIfNoTitle' => ($TSconf['simulateStaticDocuments_noTypeIfNoTitle'] ? $TSconf['simulateStaticDocuments_noTypeIfNoTitle'] : $TSconf['simulateStaticDocuments.']['noTypeIfNoTitle']),
71 'replacementChar' => (t3lib_div::compat_version('4.0') ? '-' : '_')
72 );
73
74 if ($this->conf['pEncodingOnlyP']) {
75 $tempParts = t3lib_div::trimExplode(',', $this->conf['pEncodingOnlyP'], 1);
76 foreach ($tempParts as $tempPart) {
77 $this->pEncodingAllowedParamNames[$tempPart] = 1;
78 }
79 }
80
81
82 // Checks and sets replacement character for simulateStaticDocuments.
83 $replacement = trim($TSconf['simulateStaticDocuments_replacementChar'] ? $TSconf['simulateStaticDocuments_replacementChar'] : $TSconf['simulateStaticDocuments.']['replacementChar']);
84 if ($replacement && (urlencode($replacement) == $replacement)) {
85 $this->conf['replacementChar'] = $replacement;
86 }
87
88 // Force absRefPrefix to this value is PATH_INFO is used.
89 $absRefPrefix = $TSconf['absRefPrefix'];
90 $absRefPrefix = trim($absRefPrefix);
91 if ((!strcmp($this->conf['mode'], 'PATH_INFO') || $parentObject->absRefPrefix_force) && !$absRefPrefix) {
92 $absRefPrefix = t3lib_div::dirname(t3lib_div::getIndpEnv('SCRIPT_NAME')) . '/';
93 }
94 $parentObject->absRefPrefix = $absRefPrefix;
95 $parentObject->config['config']['absRefPrefix'] = $absRefPrefix;
96
97
98 // Check PATH_INFO url
99 if ($parentObject->absRefPrefix_force && strcmp($this->conf['mode'], 'PATH_INFO')) {
100 $redirectUrl = t3lib_div::getIndpEnv('TYPO3_REQUEST_DIR') . 'index.php?id=' . $parentObject->id . '&type='.$parentObject->type;
101 if ($this->conf['dontRedirectPathInfoError']) {
102 if ($parentObject->checkPageUnavailableHandler()) {
103 $parentObject->pageUnavailableAndExit('PATH_INFO was not configured for this website, and the URL tries to find the page by PATH_INFO!');
104 } else {
105 $message = 'PATH_INFO was not configured for this website, and the URL tries to find the page by PATH_INFO!';
106 header(t3lib_utility_Http::HTTP_STATUS_503);
107 t3lib_div::sysLog($message, 'cms', t3lib_div::SYSLOG_SEVERITY_ERROR);
108 $message = 'Error: PATH_INFO not configured: ' . $message . '<br /><br /><a href="' . htmlspecialchars($redirectUrl) . '">Click here to get to the right page.</a>';
109 throw new RuntimeException($message, 1294587706);
110 }
111 } else {
112 t3lib_utility_Http::redirect($redirectUrl);
113 }
114 exit;
115 // Set no_cache if PATH_INFO is NOT used as simulateStaticDoc.
116 // and if absRefPrefix_force shows that such an URL has been passed along.
117 // $this->set_no_cache();
118 }
119 }
120
121
122 /**
123 * Hook for creating a speaking URL when using the generic linkData function
124 *
125 * @param array holds all the information about the link that is about to be created
126 * @param t3lib_TStemplate is a reference to the parent object that calls the hook
127 * @return void
128 */
129 public function hookLinkDataPostProc(array &$parameters, t3lib_TStemplate &$parentObject) {
130 if (!$this->enabled) {
131 return;
132 }
133
134 $LD = &$parameters['LD'];
135 $page = &$parameters['args']['page'];
136 $LD['type'] = '';
137
138 // MD5/base64 method limitation
139 $remainLinkVars = '';
140 $flag_pEncoding = (t3lib_div::inList('md5,base64', $this->conf['pEncoding']) && !$LD['no_cache']);
141 if ($flag_pEncoding) {
142 list($LD['linkVars'], $remainLinkVars) = $this->processEncodedQueryString($LD['linkVars']);
143 }
144
145 $url = $this->makeSimulatedFileName(
146 $page['title'],
147 ($page['alias'] ? $page['alias'] : $page['uid']),
148 intval($parameters['typeNum']),
149 $LD['linkVars'],
150 ($LD['no_cache'] ? TRUE : FALSE)
151 );
152 if ($this->conf['mode'] == 'PATH_INFO') {
153 $url = 'index.php/' . str_replace('.', '/', $url) . '/';
154 } else {
155 $url .= '.html';
156 }
157 $LD['url'] = $GLOBALS['TSFE']->absRefPrefix . $url . '?';
158
159 if ($flag_pEncoding) {
160 $LD['linkVars'] = $remainLinkVars;
161 }
162
163 // If the special key 'sectionIndex_uid' (added 'manually' in tslib/menu.php to the page-record) is set,
164 // then the link jumps directly to a section on the page.
165 $LD['sectionIndex'] = ($page['sectionIndex_uid'] ? '#c'.$page['sectionIndex_uid'] : '');
166
167 // Compile the normal total url
168 $LD['totalURL'] = $parentObject->removeQueryString($LD['url'] . $LD['type'] . $LD['no_cache'] . $LD['linkVars'] . $GLOBALS['TSFE']->getMethodUrlIdToken) . $LD['sectionIndex'];
169 }
170
171
172 /**
173 * Hook for checking to see if the URL is a speaking URL
174 *
175 * Here a .htaccess file maps all .html-files to index.php and
176 * then we extract the id and type from the name of that HTML-file. (AKA "simulateStaticDocuments")
177 * Support for RewriteRule to generate (simulateStaticDocuments)
178 * With the mod_rewrite compiled into apache, put these lines into a .htaccess in this directory:
179 * RewriteEngine On
180 * RewriteRule ^[^/]*\.html$ index.php
181 * The url must end with '.html' and the format must comply with either of these:
182 * 1: '[title].[id].[type].html' - title is just for easy recognition in the
183 * logfile!; no practical use of the title for TYPO3.
184 * 2: '[id].[type].html' - above, but title is omitted; no practical use of
185 * the title for TYPO3.
186 * 3: '[id].html' - only id, type is set to the default, zero!
187 * NOTE: In all case 'id' may be the uid-number OR the page alias (if any)
188 *
189 * @param array includes a reference to the parent Object (which is the global TSFE)
190 * @param tslib_fe is a reference to the global TSFE
191 * @return void
192 */
193 public function hookCheckAlternativeIDMethods(array &$parameters, tslib_fe &$parentObject) {
194 // If there has been a redirect (basically; we arrived here otherwise
195 // than via "index.php" in the URL)
196 // this can happend either due to a CGI-script or because of reWrite rule.
197 // Earlier we used $_SERVER['REDIRECT_URL'] to check
198 if ($parentObject->siteScript && substr($parentObject->siteScript, 0, 9) != 'index.php') {
199 $uParts = parse_url($parentObject->siteScript);
200 $fI = t3lib_div::split_fileref($uParts['path']);
201
202 if (!$fI['path'] && $fI['file'] && substr($fI['file'], -5) == '.html') {
203 $parts = explode('.', $fI['file']);
204 $pCount = count($parts);
205 if ($pCount > 2) {
206 $parentObject->type = intval($parts[$pCount-2]);
207 $parentObject->id = $parts[$pCount-3];
208 } else {
209 $parentObject->type = 0;
210 $parentObject->id = $parts[0];
211 }
212 }
213 }
214
215 // If PATH_INFO is defined as simulateStaticDocuments mode and has information:
216 if (t3lib_div::getIndpEnv('PATH_INFO') && strpos(t3lib_div::getIndpEnv('TYPO3_SITE_SCRIPT'), 'index.php/') === 0) {
217 $parts = t3lib_div::trimExplode('/', t3lib_div::getIndpEnv('PATH_INFO'), TRUE);
218 $pCount = count($parts);
219 if ($pCount > 1) {
220 $parentObject->type = intval($parts[$pCount-1]);
221 $parentObject->id = $parts[$pCount-2];
222 } else {
223 $parentObject->type = 0;
224 $parentObject->id = $parts[0];
225 }
226 $parentObject->absRefPrefix_force = 1;
227 }
228 }
229
230
231 /**
232 * Analyzes the second part of a id-string (after the "+"), looking for B6 or M5 encoding
233 * and if found it will resolve it and restore the variables in global $_GET.
234 * If values for ->cHash, ->no_cache, ->jumpurl and ->MP is found,
235 * they are also loaded into the internal vars of this class.
236 * => Not yet used, could be ported from tslib_fe as well
237 *
238 * @param string String to analyze
239 * @return void
240 */
241 protected function idPartsAnalyze($string) {
242 $getVars = '';
243 switch (substr($string, 0, 2)) {
244 case 'B6':
245 $addParams = base64_decode(str_replace('_', '=', str_replace('-', '/', substr($string, 2))));
246 parse_str($addParams, $getVars);
247 break;
248 case 'M5':
249 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('params', 'cache_md5params', 'md5hash=' . $GLOBALS['TYPO3_DB']->fullQuoteStr(substr($string, 2), 'cache_md5params'));
250 $row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
251
252 $GLOBALS['TSFE']->updateMD5paramsRecord(substr($string, 2));
253 parse_str($row['params'], $getVars);
254 break;
255 }
256 $GLOBALS['TSFE']->mergingWithGetVars($getVars);
257 }
258
259
260
261
262 /********************************************
263 *
264 * Various internal API functions
265 *
266 *******************************************/
267
268 /**
269 * 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
270 *
271 * @param array Parameter array delivered from tslib_fe::makeSimulFileName
272 * @param tslib_fe Reference to the calling TSFE instance
273 * @return string The body of the filename.
274 * @see makeSimulatedFileName()
275 * @deprecated since TYPO3 4.3, will be deleted in TYPO3 4.6
276 */
277 public function makeSimulatedFileNameCompat(array &$parameters, tslib_fe &$parentObject) {
278 t3lib_div::logDeprecatedFunction();
279
280 return $this->makeSimulatedFileName(
281 $parameters['inTitle'],
282 $parameters['page'],
283 $parameters['type'],
284 $parameters['addParams'],
285 $parameters['no_cache']
286 );
287 }
288
289
290 /**
291 * Make simulation filename (without the ".html" ending, only body of filename)
292 *
293 * @param string The page title to use
294 * @param mixed The page id (integer) or alias (string)
295 * @param integer The type number
296 * @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...)
297 * @param boolean The "no_cache" status of the link.
298 * @return string The body of the filename.
299 * @see getSimulFileName(), t3lib_tstemplate::linkData(), tslib_frameset::frameParams()
300 */
301 public function makeSimulatedFileName($inTitle, $page, $type, $addParams = '', $no_cache = FALSE) {
302 // Default value is 30 but values > 1 will be override this
303 $titleChars = intval($this->conf['addTitle']);
304 if ($titleChars == 1) {
305 $titleChars = 30;
306 }
307
308 $out = ($titleChars ? $this->fileNameASCIIPrefix($inTitle, $titleChars) : '');
309 $enc = '';
310
311 if (strcmp($addParams, '') && !$no_cache) {
312 switch ((string)$this->conf['pEncoding']) {
313 case 'md5':
314 $md5 = substr(md5($addParams), 0, 10);
315 $enc = '+M5'.$md5;
316
317 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
318 'md5hash',
319 'cache_md5params',
320 'md5hash=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($md5, 'cache_md5params')
321 );
322 if (!$GLOBALS['TYPO3_DB']->sql_num_rows($res)) {
323 $insertFields = array(
324 'md5hash' => $md5,
325 'tstamp' => $GLOBALS['EXEC_TIME'],
326 'type' => 1,
327 'params' => $addParams
328 );
329
330 $GLOBALS['TYPO3_DB']->exec_INSERTquery('cache_md5params', $insertFields);
331 }
332 $GLOBALS['TYPO3_DB']->sql_free_result($res);
333 break;
334 case 'base64':
335 $enc = '+B6' . str_replace('=', '_', str_replace('/', '-', base64_encode($addParams)));
336 break;
337 }
338 }
339 // Setting page and type number:
340 return $out . $page . $enc . (($type || $out || !$this->conf['noTypeIfNoTitle']) ? '.' . $type : '');
341 }
342
343
344 /**
345 * Returns the simulated static file name (*.html) for the current page (using the page record in $this->page)
346 *
347 * @return string The filename (without path)
348 * @see makeSimulatedFileName(), publish.php
349 */
350 public function getSimulatedFileName() {
351 return $this->makeSimulatedFileName(
352 $GLOBALS['TSFE']->page['title'],
353 ($GLOBALS['TSFE']->page['alias'] ? $GLOBALS['TSFE']->page['alias'] : $GLOBALS['TSFE']->id),
354 $GLOBALS['TSFE']->type
355 ) . '.html';
356 }
357
358
359 /**
360 * 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)
361 *
362 * @param string Query string to analyse
363 * @return array Two num keys returned, first is the parameters that MAY be encoded, second is the non-encodable parameters.
364 * @see makeSimulatedFileName(), t3lib_tstemplate::linkData()
365 */
366 public function processEncodedQueryString($linkVars) {
367 $remainingLinkVars = '';
368 if (strcmp($linkVars, '')) {
369 $parts = t3lib_div::trimExplode('&', $linkVars);
370 // This sorts the parameters - and may not be needed and further
371 // it will generate new MD5 hashes in many cases. Maybe not so smart. Hmm?
372 sort($parts);
373 $remainingParts = array();
374 foreach ($parts as $index => $value) {
375 if (strlen($value)) {
376 list($parameterName) = explode('=', $value, 2);
377 $parameterName = rawurldecode($parameterName);
378 if (!$this->pEncodingAllowedParamNames[$parameterName]) {
379 unset($parts[$index]);
380 $remainingParts[] = $value;
381 }
382 } else {
383 unset($parts[$index]);
384 }
385 }
386 $linkVars = (count($parts) ? '&' . implode('&', $parts) : '');
387 $remainingLinkVars = (count($remainingParts) ? '&' . implode('&', $remainingParts) : '');
388 }
389 return array($linkVars, $remainingLinkVars);
390 }
391
392
393 /**
394 * Converts input string to an ASCII based file name prefix
395 *
396 * @param string String to base output on
397 * @param integer Number of characters in the string
398 * @param string Character to put in the end of string to merge it with the next value.
399 * @return string Converted string
400 */
401 public function fileNameASCIIPrefix($inTitle, $maxTitleChars, $mergeChar = '.') {
402 $out = $GLOBALS['TSFE']->csConvObj->specCharsToASCII($GLOBALS['TSFE']->renderCharset, $inTitle);
403
404 // Get replacement character
405 $replacementChar = $this->conf['replacementChar'];
406 $replacementChars = '_\-' . ($replacementChar != '_' && $replacementChar != '-' ? $replacementChar : '');
407 $out = preg_replace('/[^A-Za-z0-9_-]/', $replacementChar, trim(substr($out, 0, $maxTitleChars)));
408 $out = preg_replace('/([' . $replacementChars . ']){2,}/', '\1', $out);
409 $out = preg_replace('/[' . $replacementChars . ']?$/', '', $out);
410 $out = preg_replace('/^[' . $replacementChars . ']?/', '', $out);
411
412 return (strlen($out) ? $out . $mergeChar : '');
413 }
414 }
415
416 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/simulatestatic/class.tx_simulatestatic.php'])) {
417 include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/simulatestatic/class.tx_simulatestatic.php']);
418 }
419 ?>