[BUGFIX] Reallow '0' as valid userFunc argument in TypoScript
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Configuration / TypoScript / ConditionMatching / AbstractConditionMatcher.php
1 <?php
2 namespace TYPO3\CMS\Core\Configuration\TypoScript\ConditionMatching;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Utility\GeneralUtility;
18
19 /**
20 * Matching TypoScript conditions
21 *
22 * Used with the TypoScript parser.
23 * Matches browserinfo, IPnumbers for use with templates
24 */
25 abstract class AbstractConditionMatcher
26 {
27 /**
28 * Id of the current page.
29 *
30 * @var int
31 */
32 protected $pageId;
33
34 /**
35 * The rootline for the current page.
36 *
37 * @var array
38 */
39 protected $rootline;
40
41 /**
42 * Whether to simulate the behaviour and match all conditions
43 * (used in TypoScript object browser).
44 *
45 * @var bool
46 */
47 protected $simulateMatchResult = false;
48
49 /**
50 * Whether to simulat the behaviour and match specific conditions
51 * (used in TypoScript object browser).
52 *
53 * @var array
54 */
55 protected $simulateMatchConditions = array();
56
57 /**
58 * Sets the id of the page to evaluate conditions for.
59 *
60 * @param int $pageId Id of the page (must be positive)
61 * @return void
62 */
63 public function setPageId($pageId)
64 {
65 if (is_integer($pageId) && $pageId > 0) {
66 $this->pageId = $pageId;
67 }
68 }
69
70 /**
71 * Gets the id of the page to evaluate conditions for.
72 *
73 * @return int Id of the page
74 */
75 public function getPageId()
76 {
77 return $this->pageId;
78 }
79
80 /**
81 * Sets the rootline.
82 *
83 * @param array $rootline The rootline to be used for matching (must have elements)
84 * @return void
85 */
86 public function setRootline(array $rootline)
87 {
88 if (!empty($rootline)) {
89 $this->rootline = $rootline;
90 }
91 }
92
93 /**
94 * Gets the rootline.
95 *
96 * @return array The rootline to be used for matching
97 */
98 public function getRootline()
99 {
100 return $this->rootline;
101 }
102
103 /**
104 * Sets whether to simulate the behaviour and match all conditions.
105 *
106 * @param bool $simulateMatchResult Whether to simulate positive matches
107 * @return void
108 */
109 public function setSimulateMatchResult($simulateMatchResult)
110 {
111 if (is_bool($simulateMatchResult)) {
112 $this->simulateMatchResult = $simulateMatchResult;
113 }
114 }
115
116 /**
117 * Sets whether to simulate the behaviour and match specific conditions.
118 *
119 * @param array $simulateMatchConditions Conditions to simulate a match for
120 * @return void
121 */
122 public function setSimulateMatchConditions(array $simulateMatchConditions)
123 {
124 $this->simulateMatchConditions = $simulateMatchConditions;
125 }
126
127 /**
128 * Normalizes an expression and removes the first and last square bracket.
129 * + OR normalization: "...]OR[...", "...]||[...", "...][..." --> "...]||[..."
130 * + AND normalization: "...]AND[...", "...]&&[..." --> "...]&&[..."
131 *
132 * @param string $expression The expression to be normalized (e.g. "[A] && [B] OR [C]")
133 * @return string The normalized expression (e.g. "[A]&&[B]||[C]")
134 */
135 protected function normalizeExpression($expression)
136 {
137 $normalizedExpression = preg_replace(array(
138 '/\\]\\s*(OR|\\|\\|)?\\s*\\[/i',
139 '/\\]\\s*(AND|&&)\\s*\\[/i'
140 ), array(
141 ']||[',
142 ']&&['
143 ), trim($expression));
144 return $normalizedExpression;
145 }
146
147 /**
148 * Matches a TypoScript condition expression.
149 *
150 * @param string $expression The expression to match
151 * @return bool Whether the expression matched
152 */
153 public function match($expression)
154 {
155 // Return directly if result should be simulated:
156 if ($this->simulateMatchResult) {
157 return $this->simulateMatchResult;
158 }
159 // Return directly if matching for specific condition is simulated only:
160 if (!empty($this->simulateMatchConditions)) {
161 return in_array($expression, $this->simulateMatchConditions);
162 }
163 // Sets the current pageId if not defined yet:
164 if (!isset($this->pageId)) {
165 $this->pageId = $this->determinePageId();
166 }
167 // Sets the rootline if not defined yet:
168 if (!isset($this->rootline)) {
169 $this->rootline = $this->determineRootline();
170 }
171 $result = false;
172 $normalizedExpression = $this->normalizeExpression($expression);
173 // First and last character must be square brackets (e.g. "[A]&&[B]":
174 if ($normalizedExpression[0] === '[' && substr($normalizedExpression, -1) === ']') {
175 $innerExpression = substr($normalizedExpression, 1, -1);
176 $orParts = explode(']||[', $innerExpression);
177 foreach ($orParts as $orPart) {
178 $andParts = explode(']&&[', $orPart);
179 foreach ($andParts as $andPart) {
180 $result = $this->evaluateCondition($andPart);
181 // If condition in AND context fails, the whole block is FALSE:
182 if ($result === false) {
183 break;
184 }
185 }
186 // If condition in OR context succeeds, the whole expression is TRUE:
187 if ($result === true) {
188 break;
189 }
190 }
191 }
192 return $result;
193 }
194
195 /**
196 * Evaluates a TypoScript condition given as input, eg. "[applicationContext = Production][...(other condition)...]"
197 *
198 * @param string $key The condition to match against its criteria.
199 * @param string $value
200 * @return NULL|bool Result of the evaluation; NULL if condition could not be evaluated
201 */
202 protected function evaluateConditionCommon($key, $value)
203 {
204 $lowerKey = strtolower($key);
205 if ($lowerKey === 'browser' || $lowerKey === 'device' || $lowerKey === 'version' || $lowerKey === 'system' || $lowerKey === 'useragent') {
206 GeneralUtility::deprecationLog(
207 'Usage of client related conditions (browser, device, version, system, useragent) is deprecated since 7.0.'
208 );
209 $browserInfo = $this->getBrowserInfo(GeneralUtility::getIndpEnv('HTTP_USER_AGENT'));
210 }
211 $keyParts = GeneralUtility::trimExplode('|', $key);
212 switch ($keyParts[0]) {
213 case 'applicationContext':
214 $values = GeneralUtility::trimExplode(',', $value, true);
215 $currentApplicationContext = GeneralUtility::getApplicationContext();
216 foreach ($values as $applicationContext) {
217 if ($this->searchStringWildcard($currentApplicationContext, $applicationContext)) {
218 return true;
219 }
220 }
221 return false;
222 break;
223 case 'browser':
224 $values = GeneralUtility::trimExplode(',', $value, true);
225 // take all identified browsers into account, eg chrome deliver
226 // webkit=>532.5, chrome=>4.1, safari=>532.5
227 // so comparing string will be
228 // "webkit532.5 chrome4.1 safari532.5"
229 $all = '';
230 foreach ($browserInfo['all'] as $key => $value) {
231 $all .= $key . $value . ' ';
232 }
233 foreach ($values as $test) {
234 if (stripos($all, $test) !== false) {
235 return true;
236 }
237 }
238 return false;
239 break;
240 case 'version':
241 $values = GeneralUtility::trimExplode(',', $value, true);
242 foreach ($values as $test) {
243 if (strcspn($test, '=<>') == 0) {
244 switch ($test[0]) {
245 case '=':
246 if (doubleval(substr($test, 1)) == $browserInfo['version']) {
247 return true;
248 }
249 break;
250 case '<':
251 if (doubleval(substr($test, 1)) > $browserInfo['version']) {
252 return true;
253 }
254 break;
255 case '>':
256 if (doubleval(substr($test, 1)) < $browserInfo['version']) {
257 return true;
258 }
259 break;
260 }
261 } elseif (strpos(' ' . $browserInfo['version'], $test) == 1) {
262 return true;
263 }
264 }
265 return false;
266 break;
267 case 'system':
268 $values = GeneralUtility::trimExplode(',', $value, true);
269 // Take all identified systems into account, e.g. mac for iOS, Linux
270 // for android and Windows NT for Windows XP
271 $allSystems = ' ' . implode(' ', $browserInfo['all_systems']);
272 foreach ($values as $test) {
273 if (stripos($allSystems, $test) !== false) {
274 return true;
275 }
276 }
277 return false;
278 break;
279 case 'device':
280 if (!isset($this->deviceInfo)) {
281 $this->deviceInfo = $this->getDeviceType(GeneralUtility::getIndpEnv('HTTP_USER_AGENT'));
282 }
283 $values = GeneralUtility::trimExplode(',', $value, true);
284 foreach ($values as $test) {
285 if ($this->deviceInfo == $test) {
286 return true;
287 }
288 }
289 return false;
290 break;
291 case 'useragent':
292 $test = trim($value);
293 if ($test !== '') {
294 return $this->searchStringWildcard((string)$browserInfo['useragent'], $test);
295 } else {
296 return false;
297 }
298 break;
299 case 'language':
300 if (GeneralUtility::getIndpEnv('HTTP_ACCEPT_LANGUAGE') === $value) {
301 return true;
302 }
303 $values = GeneralUtility::trimExplode(',', $value, true);
304 foreach ($values as $test) {
305 if (preg_match('/^\\*.+\\*$/', $test)) {
306 $allLanguages = preg_split('/[,;]/', GeneralUtility::getIndpEnv('HTTP_ACCEPT_LANGUAGE'));
307 if (in_array(substr($test, 1, -1), $allLanguages)) {
308 return true;
309 }
310 } elseif (GeneralUtility::getIndpEnv('HTTP_ACCEPT_LANGUAGE') == $test) {
311 return true;
312 }
313 }
314 return false;
315 break;
316 case 'IP':
317 if ($value === 'devIP') {
318 $value = trim($GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask']);
319 }
320
321 return (bool)GeneralUtility::cmpIP(GeneralUtility::getIndpEnv('REMOTE_ADDR'), $value);
322 break;
323 case 'hostname':
324 return (bool)GeneralUtility::cmpFQDN(GeneralUtility::getIndpEnv('REMOTE_ADDR'), $value);
325 break;
326 case 'hour':
327 case 'minute':
328 case 'month':
329 case 'year':
330 case 'dayofweek':
331 case 'dayofmonth':
332 case 'dayofyear':
333 // In order to simulate time properly in templates.
334 $theEvalTime = $GLOBALS['SIM_EXEC_TIME'];
335 switch ($key) {
336 case 'hour':
337 $theTestValue = date('H', $theEvalTime);
338 break;
339 case 'minute':
340 $theTestValue = date('i', $theEvalTime);
341 break;
342 case 'month':
343 $theTestValue = date('m', $theEvalTime);
344 break;
345 case 'year':
346 $theTestValue = date('Y', $theEvalTime);
347 break;
348 case 'dayofweek':
349 $theTestValue = date('w', $theEvalTime);
350 break;
351 case 'dayofmonth':
352 $theTestValue = date('d', $theEvalTime);
353 break;
354 case 'dayofyear':
355 $theTestValue = date('z', $theEvalTime);
356 break;
357 }
358 $theTestValue = (int)$theTestValue;
359 // comp
360 $values = GeneralUtility::trimExplode(',', $value, true);
361 foreach ($values as $test) {
362 if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($test)) {
363 $test = '=' . $test;
364 }
365 if ($this->compareNumber($test, $theTestValue)) {
366 return true;
367 }
368 }
369 return false;
370 break;
371 case 'compatVersion':
372 return GeneralUtility::compat_version($value);
373 break;
374 case 'loginUser':
375 if ($this->isUserLoggedIn()) {
376 $values = GeneralUtility::trimExplode(',', $value, true);
377 foreach ($values as $test) {
378 if ($test == '*' || (string)$this->getUserId() === (string)$test) {
379 return true;
380 }
381 }
382 } elseif ($value === '') {
383 return true;
384 }
385 return false;
386 break;
387 case 'page':
388 if ($keyParts[1]) {
389 $page = $this->getPage();
390 $property = $keyParts[1];
391 if (!empty($page) && isset($page[$property]) && (string)$page[$property] === (string)$value) {
392 return true;
393 }
394 }
395 return false;
396 break;
397 case 'globalVar':
398 $values = GeneralUtility::trimExplode(',', $value, true);
399 foreach ($values as $test) {
400 $point = strcspn($test, '!=<>');
401 $theVarName = substr($test, 0, $point);
402 $nv = $this->getVariable(trim($theVarName));
403 $testValue = substr($test, $point);
404 if ($this->compareNumber($testValue, $nv)) {
405 return true;
406 }
407 }
408 return false;
409 break;
410 case 'globalString':
411 $values = GeneralUtility::trimExplode(',', $value, true);
412 foreach ($values as $test) {
413 $point = strcspn($test, '=');
414 $theVarName = substr($test, 0, $point);
415 $nv = (string)$this->getVariable(trim($theVarName));
416 $testValue = substr($test, $point + 1);
417 if ($this->searchStringWildcard($nv, trim($testValue))) {
418 return true;
419 }
420 }
421 return false;
422 break;
423 case 'userFunc':
424 $matches = array();
425 preg_match_all('/^\s*([^\(\s]+)\s*(?:\((.*)\))?\s*$/', $value, $matches);
426 $funcName = $matches[1][0];
427 $funcValues = trim($matches[2][0]) !== '' ? $this->parseUserFuncArguments($matches[2][0]) : array();
428 if (is_callable($funcName) && call_user_func_array($funcName, $funcValues)) {
429 return true;
430 }
431 return false;
432 break;
433 }
434 return null;
435 }
436
437 /**
438 * Evaluates a TypoScript condition given as input with a custom class name,
439 * e.g. "[MyCompany\MyPackage\ConditionMatcher\MyOwnConditionMatcher = myvalue]"
440 *
441 * @param string $condition The condition to match
442 * @return NULL|bool Result of the evaluation; NULL if condition could not be evaluated
443 * @throws \TYPO3\CMS\Core\Configuration\TypoScript\Exception\InvalidTypoScriptConditionException
444 */
445 protected function evaluateCustomDefinedCondition($condition)
446 {
447 $conditionResult = null;
448
449 list($conditionClassName, $conditionParameters) = GeneralUtility::trimExplode(' ', $condition, false, 2);
450
451 // Check if the condition class name is a valid class
452 // This is necessary to not stop here for the conditions ELSE and GLOBAL
453 if (class_exists($conditionClassName)) {
454 // Use like this: [MyCompany\MyPackage\ConditionMatcher\MyOwnConditionMatcher = myvalue]
455 /** @var \TYPO3\CMS\Core\Configuration\TypoScript\ConditionMatching\AbstractCondition $conditionObject */
456 $conditionObject = GeneralUtility::makeInstance($conditionClassName);
457 if (($conditionObject instanceof \TYPO3\CMS\Core\Configuration\TypoScript\ConditionMatching\AbstractCondition) === false) {
458 throw new \TYPO3\CMS\Core\Configuration\TypoScript\Exception\InvalidTypoScriptConditionException(
459 '"' . $conditionClassName . '" is not a valid TypoScript Condition object.',
460 1410286153
461 );
462 }
463
464 $conditionParameters = $this->parseUserFuncArguments($conditionParameters);
465 $conditionObject->setConditionMatcherInstance($this);
466 $conditionResult = $conditionObject->matchCondition($conditionParameters);
467 }
468
469 return $conditionResult;
470 }
471
472 /**
473 * Parses arguments to the userFunc.
474 *
475 * @param string $arguments
476 * @return array
477 */
478 protected function parseUserFuncArguments($arguments)
479 {
480 $result = array();
481 $arguments = trim($arguments);
482 while ($arguments !== '') {
483 if ($arguments[0] === ',') {
484 $result[] = '';
485 $arguments = substr($arguments, 1);
486 } else {
487 $pos = strcspn($arguments, ',\'"');
488 if ($pos == 0) {
489 // We hit a quote of some kind
490 $quote = $arguments[0];
491 $segment = preg_replace('/^(.*?[^\\\])' . $quote . '.*$/', '\1', substr($arguments, 1));
492 $segment = str_replace('\\' . $quote, $quote, $segment);
493 $result[] = $segment;
494 // shorten $arguments
495 $arguments = substr($arguments, strlen($segment) + 2);
496 $offset = strpos($arguments, ',');
497 if ($offset === false) {
498 $offset = strlen($arguments);
499 }
500 $arguments = substr($arguments, $offset + 1);
501 } else {
502 $result[] = trim(substr($arguments, 0, $pos));
503 $arguments = substr($arguments, $pos + 1);
504 }
505 }
506 $arguments = trim($arguments);
507 };
508 return $result;
509 }
510
511 /**
512 * Get variable common
513 *
514 * @param array $vars
515 * @return mixed Whatever value. If none, then NULL.
516 */
517 protected function getVariableCommon(array $vars)
518 {
519 $value = null;
520 if (count($vars) === 1) {
521 $value = $this->getGlobal($vars[0]);
522 } else {
523 $splitAgain = explode('|', $vars[1], 2);
524 $k = trim($splitAgain[0]);
525 if ($k) {
526 switch ((string)trim($vars[0])) {
527 case 'GP':
528 $value = GeneralUtility::_GP($k);
529 break;
530 case 'GPmerged':
531 $value = GeneralUtility::_GPmerged($k);
532 break;
533 case 'ENV':
534 $value = getenv($k);
535 break;
536 case 'IENV':
537 $value = GeneralUtility::getIndpEnv($k);
538 break;
539 case 'LIT':
540 return trim($vars[1]);
541 break;
542 default:
543 return null;
544 }
545 // If array:
546 if (count($splitAgain) > 1) {
547 if (is_array($value) && trim($splitAgain[1]) !== '') {
548 $value = $this->getGlobal($splitAgain[1], $value);
549 } else {
550 $value = '';
551 }
552 }
553 }
554 }
555 return $value;
556 }
557
558 /**
559 * Evaluates a $leftValue based on an operator: "<", ">", "<=", ">=", "!=" or "="
560 *
561 * @param string $test The value to compare with on the form [operator][number]. Eg. "< 123
562 * @param float $leftValue The value on the left side
563 * @return bool If $value is "50" and $test is "< 123" then it will return TRUE.
564 */
565 protected function compareNumber($test, $leftValue)
566 {
567 if (preg_match('/^(!?=+|<=?|>=?)\\s*([^\\s]*)\\s*$/', $test, $matches)) {
568 $operator = $matches[1];
569 $rightValue = $matches[2];
570 switch ($operator) {
571 case '>=':
572 return $leftValue >= doubleval($rightValue);
573 break;
574 case '<=':
575 return $leftValue <= doubleval($rightValue);
576 break;
577 case '!=':
578 // multiple values may be split with '|'
579 // see if none matches ("not in list")
580 $found = false;
581 $rightValueParts = GeneralUtility::trimExplode('|', $rightValue);
582 foreach ($rightValueParts as $rightValueSingle) {
583 if ($leftValue == doubleval($rightValueSingle)) {
584 $found = true;
585 break;
586 }
587 }
588 return $found === false;
589 break;
590 case '<':
591 return $leftValue < doubleval($rightValue);
592 break;
593 case '>':
594 return $leftValue > doubleval($rightValue);
595 break;
596 default:
597 // nothing valid found except '=', use '='
598 // multiple values may be split with '|'
599 // see if one matches ("in list")
600 $found = false;
601 $rightValueParts = GeneralUtility::trimExplode('|', $rightValue);
602 foreach ($rightValueParts as $rightValueSingle) {
603 if ($leftValue == $rightValueSingle) {
604 $found = true;
605 break;
606 }
607 }
608 return $found;
609 }
610 }
611 return false;
612 }
613
614 /**
615 * Matching two strings against each other, supporting a "*" wildcard or (if wrapped in "/") PCRE regular expressions
616 *
617 * @param string $haystack The string in which to find $needle.
618 * @param string $needle The string to find in $haystack
619 * @return bool Returns TRUE if $needle matches or is found in (according to wildcards) in $haystack. Eg. if $haystack is "Netscape 6.5" and $needle is "Net*" or "Net*ape" then it returns TRUE.
620 */
621 protected function searchStringWildcard($haystack, $needle)
622 {
623 $result = false;
624 if ($haystack === $needle) {
625 $result = true;
626 } elseif ($needle) {
627 if (preg_match('/^\\/.+\\/$/', $needle)) {
628 // Regular expression, only "//" is allowed as delimiter
629 $regex = $needle;
630 } else {
631 $needle = str_replace(array('*', '?'), array('###MANY###', '###ONE###'), $needle);
632 $regex = '/^' . preg_quote($needle, '/') . '$/';
633 // Replace the marker with .* to match anything (wildcard)
634 $regex = str_replace(array('###MANY###', '###ONE###'), array('.*', '.'), $regex);
635 }
636 $result = (bool)preg_match($regex, $haystack);
637 }
638 return $result;
639 }
640
641 /**
642 * Generates an array with abstracted browser information
643 *
644 * @param string $userAgent The useragent string, \TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('HTTP_USER_AGENT')
645 * @return array Contains keys "browser", "version", "system
646 */
647 protected function getBrowserInfo($userAgent)
648 {
649 return \TYPO3\CMS\Core\Utility\ClientUtility::getBrowserInfo($userAgent);
650 }
651
652 /**
653 * Gets a code for a browsing device based on the input useragent string.
654 *
655 * @param string $userAgent The useragent string, \TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('HTTP_USER_AGENT')
656 * @return string Code for the specific device type
657 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
658 */
659 protected function getDeviceType($userAgent)
660 {
661 return \TYPO3\CMS\Core\Utility\ClientUtility::getDeviceType($userAgent);
662 }
663
664 /**
665 * Return global variable where the input string $var defines array keys separated by "|"
666 * Example: $var = "HTTP_SERVER_VARS | something" will return the value $GLOBALS['HTTP_SERVER_VARS']['something'] value
667 *
668 * @param string $var Global var key, eg. "HTTP_GET_VAR" or "HTTP_GET_VARS|id" to get the GET parameter "id" back.
669 * @param array $source Alternative array than $GLOBAL to get variables from.
670 * @return mixed Whatever value. If none, then blank string.
671 */
672 protected function getGlobal($var, $source = null)
673 {
674 $vars = explode('|', $var);
675 $c = count($vars);
676 $k = trim($vars[0]);
677 $theVar = isset($source) ? $source[$k] : $GLOBALS[$k];
678 for ($a = 1; $a < $c; $a++) {
679 if (!isset($theVar)) {
680 break;
681 }
682 $key = trim($vars[$a]);
683 if (is_object($theVar)) {
684 $theVar = $theVar->{$key};
685 } elseif (is_array($theVar)) {
686 $theVar = $theVar[$key];
687 } else {
688 return '';
689 }
690 }
691 if (!is_array($theVar) && !is_object($theVar)) {
692 return $theVar;
693 } else {
694 return '';
695 }
696 }
697
698 /**
699 * Evaluates a TypoScript condition given as input, eg. "[browser=net][...(other conditions)...]"
700 *
701 * @param string $string The condition to match against its criterias.
702 * @return bool Whether the condition matched
703 * @see \TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser::parse()
704 */
705 abstract protected function evaluateCondition($string);
706
707 /**
708 * Gets the value of a variable.
709 *
710 * Examples of names:
711 * + TSFE:id
712 * + GP:firstLevel|secondLevel
713 * + _GET|firstLevel|secondLevel
714 * + LIT:someLiteralValue
715 *
716 * @param string $name The name of the variable to fetch the value from
717 * @return mixed The value of the given variable (string) or NULL if variable did not exist
718 */
719 abstract protected function getVariable($name);
720
721 /**
722 * Gets the usergroup list of the current user.
723 *
724 * @return string The usergroup list of the current user
725 */
726 abstract protected function getGroupList();
727
728 /**
729 * Determines the current page Id.
730 *
731 * @return int The current page Id
732 */
733 abstract protected function determinePageId();
734
735 /**
736 * Gets the properties for the current page.
737 *
738 * @return array The properties for the current page.
739 */
740 abstract protected function getPage();
741
742 /**
743 * Determines the rootline for the current page.
744 *
745 * @return array The rootline for the current page.
746 */
747 abstract protected function determineRootline();
748
749 /**
750 * Gets the id of the current user.
751 *
752 * @return int The id of the current user
753 */
754 abstract protected function getUserId();
755
756 /**
757 * Determines if a user is logged in.
758 *
759 * @return bool Determines if a user is logged in
760 */
761 abstract protected function isUserLoggedIn();
762
763 /**
764 * Sets a log message.
765 *
766 * @param string $message The log message to set/write
767 * @return void
768 */
769 abstract protected function log($message);
770 }