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