[BUGFIX] Do not remove FE session cookie if fe_user is logged in
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Classes / Controller / TypoScriptFrontendController.php
1 <?php
2 namespace TYPO3\CMS\Frontend\Controller;
3
4 /**
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Error\Http\PageNotFoundException;
18 use TYPO3\CMS\Core\Utility\GeneralUtility;
19 use TYPO3\CMS\Core\Utility\HttpUtility;
20 use TYPO3\CMS\Core\Utility\MathUtility;
21 use TYPO3\CMS\Frontend\Page\PageRepository;
22
23 /**
24 * Class for the built TypoScript based frontend. Instantiated in
25 * index_ts.php script as the global object TSFE.
26 *
27 * Main frontend class, instantiated in the index_ts.php script as the global
28 * object TSFE.
29 *
30 * This class has a lot of functions and internal variable which are used from
31 * index_ts.php.
32 *
33 * The class is instantiated as $GLOBALS['TSFE'] in index_ts.php.
34 *
35 * The use of this class should be inspired by the order of function calls as
36 * found in index_ts.php.
37 *
38 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
39 */
40 class TypoScriptFrontendController {
41
42 // CURRENT PAGE
43
44 /**
45 * The page id (int)
46 * @var string
47 * @todo Define visibility
48 */
49 public $id = '';
50
51 /**
52 * The type (read-only)
53 * @var int
54 * @todo Define visibility
55 */
56 public $type = '';
57
58 /**
59 * The submitted cHash
60 * @var string
61 * @todo Define visibility
62 */
63 public $cHash = '';
64
65 /**
66 * Page will not be cached. Write only TRUE. Never clear value (some other
67 * code might have reasons to set it TRUE).
68 * @var bool
69 * @todo Define visibility
70 */
71 public $no_cache = FALSE;
72
73 /**
74 * The rootLine (all the way to tree root, not only the current site!)
75 * @var array
76 * @todo Define visibility
77 */
78 public $rootLine = '';
79
80 /**
81 * The pagerecord
82 * @var array
83 */
84 public $page = '';
85
86 /**
87 * This will normally point to the same value as id, but can be changed to
88 * point to another page from which content will then be displayed instead.
89 * @var int
90 * @todo Define visibility
91 */
92 public $contentPid = 0;
93
94 /**
95 * Gets set when we are processing a page of type mounpoint with enabled overlay in getPageAndRootline()
96 * Used later in checkPageForMountpointRedirect() to determine the final target URL where the user
97 * should be redirected to.
98 *
99 * @var array|NULL
100 */
101 protected $originalMountPointPage = NULL;
102
103 /**
104 * Gets set when we are processing a page of type shortcut in the early stages
105 * opf init.php when we do not know about languages yet, used later in init.php
106 * to determine the correct shortcut in case a translation changes the shortcut
107 * target
108 * @var array|NULL
109 */
110 protected $originalShortcutPage = NULL;
111
112 /**
113 * sys_page-object, pagefunctions
114 *
115 * @var PageRepository
116 * @todo Define visibility
117 */
118 public $sys_page = '';
119
120 /**
121 * @var string
122 * @todo Define visibility
123 */
124 public $jumpurl = '';
125
126 /**
127 * Is set to 1 if a pageNotFound handler could have been called.
128 * @var int
129 * @todo Define visibility
130 */
131 public $pageNotFound = 0;
132
133 /**
134 * Domain start page
135 * @var int
136 * @todo Define visibility
137 */
138 public $domainStartPage = 0;
139
140 /**
141 * Array containing a history of why a requested page was not accessible.
142 * @var array
143 * @todo Define visibility
144 */
145 public $pageAccessFailureHistory = array();
146
147 /**
148 * @var string
149 * @todo Define visibility
150 */
151 public $MP = '';
152
153 /**
154 * @var string
155 * @todo Define visibility
156 */
157 public $RDCT = '';
158
159 /**
160 * This can be set from applications as a way to tag cached versions of a page
161 * and later perform some external cache management, like clearing only a part
162 * of the cache of a page...
163 * @var int
164 * @todo Define visibility
165 */
166 public $page_cache_reg1 = 0;
167
168 /**
169 * Contains the value of the current script path that activated the frontend.
170 * Typically "index.php" but by rewrite rules it could be something else! Used
171 * for Speaking Urls / Simulate Static Documents.
172 * @var string
173 * @todo Define visibility
174 */
175 public $siteScript = '';
176
177 /**
178 * The frontend user
179 *
180 * @var \TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication
181 * @todo Define visibility
182 */
183 public $fe_user = '';
184
185 /**
186 * Global flag indicating that a frontend user is logged in. This is set only if
187 * a user really IS logged in. The group-list may show other groups (like added
188 * by IP filter or so) even though there is no user.
189 * @var bool
190 * @todo Define visibility
191 */
192 public $loginUser = FALSE;
193
194 /**
195 * (RO=readonly) The group list, sorted numerically. Group '0,-1' is the default
196 * group, but other groups may be added by other means than a user being logged
197 * in though...
198 * @var string
199 * @todo Define visibility
200 */
201 public $gr_list = '';
202
203 /**
204 * Flag that indicates if a backend user is logged in!
205 * @var bool
206 */
207 public $beUserLogin = FALSE;
208
209 /**
210 * Integer, that indicates which workspace is being previewed.
211 * @var int
212 * @todo Define visibility
213 */
214 public $workspacePreview = 0;
215
216 /**
217 * Shows whether logins are allowed in branch
218 * @var bool
219 * @todo Define visibility
220 */
221 public $loginAllowedInBranch = TRUE;
222
223 /**
224 * Shows specific mode (all or groups)
225 * @var string
226 * @todo Define visibility
227 */
228 public $loginAllowedInBranch_mode = '';
229
230 /**
231 * Set to backend user ID to initialize when keyword-based preview is used
232 * @var int
233 * @todo Define visibility
234 */
235 public $ADMCMD_preview_BEUSER_uid = 0;
236
237 // PREVIEW
238
239 /**
240 * Flag indication that preview is active. This is based on the login of a
241 * backend user and whether the backend user has read access to the current
242 * page. A value of 1 means ordinary preview, 2 means preview of a non-live
243 * workspace
244 * @var int
245 * @todo Define visibility
246 */
247 public $fePreview = 0;
248
249 /**
250 * Flag indicating that hidden pages should be shown, selected and so on. This
251 * goes for almost all selection of pages!
252 * @var bool
253 * @todo Define visibility
254 */
255 public $showHiddenPage = FALSE;
256
257 /**
258 * Flag indicating that hidden records should be shown. This includes
259 * sys_template, pages_language_overlay and even fe_groups in addition to all
260 * other regular content. So in effect, this includes everything except pages.
261 * @var bool
262 * @todo Define visibility
263 */
264 public $showHiddenRecords = FALSE;
265
266 /**
267 * Value that contains the simulated usergroup if any
268 * @var int
269 * @todo Define visibility
270 */
271 public $simUserGroup = 0;
272
273 // CONFIGURATION
274
275 /**
276 * Copy of $GLOBALS['TYPO3_CONF_VARS']
277 *
278 * @var array
279 */
280 public $TYPO3_CONF_VARS = array();
281
282 /**
283 * "CONFIG" object from TypoScript. Array generated based on the TypoScript
284 * configuration of the current page. Saved with the cached pages.
285 * @var array
286 */
287 public $config = '';
288
289 // TEMPLATE / CACHE
290
291 /**
292 * The TypoScript template object. Used to parse the TypoScript template
293 *
294 * @var \TYPO3\CMS\Core\TypoScript\TemplateService
295 * @todo Define visibility
296 */
297 public $tmpl = NULL;
298
299 /**
300 * Is set to the time-to-live time of cached pages. If FALSE, default is
301 * 60*60*24, which is 24 hours.
302 * @var bool|int
303 * @todo Define visibility
304 */
305 public $cacheTimeOutDefault = FALSE;
306
307 /**
308 * Set internally if cached content is fetched from the database
309 * @var bool
310 * @internal
311 * @todo Define visibility
312 */
313 public $cacheContentFlag = FALSE;
314
315 /**
316 * Set to the expire time of cached content
317 * @var int
318 * @todo Define visibility
319 */
320 public $cacheExpires = 0;
321
322 /**
323 * Set if cache headers allowing caching are sent.
324 * @var bool
325 * @todo Define visibility
326 */
327 public $isClientCachable = FALSE;
328
329 /**
330 * Used by template fetching system. This array is an identification of
331 * the template. If $this->all is empty it's because the template-data is not
332 * cached, which it must be.
333 * @var array
334 * @todo Define visibility
335 */
336 public $all = array();
337
338 /**
339 * Toplevel - objArrayName, eg 'page'
340 * @var string
341 * @todo Define visibility
342 */
343 public $sPre = '';
344
345 /**
346 * TypoScript configuration of the page-object pointed to by sPre.
347 * $this->tmpl->setup[$this->sPre.'.']
348 * @var array
349 * @todo Define visibility
350 */
351 public $pSetup = '';
352
353 /**
354 * This hash is unique to the template, the $this->id and $this->type vars and
355 * the gr_list (list of groups). Used to get and later store the cached data
356 * @var string
357 * @todo Define visibility
358 */
359 public $newHash = '';
360
361 /**
362 * If config.ftu (Frontend Track User) is set in TypoScript for the current
363 * page, the string value of this var is substituted in the rendered source-code
364 * with the string, '&ftu=[token...]' which enables GET-method usertracking as
365 * opposed to cookie based
366 * @var string
367 * @todo Define visibility
368 */
369 public $getMethodUrlIdToken = '';
370
371 /**
372 * This flag is set before inclusion of pagegen.php IF no_cache is set. If this
373 * flag is set after the inclusion of pagegen.php, no_cache is forced to be set.
374 * This is done in order to make sure that php-code from pagegen does not falsely
375 * clear the no_cache flag.
376 * @var bool
377 * @todo Define visibility
378 */
379 public $no_cacheBeforePageGen = FALSE;
380
381 /**
382 * This flag indicates if temporary content went into the cache during
383 * page-generation.
384 * @var mixed
385 * @todo Define visibility
386 */
387 public $tempContent = FALSE;
388
389 /**
390 * Passed to TypoScript template class and tells it to force template rendering
391 * @var bool
392 * @todo Define visibility
393 */
394 public $forceTemplateParsing = FALSE;
395
396 /**
397 * The array which cHash_calc is based on, see ->makeCacheHash().
398 * @var array
399 * @todo Define visibility
400 */
401 public $cHash_array = array();
402
403 /**
404 * May be set to the pagesTSconfig
405 * @var array
406 * @todo Define visibility
407 */
408 public $pagesTSconfig = '';
409
410 // PAGE-GENERATION / cOBJ
411
412 /**
413 * Eg. insert JS-functions in this array ($additionalHeaderData) to include them
414 * once. Use associative keys.
415 *
416 * Keys in use:
417 *
418 * JSFormValidate: <script type="text/javascript" src="'.$GLOBALS["TSFE"]->absRefPrefix.'typo3/sysext/frontend/Resources/Public/JavaScript/jsfunc.validateform.js"></script>
419 * JSincludeFormupdate: <script type="text/javascript" src="typo3/js/jsfunc.updateform.js"></script>
420 * JSMenuCode, JSMenuCode_menu: JavaScript for the JavaScript menu
421 * JSCode: reserved
422 * JSImgCode: reserved
423 *
424 * used to accumulate additional HTML-code for the header-section,
425 * <head>...</head>. Insert either associative keys (like
426 * additionalHeaderData['myStyleSheet'], see reserved keys above) or num-keys
427 * (like additionalHeaderData[] = '...')
428 *
429 * @var array
430 * @todo Define visibility
431 */
432 public $additionalHeaderData = array();
433
434 /**
435 * Used to accumulate additional HTML-code for the footer-section of the template
436 * @var array
437 */
438 public $additionalFooterData = array();
439
440 /**
441 * Used to accumulate additional JavaScript-code. Works like
442 * additionalHeaderData. Reserved keys at 'openPic' and 'mouseOver'
443 *
444 * @var array
445 * @todo Define visibility
446 */
447 public $additionalJavaScript = array();
448
449 /**
450 * Used to accumulate additional Style code. Works like additionalHeaderData.
451 *
452 * @var array
453 * @todo Define visibility
454 */
455 public $additionalCSS = array();
456
457 /**
458 * You can add JavaScript functions to each entry in these arrays. Please see
459 * how this is done in the GMENU_LAYERS script. The point is that many
460 * applications on a page can set handlers for onload, onmouseover and onmouseup
461 *
462 * @var array
463 * @todo Define visibility
464 */
465 public $JSeventFuncCalls = array(
466 'onmousemove' => array(),
467 'onmouseup' => array(),
468 'onkeydown' => array(),
469 'onkeyup' => array(),
470 'onkeypress' => array(),
471 'onload' => array(),
472 'onunload' => array()
473 );
474
475 /**
476 * Used to accumulate JavaScript loaded images (by menus)
477 * @var string
478 * @todo Define visibility
479 */
480 public $JSImgCode = '';
481
482 /**
483 * Used to accumulate DHTML-layers.
484 * @var string
485 * @todo Define visibility
486 */
487 public $divSection = '';
488
489 /**
490 * Default bodytag, if nothing else is set. This can be overridden by
491 * applications like TemplaVoila.
492 * @var string
493 * @todo Define visibility
494 */
495 public $defaultBodyTag = '<body>';
496
497 // RENDERING configuration, settings from TypoScript is loaded into these
498 // vars. See pagegen.php
499
500 /**
501 * Debug flag, may output special debug html-code.
502 * @var string
503 * @todo Define visibility
504 */
505 public $debug = '';
506
507 /**
508 * Default internal target
509 * @var string
510 * @todo Define visibility
511 */
512 public $intTarget = '';
513
514 /**
515 * Default external target
516 * @var string
517 * @todo Define visibility
518 */
519 public $extTarget = '';
520
521 /**
522 * Default file link target
523 * @var string
524 * @todo Define visibility
525 */
526 public $fileTarget = '';
527
528 /**
529 * Keys are page ids and values are default &MP (mount point) values to set
530 * when using the linking features...)
531 * @var array
532 * @todo Define visibility
533 */
534 public $MP_defaults = array();
535
536 /**
537 * If set, typolink() function encrypts email addresses. Is set in pagegen-class.
538 * @var string|int
539 * @todo Define visibility
540 */
541 public $spamProtectEmailAddresses = 0;
542
543 /**
544 * Absolute Reference prefix
545 * @var string
546 * @todo Define visibility
547 */
548 public $absRefPrefix = '';
549
550 /**
551 * Absolute Reference prefix force flag. This is set, if the type and id is
552 * retrieve from PATH_INFO and thus we NEED to prefix urls with at least '/'
553 * UNUSED in Core
554 *
555 * @var bool
556 * @deprecated since 6.2; will be removed two versions later
557 */
558 public $absRefPrefix_force = FALSE;
559
560 /**
561 * Factor for form-field widths compensation
562 * @var string
563 * @todo Define visibility
564 */
565 public $compensateFieldWidth = '';
566
567 /**
568 * Lock file path
569 * @var string
570 * @todo Define visibility
571 */
572 public $lockFilePath = '';
573
574 /**
575 * <A>-tag parameters
576 * @var string
577 * @todo Define visibility
578 */
579 public $ATagParams = '';
580
581 /**
582 * Search word regex, calculated if there has been search-words send. This is
583 * used to mark up the found search words on a page when jumped to from a link
584 * in a search-result.
585 * @var string
586 * @todo Define visibility
587 */
588 public $sWordRegEx = '';
589
590 /**
591 * Is set to the incoming array sword_list in case of a page-view jumped to from
592 * a search-result.
593 * @var string
594 * @todo Define visibility
595 */
596 public $sWordList = '';
597
598 /**
599 * A string prepared for insertion in all links on the page as url-parameters.
600 * Based on configuration in TypoScript where you defined which GET_VARS you
601 * would like to pass on.
602 * @var string
603 * @todo Define visibility
604 */
605 public $linkVars = '';
606
607 /**
608 * A string set with a comma list of additional GET vars which should NOT be
609 * included in the cHash calculation. These vars should otherwise be detected
610 * and involved in caching, eg. through a condition in TypoScript.
611 * @var string
612 * @todo Define visibility
613 */
614 public $excludeCHashVars = '';
615
616 /**
617 * If set, edit icons are rendered aside content records. Must be set only if
618 * the ->beUserLogin flag is set and set_no_cache() must be called as well.
619 * @var string
620 * @todo Define visibility
621 */
622 public $displayEditIcons = '';
623
624 /**
625 * If set, edit icons are rendered aside individual fields of content. Must be
626 * set only if the ->beUserLogin flag is set and set_no_cache() must be called as
627 * well.
628 * @var string
629 * @todo Define visibility
630 */
631 public $displayFieldEditIcons = '';
632
633 /**
634 * Site language, 0 (zero) is default, int+ is uid pointing to a sys_language
635 * record. Should reflect which language menus, templates etc is displayed in
636 * (master language) - but not necessarily the content which could be falling
637 * back to default (see sys_language_content)
638 * @var int
639 * @todo Define visibility
640 */
641 public $sys_language_uid = 0;
642
643 /**
644 * Site language mode for content fall back.
645 * @var string
646 * @todo Define visibility
647 */
648 public $sys_language_mode = '';
649
650 /**
651 * Site content selection uid (can be different from sys_language_uid if content
652 * is to be selected from a fall-back language. Depends on sys_language_mode)
653 * @var int
654 * @todo Define visibility
655 */
656 public $sys_language_content = 0;
657
658 /**
659 * Site content overlay flag; If set - and sys_language_content is > 0 - ,
660 * records selected will try to look for a translation pointing to their uid. (If
661 * configured in [ctrl][languageField] / [ctrl][transOrigP...]
662 * @var int
663 * @todo Define visibility
664 */
665 public $sys_language_contentOL = 0;
666
667 /**
668 * Is set to the iso code of the sys_language_content if that is properly defined
669 * by the sys_language record representing the sys_language_uid. (Requires the
670 * extension "static_info_tables")
671 * @var string
672 * @todo Define visibility
673 */
674 public $sys_language_isocode = '';
675
676 // RENDERING data
677
678 /**
679 * 'Global' Storage for various applications. Keys should be 'tx_'.extKey for
680 * extensions.
681 * @var array
682 * @todo Define visibility
683 */
684 public $applicationData = array();
685
686 /**
687 * @var array
688 * @todo Define visibility
689 */
690 public $register = array();
691
692 /**
693 * Stack used for storing array and retrieving register arrays (see
694 * LOAD_REGISTER and RESTORE_REGISTER)
695 * @var array
696 * @todo Define visibility
697 */
698 public $registerStack = array();
699
700 /**
701 * Checking that the function is not called eternally. This is done by
702 * interrupting at a depth of 50
703 * @var int
704 * @todo Define visibility
705 */
706 public $cObjectDepthCounter = 50;
707
708 /**
709 * Used by cObj->RECORDS and cObj->CONTENT to ensure the a records is NOT
710 * rendered twice through it!
711 * @var array
712 * @todo Define visibility
713 */
714 public $recordRegister = array();
715
716 /**
717 * This is set to the [table]:[uid] of the latest record rendered. Note that
718 * class ContentObjectRenderer has an equal value, but that is pointing to the
719 * record delivered in the $data-array of the ContentObjectRenderer instance, if
720 * the cObjects CONTENT or RECORD created that instance
721 * @var string
722 * @todo Define visibility
723 */
724 public $currentRecord = '';
725
726 /**
727 * Used by class \TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject
728 * to keep track of access-keys.
729 * @var array
730 * @todo Define visibility
731 */
732 public $accessKey = array();
733
734 /**
735 * Numerical array where image filenames are added if they are referenced in the
736 * rendered document. This includes only TYPO3 generated/inserted images.
737 * @var array
738 * @todo Define visibility
739 */
740 public $imagesOnPage = array();
741
742 /**
743 * Is set in ContentObjectRenderer->cImage() function to the info-array of the
744 * most recent rendered image. The information is used in
745 * ContentObjectRenderer->IMGTEXT
746 * @var array
747 * @todo Define visibility
748 */
749 public $lastImageInfo = array();
750
751 /**
752 * Used to generate page-unique keys. Point is that uniqid() functions is very
753 * slow, so a unikey key is made based on this, see function uniqueHash()
754 * @var int
755 * @todo Define visibility
756 */
757 public $uniqueCounter = 0;
758
759 /**
760 * @var string
761 * @todo Define visibility
762 */
763 public $uniqueString = '';
764
765 /**
766 * This value will be used as the title for the page in the indexer (if
767 * indexing happens)
768 * @var string
769 * @todo Define visibility
770 */
771 public $indexedDocTitle = '';
772
773 /**
774 * Alternative page title (normally the title of the page record). Can be set
775 * from applications you make.
776 * @var string
777 * @todo Define visibility
778 */
779 public $altPageTitle = '';
780
781 /**
782 * The base URL set for the page header.
783 * @var string
784 * @todo Define visibility
785 */
786 public $baseUrl = '';
787
788 /**
789 * The proper anchor prefix needed when using speaking urls. (only set if
790 * baseUrl is set)
791 * @var string
792 * @todo Define visibility
793 */
794 public $anchorPrefix = '';
795
796 /**
797 * IDs we already rendered for this page (to make sure they are unique)
798 * @var array
799 */
800 private $usedUniqueIds = array();
801
802 /**
803 * Page content render object
804 *
805 * @var \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
806 * @todo Define visibility
807 */
808 public $cObj = '';
809
810 // CONTENT accumulation
811
812 /**
813 * All page content is accumulated in this variable. See pagegen.php
814 * @var string
815 */
816 public $content = '';
817
818 // GENERAL
819
820 /**
821 * Set to the browser: net / msie if 4+ browsers
822 * @var string
823 * @todo Define visibility
824 */
825 public $clientInfo = '';
826
827 /**
828 * @var int
829 * @todo Define visibility
830 */
831 public $scriptParseTime = 0;
832
833 /**
834 * Character set (charset) conversion object:
835 * charset conversion class. May be used by any application.
836 *
837 * @var \TYPO3\CMS\Core\Charset\CharsetConverter
838 * @todo Define visibility
839 */
840 public $csConvObj;
841
842 /**
843 * The default charset used in the frontend if nothing else is set.
844 * @var string
845 * @todo Define visibility
846 */
847 public $defaultCharSet = 'utf-8';
848
849 /**
850 * Internal charset of the frontend during rendering. (Default: UTF-8)
851 * @var string
852 * @todo Define visibility
853 */
854 public $renderCharset = '';
855
856 /**
857 * Output charset of the websites content. This is the charset found in the
858 * header, meta tag etc. If different from $renderCharset a conversion
859 * happens before output to browser. Defaults to ->renderCharset if not set.
860 * @var string
861 * @todo Define visibility
862 */
863 public $metaCharset = '';
864
865 /**
866 * Assumed charset of locale strings.
867 * @var string
868 * @todo Define visibility
869 */
870 public $localeCharset = '';
871
872 // LANG
873
874 /**
875 * Set to the system language key (used on the site)
876 * @var string
877 * @todo Define visibility
878 */
879 public $lang = '';
880
881 /**
882 * @var array
883 * @todo Define visibility
884 */
885 public $LL_labels_cache = array();
886
887 /**
888 * @var array
889 * @todo Define visibility
890 */
891 public $LL_files_cache = array();
892
893 /**
894 * List of language dependencies for actual language. This is used for local
895 * variants of a language that depend on their "main" language, like Brazilian,
896 * Portuguese or Canadian French.
897 *
898 * @var array
899 */
900 protected $languageDependencies = array();
901
902 /**
903 * Locking object for accessing "cache_pagesection"
904 *
905 * @var \TYPO3\CMS\Core\Locking\Locker
906 * @todo Define visibility
907 */
908 public $pagesection_lockObj;
909
910 /**
911 * Locking object for accessing "cache_pages"
912 *
913 * @var \TYPO3\CMS\Core\Locking\Locker
914 * @todo Define visibility
915 */
916 public $pages_lockObj;
917
918 /**
919 * @var \TYPO3\CMS\Core\Page\PageRenderer
920 */
921 protected $pageRenderer;
922
923 /**
924 * The page cache object, use this to save pages to the cache and to
925 * retrieve them again
926 *
927 * @var \TYPO3\CMS\Core\Cache\Backend\AbstractBackend
928 */
929 protected $pageCache;
930
931 /**
932 * @var array
933 */
934 protected $pageCacheTags = array();
935
936 /**
937 * @var \TYPO3\CMS\Frontend\Page\CacheHashCalculator The cHash Service class used for cHash related functionality
938 */
939 protected $cacheHash;
940
941 /**
942 * Runtime cache of domains per processed page ids.
943 *
944 * @var array
945 */
946 protected $domainDataCache = array();
947
948 /**
949 * Class constructor
950 * Takes a number of GET/POST input variable as arguments and stores them internally.
951 * The processing of these variables goes on later in this class.
952 * Also sets internal clientInfo array (browser information) and a unique string (->uniqueString) for this script instance; A md5 hash of the microtime()
953 *
954 * @param array $TYPO3_CONF_VARS The global $TYPO3_CONF_VARS array. Will be set internally in ->TYPO3_CONF_VARS
955 * @param mixed $id The value of \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('id')
956 * @param integer $type The value of \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('type')
957 * @param bool|string $no_cache The value of \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('no_cache'), evaluated to 1/0
958 * @param string $cHash The value of \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('cHash')
959 * @param string $jumpurl The value of \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('jumpurl')
960 * @param string $MP The value of \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('MP')
961 * @param string $RDCT The value of \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('RDCT')
962 * @see index_ts.php
963 * @todo Define visibility
964 */
965 public function __construct($TYPO3_CONF_VARS, $id, $type, $no_cache = '', $cHash = '', $jumpurl = '', $MP = '', $RDCT = '') {
966 // Setting some variables:
967 $this->TYPO3_CONF_VARS = $TYPO3_CONF_VARS;
968 $this->id = $id;
969 $this->type = $type;
970 if ($no_cache) {
971 if ($this->TYPO3_CONF_VARS['FE']['disableNoCacheParameter']) {
972 $warning = '&no_cache=1 has been ignored because $TYPO3_CONF_VARS[\'FE\'][\'disableNoCacheParameter\'] is set!';
973 $GLOBALS['TT']->setTSlogMessage($warning, 2);
974 } else {
975 $warning = '&no_cache=1 has been supplied, so caching is disabled! URL: "' . GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL') . '"';
976 $this->disableCache();
977 }
978 GeneralUtility::sysLog($warning, 'cms', GeneralUtility::SYSLOG_SEVERITY_WARNING);
979 }
980 $this->cHash = $cHash;
981 $this->jumpurl = $jumpurl;
982 $this->MP = $this->TYPO3_CONF_VARS['FE']['enable_mount_pids'] ? (string) $MP : '';
983 $this->RDCT = $RDCT;
984 $this->clientInfo = GeneralUtility::clientInfo();
985 $this->uniqueString = md5(microtime());
986 $this->csConvObj = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Charset\\CharsetConverter');
987 // Call post processing function for constructor:
988 if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['tslib_fe-PostProc'])) {
989 $_params = array('pObj' => &$this);
990 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['tslib_fe-PostProc'] as $_funcRef) {
991 GeneralUtility::callUserFunction($_funcRef, $_params, $this);
992 }
993 }
994 $this->cacheHash = GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\Page\\CacheHashCalculator');
995 $this->initCaches();
996 }
997
998 /**
999 * Connect to SQL database. May exit after outputting an error message
1000 * or some JavaScript redirecting to the install tool.
1001 *
1002 * @throws \RuntimeException
1003 * @throws \TYPO3\CMS\Core\Error\Http\ServiceUnavailableException
1004 * @return void
1005 */
1006 public function connectToDB() {
1007 try {
1008 $GLOBALS['TYPO3_DB']->connectDB();
1009 } catch (\RuntimeException $exception) {
1010 switch ($exception->getCode()) {
1011 case 1270853883:
1012 // Cannot connect to current database
1013 $message = 'Cannot connect to the configured database "' . TYPO3_db . '"';
1014 if ($this->checkPageUnavailableHandler()) {
1015 $this->pageUnavailableAndExit($message);
1016 } else {
1017 GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
1018 throw new \TYPO3\CMS\Core\Error\Http\ServiceUnavailableException($message, 1301648782);
1019 }
1020 break;
1021 case 1270853884:
1022 // Username / password not accepted
1023 $message = 'The current username, password or host was not accepted when' . ' the connection to the database was attempted to be established!';
1024 if ($this->checkPageUnavailableHandler()) {
1025 $this->pageUnavailableAndExit($message);
1026 } else {
1027 GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
1028 throw new \TYPO3\CMS\Core\Error\Http\ServiceUnavailableException('Database Error: ' . $message, 1301648945);
1029 }
1030 break;
1031 default:
1032 throw $exception;
1033 }
1034 }
1035 // Call post processing function for DB connection:
1036 if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['connectToDB'])) {
1037 $_params = array('pObj' => &$this);
1038 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['connectToDB'] as $_funcRef) {
1039 GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1040 }
1041 }
1042 }
1043
1044 /**
1045 * Looks up the value of $this->RDCT in the database and if it is
1046 * found to be associated with a redirect URL then the redirection
1047 * is carried out with a 'Location:' header
1048 * May exit after sending a location-header.
1049 *
1050 * @return void
1051 */
1052 public function sendRedirect() {
1053 if ($this->RDCT) {
1054 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('params', 'cache_md5params', 'md5hash=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($this->RDCT, 'cache_md5params'));
1055 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
1056 $this->updateMD5paramsRecord($this->RDCT);
1057 header('Location: ' . $row['params']);
1058 die;
1059 }
1060 }
1061 }
1062
1063 /**
1064 * Gets instance of PageRenderer
1065 *
1066 * @return \TYPO3\CMS\Core\Page\PageRenderer
1067 */
1068 public function getPageRenderer() {
1069 if (!isset($this->pageRenderer)) {
1070 $this->pageRenderer = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Page\\PageRenderer');
1071 $this->pageRenderer->setTemplateFile(PATH_tslib . 'templates/tslib_page_frontend.html');
1072 $this->pageRenderer->setBackPath(TYPO3_mainDir);
1073 }
1074 return $this->pageRenderer;
1075 }
1076
1077 /**
1078 * This is needed for USER_INT processing
1079 *
1080 * @param \TYPO3\CMS\Core\Page\PageRenderer $pageRenderer
1081 */
1082 protected function setPageRenderer(\TYPO3\CMS\Core\Page\PageRenderer $pageRenderer) {
1083 $this->pageRenderer = $pageRenderer;
1084 }
1085
1086 /********************************************
1087 *
1088 * Initializing, resolving page id
1089 *
1090 ********************************************/
1091 /**
1092 * Initializes the caching system.
1093 *
1094 * @return void
1095 */
1096 protected function initCaches() {
1097 $this->pageCache = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Cache\\CacheManager')->getCache('cache_pages');
1098 }
1099
1100 /**
1101 * Initializes the front-end login user.
1102 *
1103 * @return void
1104 */
1105 public function initFEuser() {
1106 $this->fe_user = GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\Authentication\\FrontendUserAuthentication');
1107 $this->fe_user->lockIP = $this->TYPO3_CONF_VARS['FE']['lockIP'];
1108 $this->fe_user->checkPid = $this->TYPO3_CONF_VARS['FE']['checkFeUserPid'];
1109 $this->fe_user->lifetime = (int)$this->TYPO3_CONF_VARS['FE']['lifetime'];
1110 // List of pid's acceptable
1111 $pid = GeneralUtility::_GP('pid');
1112 $this->fe_user->checkPid_value = $pid ? $GLOBALS['TYPO3_DB']->cleanIntList($pid) : 0;
1113 // Check if a session is transferred:
1114 if (GeneralUtility::_GP('FE_SESSION_KEY')) {
1115 $fe_sParts = explode('-', GeneralUtility::_GP('FE_SESSION_KEY'));
1116 // If the session key hash check is OK:
1117 if (md5(($fe_sParts[0] . '/' . $this->TYPO3_CONF_VARS['SYS']['encryptionKey'])) === (string)$fe_sParts[1]) {
1118 $cookieName = \TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication::getCookieName();
1119 $_COOKIE[$cookieName] = $fe_sParts[0];
1120 if (isset($_SERVER['HTTP_COOKIE'])) {
1121 // See http://forge.typo3.org/issues/27740
1122 $_SERVER['HTTP_COOKIE'] .= ';' . $cookieName . '=' . $fe_sParts[0];
1123 }
1124 $this->fe_user->forceSetCookie = 1;
1125 unset($cookieName);
1126 }
1127 }
1128 $this->fe_user->start();
1129 $this->fe_user->unpack_uc('');
1130 // Gets session data
1131 $this->fe_user->fetchSessionData();
1132 $recs = GeneralUtility::_GP('recs');
1133 // If any record registration is submitted, register the record.
1134 if (is_array($recs)) {
1135 $this->fe_user->record_registration($recs, $this->TYPO3_CONF_VARS['FE']['maxSessionDataSize']);
1136 }
1137 // Call hook for possible manipulation of frontend user object
1138 if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['initFEuser'])) {
1139 $_params = array('pObj' => &$this);
1140 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['initFEuser'] as $_funcRef) {
1141 GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1142 }
1143 }
1144 // For every 60 seconds the is_online timestamp is updated.
1145 if (is_array($this->fe_user->user) && $this->fe_user->user['uid'] && $this->fe_user->user['is_online'] < $GLOBALS['EXEC_TIME'] - 60) {
1146 $GLOBALS['TYPO3_DB']->exec_UPDATEquery('fe_users', 'uid=' . (int)$this->fe_user->user['uid'], array('is_online' => $GLOBALS['EXEC_TIME']));
1147 }
1148 }
1149
1150 /**
1151 * Initializes the front-end user groups.
1152 * Sets ->loginUser and ->gr_list based on front-end user status.
1153 *
1154 * @return void
1155 * @todo Define visibility
1156 */
1157 public function initUserGroups() {
1158 // This affects the hidden-flag selecting the fe_groups for the user!
1159 $this->fe_user->showHiddenRecords = $this->showHiddenRecords;
1160 // no matter if we have an active user we try to fetch matching groups which can be set without an user (simulation for instance!)
1161 $this->fe_user->fetchGroupData();
1162 if (is_array($this->fe_user->user) && count($this->fe_user->groupData['uid'])) {
1163 // global flag!
1164 $this->loginUser = TRUE;
1165 // group -2 is not an existing group, but denotes a 'default' group when a user IS logged in. This is used to let elements be shown for all logged in users!
1166 $this->gr_list = '0,-2';
1167 $gr_array = $this->fe_user->groupData['uid'];
1168 } else {
1169 $this->loginUser = FALSE;
1170 // group -1 is not an existing group, but denotes a 'default' group when not logged in. This is used to let elements be hidden, when a user is logged in!
1171 $this->gr_list = '0,-1';
1172 if ($this->loginAllowedInBranch) {
1173 // For cases where logins are not banned from a branch usergroups can be set based on IP masks so we should add the usergroups uids.
1174 $gr_array = $this->fe_user->groupData['uid'];
1175 } else {
1176 // Set to blank since we will NOT risk any groups being set when no logins are allowed!
1177 $gr_array = array();
1178 }
1179 }
1180 // Clean up.
1181 // Make unique...
1182 $gr_array = array_unique($gr_array);
1183 // sort
1184 sort($gr_array);
1185 if (count($gr_array) && !$this->loginAllowedInBranch_mode) {
1186 $this->gr_list .= ',' . implode(',', $gr_array);
1187 }
1188 if ($this->fe_user->writeDevLog) {
1189 GeneralUtility::devLog('Valid usergroups for TSFE: ' . $this->gr_list, 'TYPO3\\CMS\\Frontend\\Controller\\TypoScriptFrontendController');
1190 }
1191 }
1192
1193 /**
1194 * Checking if a user is logged in or a group constellation different from "0,-1"
1195 *
1196 * @return boolean TRUE if either a login user is found (array fe_user->user) OR if the gr_list is set to something else than '0,-1' (could be done even without a user being logged in!)
1197 * @todo Define visibility
1198 */
1199 public function isUserOrGroupSet() {
1200 return is_array($this->fe_user->user) || $this->gr_list !== '0,-1';
1201 }
1202
1203 /**
1204 * Provides ways to bypass the '?id=[xxx]&type=[xx]' format, using either PATH_INFO or virtual HTML-documents (using Apache mod_rewrite)
1205 *
1206 * Two options:
1207 * 1) Use PATH_INFO (also Apache) to extract id and type from that var. Does not require any special modules compiled with apache. (less typical)
1208 * 2) Using hook which enables features like those provided from "realurl" extension (AKA "Speaking URLs")
1209 *
1210 * @return void
1211 */
1212 public function checkAlternativeIdMethods() {
1213 $this->siteScript = GeneralUtility::getIndpEnv('TYPO3_SITE_SCRIPT');
1214 // Call post processing function for custom URL methods.
1215 if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['checkAlternativeIdMethods-PostProc'])) {
1216 $_params = array('pObj' => &$this);
1217 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['checkAlternativeIdMethods-PostProc'] as $_funcRef) {
1218 GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1219 }
1220 }
1221 }
1222
1223 /**
1224 * Clears the preview-flags, sets sim_exec_time to current time.
1225 * Hidden pages must be hidden as default, $GLOBALS['SIM_EXEC_TIME'] is set to $GLOBALS['EXEC_TIME']
1226 * in bootstrap initializeGlobalTimeVariables(). Alter it by adding or subtracting seconds.
1227 *
1228 * @return void
1229 */
1230 public function clear_preview() {
1231 $this->showHiddenPage = FALSE;
1232 $this->showHiddenRecords = FALSE;
1233 $GLOBALS['SIM_EXEC_TIME'] = $GLOBALS['EXEC_TIME'];
1234 $GLOBALS['SIM_ACCESS_TIME'] = $GLOBALS['ACCESS_TIME'];
1235 $this->fePreview = 0;
1236 }
1237
1238 /**
1239 * Checks if a backend user is logged in
1240 *
1241 * @return boolean whether a backend user is logged in
1242 */
1243 public function isBackendUserLoggedIn() {
1244 return (bool)$this->beUserLogin;
1245 }
1246
1247 /**
1248 * Creates the backend user object and returns it.
1249 *
1250 * @return \TYPO3\CMS\Backend\FrontendBackendUserAuthentication the backend user object
1251 */
1252 public function initializeBackendUser() {
1253 // PRE BE_USER HOOK
1254 if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/index_ts.php']['preBeUser'])) {
1255 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/index_ts.php']['preBeUser'] as $_funcRef) {
1256 $_params = array();
1257 GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1258 }
1259 }
1260 /** @var $BE_USER \TYPO3\CMS\Backend\FrontendBackendUserAuthentication */
1261 $BE_USER = NULL;
1262 // If the backend cookie is set,
1263 // we proceed and check if a backend user is logged in.
1264 if ($_COOKIE[\TYPO3\CMS\Core\Authentication\BackendUserAuthentication::getCookieName()]) {
1265 $GLOBALS['TYPO3_MISC']['microtime_BE_USER_start'] = microtime(TRUE);
1266 $GLOBALS['TT']->push('Back End user initialized', '');
1267 // TODO: validate the comment below: is this necessary? if so,
1268 // formfield_status should be set to "" in \TYPO3\CMS\Backend\FrontendBackendUserAuthentication
1269 // which is a subclass of \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
1270 // ----
1271 // the value this->formfield_status is set to empty in order to
1272 // disable login-attempts to the backend account through this script
1273 // New backend user object
1274 $BE_USER = GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\FrontendBackendUserAuthentication');
1275 $BE_USER->OS = TYPO3_OS;
1276 $BE_USER->lockIP = $this->TYPO3_CONF_VARS['BE']['lockIP'];
1277 // Object is initialized
1278 $BE_USER->start();
1279 $BE_USER->unpack_uc('');
1280 if (!empty($BE_USER->user['uid'])) {
1281 $BE_USER->fetchGroupData();
1282 $this->beUserLogin = TRUE;
1283 }
1284 // Unset the user initialization.
1285 if (!$BE_USER->checkLockToIP() || !$BE_USER->checkBackendAccessSettingsFromInitPhp() || empty($BE_USER->user['uid'])) {
1286 $BE_USER = NULL;
1287 $this->beUserLogin = FALSE;
1288 $_SESSION['TYPO3-TT-start'] = FALSE;
1289 }
1290 $GLOBALS['TT']->pull();
1291 $GLOBALS['TYPO3_MISC']['microtime_BE_USER_end'] = microtime(TRUE);
1292 }
1293 // POST BE_USER HOOK
1294 if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/index_ts.php']['postBeUser'])) {
1295 $_params = array(
1296 'BE_USER' => &$BE_USER
1297 );
1298 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/index_ts.php']['postBeUser'] as $_funcRef) {
1299 GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1300 }
1301 }
1302 return $BE_USER;
1303 }
1304
1305 /**
1306 * Determines the id and evaluates any preview settings
1307 * Basically this function is about determining whether a backend user is logged in, if he has read access to the page and if he's previewing the page. That all determines which id to show and how to initialize the id.
1308 *
1309 * @return void
1310 */
1311 public function determineId() {
1312 // Call pre processing function for id determination
1313 if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['determineId-PreProcessing'])) {
1314 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['determineId-PreProcessing'] as $functionReference) {
1315 $parameters = array('parentObject' => $this);
1316 GeneralUtility::callUserFunction($functionReference, $parameters, $this);
1317 }
1318 }
1319 // Getting ARG-v values if some
1320 $this->setIDfromArgV();
1321 // If there is a Backend login we are going to check for any preview settings:
1322 $GLOBALS['TT']->push('beUserLogin', '');
1323 $originalFrontendUser = NULL;
1324 if ($this->beUserLogin || $this->doWorkspacePreview()) {
1325 // Backend user preview features:
1326 if ($this->beUserLogin && $GLOBALS['BE_USER']->adminPanel instanceof \TYPO3\CMS\Frontend\View\AdminPanelView) {
1327 $this->fePreview = (bool)$GLOBALS['BE_USER']->adminPanel->extGetFeAdminValue('preview');
1328 // If admin panel preview is enabled...
1329 if ($this->fePreview) {
1330 if ($this->fe_user->user) {
1331 $originalFrontendUser = $this->fe_user->user;
1332 }
1333 $this->showHiddenPage = (bool)$GLOBALS['BE_USER']->adminPanel->extGetFeAdminValue('preview', 'showHiddenPages');
1334 $this->showHiddenRecords = (bool)$GLOBALS['BE_USER']->adminPanel->extGetFeAdminValue('preview', 'showHiddenRecords');
1335 // Simulate date
1336 $simTime = $GLOBALS['BE_USER']->adminPanel->extGetFeAdminValue('preview', 'simulateDate');
1337 if ($simTime) {
1338 $GLOBALS['SIM_EXEC_TIME'] = $simTime;
1339 $GLOBALS['SIM_ACCESS_TIME'] = $simTime - $simTime % 60;
1340 }
1341 // simulate user
1342 $simUserGroup = $GLOBALS['BE_USER']->adminPanel->extGetFeAdminValue('preview', 'simulateUserGroup');
1343 $this->simUserGroup = $simUserGroup;
1344 if ($simUserGroup) {
1345 if ($this->fe_user->user) {
1346 $this->fe_user->user[$this->fe_user->usergroup_column] = $simUserGroup;
1347 } else {
1348 $this->fe_user->user = array(
1349 $this->fe_user->usergroup_column => $simUserGroup
1350 );
1351 }
1352 }
1353 if (!$simUserGroup && !$simTime && !$this->showHiddenPage && !$this->showHiddenRecords) {
1354 $this->fePreview = 0;
1355 }
1356 }
1357 }
1358 if ($this->id) {
1359 if ($this->determineIdIsHiddenPage()) {
1360 // The preview flag is set only if the current page turns out to actually be hidden!
1361 $this->fePreview = 1;
1362 $this->showHiddenPage = TRUE;
1363 }
1364 // For Live workspace: Check root line for proper connection to tree root (done because of possible preview of page / branch versions)
1365 if (!$this->fePreview && $this->whichWorkspace() === 0) {
1366 // Initialize the page-select functions to check rootline:
1367 $temp_sys_page = GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\Page\\PageRepository');
1368 $temp_sys_page->init($this->showHiddenPage);
1369 // If root line contained NO records and ->error_getRootLine_failPid tells us that it was because of a pid=-1 (indicating a "version" record)...:
1370 if (!count($temp_sys_page->getRootLine($this->id, $this->MP)) && $temp_sys_page->error_getRootLine_failPid == -1) {
1371 // Setting versioningPreview flag and try again:
1372 $temp_sys_page->versioningPreview = TRUE;
1373 if (count($temp_sys_page->getRootLine($this->id, $this->MP))) {
1374 // Finally, we got a root line (meaning that it WAS due to versioning preview of a page somewhere) and we set the fePreview flag which in itself will allow sys_page class to display previews of versionized records.
1375 $this->fePreview = 1;
1376 }
1377 }
1378 }
1379 }
1380 // The preview flag will be set if a backend user is in an offline workspace
1381 if (($GLOBALS['BE_USER']->user['workspace_preview'] || GeneralUtility::_GP('ADMCMD_view') || $this->doWorkspacePreview()) && ($this->whichWorkspace() === -1 || $this->whichWorkspace() > 0)) {
1382 // Will show special preview message.
1383 $this->fePreview = 2;
1384 }
1385 // If the front-end is showing a preview, caching MUST be disabled.
1386 if ($this->fePreview) {
1387 $this->disableCache();
1388 }
1389 }
1390 $GLOBALS['TT']->pull();
1391 // Now, get the id, validate access etc:
1392 $this->fetch_the_id();
1393 // Check if backend user has read access to this page. If not, recalculate the id.
1394 if ($this->beUserLogin && $this->fePreview) {
1395 if (!$GLOBALS['BE_USER']->doesUserHaveAccess($this->page, 1)) {
1396 // Resetting
1397 $this->clear_preview();
1398 $this->fe_user->user = $originalFrontendUser;
1399 // Fetching the id again, now with the preview settings reset.
1400 $this->fetch_the_id();
1401 }
1402 }
1403 // Checks if user logins are blocked for a certain branch and if so, will unset user login and re-fetch ID.
1404 $this->loginAllowedInBranch = $this->checkIfLoginAllowedInBranch();
1405 // Logins are not allowed:
1406 if (!$this->loginAllowedInBranch) {
1407 // Only if there is a login will we run this...
1408 if ($this->isUserOrGroupSet()) {
1409 if ($this->loginAllowedInBranch_mode == 'all') {
1410 // Clear out user and group:
1411 $this->fe_user->hideActiveLogin();
1412 $this->gr_list = '0,-1';
1413 } else {
1414 $this->gr_list = '0,-2';
1415 }
1416 // Fetching the id again, now with the preview settings reset.
1417 $this->fetch_the_id();
1418 }
1419 }
1420 // Final cleaning.
1421 // Make sure it's an integer
1422 $this->id = ($this->contentPid = (int)$this->id);
1423 // Make sure it's an integer
1424 $this->type = (int)$this->type;
1425 // Call post processing function for id determination:
1426 if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['determineId-PostProc'])) {
1427 $_params = array('pObj' => &$this);
1428 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['determineId-PostProc'] as $_funcRef) {
1429 GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1430 }
1431 }
1432 }
1433
1434 /**
1435 * Checks if the page is hidden in the active workspace.
1436 * If it is hidden, preview flags will be set.
1437 *
1438 * @return boolean
1439 */
1440 protected function determineIdIsHiddenPage() {
1441 $field = \TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($this->id) ? 'uid' : 'alias';
1442 $pageSelectCondition = $field . '=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($this->id, 'pages');
1443 $page = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('uid,hidden,starttime,endtime', 'pages', $pageSelectCondition . ' AND pid>=0 AND deleted=0');
1444 $workspace = $this->whichWorkspace();
1445 if ($workspace !== 0 && $workspace !== FALSE) {
1446 // Fetch overlay of page if in workspace and check if it is hidden
1447 $pageSelectObject = GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\Page\\PageRepository');
1448 $pageSelectObject->versioningPreview = TRUE;
1449 $pageSelectObject->init(FALSE);
1450 $targetPage = $pageSelectObject->getWorkspaceVersionOfRecord($this->whichWorkspace(), 'pages', $page['uid']);
1451 $result = $targetPage === -1 || $targetPage === -2;
1452 } else {
1453 $result = is_array($page) && ($page['hidden'] || $page['starttime'] > $GLOBALS['SIM_EXEC_TIME'] || $page['endtime'] != 0 && $page['endtime'] <= $GLOBALS['SIM_EXEC_TIME']);
1454 }
1455 return $result;
1456 }
1457
1458 /**
1459 * Get The Page ID
1460 * This gets the id of the page, checks if the page is in the domain and if the page is accessible
1461 * Sets variables such as $this->sys_page, $this->loginUser, $this->gr_list, $this->id, $this->type, $this->domainStartPage
1462 *
1463 * @throws \TYPO3\CMS\Core\Error\Http\ServiceUnavailableException
1464 * @return void
1465 * @access private
1466 * @todo Define visibility
1467 */
1468 public function fetch_the_id() {
1469 $GLOBALS['TT']->push('fetch_the_id initialize/', '');
1470 // Initialize the page-select functions.
1471 $this->sys_page = GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\Page\\PageRepository');
1472 $this->sys_page->versioningPreview = $this->fePreview === 2 || (int)$this->workspacePreview || (bool)GeneralUtility::_GP('ADMCMD_view');
1473 $this->sys_page->versioningWorkspaceId = $this->whichWorkspace();
1474 $this->sys_page->init($this->showHiddenPage);
1475 // Set the valid usergroups for FE
1476 $this->initUserGroups();
1477 // Sets sys_page where-clause
1478 $this->setSysPageWhereClause();
1479 // Splitting $this->id by a period (.).
1480 // First part is 'id' and second part (if exists) will overrule the &type param
1481 $idParts = explode('.', $this->id, 2);
1482 $this->id = $idParts[0];
1483 if (isset($idParts[1])) {
1484 $this->type = $idParts[1];
1485 }
1486
1487 // If $this->id is a string, it's an alias
1488 $this->checkAndSetAlias();
1489 // The id and type is set to the integer-value - just to be sure...
1490 $this->id = (int)$this->id;
1491 $this->type = (int)$this->type;
1492 $GLOBALS['TT']->pull();
1493 // We find the first page belonging to the current domain
1494 $GLOBALS['TT']->push('fetch_the_id domain/', '');
1495 // The page_id of the current domain
1496 $this->domainStartPage = $this->findDomainRecord($this->TYPO3_CONF_VARS['SYS']['recursiveDomainSearch']);
1497 if (!$this->id) {
1498 if ($this->domainStartPage) {
1499 // If the id was not previously set, set it to the id of the domain.
1500 $this->id = $this->domainStartPage;
1501 } else {
1502 // Find the first 'visible' page in that domain
1503 $theFirstPage = $this->sys_page->getFirstWebPage($this->id);
1504 if ($theFirstPage) {
1505 $this->id = $theFirstPage['uid'];
1506 } else {
1507 $message = 'No pages are found on the rootlevel!';
1508 if ($this->checkPageUnavailableHandler()) {
1509 $this->pageUnavailableAndExit($message);
1510 } else {
1511 GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
1512 throw new \TYPO3\CMS\Core\Error\Http\ServiceUnavailableException($message, 1301648975);
1513 }
1514 }
1515 }
1516 }
1517 $GLOBALS['TT']->pull();
1518 $GLOBALS['TT']->push('fetch_the_id rootLine/', '');
1519 // We store the originally requested id
1520 $requestedId = $this->id;
1521 $this->getPageAndRootlineWithDomain($this->domainStartPage);
1522 $GLOBALS['TT']->pull();
1523 if ($this->pageNotFound && $this->TYPO3_CONF_VARS['FE']['pageNotFound_handling']) {
1524 $pNotFoundMsg = array(
1525 1 => 'ID was not an accessible page',
1526 2 => 'Subsection was found and not accessible',
1527 3 => 'ID was outside the domain',
1528 4 => 'The requested page alias does not exist'
1529 );
1530 $this->pageNotFoundAndExit($pNotFoundMsg[$this->pageNotFound]);
1531 }
1532 if ($this->page['url_scheme'] > 0) {
1533 $newUrl = '';
1534 $requestUrlScheme = parse_url(GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL'), PHP_URL_SCHEME);
1535 if ((int)$this->page['url_scheme'] === HttpUtility::SCHEME_HTTP && $requestUrlScheme == 'https') {
1536 $newUrl = 'http://' . substr(GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL'), 8);
1537 } elseif ((int)$this->page['url_scheme'] === HttpUtility::SCHEME_HTTPS && $requestUrlScheme == 'http') {
1538 $newUrl = 'https://' . substr(GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL'), 7);
1539 }
1540 if ($newUrl !== '') {
1541 if ($_SERVER['REQUEST_METHOD'] === 'POST') {
1542 $headerCode = HttpUtility::HTTP_STATUS_303;
1543 } else {
1544 $headerCode = HttpUtility::HTTP_STATUS_301;
1545 }
1546 HttpUtility::redirect($newUrl, $headerCode);
1547 }
1548 }
1549 // Set no_cache if set
1550 if ($this->page['no_cache']) {
1551 $this->set_no_cache('no_cache is set in page properties');
1552 }
1553 // Init SYS_LASTCHANGED
1554 $this->register['SYS_LASTCHANGED'] = (int)$this->page['tstamp'];
1555 if ($this->register['SYS_LASTCHANGED'] < (int)$this->page['SYS_LASTCHANGED']) {
1556 $this->register['SYS_LASTCHANGED'] = (int)$this->page['SYS_LASTCHANGED'];
1557 }
1558 if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['fetchPageId-PostProcessing'])) {
1559 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['fetchPageId-PostProcessing'] as $functionReference) {
1560 $parameters = array('parentObject' => $this);
1561 GeneralUtility::callUserFunction($functionReference, $parameters, $this);
1562 }
1563 }
1564 }
1565
1566 /**
1567 * Gets the page and rootline arrays based on the id, $this->id
1568 *
1569 * If the id does not correspond to a proper page, the 'previous' valid page in the rootline is found
1570 * If the page is a shortcut (doktype=4), the ->id is loaded with that id
1571 *
1572 * Whether or not the ->id is changed to the shortcut id or the previous id in rootline (eg if a page is hidden), the ->page-array and ->rootline is found and must also be valid.
1573 *
1574 * Sets or manipulates internal variables such as: $this->id, $this->page, $this->rootLine, $this->MP, $this->pageNotFound
1575 *
1576 * @throws \TYPO3\CMS\Core\Error\Http\ServiceUnavailableException
1577 * @throws PageNotFoundException
1578 * @return void
1579 * @access private
1580 * @todo Define visibility
1581 */
1582 public function getPageAndRootline() {
1583 $this->page = $this->sys_page->getPage($this->id);
1584 if (!count($this->page)) {
1585 // If no page, we try to find the page before in the rootLine.
1586 // Page is 'not found' in case the id itself was not an accessible page. code 1
1587 $this->pageNotFound = 1;
1588 $this->rootLine = $this->sys_page->getRootLine($this->id, $this->MP);
1589 if (count($this->rootLine)) {
1590 $c = count($this->rootLine) - 1;
1591 while ($c > 0) {
1592 // Add to page access failure history:
1593 $this->pageAccessFailureHistory['direct_access'][] = $this->rootLine[$c];
1594 // Decrease to next page in rootline and check the access to that, if OK, set as page record and ID value.
1595 $c--;
1596 $this->id = $this->rootLine[$c]['uid'];
1597 $this->page = $this->sys_page->getPage($this->id);
1598 if (count($this->page)) {
1599 break;
1600 }
1601 }
1602 }
1603 // If still no page...
1604 if (!count($this->page)) {
1605 $message = 'The requested page does not exist!';
1606 if ($this->TYPO3_CONF_VARS['FE']['pageNotFound_handling']) {
1607 $this->pageNotFoundAndExit($message);
1608 } else {
1609 GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
1610 throw new PageNotFoundException($message, 1301648780);
1611 }
1612 }
1613 }
1614 // Spacer is not accessible in frontend
1615 if ($this->page['doktype'] == PageRepository::DOKTYPE_SPACER) {
1616 $message = 'The requested page does not exist!';
1617 if ($this->TYPO3_CONF_VARS['FE']['pageNotFound_handling']) {
1618 $this->pageNotFoundAndExit($message);
1619 } else {
1620 GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
1621 throw new PageNotFoundException($message, 1301648781);
1622 }
1623 }
1624 // Is the ID a link to another page??
1625 if ($this->page['doktype'] == PageRepository::DOKTYPE_SHORTCUT) {
1626 // We need to clear MP if the page is a shortcut. Reason is if the short cut goes to another page, then we LEAVE the rootline which the MP expects.
1627 $this->MP = '';
1628 // saving the page so that we can check later - when we know
1629 // about languages - whether we took the correct shortcut or
1630 // whether a translation of the page overwrites the shortcut
1631 // target and we need to follow the new target
1632 $this->originalShortcutPage = $this->page;
1633 $this->page = $this->getPageShortcut($this->page['shortcut'], $this->page['shortcut_mode'], $this->page['uid']);
1634 $this->id = $this->page['uid'];
1635 }
1636 // If the page is a mountpoint which should be overlaid with the contents of the mounted page,
1637 // it must never be accessible directly, but only in the mountpoint context. Therefore we change
1638 // the current ID and the user is redirected by checkPageForMountpointRedirect().
1639 if ($this->page['doktype'] == PageRepository::DOKTYPE_MOUNTPOINT && $this->page['mount_pid_ol']) {
1640 $this->originalMountPointPage = $this->page;
1641 $this->page = $this->sys_page->getPage($this->page['mount_pid']);
1642 if (empty($this->page)) {
1643 $message = 'This page (ID ' . $this->originalMountPointPage['uid'] . ') is of type "Mount point" and '
1644 . 'mounts a page which is not accessible (ID ' . $this->originalMountPointPage['mount_pid'] . ').';
1645 throw new PageNotFoundException($message, 1402043263);
1646 }
1647 $this->MP = $this->page['uid'] . '-' . $this->originalMountPointPage['uid'];
1648 $this->id = $this->page['uid'];
1649 }
1650 // Gets the rootLine
1651 $this->rootLine = $this->sys_page->getRootLine($this->id, $this->MP);
1652 // If not rootline we're off...
1653 if (!count($this->rootLine)) {
1654 $ws = $this->whichWorkspace();
1655 if ($this->sys_page->error_getRootLine_failPid == -1 && $ws) {
1656 $this->sys_page->versioningPreview = TRUE;
1657 $this->versioningWorkspaceId = $ws;
1658 $this->rootLine = $this->sys_page->getRootLine($this->id, $this->MP);
1659 }
1660 if (!count($this->rootLine)) {
1661 $message = 'The requested page didn\'t have a proper connection to the tree-root!';
1662 if ($this->checkPageUnavailableHandler()) {
1663 $this->pageUnavailableAndExit($message);
1664 } else {
1665 $rootline = '(' . $this->sys_page->error_getRootLine . ')';
1666 GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
1667 throw new \TYPO3\CMS\Core\Error\Http\ServiceUnavailableException($message . '<br /><br />' . $rootline, 1301648167);
1668 }
1669 }
1670 $this->fePreview = 1;
1671 }
1672 // Checking for include section regarding the hidden/starttime/endtime/fe_user (that is access control of a whole subbranch!)
1673 if ($this->checkRootlineForIncludeSection()) {
1674 if (!count($this->rootLine)) {
1675 $message = 'The requested page was not accessible!';
1676 if ($this->checkPageUnavailableHandler()) {
1677 $this->pageUnavailableAndExit($message);
1678 } else {
1679 GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
1680 throw new \TYPO3\CMS\Core\Error\Http\ServiceUnavailableException($message, 1301648234);
1681 }
1682 } else {
1683 $el = reset($this->rootLine);
1684 $this->id = $el['uid'];
1685 $this->page = $this->sys_page->getPage($this->id);
1686 $this->rootLine = $this->sys_page->getRootLine($this->id, $this->MP);
1687 }
1688 }
1689 }
1690
1691 /**
1692 * Get page shortcut; Finds the records pointed to by input value $SC (the shortcut value)
1693 *
1694 * @param integer $SC The value of the "shortcut" field from the pages record
1695 * @param integer $mode The shortcut mode: 1 will select first subpage, 2 a random subpage, 3 the parent page; default is the page pointed to by $SC
1696 * @param integer $thisUid The current page UID of the page which is a shortcut
1697 * @param integer $itera Safety feature which makes sure that the function is calling itself recursively max 20 times (since this function can find shortcuts to other shortcuts to other shortcuts...)
1698 * @param array $pageLog An array filled with previous page uids tested by the function - new page uids are evaluated against this to avoid going in circles.
1699 * @param bool $disableGroupCheck If true, the group check is disabled when fetching the target page (needed e.g. for menu generation)
1700 * @throws \RuntimeException
1701 * @throws PageNotFoundException
1702 * @return mixed Returns the page record of the page that the shortcut pointed to.
1703 * @access private
1704 * @see getPageAndRootline()
1705 * @todo Define visibility
1706 */
1707 public function getPageShortcut($SC, $mode, $thisUid, $itera = 20, $pageLog = array(), $disableGroupCheck = FALSE) {
1708 $idArray = GeneralUtility::intExplode(',', $SC);
1709 // Find $page record depending on shortcut mode:
1710 switch ($mode) {
1711 case PageRepository::SHORTCUT_MODE_FIRST_SUBPAGE:
1712
1713 case PageRepository::SHORTCUT_MODE_RANDOM_SUBPAGE:
1714 $pageArray = $this->sys_page->getMenu($idArray[0] ? $idArray[0] : $thisUid, '*', 'sorting', 'AND pages.doktype<199 AND pages.doktype!=' . PageRepository::DOKTYPE_BE_USER_SECTION);
1715 $pO = 0;
1716 if ($mode == PageRepository::SHORTCUT_MODE_RANDOM_SUBPAGE && count($pageArray)) {
1717 $randval = (int)rand(0, count($pageArray) - 1);
1718 $pO = $randval;
1719 }
1720 $c = 0;
1721 foreach ($pageArray as $pV) {
1722 if ($c == $pO) {
1723 $page = $pV;
1724 break;
1725 }
1726 $c++;
1727 }
1728 if (count($page) == 0) {
1729 $message = 'This page (ID ' . $thisUid . ') is of type "Shortcut" and configured to redirect to a subpage. ' . 'However, this page has no accessible subpages.';
1730 throw new PageNotFoundException($message, 1301648328);
1731 }
1732 break;
1733 case PageRepository::SHORTCUT_MODE_PARENT_PAGE:
1734 $parent = $this->sys_page->getPage($thisUid, $disableGroupCheck);
1735 $page = $this->sys_page->getPage($parent['pid'], $disableGroupCheck);
1736
1737 if (count($page) == 0) {
1738 $message = 'This page (ID ' . $thisUid . ') is of type "Shortcut" and configured to redirect to its parent page. ' . 'However, the parent page is not accessible.';
1739 throw new PageNotFoundException($message, 1301648358);
1740 }
1741 break;
1742 default:
1743 $page = $this->sys_page->getPage($idArray[0], $disableGroupCheck);
1744 if (count($page) == 0) {
1745 $message = 'This page (ID ' . $thisUid . ') is of type "Shortcut" and configured to redirect to a page, which is not accessible (ID ' . $idArray[0] . ').';
1746 throw new PageNotFoundException($message, 1301648404);
1747 }
1748 }
1749 // Check if short cut page was a shortcut itself, if so look up recursively:
1750 if ($page['doktype'] == PageRepository::DOKTYPE_SHORTCUT) {
1751 if (!in_array($page['uid'], $pageLog) && $itera > 0) {
1752 $pageLog[] = $page['uid'];
1753 $page = $this->getPageShortcut($page['shortcut'], $page['shortcut_mode'], $page['uid'], $itera - 1, $pageLog, $disableGroupCheck);
1754 } else {
1755 $pageLog[] = $page['uid'];
1756 $message = 'Page shortcuts were looping in uids ' . implode(',', $pageLog) . '...!';
1757 GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
1758 throw new \RuntimeException($message, 1294587212);
1759 }
1760 }
1761 // Return resulting page:
1762 return $page;
1763 }
1764
1765 /**
1766 * Checks the current rootline for defined sections.
1767 *
1768 * @return boolean
1769 * @access private
1770 * @todo Define visibility
1771 */
1772 public function checkRootlineForIncludeSection() {
1773 $c = count($this->rootLine);
1774 $removeTheRestFlag = 0;
1775 for ($a = 0; $a < $c; $a++) {
1776 if (!$this->checkPagerecordForIncludeSection($this->rootLine[$a])) {
1777 // Add to page access failure history:
1778 $this->pageAccessFailureHistory['sub_section'][] = $this->rootLine[$a];
1779 $removeTheRestFlag = 1;
1780 }
1781 if ($this->rootLine[$a]['doktype'] == PageRepository::DOKTYPE_BE_USER_SECTION) {
1782 // If there is a backend user logged in, check if he has read access to the page:
1783 if ($this->beUserLogin) {
1784 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', 'pages', 'uid=' . (int)$this->id . ' AND ' . $GLOBALS['BE_USER']->getPagePermsClause(1));
1785 // versionOL()?
1786 list($isPage) = $GLOBALS['TYPO3_DB']->sql_fetch_row($res);
1787 if (!$isPage) {
1788 // If there was no page selected, the user apparently did not have read access to the current PAGE (not position in rootline) and we set the remove-flag...
1789 $removeTheRestFlag = 1;
1790 }
1791 } else {
1792 // Dont go here, if there is no backend user logged in.
1793 $removeTheRestFlag = 1;
1794 }
1795 }
1796 if ($removeTheRestFlag) {
1797 // Page is 'not found' in case a subsection was found and not accessible, code 2
1798 $this->pageNotFound = 2;
1799 unset($this->rootLine[$a]);
1800 }
1801 }
1802 return $removeTheRestFlag;
1803 }
1804
1805 /**
1806 * Checks page record for enableFields
1807 * Returns TRUE if enableFields does not disable the page record.
1808 * Takes notice of the ->showHiddenPage flag and uses SIM_ACCESS_TIME for start/endtime evaluation
1809 *
1810 * @param array $row The page record to evaluate (needs fields: hidden, starttime, endtime, fe_group)
1811 * @param boolean $bypassGroupCheck Bypass group-check
1812 * @return boolean TRUE, if record is viewable.
1813 * @see TYPO3\\CMS\\Frontend\\ContentObject\\ContentObjectRenderer::getTreeList(), checkPagerecordForIncludeSection()
1814 * @todo Define visibility
1815 */
1816 public function checkEnableFields($row, $bypassGroupCheck = FALSE) {
1817 if (isset($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['hook_checkEnableFields']) && is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['hook_checkEnableFields'])) {
1818 $_params = array('pObj' => $this, 'row' => &$row, 'bypassGroupCheck' => &$bypassGroupCheck);
1819 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['hook_checkEnableFields'] as $_funcRef) {
1820 // Call hooks: If one returns FALSE, method execution is aborted with result "This record is not available"
1821 $return = GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1822 if ($return === FALSE) {
1823 return FALSE;
1824 }
1825 }
1826 }
1827 if ((!$row['hidden'] || $this->showHiddenPage) && $row['starttime'] <= $GLOBALS['SIM_ACCESS_TIME'] && ($row['endtime'] == 0 || $row['endtime'] > $GLOBALS['SIM_ACCESS_TIME']) && ($bypassGroupCheck || $this->checkPageGroupAccess($row))) {
1828 return TRUE;
1829 }
1830 }
1831
1832 /**
1833 * Check group access against a page record
1834 *
1835 * @param array $row The page record to evaluate (needs field: fe_group)
1836 * @param mixed $groupList List of group id's (comma list or array). Default is $this->gr_list
1837 * @return boolean TRUE, if group access is granted.
1838 * @access private
1839 * @todo Define visibility
1840 */
1841 public function checkPageGroupAccess($row, $groupList = NULL) {
1842 if (is_null($groupList)) {
1843 $groupList = $this->gr_list;
1844 }
1845 if (!is_array($groupList)) {
1846 $groupList = explode(',', $groupList);
1847 }
1848 $pageGroupList = explode(',', $row['fe_group'] ?: 0);
1849 return count(array_intersect($groupList, $pageGroupList)) > 0;
1850 }
1851
1852 /**
1853 * Checks page record for include section
1854 *
1855 * @param array $row The page record to evaluate (needs fields: extendToSubpages + hidden, starttime, endtime, fe_group)
1856 * @return boolean Returns TRUE if either extendToSubpages is not checked or if the enableFields does not disable the page record.
1857 * @access private
1858 * @see checkEnableFields(), TYPO3\\CMS\\Frontend\\ContentObject\\ContentObjectRenderer::getTreeList(), checkRootlineForIncludeSection()
1859 * @todo Define visibility
1860 */
1861 public function checkPagerecordForIncludeSection($row) {
1862 return !$row['extendToSubpages'] || $this->checkEnableFields($row) ? 1 : 0;
1863 }
1864
1865 /**
1866 * Checks if logins are allowed in the current branch of the page tree. Traverses the full root line and returns TRUE if logins are OK, otherwise FALSE (and then the login user must be unset!)
1867 *
1868 * @return boolean returns TRUE if logins are OK, otherwise FALSE (and then the login user must be unset!)
1869 * @todo Define visibility
1870 */
1871 public function checkIfLoginAllowedInBranch() {
1872 // Initialize:
1873 $c = count($this->rootLine);
1874 $disable = FALSE;
1875 // Traverse root line from root and outwards:
1876 for ($a = 0; $a < $c; $a++) {
1877 // If a value is set for login state:
1878 if ($this->rootLine[$a]['fe_login_mode'] > 0) {
1879 // Determine state from value:
1880 if ((int)$this->rootLine[$a]['fe_login_mode'] === 1) {
1881 $disable = TRUE;
1882 $this->loginAllowedInBranch_mode = 'all';
1883 } elseif ((int)$this->rootLine[$a]['fe_login_mode'] === 3) {
1884 $disable = TRUE;
1885 $this->loginAllowedInBranch_mode = 'groups';
1886 } else {
1887 $disable = FALSE;
1888 }
1889 }
1890 }
1891 return !$disable;
1892 }
1893
1894 /**
1895 * Analysing $this->pageAccessFailureHistory into a summary array telling which features disabled display and on which pages and conditions. That data can be used inside a page-not-found handler
1896 *
1897 * @return array Summary of why page access was not allowed.
1898 * @todo Define visibility
1899 */
1900 public function getPageAccessFailureReasons() {
1901 $output = array();
1902 $combinedRecords = array_merge(is_array($this->pageAccessFailureHistory['direct_access']) ? $this->pageAccessFailureHistory['direct_access'] : array(array('fe_group' => 0)), is_array($this->pageAccessFailureHistory['sub_section']) ? $this->pageAccessFailureHistory['sub_section'] : array());
1903 if (count($combinedRecords)) {
1904 foreach ($combinedRecords as $k => $pagerec) {
1905 // If $k=0 then it is the very first page the original ID was pointing at and that will get a full check of course
1906 // If $k>0 it is parent pages being tested. They are only significant for the access to the first page IF they had the extendToSubpages flag set, hence checked only then!
1907 if (!$k || $pagerec['extendToSubpages']) {
1908 if ($pagerec['hidden']) {
1909 $output['hidden'][$pagerec['uid']] = TRUE;
1910 }
1911 if ($pagerec['starttime'] > $GLOBALS['SIM_ACCESS_TIME']) {
1912 $output['starttime'][$pagerec['uid']] = $pagerec['starttime'];
1913 }
1914 if ($pagerec['endtime'] != 0 && $pagerec['endtime'] <= $GLOBALS['SIM_ACCESS_TIME']) {
1915 $output['endtime'][$pagerec['uid']] = $pagerec['endtime'];
1916 }
1917 if (!$this->checkPageGroupAccess($pagerec)) {
1918 $output['fe_group'][$pagerec['uid']] = $pagerec['fe_group'];
1919 }
1920 }
1921 }
1922 }
1923 return $output;
1924 }
1925
1926 /**
1927 * This checks if there are ARGV-parameters in the QUERY_STRING and if so, those are used for the id
1928 * $this->id must be 'FALSE' in order for any processing to happen in here
1929 * If an id/alias value is extracted from the QUERY_STRING it is set in $this->id
1930 *
1931 * @return void
1932 * @access private
1933 * @todo Define visibility
1934 */
1935 public function setIDfromArgV() {
1936 if (!$this->id) {
1937 list($theAlias) = explode('&', GeneralUtility::getIndpEnv('QUERY_STRING'));
1938 $theAlias = trim($theAlias);
1939 $this->id = $theAlias != '' && strpos($theAlias, '=') === FALSE ? $theAlias : 0;
1940 }
1941 }
1942
1943 /**
1944 * Gets ->page and ->rootline information based on ->id. ->id may change during this operation.
1945 * If not inside domain, then default to first page in domain.
1946 *
1947 * @param integer $domainStartPage Page uid of the page where the found domain record is (pid of the domain record)
1948 * @return void
1949 * @access private
1950 * @todo Define visibility
1951 */
1952 public function getPageAndRootlineWithDomain($domainStartPage) {
1953 $this->getPageAndRootline();
1954 // Checks if the $domain-startpage is in the rootLine. This is necessary so that references to page-id's from other domains are not possible.
1955 if ($domainStartPage && is_array($this->rootLine)) {
1956 $idFound = 0;
1957 foreach ($this->rootLine as $key => $val) {
1958 if ($val['uid'] == $domainStartPage) {
1959 $idFound = 1;
1960 break;
1961 }
1962 }
1963 if (!$idFound) {
1964 // Page is 'not found' in case the id was outside the domain, code 3
1965 $this->pageNotFound = 3;
1966 $this->id = $domainStartPage;
1967 // re-get the page and rootline if the id was not found.
1968 $this->getPageAndRootline();
1969 }
1970 }
1971 }
1972
1973 /**
1974 * Sets sys_page where-clause
1975 *
1976 * @return void
1977 * @access private
1978 * @todo Define visibility
1979 */
1980 public function setSysPageWhereClause() {
1981 $this->sys_page->where_hid_del .= ' AND pages.doktype<200';
1982 $this->sys_page->where_groupAccess = $this->sys_page->getMultipleGroupsWhereClause('pages.fe_group', 'pages');
1983 }
1984
1985 /**
1986 * Looking up a domain record based on HTTP_HOST
1987 *
1988 * @param boolean $recursive If set, it looks "recursively" meaning that a domain like "123.456.typo3.com" would find a domain record like "typo3.com" if "123.456.typo3.com" or "456.typo3.com" did not exist.
1989 * @return integer Returns the page id of the page where the domain record was found.
1990 * @access private
1991 * @todo Define visibility
1992 */
1993 public function findDomainRecord($recursive = FALSE) {
1994 if ($recursive) {
1995 $host = explode('.', GeneralUtility::getIndpEnv('HTTP_HOST'));
1996 while (count($host)) {
1997 $pageUid = $this->sys_page->getDomainStartPage(implode('.', $host), GeneralUtility::getIndpEnv('SCRIPT_NAME'), GeneralUtility::getIndpEnv('REQUEST_URI'));
1998 if ($pageUid) {
1999 return $pageUid;
2000 } else {
2001 array_shift($host);
2002 }
2003 }
2004 return $pageUid;
2005 } else {
2006 return $this->sys_page->getDomainStartPage(GeneralUtility::getIndpEnv('HTTP_HOST'), GeneralUtility::getIndpEnv('SCRIPT_NAME'), GeneralUtility::getIndpEnv('REQUEST_URI'));
2007 }
2008 }
2009
2010 /**
2011 * Page unavailable handler for use in frontend plugins from extensions.
2012 *
2013 * @param string $reason Reason text
2014 * @param string $header HTTP header to send
2015 * @return void Function exits.
2016 */
2017 public function pageUnavailableAndExit($reason = '', $header = '') {
2018 $header = $header ?: $this->TYPO3_CONF_VARS['FE']['pageUnavailable_handling_statheader'];
2019 $this->pageUnavailableHandler($this->TYPO3_CONF_VARS['FE']['pageUnavailable_handling'], $header, $reason);
2020 die;
2021 }
2022
2023 /**
2024 * Page-not-found handler for use in frontend plugins from extensions.
2025 *
2026 * @param string $reason Reason text
2027 * @param string $header HTTP header to send
2028 * @return void Function exits.
2029 * @todo Define visibility
2030 */
2031 public function pageNotFoundAndExit($reason = '', $header = '') {
2032 $header = $header ?: $this->TYPO3_CONF_VARS['FE']['pageNotFound_handling_statheader'];
2033 $this->pageNotFoundHandler($this->TYPO3_CONF_VARS['FE']['pageNotFound_handling'], $header, $reason);
2034 die;
2035 }
2036
2037 /**
2038 * Checks whether the pageUnavailableHandler should be used. To be used, pageUnavailable_handling must be set
2039 * and devIPMask must not match the current visitor's IP address.
2040 *
2041 * @return boolean TRUE/FALSE whether the pageUnavailable_handler should be used.
2042 * @todo Define visibility
2043 */
2044 public function checkPageUnavailableHandler() {
2045 if (
2046 $this->TYPO3_CONF_VARS['FE']['pageUnavailable_handling']
2047 && !GeneralUtility::cmpIP(
2048 GeneralUtility::getIndpEnv('REMOTE_ADDR'),
2049 $this->TYPO3_CONF_VARS['SYS']['devIPmask']
2050 )
2051 ) {
2052 $checkPageUnavailableHandler = TRUE;
2053 } else {
2054 $checkPageUnavailableHandler = FALSE;
2055 }
2056 return $checkPageUnavailableHandler;
2057 }
2058
2059 /**
2060 * Page unavailable handler. Acts a wrapper for the pageErrorHandler method.
2061 *
2062 * @param mixed $code Which type of handling; If a true PHP-boolean or TRUE then a \TYPO3\CMS\Core\Messaging\ErrorpageMessage is outputted. If integer an error message with that number is shown. Otherwise the $code value is expected to be a "Location:" header value.
2063 * @param string $header If set, this is passed directly to the PHP function, header()
2064 * @param string $reason If set, error messages will also mention this as the reason for the page-not-found.
2065 * @return void (The function exits!)
2066 * @todo Define visibility
2067 */
2068 public function pageUnavailableHandler($code, $header, $reason) {
2069 $this->pageErrorHandler($code, $header, $reason);
2070 }
2071
2072 /**
2073 * Page not found handler. Acts a wrapper for the pageErrorHandler method.
2074 *
2075 * @param mixed $code Which type of handling; If a true PHP-boolean or TRUE then a \TYPO3\CMS\Core\Messaging\ErrorpageMessage is outputted. If integer an error message with that number is shown. Otherwise the $code value is expected to be a "Location:" header value.
2076 * @param string $header If set, this is passed directly to the PHP function, header()
2077 * @param string $reason If set, error messages will also mention this as the reason for the page-not-found.
2078 * @return void (The function exits!)
2079 * @todo Define visibility
2080 */
2081 public function pageNotFoundHandler($code, $header = '', $reason = '') {
2082 $this->pageErrorHandler($code, $header, $reason);
2083 }
2084
2085 /**
2086 * Generic error page handler.
2087 * Exits.
2088 *
2089 * @param mixed $code Which type of handling; If a true PHP-boolean or TRUE then a \TYPO3\CMS\Core\Messaging\ErrorpageMessage is outputted. If integer an error message with that number is shown. Otherwise the $code value is expected to be a "Location:" header value.
2090 * @param string $header If set, this is passed directly to the PHP function, header()
2091 * @param string $reason If set, error messages will also mention this as the reason for the page-not-found.
2092 * @throws \RuntimeException
2093 * @return void (The function exits!)
2094 * @todo Define visibility
2095 */
2096 public function pageErrorHandler($code, $header = '', $reason = '') {
2097 // Issue header in any case:
2098 if ($header) {
2099 $headerArr = preg_split('/\\r|\\n/', $header, -1, PREG_SPLIT_NO_EMPTY);
2100 foreach ($headerArr as $header) {
2101 header($header);
2102 }
2103 }
2104 // Create response:
2105 // Simply boolean; Just shows TYPO3 error page with reason:
2106 if (gettype($code) == 'boolean' || (string)$code === '1') {
2107 $title = 'Page Not Found';
2108 $message = 'The page did not exist or was inaccessible.' . ($reason ? ' Reason: ' . htmlspecialchars($reason) : '');
2109 $messagePage = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Messaging\\ErrorpageMessage', $message, $title);
2110 $messagePage->output();
2111 die;
2112 } elseif (GeneralUtility::isFirstPartOfStr($code, 'USER_FUNCTION:')) {
2113 $funcRef = trim(substr($code, 14));
2114 $params = array(
2115 'currentUrl' => GeneralUtility::getIndpEnv('REQUEST_URI'),
2116 'reasonText' => $reason,
2117 'pageAccessFailureReasons' => $this->getPageAccessFailureReasons()
2118 );
2119 echo GeneralUtility::callUserFunction($funcRef, $params, $this);
2120 } elseif (GeneralUtility::isFirstPartOfStr($code, 'READFILE:')) {
2121 $readFile = GeneralUtility::getFileAbsFileName(trim(substr($code, 9)));
2122 if (@is_file($readFile)) {
2123 echo str_replace(
2124 array(
2125 '###CURRENT_URL###',
2126 '###REASON###'
2127 ),
2128 array(
2129 GeneralUtility::getIndpEnv('REQUEST_URI'),
2130 htmlspecialchars($reason)
2131 ),
2132 GeneralUtility::getUrl($readFile)
2133 );
2134 } else {
2135 throw new \RuntimeException('Configuration Error: 404 page "' . $readFile . '" could not be found.', 1294587214);
2136 }
2137 } elseif (GeneralUtility::isFirstPartOfStr($code, 'REDIRECT:')) {
2138 HttpUtility::redirect(substr($code, 9));
2139 } elseif (strlen($code)) {
2140 // Check if URL is relative
2141 $url_parts = parse_url($code);
2142 if ($url_parts['host'] == '') {
2143 $url_parts['host'] = GeneralUtility::getIndpEnv('HTTP_HOST');
2144 if ($code[0] === '/') {
2145 $code = GeneralUtility::getIndpEnv('TYPO3_REQUEST_HOST') . $code;
2146 } else {
2147 $code = GeneralUtility::getIndpEnv('TYPO3_REQUEST_DIR') . $code;
2148 }
2149 $checkBaseTag = FALSE;
2150 } else {
2151 $checkBaseTag = TRUE;
2152 }
2153 // Check recursion
2154 if ($code == GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL')) {
2155 if ($reason == '') {
2156 $reason = 'Page cannot be found.';
2157 }
2158 $reason .= LF . LF . 'Additionally, ' . $code . ' was not found while trying to retrieve the error document.';
2159 throw new \RuntimeException(nl2br(htmlspecialchars($reason)), 1294587215);
2160 }
2161 // Prepare headers
2162 $headerArr = array(
2163 'User-agent: ' . GeneralUtility::getIndpEnv('HTTP_USER_AGENT'),
2164 'Referer: ' . GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL')
2165 );
2166 $res = GeneralUtility::getUrl($code, 1, $headerArr);
2167 // Header and content are separated by an empty line
2168 list($header, $content) = explode(CRLF . CRLF, $res, 2);
2169 $content .= CRLF;
2170 if (FALSE === $res) {
2171 // Last chance -- redirect
2172 HttpUtility::redirect($code);
2173 } else {
2174 // Forward these response headers to the client
2175 $forwardHeaders = array(
2176 'Content-Type:'
2177 );
2178 $headerArr = preg_split('/\\r|\\n/', $header, -1, PREG_SPLIT_NO_EMPTY);
2179 foreach ($headerArr as $header) {
2180 foreach ($forwardHeaders as $h) {
2181 if (preg_match('/^' . $h . '/', $header)) {
2182 header($header);
2183 }
2184 }
2185 }
2186 // Put <base> if necesary
2187 if ($checkBaseTag) {
2188 // If content already has <base> tag, we do not need to do anything
2189 if (FALSE === stristr($content, '<base ')) {
2190 // Generate href for base tag
2191 $base = $url_parts['scheme'] . '://';
2192 if ($url_parts['user'] != '') {
2193 $base .= $url_parts['user'];
2194 if ($url_parts['pass'] != '') {
2195 $base .= ':' . $url_parts['pass'];
2196 }
2197 $base .= '@';
2198 }
2199 $base .= $url_parts['host'];
2200 // Add path portion skipping possible file name
2201 $base .= preg_replace('/(.*\\/)[^\\/]*/', '${1}', $url_parts['path']);
2202 // Put it into content (generate also <head> if necessary)
2203 $replacement = LF . '<base href="' . htmlentities($base) . '" />' . LF;
2204 if (stristr($content, '<head>')) {
2205 $content = preg_replace('/(<head>)/i', '\\1' . $replacement, $content);
2206 } else {
2207 $content = preg_replace('/(<html[^>]*>)/i', '\\1<head>' . $replacement . '</head>', $content);
2208 }
2209 }
2210 }
2211 // Output the content
2212 echo $content;
2213 }
2214 } else {
2215 $title = 'Page Not Found';
2216 $message = $reason ? 'Reason: ' . htmlspecialchars($reason) : 'Page cannot be found.';
2217 $messagePage = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Messaging\\ErrorpageMessage', $message, $title);
2218 $messagePage->output();
2219 }
2220 die;
2221 }
2222
2223 /**
2224 * Fetches the integer page id for a page alias.
2225 * Looks if ->id is not an integer and if so it will search for a page alias and if found the page uid of that page is stored in $this->id
2226 *
2227 * @return void
2228 * @access private
2229 * @todo Define visibility
2230 */
2231 public function checkAndSetAlias() {
2232 if ($this->id && !\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($this->id)) {
2233 $aid = $this->sys_page->getPageIdFromAlias($this->id);
2234 if ($aid) {
2235 $this->id = $aid;
2236 } else {
2237 $this->pageNotFound = 4;
2238 }
2239 }
2240 }
2241
2242 /**
2243 * Merging values into the global $_GET
2244 *
2245 * @param array $GET_VARS Array of key/value pairs that will be merged into the current GET-vars. (Non-escaped values)
2246 * @return void
2247 * @todo Define visibility
2248 */
2249 public function mergingWithGetVars($GET_VARS) {
2250 if (is_array($GET_VARS)) {
2251 // Getting $_GET var, unescaped.
2252 $realGet = GeneralUtility::_GET();
2253 if (!is_array($realGet)) {
2254 $realGet = array();
2255 }
2256 // Merge new values on top:
2257 \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($realGet, $GET_VARS);
2258 // Write values back to $_GET:
2259 GeneralUtility::_GETset($realGet);
2260 // Setting these specifically (like in the init-function):
2261 if (isset($GET_VARS['type'])) {
2262 $this->type = (int)$GET_VARS['type'];
2263 }
2264 if (isset($GET_VARS['cHash'])) {
2265 $this->cHash = $GET_VARS['cHash'];
2266 }
2267 if (isset($GET_VARS['jumpurl'])) {
2268 $this->jumpurl = $GET_VARS['jumpurl'];
2269 }
2270 if (isset($GET_VARS['MP'])) {
2271 $this->MP = $this->TYPO3_CONF_VARS['FE']['enable_mount_pids'] ? $GET_VARS['MP'] : '';
2272 }
2273 if (isset($GET_VARS['no_cache']) && $GET_VARS['no_cache']) {
2274 $this->set_no_cache('no_cache is requested via GET parameter');
2275 }
2276 }
2277 }
2278
2279 /********************************************
2280 *
2281 * Template and caching related functions.
2282 *
2283 *******************************************/
2284 /**
2285 * Calculates a hash string based on additional parameters in the url.
2286 *
2287 * Calculated hash is stored in $this->cHash_array.
2288 * This is used to cache pages with more parameters than just id and type.
2289 *
2290 * @return void
2291 * @see reqCHash()
2292 */
2293 public function makeCacheHash() {
2294 // No need to test anything if caching was already disabled.
2295 if ($this->no_cache && !$this->TYPO3_CONF_VARS['FE']['pageNotFoundOnCHashError']) {
2296 return;
2297 }
2298 $GET = GeneralUtility::_GET();
2299 if ($this->cHash && is_array($GET)) {
2300 $this->cHash_array = $this->cacheHash->getRelevantParameters(GeneralUtility::implodeArrayForUrl('', $GET));
2301 $cHash_calc = $this->cacheHash->calculateCacheHash($this->cHash_array);
2302 if ($cHash_calc != $this->cHash) {
2303 if ($this->TYPO3_CONF_VARS['FE']['pageNotFoundOnCHashError']) {
2304 $this->pageNotFoundAndExit('Request parameters could not be validated (&cHash comparison failed)');
2305 } else {
2306 $this->disableCache();
2307 $GLOBALS['TT']->setTSlogMessage('The incoming cHash "' . $this->cHash . '" and calculated cHash "' . $cHash_calc . '" did not match, so caching was disabled. The fieldlist used was "' . implode(',', array_keys($this->cHash_array)) . '"', 2);
2308 }
2309 }
2310 } elseif (is_array($GET)) {
2311 // No cHash is set, check if that is correct
2312 if ($this->cacheHash->doParametersRequireCacheHash(GeneralUtility::implodeArrayForUrl('', $GET))) {
2313 $this->reqCHash();
2314 }
2315 }
2316 }
2317
2318 /**
2319 * Will disable caching if the cHash value was not set.
2320 * This function should be called to check the _existence_ of "&cHash" whenever a plugin generating cachable output is using extra GET variables. If there _is_ a cHash value the validation of it automatically takes place in makeCacheHash() (see above)
2321 *
2322 * @return void
2323 * @see makeCacheHash(), \TYPO3\CMS\Frontend\Plugin\AbstractPlugin::pi_cHashCheck()
2324 * @todo Define visibility
2325 */
2326 public function reqCHash() {
2327 if (!$this->cHash) {
2328 if ($this->TYPO3_CONF_VARS['FE']['pageNotFoundOnCHashError']) {
2329 if ($this->tempContent) {
2330 $this->clearPageCacheContent();
2331 }
2332 $this->pageNotFoundAndExit('Request parameters could not be validated (&cHash empty)');
2333 } else {
2334 $this->disableCache();
2335 $GLOBALS['TT']->setTSlogMessage('TSFE->reqCHash(): No &cHash parameter was sent for GET vars though required so caching is disabled', 2);
2336 }
2337 }
2338 }
2339
2340 /**
2341 * Initialize the TypoScript template parser
2342 *
2343 * @return void
2344 */
2345 public function initTemplate() {
2346 $this->tmpl = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\TypoScript\\TemplateService');
2347 $this->tmpl->init();
2348 $this->tmpl->tt_track = (bool)$this->beUserLogin;
2349 }
2350
2351 /**
2352 * See if page is in cache and get it if so
2353 * Stores the page content in $this->content if something is found.
2354 *
2355 * @return void
2356 */
2357 public function getFromCache() {
2358 if (!$this->no_cache) {
2359 $cc = $this->tmpl->getCurrentPageData();
2360 if (!is_array($cc)) {
2361 $key = $this->id . '::' . $this->MP;
2362 // Returns TRUE if the lock is active now
2363 $isLocked = $this->acquirePageGenerationLock($this->pagesection_lockObj, $key);
2364 if (!$isLocked) {
2365 // Lock is no longer active, the data in "cache_pagesection" is now ready
2366 $cc = $this->tmpl->getCurrentPageData();
2367 if (is_array($cc)) {
2368 // Release the lock
2369 $this->releasePageGenerationLock($this->pagesection_lockObj);
2370 }
2371 }
2372 }
2373 if (is_array($cc)) {
2374 // BE CAREFUL to change the content of the cc-array. This array is serialized and an md5-hash based on this is used for caching the page.
2375 // If this hash is not the same in here in this section and after page-generation, then the page will not be properly cached!
2376 // This array is an identification of the template. If $this->all is empty it's because the template-data is not cached, which it must be.
2377 $cc = $this->tmpl->matching($cc);
2378 ksort($cc);
2379 $this->all = $cc;
2380 }
2381 unset($cc);
2382 }
2383 // clearing the content-variable, which will hold the pagecontent
2384 $this->content = '';
2385 // Unsetting the lowlevel config
2386 unset($this->config);
2387 $this->cacheContentFlag = FALSE;
2388 // Look for page in cache only if caching is not disabled and if a shift-reload is not sent to the server.
2389 if (!$this->no_cache && !$this->headerNoCache()) {
2390 $lockHash = $this->getLockHash();
2391 if ($this->all) {
2392 $this->newHash = $this->getHash();
2393 $GLOBALS['TT']->push('Cache Row', '');
2394 $row = $this->getFromCache_queryRow();
2395 if (!is_array($row)) {
2396 $isLocked = $this->acquirePageGenerationLock($this->pages_lockObj, $lockHash);
2397 if (!$isLocked) {
2398 // Lock is no longer active, the data in "cache_pages" is now ready
2399 $row = $this->getFromCache_queryRow();
2400 if (is_array($row)) {
2401 // Release the lock
2402 $this->releasePageGenerationLock($this->pages_lockObj);
2403 }
2404 }
2405 }
2406 if (is_array($row)) {
2407 // Release this lock
2408 $this->releasePageGenerationLock($this->pages_lockObj);
2409 // Call hook when a page is retrieved from cache:
2410 if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['pageLoadedFromCache'])) {
2411 $_params = array('pObj' => &$this, 'cache_pages_row' => &$row);
2412 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['pageLoadedFromCache'] as $_funcRef) {
2413 GeneralUtility::callUserFunction($_funcRef, $_params, $this);
2414 }
2415 }
2416 // Fetches the lowlevel config stored with the cached data
2417 $this->config = $row['cache_data'];
2418 // Getting the content
2419 $this->content = $row['content'];
2420 // Flag for temp content
2421 $this->tempContent = $row['temp_content'];
2422 // Setting flag, so we know, that some cached content has been loaded
2423 $this->cacheContentFlag = TRUE;
2424 $this->cacheExpires = $row['expires'];
2425
2426 if (isset($this->config['config']['debug'])) {
2427 $debugCacheTime = (bool)$this->config['config']['debug'];
2428 } else {
2429 $debugCacheTime = !empty($this->TYPO3_CONF_VARS['FE']['debug']);
2430 }
2431 if ($debugCacheTime) {
2432 $dateFormat = $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'];
2433 $timeFormat = $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'];
2434 $this->content .= LF . '<!-- Cached page generated ' . date(($dateFormat . ' ' . $timeFormat), $row['tstamp']) . '. Expires ' . Date(($dateFormat . ' ' . $timeFormat), $row['expires']) . ' -->';
2435 }
2436 }
2437 $GLOBALS['TT']->pull();
2438 } else {
2439 $this->acquirePageGenerationLock($this->pages_lockObj, $lockHash);
2440 }
2441 }
2442 }
2443
2444 /**
2445 * Returning the cached version of page with hash = newHash
2446 *
2447 * @return array Cached row, if any. Otherwise void.
2448 * @todo Define visibility
2449 */
2450 public function getFromCache_queryRow() {
2451 $GLOBALS['TT']->push('Cache Query', '');
2452 $row = $this->pageCache->get($this->newHash);
2453 $GLOBALS['TT']->pull();
2454 return $row;
2455 }
2456
2457 /**
2458 * Detecting if shift-reload has been clicked
2459 * Will not be called if re-generation of page happens by other reasons (for instance that the page is not in cache yet!)
2460 * Also, a backend user MUST be logged in for the shift-reload to be detected due to DoS-attack-security reasons.
2461 *
2462 * @return boolean If shift-reload in client browser has been clicked, disable getting cached page (and regenerate it).
2463 * @todo Define visibility
2464 */
2465 public function headerNoCache() {
2466 $disableAcquireCacheData = FALSE;
2467 if ($this->beUserLogin) {
2468 if (strtolower($_SERVER['HTTP_CACHE_CONTROL']) === 'no-cache' || strtolower($_SERVER['HTTP_PRAGMA']) === 'no-cache') {
2469 $disableAcquireCacheData = TRUE;
2470 }
2471 }
2472 // Call hook for possible by-pass of requiring of page cache (for recaching purpose)
2473 if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['headerNoCache'])) {
2474 $_params = array('pObj' => &$this, 'disableAcquireCacheData' => &$disableAcquireCacheData);
2475 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['headerNoCache'] as $_funcRef) {
2476 GeneralUtility::callUserFunction($_funcRef, $_params, $this);
2477 }
2478 }
2479 return $disableAcquireCacheData;
2480 }
2481
2482 /**
2483 * Calculates the cache-hash
2484 * This hash is unique to the template, the variables ->id, ->type, ->gr_list (list of groups), ->MP (Mount Points) and cHash array
2485 * Used to get and later store the cached data.
2486 *
2487 * @return string MD5 hash of serialized hash base from createHashBase()
2488 * @access private
2489 * @see getFromCache(), getLockHash()
2490 * @todo Define visibility
2491 */
2492 public function getHash() {
2493 return md5($this->createHashBase(FALSE));
2494 }
2495
2496 /**
2497 * Calculates the lock-hash
2498 * This hash is unique to the above hash, except that it doesn't contain the template information in $this->all.
2499 *
2500 * @return string MD5 hash
2501 * @access private
2502 * @see getFromCache(), getHash()
2503 * @todo Define visibility
2504 */
2505 public function getLockHash() {
2506 $lockHash = $this->createHashBase(TRUE);
2507 return md5($lockHash);
2508 }
2509
2510 /**
2511 * Calculates the cache-hash (or the lock-hash)
2512 * This hash is unique to the template,
2513 * the variables ->id, ->type, ->gr_list (list of groups),
2514 * ->MP (Mount Points) and cHash array
2515 * Used to get and later store the cached data.
2516 *
2517 * @param boolean $createLockHashBase Whether to create the lock hash, which doesn't contain the "this->all" (the template information)
2518 * @return string the serialized hash base
2519 */
2520 protected function createHashBase($createLockHashBase = FALSE) {
2521 $hashParameters = array(
2522 'id' => (int)$this->id,
2523 'type' => (int)$this->type,
2524 'gr_list' => (string) $this->gr_list,
2525 'MP' => (string) $this->MP,
2526 'cHash' => $this->cHash_array,
2527 'domainStartPage' => $this->domainStartPage
2528 );
2529 // Include the template information if we shouldn't create a lock hash
2530 if (!$createLockHashBase) {
2531 $hashParameters['all'] = $this->all;
2532 }
2533 // Call hook to influence the hash calculation
2534 if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['createHashBase'])) {
2535 $_params = array(
2536 'hashParameters' => &$hashParameters,
2537 'createLockHashBase' => $createLockHashBase
2538 );
2539 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['createHashBase'] as $_funcRef) {
2540 GeneralUtility::callUserFunction($_funcRef, $_params, $this);
2541 }
2542 }
2543 return serialize($hashParameters);
2544 }
2545
2546 /**
2547 * Checks if config-array exists already but if not, gets it
2548 *
2549 * @throws \TYPO3\CMS\Core\Error\Http\ServiceUnavailableException
2550 * @return void
2551 */
2552 public function getConfigArray() {
2553 $setStatPageName = FALSE;
2554 // If config is not set by the cache (which would be a major mistake somewhere) OR if INTincScripts-include-scripts have been registered, then we must parse the template in order to get it
2555 if (!is_array($this->config) || is_array($this->config['INTincScript']) || $this->forceTemplateParsing) {
2556 $GLOBALS['TT']->push('Parse template', '');
2557 // Force parsing, if set?:
2558 $this->tmpl->forceTemplateParsing = $this->forceTemplateParsing;
2559 // Start parsing the TS template. Might return cached version.
2560 $this->tmpl->start($this->rootLine);
2561 $GLOBALS['TT']->pull();
2562 if ($this->tmpl->loaded) {
2563 $GLOBALS['TT']->push('Setting the config-array', '');
2564 // toplevel - objArrayName
2565 $this->sPre = $this->tmpl->setup['types.'][$this->type];
2566 $this->pSetup = $this->tmpl->setup[$this->sPre . '.'];
2567 if (!is_array($this->pSetup)) {
2568 $message = 'The page is not configured! [type=' . $this->type . '][' . $this->sPre . '].';
2569 if ($this->checkPageUnavailableHandler()) {
2570 $this->pageUnavailableAndExit($message);
2571 } else {
2572 $explanation = 'This means that there is no TypoScript object of type PAGE with typeNum=' . $this->type . ' configured.';
2573 GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
2574 throw new \TYPO3\CMS\Core\Error\Http\ServiceUnavailableException($message . ' ' . $explanation, 1294587217);
2575 }
2576 } else {
2577 $this->config['config'] = array();
2578 // Filling the config-array, first with the main "config." part
2579 if (is_array($this->tmpl->setup['config.'])) {
2580 $this->config['config'] = $this->tmpl->setup['config.'];
2581 }
2582 // override it with the page/type-specific "config."
2583 if (is_array($this->pSetup['config.'])) {
2584 \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($this->config['config'], $this->pSetup['config.']);
2585 }
2586 if ($this->config['config']['typolinkEnableLinksAcrossDomains']) {
2587 $this->config['config']['typolinkCheckRootline'] = TRUE;
2588 }
2589 // Set default values for removeDefaultJS and inlineStyle2TempFile so CSS and JS are externalized if compatversion is higher than 4.0
2590 if (GeneralUtility::compat_version('4.0')) {
2591 if (!isset($this->config['config']['removeDefaultJS'])) {
2592 $this->config['config']['removeDefaultJS'] = 'external';
2593 }
2594 if (!isset($this->config['config']['inlineStyle2TempFile'])) {
2595 $this->config['config']['inlineStyle2TempFile'] = 1;
2596 }
2597 }
2598 if (!isset($this->config['config']['compressJs'])) {
2599 $this->config['config']['compressJs'] = 0;
2600 }
2601 // Processing for the config_array:
2602 $this->config['rootLine'] = $this->tmpl->rootLine;
2603 $this->config['mainScript'] = trim($this->config['config']['mainScript']) ?: 'index.php';
2604 // Class for render Header and Footer parts
2605 $template = '';
2606 if ($this->pSetup['pageHeaderFooterTemplateFile']) {
2607 $file = $this->tmpl->getFileName($this->pSetup['pageHeaderFooterTemplateFile']);
2608 if ($file) {
2609 $this->getPageRenderer()->setTemplateFile($file);
2610 }
2611 }
2612 }
2613 $GLOBALS['TT']->pull();
2614 } else {
2615 if ($this->checkPageUnavailableHandler()) {
2616 $this->pageUnavailableAndExit('No TypoScript template found!');
2617 } else {
2618 $message = 'No TypoScript template found!';
2619 GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
2620 throw new \TYPO3\CMS\Core\Error\Http\ServiceUnavailableException($message, 1294587218);
2621 }
2622 }
2623 }
2624
2625 // No cache
2626 // Set $this->no_cache TRUE if the config.no_cache value is set!
2627 if ($this->config['config']['no_cache']) {
2628 $this->set_no_cache('config.no_cache is set');
2629 }
2630 // Merge GET with defaultGetVars
2631 if (!empty($this->config['config']['defaultGetVars.'])) {
2632 $modifiedGetVars = GeneralUtility::removeDotsFromTS($this->config['config']['defaultGetVars.']);
2633 \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($modifiedGetVars, GeneralUtility::_GET());
2634 GeneralUtility::_GETset($modifiedGetVars);
2635 }
2636 // Hook for postProcessing the configuration array
2637 if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['configArrayPostProc'])) {
2638 $params = array('config' => &$this->config['config']);
2639 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['configArrayPostProc'] as $funcRef) {
2640 GeneralUtility::callUserFunction($funcRef, $params, $this);
2641 }
2642 }
2643 }
2644
2645 /********************************************
2646 *
2647 * Further initialization and data processing
2648 * (jumpurl/submission of forms)
2649 *
2650 *******************************************/
2651
2652 /**
2653 * Get the compressed $GLOBALS['TCA'] array for use in the front-end
2654 * A compressed $GLOBALS['TCA'] array holds only the ctrl- and feInterface-part for each table.
2655 * But the column-definitions are omitted in order to save some memory and be more efficient.
2656 * Operates on the global variable, $TCA
2657 *
2658 * @return void
2659 * @deprecated since 6,1, will be removed in two versions.
2660 */
2661 public function getCompressedTCarray() {
2662 // Full TCA is always loaded during bootstrap in FE, this method is obsolete.
2663 GeneralUtility::logDeprecatedFunction();
2664 }
2665
2666 /**
2667 * Includes TCA definitions from loaded extensions (ext_tables.php files).
2668 * Normally in the frontend only a part of the global $TCA array is loaded,
2669 * namely the "ctrl" part. Thus it doesn't take up too much memory. To load
2670 * full TCA for the table, use GeneralUtility::loadTCA($tableName)
2671 * after calling this function.
2672 *
2673 * @param integer $TCAloaded Probably, keep hands of this value. Just don't set it.
2674 * @return void
2675 * @see getCompressedTCarray()
2676 * @deprecated since 6.1, will be removed in two versions. Obsolete in regular frontend, eid scripts should use \TYPO3\CMS\Frontend\Utility\EidUtility::initTCA()
2677 */
2678 public function includeTCA($TCAloaded = 1) {
2679 // Full TCA is always loaded during bootstrap in FE, this method is obsolete.
2680 GeneralUtility::logDeprecatedFunction();
2681
2682 // Compatibility layer:
2683 // The if below is NOT true in usual frontend (non eid) context, TCA is loaded by bootstrap.
2684 // If an eid script calls this method to load TCA, use
2685 // \TYPO3\CMS\Frontend\Utility\EidUtility::initTCA() instead.
2686 if (!isset($GLOBALS['TCA']['pages'])) {
2687 \TYPO3\CMS\Core\Core\Bootstrap::getInstance()->loadCachedTca();
2688 }
2689 }
2690
2691 /**
2692 * Setting the language key that will be used by the current page.
2693 * In this function it should be checked, 1) that this language exists, 2) that a page_overlay_record exists, .. and if not the default language, 0 (zero), should be set.
2694 *
2695 * @return void
2696 * @access private
2697 */
2698 public function settingLanguage() {
2699 if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['settingLanguage_preProcess'])) {
2700 $_params = array();
2701 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['settingLanguage_preProcess'] as $_funcRef) {
2702 GeneralUtility::callUserFunction($_funcRef, $_params, $this);
2703 }
2704 }
2705
2706 // Initialize charset settings etc.
2707 $this->initLLvars();
2708
2709 // Get values from TypoScript:
2710 $this->sys_language_uid = ($this->sys_language_content = (int)$this->config['config']['sys_language_uid']);
2711 list($this->sys_language_mode, $sys_language_content) = GeneralUtility::trimExplode(';', $this->config['config']['sys_language_mode']);
2712 $this->sys_language_contentOL = $this->config['config']['sys_language_overlay'];
2713 // If sys_language_uid is set to another language than default:
2714 if ($this->sys_language_uid > 0) {
2715 // check whether a shortcut is overwritten by a translated page
2716 // we can only do this now, as this is the place where we get
2717 // to know about translations
2718 $this->checkTranslatedShortcut();
2719 // Request the overlay record for the sys_language_uid:
2720 $olRec = $this->sys_page->getPageOverlay($this->id, $this->sys_language_uid);
2721 if (!count($olRec)) {
2722 // If no OL record exists and a foreign language is asked for...
2723 if ($this->sys_language_uid) {
2724 // If requested translation is not available:
2725 if (GeneralUtility::hideIfNotTranslated($this->page['l18n_cfg'])) {
2726 $this->pageNotFoundAndExit('Page is not available in the requested language.');
2727 } else {
2728 switch ((string) $this->sys_language_mode) {
2729 case 'strict':
2730 $this->pageNotFoundAndExit('Page is not available in the requested language (strict).');
2731 break;
2732 case 'content_fallback':
2733 $fallBackOrder = GeneralUtility::intExplode(',', $sys_language_content);
2734 foreach ($fallBackOrder as $orderValue) {
2735 if ((string)$orderValue === '0' || count($this->sys_page->getPageOverlay($this->id, $orderValue))) {
2736 $this->sys_language_content = $orderValue;
2737 // Setting content uid (but leaving the sys_language_uid)
2738 break;
2739 }
2740 }
2741 break;
2742 case 'ignore':
2743 $this->sys_language_content = $this->sys_language_uid;
2744 break;
2745 default:
2746 // Default is that everything defaults to the default language...
2747 $this->sys_language_uid = ($this->sys_language_content = 0);
2748 }
2749 }
2750 }
2751 } else {
2752 // Setting sys_language if an overlay record was found (which it is only if a language is used)
2753 $this->page = $this->sys_page->getPageOverlay($this->page, $this->sys_language_uid);
2754 }
2755 }
2756 // Setting sys_language_uid inside sys-page:
2757 $this->sys_page->sys_language_uid = $this->sys_language_uid;
2758 // If default translation is not available:
2759 if ((!$this->sys_language_uid || !$this->sys_language_content) && $this->page['l18n_cfg'] & 1) {
2760 $message = 'Page is not available in default language.';
2761 GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
2762 $this->pageNotFoundAndExit($message);
2763 }
2764 $this->updateRootLinesWithTranslations();
2765 // Finding the ISO code:
2766 if (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('static_info_tables') && $this->sys_language_content) {
2767 // using sys_language_content because the ISO code only (currently) affect content selection from FlexForms - which should follow "sys_language_content"
2768 // Set the fourth parameter to TRUE in the next two getRawRecord() calls to
2769 // avoid versioning overlay to be applied as it generates an SQL error
2770 $sys_language_row = $this->sys_page->getRawRecord('sys_language', $this->sys_language_content, 'static_lang_isocode', TRUE);
2771 if (is_array($sys_language_row) && $sys_language_row['static_lang_isocode']) {
2772 $stLrow = $this->sys_page->getRawRecord('static_languages', $sys_language_row['static_lang_isocode'], 'lg_iso_2', TRUE);
2773 $this->sys_language_isocode = $stLrow['lg_iso_2'];
2774 }
2775 }
2776 // Setting softMergeIfNotBlank:
2777 $table_fields = GeneralUtility::trimExplode(',', $this->config['config']['sys_language_softMergeIfNotBlank'], TRUE);
2778 foreach ($table_fields as $TF) {
2779 list($tN, $fN) = explode(':', $TF);
2780 $GLOBALS['TCA'][$tN]['columns'][$fN]['l10n_mode'] = 'mergeIfNotBlank';
2781 }
2782 // Setting softExclude:
2783 $table_fields = GeneralUtility::trimExplode(',', $this->config['config']['sys_language_softExclude'], TRUE);
2784 foreach ($table_fields as $TF) {
2785 list($tN, $fN) = explode(':', $TF);
2786 $GLOBALS['TCA'][$tN]['columns'][$fN]['l10n_mode'] = 'exclude';
2787 }
2788 if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['settingLanguage_postProcess'])) {
2789 $_params = array();
2790 foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['settingLanguage_postProcess'] as $_funcRef) {
2791 GeneralUtility::callUserFunction($_funcRef, $_params, $this);
2792 }
2793 }
2794 }
2795
2796 /**
2797 * Updating content of the two rootLines IF the language key is set!
2798 */
2799 protected function updateRootLinesWithTranslations() {
2800 if ($this->sys_language_uid) {
2801 $this->rootLine = $this->sys_page->getRootLine($this->id, $this->MP);
2802 $this->tmpl->updateRootlineData($this->rootLine);
2803 }
2804 }
2805
2806 /**
2807 * Setting locale for frontend rendering
2808 *
2809 * @return void
2810 */
2811 public function settingLocale() {
2812 // Setting locale
2813 if ($this->config['config']['locale_all']) {
2814 // There's a problem that PHP parses float values in scripts wrong if the
2815 // locale LC_NUMERIC is set to something with a comma as decimal point
2816 // Do we set all except LC_NUMERIC
2817 $locale = setlocale(LC_COLLATE, $this->config['config']['locale_all']);
2818 if ($locale) {
2819 // PHP fatals with uppercase I characters in method names with turkish locale LC_CTYPE
2820 // @see http://bugs.php.net/bug.php?id=35050
2821 if (substr($this->config['config']['locale_all'], 0, 2) != 'tr') {
2822 setlocale(LC_CTYPE, $this->config['config']['locale_all']);
2823 }
2824 setlocale(LC_MONETARY, $this->config['config']['locale_all']);
2825 setlocale(LC_TIME, $this->config['config']['locale_all']);
2826 $this->localeCharset = $this->csConvObj->get_locale_charset($this->config['config']['locale_all']);
2827 } else {
2828 $GLOBALS['TT']->setTSlogMessage('Locale "' . htmlspecialchars($this->config['config']['locale_all']) . '" not found.', 3);
2829 }
2830 }
2831 }
2832
2833 /**
2834 * Checks whether a translated shortcut page has a different shortcut
2835 * target than the original language page.
2836 * If that is the case, things get corrected to follow that alternative
2837 * shortcut
2838 *
2839 * @return void
2840 * @author Ingo Renner <ingo@typo3.org>
2841 */
2842 protected function checkTranslatedShortcut() {
2843 if (!is_null($this->originalShortcutPage)) {
2844 $originalShortcutPageOverlay = $this->sys_page->getPageOverlay($this->originalShortcutPage['uid'], $this->sys_language_uid);
2845 if (!empty($originalShortcutPageOverlay['shortcut']) && $originalShortcutPageOverlay['shortcut'] != $this->id) {
2846 // the translation of the original shortcut page has a different shortcut target!
2847 // set the correct page and id
2848 $shortcut = $this->getPageShortcut($originalShortcutPageOverlay['shortcut'], $originalShortcutPageOverlay['shortcut_mode'], $originalShortcutPageOverlay['uid']);
2849 $this->id = ($this->contentPid = $shortcut['uid']);
2850 $this->page = $this->sys_page->getPage($this->id);
2851 // Fix various effects on things like menus f.e.
2852 $this->fetch_the_id();
2853 $this->tmpl->rootLine = array_reverse($this->rootLine);
2854 }
2855 }
2856 }
2857
2858 /**
2859 * Handle data submission
2860 *
2861 * @return void
2862 */
2863 public function handleDataSubmission() {
2864 // Check Submission of data.
2865 // This is done at this point, because we need the config values
2866 switch ($this->checkDataSubmission()) {
2867