Completed
Push — 7LTS_compatible ( b51d35...40d99a )
by Tomas Norre
32:08
created

VisibilityService::__construct()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 10
rs 9.2
c 1
b 0
f 0
cc 4
eloc 7
nc 3
nop 0
1
<?php
2
3
namespace AOE\Languagevisibility\Services;
4
5
/***************************************************************
6
 * Copyright notice
7
 *
8
 * (c) 2007 AOE media ([email protected])
9
 * All rights reserved
10
 *
11
 * This script is part of the TYPO3 project. The TYPO3 project is
12
 * free software; you can redistribute it and/or modify
13
 * it under the terms of the GNU General Public License as published by
14
 * the Free Software Foundation; either version 2 of the License, or
15
 * (at your option) any later version.
16
 *
17
 * The GNU General Public License can be found at
18
 * http://www.gnu.org/copyleft/gpl.html.
19
 *
20
 * This script is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 * GNU General Public License for more details.
24
 *
25
 * This copyright notice MUST APPEAR in all copies of the script!
26
 ***************************************************************/
27
use AOE\Languagevisibility\PageElement;
28
29
/**
30
 *
31
 * @author	Daniel Poetzinger <[email protected]>
32
 * @coauthor Tolleiv Nietsch <[email protected]>
33
 * @coauthor Timo Schmidt <[email protected]>
34
 */
35
class VisibilityService {
36
37
	/**
38
	 * @var boolean holds the state if inheritance is enabled or not
39
	 */
40
	protected static $useInheritance;
41
42
	/**
43
	 * @var array
44
	 */
45
	private static $supportedTables;
46
47
	/**
48
	 * Constructor of the service, used to initialize the service with the usage of the inheritance feature.
49
	 */
50
	public function __construct() {
51
		if (!isset(self::$useInheritance)) {
52
			$confArr = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['languagevisibility']);
53
			if (is_array($confArr) && $confArr['inheritanceEnabled']) {
54
				self::setUseInheritance();
55
			} else {
56
				self::setUseInheritance(false);
57
			}
58
		}
59
	}
60
61
	/**
62
	 * This method returns the configuration of the inheritance flag. If an inheritance flag is set
63
	 * this method can be used to read it.
64
	 *
65
	 * @return boolean
66
	 */
67
	public static function getUseInheritance() {
68
		return self::$useInheritance;
69
	}
70
71
	/**
72
	 * Function to configure the visibilityService to use inherited settings.
73
	 *
74
	 * @param boolean $useInheritance
75
	 */
76
	public static function setUseInheritance($useInheritance = TRUE) {
77
		self::$useInheritance = $useInheritance;
78
	}
79
80
	/**
81
	 * returns relevant languageid for overlay record or FALSE if element is not visible for guven language
82
	 *
83
	 * @param tx_languagevisibility_language $language
84
	 * @param tx_languagevisibility_element $element
85
	 * @return mixed
86
	 */
87
	function getOverlayLanguageIdForLanguageAndElement(Language $language, Element $element) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
88
		if ($this->isVisible($language, $element)) {
89
			return $this->_relevantOverlayLanguageId;
0 ignored issues
show
Bug introduced by
The property _relevantOverlayLanguageId does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
90
		} else {
91
			return FALSE;
92
		}
93
	}
94
95
	/**
96
	 * currently used to get correct r
97
	 * page rootline - also if a page in rootline is not vivible
98
	 *
99
	 * @todo can this resolved diffrent? the relevantOverlayLanguageId is set in isVisible
100
	 * @return int
101
	 */
102
	public function getLastRelevantOverlayLanguageId() {
103
		return $this->_relevantOverlayLanguageId;
104
	}
105
106
	/**
107
	 * Gets the tables configured with language visibility support.
108
	 *
109
	 * @static
110
	 * @return array with all supported tables
111
	 */
112
	public static function getSupportedTables() {
113
		if (!isset(self::$supportedTables)) {
114
			self::$supportedTables = array('pages', 'tt_content', 'tt_news', 'pages_language_overlay');
115
116
			if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['languagevisibility']['getElementForTable'])
117
				&& is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['languagevisibility']['getElementForTable'])
118
			) {
119
				self::$supportedTables = array_merge(
120
					self::$supportedTables,
121
					array_keys($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['languagevisibility']['getElementForTable'])
122
				);
123
			}
124
125
			if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['languagevisibility']['recordElementSupportedTables'])
126
				&& is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['languagevisibility']['recordElementSupportedTables'])
127
			) {
128
				self::$supportedTables = array_merge(
129
					self::$supportedTables,
130
					array_keys($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['languagevisibility']['recordElementSupportedTables'])
131
				);
132
			}
133
		}
134
135
		return self::$supportedTables;
136
	}
137
138
	/**
139
	 * Returns true or FALSE wether the element is visible in the certain language.
140
	 * (sets for internal access only $this->_relevantOverlayLanguageId which holds the overlay languageid)
141
	 *
142
	 * @param tx_languagevisibility_language $language
143
	 * @param \tx_languagevisibility_element $element
144
	 * @param bool $omitLocal
145
	 * @throws Exception
146
	 * @return boolean
147
	 */
148
	public function isVisible(Language $language, Element $element, $omitLocal = FALSE) {
149
		$this->_relevantOverlayLanguageId = $language->getUid();
150
151
		$visibility = $this->getVisibilitySetting($language, $element, $omitLocal);
152
		if ($visibility == 'yes') {
153
			if (!$element->hasTranslation($language->getUid())) {
154
				$this->_relevantOverlayLanguageId = 0;
155
			}
156
			$result = TRUE;
157
		} elseif ($visibility == 'no+') {
158
			$result = FALSE;
159
		} elseif ($visibility == 'no') {
160
			$result = FALSE;
161
		} elseif ($visibility == 't') {
162
			if ($element->hasTranslation($language->getUid())) {
163
				$result = TRUE;
164
			} else {
165
				$result = FALSE;
166
			}
167
		} elseif ($visibility == 'f') {
168
			if ($element->hasTranslation($language->getUid())) {
169
				$result = TRUE;
170
			} else {
171
				$result = FALSE;
172
173
					// there is no direct translation for this element, therefore check languages in fallback
174
				$fallBackOrder = $element->getFallbackOrder($language);
175
				if (!is_array($fallBackOrder)) {
176
					throw new Exception(print_r($element, TRUE));
177
				}
178
179
				foreach ($fallBackOrder as $languageid) {
180
					if ($element->hasTranslation($languageid)) {
181
						$this->_relevantOverlayLanguageId = $languageid;
182
						$result = TRUE;
183
						break;
184
					}
185
				}
186
			}
187
		} else {
188
				// no setting or default:
189
			if ($language->getUid() == '0') {
190
				$result = TRUE;
191
			} else {
192
				$result = FALSE;
193
			}
194
		}
195
		return $result;
196
	}
197
198
	/**
199
	 * This method is used to get all bequeathing elements of an element (makes only sence for pages)
200
	 * it checks if there is any element in the rootline which has any inherited visibility setting (like no+, yes+)  as configured visibility.
201
	 *
202
	 * @param tx_languagevisibility_language
203
	 * @param tx_languagevisibility_element
204
	 *
205
	 * @return tx_languagevisibility_visibility $visibility
206
	 */
207
	protected function getInheritedVisibility(Language $language, Element $element) {
208
209
		$dao = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('tx_languagevisibility_daocommon');
210
		$elementfactory = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('tx_languagevisibility_elementFactory', $dao);
211
		$elements = $elementfactory->getParentElementsFromElement($element, $language);
212
213
		if (is_array($elements) && count($elements) > 0) {
214
			foreach ($elements as $element) {
215
				/* @var $element PageElement */
216
				$visibility = new Visibility();
217
				$visibility->setVisibilityString($element->getLocalVisibilitySetting($language->getUid()));
218
					// is the setting a inheritable setting:
219
				if ($visibility->getVisibilityString() == 'no+' || $visibility->getVisibilityString() == 'yes+') {
220
					$visibility->setVisibilityDescription('inherited from uid ' . $element->getUid());
221
					return $visibility;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $visibility; (AOE\Languagevisibility\Services\Visibility) is incompatible with the return type documented by AOE\Languagevisibility\S...:getInheritedVisibility of type AOE\Languagevisibility\S...gevisibility_visibility.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
222
				}
223
			}
224
		}
225
226
		if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['languagevisibility']['getInheritedVisibility'])) {
227
			foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['languagevisibility']['getInheritedVisibility'] as $classRef) {
228
				$hookObj = \TYPO3\CMS\Core\Utility\GeneralUtility::getUserObj($classRef);
229
				if (method_exists($hookObj, 'getInheritedVisibility')) {
230
					$visibility = $hookObj->getInheritedVisibility($language, $elements, $element);
231
					if (substr($visibility->getVisibilityString(), -1) == '+') {
232
						return $visibility;
233
					}
234
				}
235
			}
236
		}
237
238
		$visibility = new Visibility();
239
		$visibility->setVisibilityString('-');
240
241
		return $visibility;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $visibility; (AOE\Languagevisibility\Services\Visibility) is incompatible with the return type documented by AOE\Languagevisibility\S...:getInheritedVisibility of type AOE\Languagevisibility\S...gevisibility_visibility.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
242
	}
243
244
	/**
245
	 * return the accumulated visibility setting: reads default for language then reads local for element and merges them.
246
	 * if local is default, then the global is used or it is forced to be "yes" if the language was set to all.
247
	 * if the element itself is a translated original record the element is only visible in the specific language
248
	 * If nothing is set the hardcoded default "t" (translated) is returned
249
	 *
250
	 * @param tx_languagevisibility_language $language
251
	 * @param tx_languagevisibility_element $element
252
	 * @param boolean
253
	 * @return string
254
	 */
255
	public function getVisibilitySetting(Language $language, Element $element, $omitLocal = FALSE) {
256
		$cacheManager = CacheManager::getInstance();
257
		$cacheData = $cacheManager->get('visibilitySettingCache');
258
		$isCacheEnabled = $cacheManager->isCacheEnabled();
259
260
		$elementTable = $element->getTable();
261
		$elementUid = $element->getUid();
262
		$languageUid = $language->getUid();
263
264
		$cacheKey = $languageUid . '_' . $elementUid . '_' . $elementTable . '_' . $omitLocal;
265
		if (!$isCacheEnabled || !isset($cacheData[$cacheKey])) {
266
			$cacheData[$cacheKey] = $this->getVisibility($language, $element, $omitLocal)->getVisibilityString();
267
			$cacheManager->set('visibilitySettingCache', $cacheData);
268
		}
269
270
		return $cacheData[$cacheKey];
271
	}
272
273
	/**
274
	 * This method can be used to retrieve an informal description for the visibility of an element
275
	 *
276
	 * @param tx_languagevisibility_language $language
277
	 * @param tx_languagevisibility_element $element
278
	 * @return string
279
	 */
280
	public function getVisibilityDescription(Language $language, Element $element) {
281
		return $this->getVisibility($language, $element)->getVisibilityDescription();
282
	}
283
284
	/**
285
	 * Create a visiblity object for an element for a given language.
286
	 * @param tx_languagevisibility_language $language
287
	 * @param tx_languagevisibility_element $element
288
	 * @param boolean $omitLocal
289
	 * @return tx_languagevisibility_visibility
290
	 */
291
	protected function getVisibility(Language $language, Element $element, $omitLocal = FALSE) {
292
		$visibility = new Visibility();
293
		$local = $element->getLocalVisibilitySetting($language->getUid());
294
295
		if (!$omitLocal && ($local != '' && $local != '-')) {
296
			$visibility->setVisibilityString($local)->setVisibilityDescription('local setting ' . $local);
297
			return $visibility;
298
		} else {
299
			if ($element->isLanguageSetToAll()) {
300
				$visibility->setVisibilityString('yes')->setVisibilityDescription('language configured to all');
301
				return $visibility;
302
			}
303
304
			if ($element->isMonolithicTranslated()) {
305
				if ($element->languageEquals($language)) {
306
					$visibility->setVisibilityString('yes')->setVisibilityDescription('');
307
				} else {
308
					$visibility->setVisibilityString('no')->setVisibilityDescription('');
309
				}
310
311
				return $visibility;
312
			}
313
314
			if ($element->getFieldToUseForDefaultVisibility() == 'page') {
315
				if ($this->getUseInheritance()) {
316
						// gibt es in der rootline das visibiklitysetting no+ für die sprache dann return 'no'
317
					$inheritedVisibility = $this->getInheritedVisibility($language, $element);
318
319
					switch ($inheritedVisibility->getVisibilityString()) {
320
						case 'no+' :
321
								// if no+ is found it means the current element should be threated as if it has no set
322
							$visibility->setVisibilityString('no')->setVisibilityDescription('force to no (' . $inheritedVisibility->getVisibilityDescription() . ')');
323
							break;
324
						case 'yes+' :
325
							$visibility->setVisibilityString('yes')->setVisibilityDescription('force to yes (' . $inheritedVisibility->getVisibilityDescription() . ')');
326
							break;
327
						default :
328
							$setting = $language->getDefaultVisibilityForPage($element);
329
							$visibility->setVisibilityString($setting)->setVisibilityDescription('default visibility  for page (' . $setting . ')');
330
							break;
331
					}
332
				} else {
333
						// inheritance is disabled
334
					$setting = $language->getDefaultVisibilityForPage($element);
335
					$visibility->setVisibilityString($setting)->setVisibilityDescription('default visibility  for page (' . $setting . ')');
336
				}
337
			} elseif ($element->getFieldToUseForDefaultVisibility() == 'tt_news') {
338
				$setting = $language->getDefaultVisibilityForTTNewsElement($element);
339
				$visibility->setVisibilityString($setting)->setVisibilityDescription('default visibility  for news (' . $setting . ')');
340
			} else {
341
				$setting = $language->getDefaultVisibilityForElement($element);
342
				$visibility->setVisibilityString($setting)->setVisibilityDescription('default visibility  for element (' . $setting . ')');
343
			}
344
345
			if ($visibility->getVisibilityString() == '') {
346
				$visibility->setVisibilityString('t')->setVisibilityDescription('no visibility configured using default setting "t"');
347
			}
348
349
			return $visibility;
350
		}
351
	}
352
}
353