BackendServices.php 9.24 KB
Newer Older
Fabian Galinski's avatar
Fabian Galinski committed
1 2 3
<?php

namespace TYPO3\Languagevisibility\Service;
4 5

use Exception;
6 7
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
use TYPO3\CMS\Core\Site\SiteFinder;
Fabian Galinski's avatar
Fabian Galinski committed
8 9 10 11
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
use TYPO3\Languagevisibility\Element\Element;
use TYPO3\Languagevisibility\Element\ElementFactory;
Kevin Ditscheid's avatar
Kevin Ditscheid committed
12
use TYPO3\Languagevisibility\Utility\ExtensionUtility;
Fabian Galinski's avatar
Fabian Galinski committed
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36

/***************************************************************
 * Copyright notice
 *
 * (c) 2007 AOE media (dev@aoemedia.de)
 * All rights reserved
 *
 * This script is part of the TYPO3 project. The TYPO3 project is
 * free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * The GNU General Public License can be found at
 * http://www.gnu.org/copyleft/gpl.html.
 *
 * This script is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * This copyright notice MUST APPEAR in all copies of the script!
 ***************************************************************/

Stefan Galinski's avatar
Stefan Galinski committed
37 38 39 40 41 42
/**
 * Class BackendServices
 *
 * @package TYPO3\Languagevisibility\Service
 */
class BackendServices extends AbstractServices {
43

Kevin Ditscheid's avatar
Kevin Ditscheid committed
44 45 46
	/**
	 * @var array
	 */
47
	protected static $cache_isVisible = [];
Fabian Galinski's avatar
Fabian Galinski committed
48 49 50 51

	/**
	 * Helper function to check i an element with a given uid and tablename is visible for a languageid.
	 *
52
	 * @param array $row
Fabian Galinski's avatar
Fabian Galinski committed
53 54
	 * @param string $table
	 * @param int $languageUid
55
	 * @param boolean $omitLocal DEPRECATED since TYPO3 CMS 9 - is not used anymore because we only use the language visibility settings from the default language record
56
	 * @return bool
57
	 * @throws Exception
Fabian Galinski's avatar
Fabian Galinski committed
58
	 */
59
	public static function isVisible(array $row, string $table, int $languageUid, bool $omitLocal = FALSE): bool {
Fabian Galinski's avatar
Fabian Galinski committed
60

61
		$cacheKey = sprintf('%s:%d:%d', $table, $row['uid'], $languageUid);
Fabian Galinski's avatar
Fabian Galinski committed
62 63

		if (!isset(self::$cache_isVisible[$cacheKey])) {
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81

			$siteFinder = GeneralUtility::makeInstance(SiteFinder::class);

			if ($table === 'pages') {
				$pid = $row['uid'];
			} else {
				$pid = $row['pid'];
			}

			try {
				$site = $siteFinder->getSiteByPageId($pid);
			} catch (\Exception $e) {
				return FALSE;
			}

			$language = $site->getLanguageById($languageUid);

			if (!$language instanceof SiteLanguage) {
82 83
				return FALSE;
			}
Fabian Galinski's avatar
Fabian Galinski committed
84

85
			$elementfactory = GeneralUtility::makeInstance(ElementFactory::class);
Fabian Galinski's avatar
Fabian Galinski committed
86 87

			try {
88
				$element = $elementfactory->getElementForTable($table, $row);
89
			} catch (Exception $e) {
Fabian Galinski's avatar
Fabian Galinski committed
90 91 92 93 94
				return FALSE;
			}

			$visibility = GeneralUtility::makeInstance(VisibilityService::class);

95
			self::$cache_isVisible[$cacheKey] = $visibility->isVisible($language, $element);
Fabian Galinski's avatar
Fabian Galinski committed
96 97 98 99 100 101 102 103 104 105
		}

		return self::$cache_isVisible[$cacheKey];
	}

	/**
	 * Helper function to check if a record from a given table in an overlayrecord
	 *
	 * @param array $row
	 * @param string $table
106
	 * @return bool
Fabian Galinski's avatar
Fabian Galinski committed
107
	 */
108
	public static function isOverlayRecord(array $row, string $table): bool {
Fabian Galinski's avatar
Fabian Galinski committed
109
		$result = FALSE;
110

111
		if (in_array($table, VisibilityService::getSupportedTables(), TRUE)) {
112 113
			$translationIdField = $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'];
			if ($translationIdField !== '') {
114
				// if the field which points to the original of the translation is
115
				// not 0 a translation exists and we have an overlay record
116 117 118 119 120 121
				if (is_array($row[$translationIdField])) {
					// somehow the sys_language_uid field could contain 0 => 0
					$result = (int) $row[$translationIdField][0] !== 0;
				} else {
					$result = (int) $row[$translationIdField] !== 0;
				}
122
			}
Fabian Galinski's avatar
Fabian Galinski committed
123 124 125 126 127 128 129 130 131 132
		}

		return $result;
	}

	/**
	 * checks if the current BE_USER has access to the page record:
	 * that is the case if:
	 * a) new page created -> always because then the languagevisibility is set to never for all languages where the user has no access
	 * b) edit page record: only if the record is only visible in languages where the user has access to
133
	 * b.1) also if the languages that are visible and falls back to allowed languages
Fabian Galinski's avatar
Fabian Galinski committed
134
	 * c) delete: same as for edit (only if user has access to all visible languages)
135 136 137 138
	 *
	 * @param array $row
	 * @param string $cmd
	 * @return bool
139
	 * @throws Exception
Fabian Galinski's avatar
Fabian Galinski committed
140
	 */
141
	public static function hasUserAccessToPageRecord(array $row, string $cmd = 'edit'): bool {
Kevin Ditscheid's avatar
Kevin Ditscheid committed
142
		if ($cmd === 'new') {
Fabian Galinski's avatar
Fabian Galinski committed
143 144
			return TRUE;
		}
145 146 147 148 149 150 151 152 153 154 155 156 157 158

		$siteFinder = GeneralUtility::makeInstance(SiteFinder::class);

		try {
			$site = $siteFinder->getSiteByPageId($row['uid']);
		} catch (\Exception $e) {
			return FALSE;
		}

		$availableLanguages = $site->getAllLanguages();

		foreach ($availableLanguages as $language) {
			if (self::isVisible($row, 'pages', $language->getLanguageId())) {
				if (!$GLOBALS['BE_USER']->checkLanguageAccess($language->getLanguageId())) {
159
					//no access to a visible language: check fallbacks
Fabian Galinski's avatar
Fabian Galinski committed
160
					$isInFallback = FALSE;
161 162
					$fallbacks = $language->getFallbackLanguageIds();

Fabian Galinski's avatar
Fabian Galinski committed
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
					foreach ($fallbacks as $lId) {
						if ($GLOBALS['BE_USER']->checkLanguageAccess($lId)) {
							$isInFallback = TRUE;
							continue;
						}
					}
					if (!$isInFallback) {
						return FALSE;
					}
				}
			}
		}
		return TRUE;
	}

	/**
	 * checks if the current BE_USER has access to a record:
	 * that is the case if:
	 * a) new page created -> always because then the languagevisibility is set to never for all languages where the user has no access
	 * b) edit page record: only if the record is only visible in languages where the user has access to
183 184 185 186
	 *
	 * @param string $table
	 * @param array $row
	 * @return bool
187
	 * @throws Exception
Fabian Galinski's avatar
Fabian Galinski committed
188
	 */
189
	public static function hasUserAccessToEditRecord(string $table, array $row): bool {
Fabian Galinski's avatar
Fabian Galinski committed
190 191 192 193 194 195 196 197
		if (!self::isSupportedTable($table)) {
			return TRUE;
		}

		if (self::isOverlayRecord($row, $table)) {
			if ($GLOBALS['BE_USER']->checkLanguageAccess($row['sys_language_uid'])) {
				return TRUE;
			}
198 199

			return FALSE;
Fabian Galinski's avatar
Fabian Galinski committed
200 201
		}

202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
		$siteFinder = GeneralUtility::makeInstance(SiteFinder::class);

		if ($table === 'pages') {
			$pid = $row['uid'];
		} else {
			$pid = $row['pid'];
		}

		try {
			$site = $siteFinder->getSiteByPageId($pid);
		} catch (\Exception $e) {
			return FALSE;
		}

		$availableLanguages = $site->getAllLanguages();

		foreach ($availableLanguages as $language) {
			if (self::isVisible($row, $table, $language->getLanguageId())) {
				if (!$GLOBALS['BE_USER']->checkLanguageAccess($language->getLanguageId())) {
221
					// no access to a visible language: check fallbacks
Fabian Galinski's avatar
Fabian Galinski committed
222
					$isInFallback = FALSE;
223 224
					$fallbacks = $language->getFallbackLanguageIds();

Fabian Galinski's avatar
Fabian Galinski committed
225 226
					foreach ($fallbacks as $lId) {
						if ($GLOBALS['BE_USER']->checkLanguageAccess($lId)) {
227
							// TODO - write testcase - this can't be right
Fabian Galinski's avatar
Fabian Galinski committed
228 229 230 231
							$isInFallback = TRUE;
							continue;
						}
					}
232

Fabian Galinski's avatar
Fabian Galinski committed
233 234 235 236 237 238
					if (!$isInFallback) {
						return FALSE;
					}
				}
			}
		}
239

Fabian Galinski's avatar
Fabian Galinski committed
240 241 242 243
		return TRUE;
	}

	/**
Fabio Stegmeyer's avatar
Fabio Stegmeyer committed
244
	 * Method to check if the translatedAsDefaultEnabled is enabled or not
Fabian Galinski's avatar
Fabian Galinski committed
245 246 247
	 *
	 * @return boolean
	 */
248
	protected static function isTranslatedAsDefaultEnabled(): bool {
Kevin Ditscheid's avatar
Kevin Ditscheid committed
249
		$confArr = ExtensionUtility::getExtensionConfiguration();
Fabian Galinski's avatar
Fabian Galinski committed
250
		if (is_array($confArr)) {
Kevin Ditscheid's avatar
Kevin Ditscheid committed
251
			return (bool) $confArr['translatedAsDefaultEnabled'];
Fabian Galinski's avatar
Fabian Galinski committed
252
		}
253 254

		return FALSE;
Fabian Galinski's avatar
Fabian Galinski committed
255 256 257 258 259
	}

	/**
	 * returns array with the visibility options that are allowed for the current user.
	 *
260
	 * @param SiteLanguage $language
Fabian Galinski's avatar
Fabian Galinski committed
261
	 * @param bool $isOverlay
262
	 * @param Element $element
Fabian Galinski's avatar
Fabian Galinski committed
263 264
	 * @return array
	 */
265
	public static function getAvailableOptionsForLanguage(
266
		SiteLanguage $language, bool $isOverlay = FALSE, Element $element = NULL
267
	): array {
268

269
		$uid = $language->getLanguageId();
270
		$select = [];
Fabian Galinski's avatar
Fabian Galinski committed
271

272
		if (!$isOverlay) {
Kevin Ditscheid's avatar
Kevin Ditscheid committed
273
			if ($uid === 0) {
274 275 276
				$select['active'] = 'active';
				$select['enforce'] = 'enforce';
				$select['inactive'] = 'inactive';
Fabian Galinski's avatar
Fabian Galinski committed
277
			} else {
278 279 280 281 282
				$select['active'] = 'active';
				$select['enforce'] = 'enforce';
				$select['translated'] = 'translated';
				$select['fallback'] = 'fallback';
				$select['inactive'] = 'inactive';
Fabian Galinski's avatar
Fabian Galinski committed
283 284
			}

285
			//check permissions, if user has no permission only no for the language is allowed
286
			// if the user has permissions for languages that act as fallback language: then the languages that falls back can have "-" in the options!
287
			if (!$GLOBALS['BE_USER']->checkLanguageAccess($uid)) {
Fabian Galinski's avatar
Fabian Galinski committed
288

289
				//check if the language falls back to one of the languages the user has permissions:
Fabian Galinski's avatar
Fabian Galinski committed
290
				$isInFallback = FALSE;
291 292
				$fallbacks = $language->getFallbackLanguageIds();

293
				foreach ($fallbacks as $lId) {
Fabian Galinski's avatar
Fabian Galinski committed
294 295 296 297 298
					if ($GLOBALS['BE_USER']->checkLanguageAccess($lId)) {
						$isInFallback = TRUE;
						continue;
					}
				}
299
				$select = [];
Fabian Galinski's avatar
Fabian Galinski committed
300
				if ($isInFallback) {
301
					$select['active'] = 'active';
Fabian Galinski's avatar
Fabian Galinski committed
302 303
				}

Kevin Ditscheid's avatar
Kevin Ditscheid committed
304
				if ($uid !== 0 && self::isTranslatedAsDefaultEnabled()) {
305
					$select['translated'] = 'translated';
Fabian Galinski's avatar
Fabian Galinski committed
306
				}
307
				$select['inactive'] = 'inactive';
Fabian Galinski's avatar
Fabian Galinski committed
308 309
			}
		} else {
Fabio Stegmeyer's avatar
Fabio Stegmeyer committed
310
			//overlays elements can only have "force to no"
311 312
			$select['active'] = 'active';
			$select['inactive'] = 'inactive';
Fabian Galinski's avatar
Fabian Galinski committed
313 314 315 316 317
		}

		/**
		 * Get translations of labels from the locallang file
		 */
318 319 320 321 322
		foreach ($select as $k => $v) {
			$select[$k] = LocalizationUtility::translate(
				'LLL:EXT:languagevisibility/Resources/Private/Language/locallang_db.xlf:tx_languagevisibility_visibility.I.' . $v,
				'languagevisibility'
			);
Fabian Galinski's avatar
Fabian Galinski committed
323 324 325 326 327
		}

		return $select;
	}
}