[FEATURE] Allow .ts file extension for static typoscript templates
[Packages/TYPO3.CMS.git] / t3lib / class.t3lib_userauthgroup.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 1999-2011 Kasper Skårhøj (kasperYYYY@typo3.com)
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
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
28 /**
29 * Extension to class.t3lib_userauth.php; Authentication of users in TYPO3 Backend
30 *
31 * Actually this class is extended again by t3lib_beuserauth which is the actual backend user class that will be instantiated.
32 * In fact the two classes t3lib_beuserauth and this class could just as well be one, single class since t3lib_userauthgroup is not - to my knowledge - used separately elsewhere. But for historical reasons they are two separate classes.
33 *
34 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
35 * @package TYPO3
36 * @subpackage t3lib
37 * @deprecated since 6.0, will be removed in 6.2. The functionality was merged wih t3lib_beUserAuth
38 */
39 abstract class t3lib_userAuthGroup extends t3lib_userAuth {
40 // Should be set to the usergroup-column (id-list) in the user-record
41 var $usergroup_column = 'usergroup';
42 // The name of the group-table
43 var $usergroup_table = 'be_groups';
44
45 // Internal
46 // This array holds lists of eg. tables, fields and other values related to the permission-system. See fetchGroupData
47 var $groupData = array(
48 'filemounts' => array() // Filemounts are loaded here
49 );
50 // User workspace. -99 is ERROR (none available), -1 is offline, 0 is online, >0 is custom workspaces.
51 var $workspace = -99;
52 // Custom workspace record if any
53 var $workspaceRec = array();
54 // This array will hold the groups that the user is a member of
55 var $userGroups = array();
56 // This array holds the uid's of the groups in the listed order
57 var $userGroupsUID = array();
58 // This is $this->userGroupsUID imploded to a comma list... Will correspond to the 'usergroup_cached_list'
59 var $groupList = '';
60 // Used internally to accumulate data for the user-group. DONT USE THIS EXTERNALLY! Use $this->groupData instead
61 var $dataLists = array(
62 'webmount_list' => '',
63 'filemount_list' => '',
64 'fileoper_perms' => 0,
65 'modList' => '',
66 'tables_select' => '',
67 'tables_modify' => '',
68 'pagetypes_select' => '',
69 'non_exclude_fields' => '',
70 'explicit_allowdeny' => '',
71 'allowed_languages' => '',
72 'workspace_perms' => '',
73 'custom_options' => '',
74 );
75 // For debugging/display of order in which subgroups are included.
76 var $includeHierarchy = array();
77 // List of group_id's in the order they are processed.
78 var $includeGroupArray = array();
79 // Set to 'WIN', if windows
80 var $OS = '';
81 // Used to accumulate the TSconfig data of the user
82 var $TSdataArray = array();
83 // Contains the non-parsed user TSconfig
84 var $userTS_text = '';
85 // Contains the parsed user TSconfig
86 var $userTS = array();
87 // Set internally if the user TSconfig was parsed and needs to be cached.
88 var $userTSUpdated = 0;
89 // Set this from outside if you want the user TSconfig to ALWAYS be parsed and not fetched from cache.
90 var $userTS_dontGetCached = 0;
91 // RTE availability errors collected.
92 var $RTE_errors = array();
93 // Contains last error message
94 var $errorMsg = '';
95 // Cache for checkWorkspaceCurrent()
96 var $checkWorkspaceCurrent_cache = NULL;
97
98 /**
99 * @var t3lib_file_Storage[]
100 */
101 protected $fileStorages;
102
103 /**
104 * @var array
105 */
106 protected $filePermissions;
107
108 /**
109 * Constructor deprecates class.
110 */
111 public function __construct() {
112 t3lib_div::logDeprecatedFunction(
113 'Class t3lib_userAuthGroup is deprecated and unused since TYPO3 6.0. ' .
114 'All methods are merged with t3lib_beUserAuth.'
115 );
116 }
117
118 /************************************
119 *
120 * Permission checking functions:
121 *
122 ************************************/
123
124 /**
125 * Returns TRUE if user is admin
126 * Basically this function evaluates if the ->user[admin] field has bit 0 set. If so, user is admin.
127 *
128 * @return boolean
129 */
130 function isAdmin() {
131 return (($this->user['admin'] & 1) == 1);
132 }
133
134 /**
135 * Returns TRUE if the current user is a member of group $groupId
136 * $groupId must be set. $this->groupList must contain groups
137 * Will return TRUE also if the user is a member of a group through subgroups.
138 *
139 * @param integer $groupId Group ID to look for in $this->groupList
140 * @return boolean
141 */
142 function isMemberOfGroup($groupId) {
143 $groupId = intval($groupId);
144 if ($this->groupList && $groupId) {
145 return t3lib_div::inList($this->groupList, $groupId);
146 }
147 }
148
149 /**
150 * Checks if the permissions is granted based on a page-record ($row) and $perms (binary and'ed)
151 *
152 * Bits for permissions, see $perms variable:
153 *
154 * 1 - Show: See/Copy page and the pagecontent.
155 * 16- Edit pagecontent: Change/Add/Delete/Move pagecontent.
156 * 2- Edit page: Change/Move the page, eg. change title, startdate, hidden.
157 * 4- Delete page: Delete the page and pagecontent.
158 * 8- New pages: Create new pages under the page.
159 *
160 * @param array $row Is the pagerow for which the permissions is checked
161 * @param integer $perms Is the binary representation of the permission we are going to check. Every bit in this number represents a permission that must be set. See function explanation.
162 * @return boolean TRUE or False upon evaluation
163 */
164 function doesUserHaveAccess($row, $perms) {
165 $userPerms = $this->calcPerms($row);
166 return ($userPerms & $perms) == $perms;
167 }
168
169 /**
170 * Checks if the page id, $id, is found within the webmounts set up for the user.
171 * This should ALWAYS be checked for any page id a user works with, whether it's about reading, writing or whatever.
172 * The point is that this will add the security that a user can NEVER touch parts outside his mounted pages in the page tree. This is otherwise possible if the raw page permissions allows for it. So this security check just makes it easier to make safe user configurations.
173 * If the user is admin OR if this feature is disabled (fx. by setting TYPO3_CONF_VARS['BE']['lockBeUserToDBmounts']=0) then it returns "1" right away
174 * Otherwise the function will return the uid of the webmount which was first found in the rootline of the input page $id
175 *
176 * @param integer $id Page ID to check
177 * @param string $readPerms Content of "->getPagePermsClause(1)" (read-permissions). If not set, they will be internally calculated (but if you have the correct value right away you can save that database lookup!)
178 * @param boolean $exitOnError If set, then the function will exit with an error message.
179 * @return integer The page UID of a page in the rootline that matched a mount point
180 */
181 function isInWebMount($id, $readPerms = '', $exitOnError = 0) {
182 if (!$GLOBALS['TYPO3_CONF_VARS']['BE']['lockBeUserToDBmounts'] || $this->isAdmin()) {
183 return 1;
184 }
185 $id = intval($id);
186
187 // Check if input id is an offline version page in which case we will map id to the online version:
188 $checkRec = t3lib_beFUnc::getRecord('pages', $id, 'pid,t3ver_oid');
189 if ($checkRec['pid'] == -1) {
190 $id = intval($checkRec['t3ver_oid']);
191 }
192
193 if (!$readPerms) {
194 $readPerms = $this->getPagePermsClause(1);
195 }
196 if ($id > 0) {
197 $wM = $this->returnWebmounts();
198 $rL = t3lib_BEfunc::BEgetRootLine($id, ' AND ' . $readPerms);
199
200 foreach ($rL as $v) {
201 if ($v['uid'] && in_array($v['uid'], $wM)) {
202 return $v['uid'];
203 }
204 }
205 }
206 if ($exitOnError) {
207 throw new RuntimeException('Access Error: This page is not within your DB-mounts', 1294586445);
208 }
209 }
210
211 /**
212 * Checks access to a backend module with the $MCONF passed as first argument
213 *
214 * @param array $conf $MCONF array of a backend module!
215 * @param boolean $exitOnError If set, an array will issue an error message and exit.
216 * @return boolean Will return TRUE if $MCONF['access'] is not set at all, if the BE_USER is admin or if the module is enabled in the be_users/be_groups records of the user (specifically enabled). Will return FALSE if the module name is not even found in $TBE_MODULES
217 */
218 function modAccess($conf, $exitOnError) {
219 if (!t3lib_BEfunc::isModuleSetInTBE_MODULES($conf['name'])) {
220 if ($exitOnError) {
221 throw new RuntimeException('Fatal Error: This module "' . $conf['name'] . '" is not enabled in TBE_MODULES', 1294586446);
222 }
223 return FALSE;
224 }
225
226 // Workspaces check:
227 if (t3lib_extMgm::isLoaded('workspaces') && $conf['workspaces']) {
228 if (($this->workspace === 0 && t3lib_div::inList($conf['workspaces'], 'online')) ||
229 ($this->workspace === -1 && t3lib_div::inList($conf['workspaces'], 'offline')) ||
230 ($this->workspace > 0 && t3lib_div::inList($conf['workspaces'], 'custom'))) {
231 // ok, go on...
232 } else {
233 if ($exitOnError) {
234 throw new RuntimeException('Workspace Error: This module "' . $conf['name'] . '" is not available under the current workspace', 1294586447);
235 }
236 return FALSE;
237 }
238 }
239
240 // Returns TRUE if conf[access] is not set at all or if the user is admin
241 if (!$conf['access'] || $this->isAdmin()) {
242 return TRUE;
243 }
244
245 // If $conf['access'] is set but not with 'admin' then we return TRUE, if the module is found in the modList
246 if (!strstr($conf['access'], 'admin') && $conf['name']) {
247 $acs = $this->check('modules', $conf['name']);
248 }
249 if (!$acs && $exitOnError) {
250 throw new RuntimeException('Access Error: You don\'t have access to this module.', 1294586448);
251 } else {
252 return $acs;
253 }
254 }
255
256 /**
257 * Returns a WHERE-clause for the pages-table where user permissions according to input argument, $perms, is validated.
258 * $perms is the "mask" used to select. Fx. if $perms is 1 then you'll get all pages that a user can actually see!
259 * 2^0 = show (1)
260 * 2^1 = edit (2)
261 * 2^2 = delete (4)
262 * 2^3 = new (8)
263 * If the user is 'admin' " 1=1" is returned (no effect)
264 * If the user is not set at all (->user is not an array), then " 1=0" is returned (will cause no selection results at all)
265 * The 95% use of this function is "->getPagePermsClause(1)" which will return WHERE clauses for *selecting* pages in backend listings - in other words this will check read permissions.
266 *
267 * @param integer $perms Permission mask to use, see function description
268 * @return string Part of where clause. Prefix " AND " to this.
269 */
270 function getPagePermsClause($perms) {
271 if (is_array($this->user)) {
272 if ($this->isAdmin()) {
273 return ' 1=1';
274 }
275
276 $perms = intval($perms); // Make sure it's integer.
277 $str = ' (' .
278 '(pages.perms_everybody & ' . $perms . ' = ' . $perms . ')' . // Everybody
279 ' OR (pages.perms_userid = ' . $this->user['uid'] . ' AND pages.perms_user & ' . $perms . ' = ' . $perms . ')'; // User
280 if ($this->groupList) {
281 // Group (if any is set)
282 $str .= ' OR (pages.perms_groupid in (' . $this->groupList . ') AND pages.perms_group & ' . $perms . ' = ' . $perms . ')';
283 }
284 $str .= ')';
285
286 // ****************
287 // getPagePermsClause-HOOK
288 // ****************
289 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['getPagePermsClause'])) {
290
291 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['getPagePermsClause'] as $_funcRef) {
292 $_params = array('currentClause' => $str, 'perms' => $perms);
293 $str = t3lib_div::callUserFunction($_funcRef, $_params, $this);
294 }
295 }
296
297 return $str;
298 } else {
299 return ' 1=0';
300 }
301 }
302
303 /**
304 * Returns a combined binary representation of the current users permissions for the page-record, $row.
305 * The perms for user, group and everybody is OR'ed together (provided that the page-owner is the user and for the groups that the user is a member of the group
306 * If the user is admin, 31 is returned (full permissions for all five flags)
307 *
308 * @param array $row Input page row with all perms_* fields available.
309 * @return integer Bitwise representation of the users permissions in relation to input page row, $row
310 */
311 function calcPerms($row) {
312 // Return 31 for admin users.
313
314 if ($this->isAdmin()) {
315 return 31;
316 }
317
318 $out = 0;
319 if (isset($row['perms_userid']) && isset($row['perms_user']) && isset($row['perms_groupid']) && isset($row['perms_group']) && isset($row['perms_everybody']) && isset($this->groupList)) {
320 if ($this->user['uid'] == $row['perms_userid']) {
321 $out |= $row['perms_user'];
322 }
323 if ($this->isMemberOfGroup($row['perms_groupid'])) {
324 $out |= $row['perms_group'];
325 }
326 $out |= $row['perms_everybody'];
327 }
328
329 // ****************
330 // CALCPERMS hook
331 // ****************
332 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['calcPerms'])) {
333 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['calcPerms'] as $_funcRef) {
334 $_params = array(
335 'row' => $row,
336 'outputPermissions' => $out
337 );
338 $out = t3lib_div::callUserFunction($_funcRef, $_params, $this);
339 }
340 }
341
342 return $out;
343 }
344
345 /**
346 * Returns TRUE if the RTE (Rich Text Editor) can be enabled for the user
347 * Strictly this is not permissions being checked but rather a series of settings like a loaded extension, browser/client type and a configuration option in ->uc[edit_RTE]
348 * The reasons for a FALSE return can be found in $this->RTE_errors
349 *
350 * @return boolean
351 */
352 function isRTE() {
353 // Start:
354 $this->RTE_errors = array();
355 if (!$this->uc['edit_RTE']) {
356 $this->RTE_errors[] = 'RTE is not enabled for user!';
357 }
358 if (!$GLOBALS['TYPO3_CONF_VARS']['BE']['RTEenabled']) {
359 $this->RTE_errors[] = 'RTE is not enabled in $TYPO3_CONF_VARS["BE"]["RTEenabled"]';
360 }
361
362 // Acquire RTE object:
363 $RTE = t3lib_BEfunc::RTEgetObj();
364 if (!is_object($RTE)) {
365 $this->RTE_errors = array_merge($this->RTE_errors, $RTE);
366 }
367
368 if (!count($this->RTE_errors)) {
369 return TRUE;
370 } else {
371 return FALSE;
372 }
373 }
374
375 /**
376 * Returns TRUE if the $value is found in the list in a $this->groupData[] index pointed to by $type (array key).
377 * Can thus be users to check for modules, exclude-fields, select/modify permissions for tables etc.
378 * If user is admin TRUE is also returned
379 * Please see the document Inside TYPO3 for examples.
380 *
381 * @param string $type The type value; "webmounts", "filemounts", "pagetypes_select", "tables_select", "tables_modify", "non_exclude_fields", "modules"
382 * @param string $value String to search for in the groupData-list
383 * @return boolean TRUE if permission is granted (that is, the value was found in the groupData list - or the BE_USER is "admin")
384 */
385 function check($type, $value) {
386 if (isset($this->groupData[$type])) {
387 if ($this->isAdmin() || t3lib_div::inList($this->groupData[$type], $value)) {
388 return TRUE;
389 }
390 }
391 return FALSE;
392 }
393
394 /**
395 * Checking the authMode of a select field with authMode set
396 *
397 * @param string $table Table name
398 * @param string $field Field name (must be configured in TCA and of type "select" with authMode set!)
399 * @param string $value Value to evaluation (single value, must not contain any of the chars ":,|")
400 * @param string $authMode Auth mode keyword (explicitAllow, explicitDeny, individual)
401 * @return boolean TRUE or FALSE whether access is granted or not.
402 */
403 function checkAuthMode($table, $field, $value, $authMode) {
404
405 // Admin users can do anything:
406 if ($this->isAdmin()) {
407 return TRUE;
408 }
409
410 // Allow all blank values:
411 if (!strcmp($value, '')) {
412 return TRUE;
413 }
414
415 // Certain characters are not allowed in the value
416 if (preg_match('/[:|,]/', $value)) {
417 return FALSE;
418 }
419
420 // Initialize:
421 $testValue = $table . ':' . $field . ':' . $value;
422 $out = TRUE;
423
424 // Checking value:
425 switch ((string) $authMode) {
426 case 'explicitAllow':
427 if (!t3lib_div::inList($this->groupData['explicit_allowdeny'], $testValue . ':ALLOW')) {
428 $out = FALSE;
429 }
430 break;
431 case 'explicitDeny':
432 if (t3lib_div::inList($this->groupData['explicit_allowdeny'], $testValue . ':DENY')) {
433 $out = FALSE;
434 }
435 break;
436 case 'individual':
437 t3lib_div::loadTCA($table);
438 if (is_array($GLOBALS['TCA'][$table]) && is_array($GLOBALS['TCA'][$table]['columns'][$field])) {
439 $items = $GLOBALS['TCA'][$table]['columns'][$field]['config']['items'];
440 if (is_array($items)) {
441 foreach ($items as $iCfg) {
442 if (!strcmp($iCfg[1], $value) && $iCfg[4]) {
443 switch ((string) $iCfg[4]) {
444 case 'EXPL_ALLOW':
445 if (!t3lib_div::inList($this->groupData['explicit_allowdeny'], $testValue . ':ALLOW')) {
446 $out = FALSE;
447 }
448 break;
449 case 'EXPL_DENY':
450 if (t3lib_div::inList($this->groupData['explicit_allowdeny'], $testValue . ':DENY')) {
451 $out = FALSE;
452 }
453 break;
454 }
455 break;
456 }
457 }
458 }
459 }
460 break;
461 }
462
463 return $out;
464 }
465
466 /**
467 * Checking if a language value (-1, 0 and >0 for sys_language records) is allowed to be edited by the user.
468 *
469 * @param integer $langValue Language value to evaluate
470 * @return boolean Returns TRUE if the language value is allowed, otherwise FALSE.
471 */
472 function checkLanguageAccess($langValue) {
473 // The users language list must be non-blank - otherwise all languages are allowed.
474 if (strcmp(trim($this->groupData['allowed_languages']), '')) {
475 $langValue = intval($langValue);
476 // Language must either be explicitly allowed OR the lang Value be "-1" (all languages)
477 if ($langValue != -1 && !$this->check('allowed_languages', $langValue)) {
478 return FALSE;
479 }
480 }
481 return TRUE;
482 }
483
484 /**
485 * Check if user has access to all existing localizations for a certain record
486 *
487 * @param string $table The table
488 * @param array $record The current record
489 * @return boolean
490 */
491 function checkFullLanguagesAccess($table, $record) {
492 $recordLocalizationAccess = $this->checkLanguageAccess(0);
493 if ($recordLocalizationAccess
494 && (
495 t3lib_BEfunc::isTableLocalizable($table)
496 || isset($GLOBALS['TCA'][$table]['ctrl']['transForeignTable'])
497 )
498 ) {
499
500 if (isset($GLOBALS['TCA'][$table]['ctrl']['transForeignTable'])) {
501 $l10nTable = $GLOBALS['TCA'][$table]['ctrl']['transForeignTable'];
502 $pointerField = $GLOBALS['TCA'][$l10nTable]['ctrl']['transOrigPointerField'];
503 $pointerValue = $record['uid'];
504 } else {
505 $l10nTable = $table;
506 $pointerField = $GLOBALS['TCA'][$l10nTable]['ctrl']['transOrigPointerField'];
507 $pointerValue = $record[$pointerField] > 0 ? $record[$pointerField] : $record['uid'];
508 }
509
510 $recordLocalizations = t3lib_BEfunc::getRecordsByField(
511 $l10nTable,
512 $pointerField,
513 $pointerValue,
514 '',
515 '',
516 '',
517 '1'
518 );
519
520 if (is_array($recordLocalizations)) {
521 foreach ($recordLocalizations as $localization) {
522 $recordLocalizationAccess = $recordLocalizationAccess
523 && $this->checkLanguageAccess($localization[$GLOBALS['TCA'][$l10nTable]['ctrl']['languageField']]);
524 if (!$recordLocalizationAccess) {
525 break;
526 }
527 }
528 }
529
530 }
531 return $recordLocalizationAccess;
532 }
533
534 /**
535 * Checking if a user has editing access to a record from a $GLOBALS['TCA'] table.
536 * The checks does not take page permissions and other "environmental" things into account. It only deal with record internals; If any values in the record fields disallows it.
537 * For instance languages settings, authMode selector boxes are evaluated (and maybe more in the future).
538 * It will check for workspace dependent access.
539 * The function takes an ID (integer) or row (array) as second argument.
540 *
541 * @param string $table Table name
542 * @param mixed $idOrRow If integer, then this is the ID of the record. If Array this just represents fields in the record.
543 * @param boolean $newRecord Set, if testing a new (non-existing) record array. Will disable certain checks that doesn't make much sense in that context.
544 * @param boolean $deletedRecord Set, if testing a deleted record array.
545 * @param boolean $checkFullLanguageAccess Set, whenever access to all translations of the record is required
546 * @return boolean TRUE if OK, otherwise FALSE
547 */
548 function recordEditAccessInternals($table, $idOrRow, $newRecord = FALSE, $deletedRecord = FALSE, $checkFullLanguageAccess = FALSE) {
549 if (isset($GLOBALS['TCA'][$table])) {
550 t3lib_div::loadTCA($table);
551
552 // Always return TRUE for Admin users.
553 if ($this->isAdmin()) {
554 return TRUE;
555 }
556
557 // Fetching the record if the $idOrRow variable was not an array on input:
558 if (!is_array($idOrRow)) {
559 if ($deletedRecord) {
560 $idOrRow = t3lib_BEfunc::getRecord($table, $idOrRow, '*', '', FALSE);
561 } else {
562 $idOrRow = t3lib_BEfunc::getRecord($table, $idOrRow);
563 }
564 if (!is_array($idOrRow)) {
565 $this->errorMsg = 'ERROR: Record could not be fetched.';
566 return FALSE;
567 }
568 }
569
570 // Checking languages:
571 if ($GLOBALS['TCA'][$table]['ctrl']['languageField']) {
572 // Language field must be found in input row - otherwise it does not make sense.
573 if (isset($idOrRow[$GLOBALS['TCA'][$table]['ctrl']['languageField']])) {
574 if (!$this->checkLanguageAccess($idOrRow[$GLOBALS['TCA'][$table]['ctrl']['languageField']])) {
575 $this->errorMsg = 'ERROR: Language was not allowed.';
576 return FALSE;
577 } elseif ($checkFullLanguageAccess && $idOrRow[$GLOBALS['TCA'][$table]['ctrl']['languageField']] == 0 && !$this->checkFullLanguagesAccess($table, $idOrRow)) {
578 $this->errorMsg = 'ERROR: Related/affected language was not allowed.';
579 return FALSE;
580 }
581 } else {
582 $this->errorMsg = 'ERROR: The "languageField" field named "' .
583 $GLOBALS['TCA'][$table]['ctrl']['languageField'] .
584 '" was not found in testing record!';
585 return FALSE;
586 }
587 } elseif (isset($GLOBALS['TCA'][$table]['ctrl']['transForeignTable']) && $checkFullLanguageAccess && !$this->checkFullLanguagesAccess($table, $idOrRow)) {
588 return FALSE;
589 }
590
591 // Checking authMode fields:
592 if (is_array($GLOBALS['TCA'][$table]['columns'])) {
593 foreach ($GLOBALS['TCA'][$table]['columns'] as $fieldName => $fieldValue) {
594 if (isset($idOrRow[$fieldName])) {
595 if ($fieldValue['config']['type'] == 'select' && $fieldValue['config']['authMode'] && !strcmp($fieldValue['config']['authMode_enforce'], 'strict')) {
596 if (!$this->checkAuthMode($table, $fieldName, $idOrRow[$fieldName], $fieldValue['config']['authMode'])) {
597 $this->errorMsg = 'ERROR: authMode "' . $fieldValue['config']['authMode'] . '" failed for field "' . $fieldName . '" with value "' . $idOrRow[$fieldName] . '" evaluated';
598 return FALSE;
599 }
600 }
601 }
602 }
603 }
604
605 // Checking "editlock" feature (doesn't apply to new records)
606 if (!$newRecord && $GLOBALS['TCA'][$table]['ctrl']['editlock']) {
607 if (isset($idOrRow[$GLOBALS['TCA'][$table]['ctrl']['editlock']])) {
608 if ($idOrRow[$GLOBALS['TCA'][$table]['ctrl']['editlock']]) {
609 $this->errorMsg = 'ERROR: Record was locked for editing. Only admin users can change this state.';
610 return FALSE;
611 }
612 } else {
613 $this->errorMsg = 'ERROR: The "editLock" field named "' .
614 $GLOBALS['TCA'][$table]['ctrl']['editlock'] .
615 '" was not found in testing record!';
616 return FALSE;
617 }
618 }
619
620 // Checking record permissions
621 // THIS is where we can include a check for "perms_" fields for other records than pages...
622
623 // Process any hooks
624 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['recordEditAccessInternals'])) {
625 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['recordEditAccessInternals'] as $funcRef) {
626 $params = array(
627 'table' => $table,
628 'idOrRow' => $idOrRow,
629 'newRecord' => $newRecord
630 );
631 if (!t3lib_div::callUserFunction($funcRef, $params, $this)) {
632 return FALSE;
633 }
634 }
635 }
636
637 // Finally, return TRUE if all is well.
638 return TRUE;
639 }
640 }
641
642 /**
643 * Checks a type of permission against the compiled permission integer, $compiledPermissions, and in relation to table, $tableName
644 *
645 * @param integer $compiledPermissions Could typically be the "compiled permissions" integer returned by ->calcPerms
646 * @param string $tableName Is the tablename to check: If "pages" table then edit,new,delete and editcontent permissions can be checked. Other tables will be checked for "editcontent" only (and $type will be ignored)
647 * @param string $actionType For $tableName='pages' this can be 'edit' (2), 'new' (8 or 16), 'delete' (4), 'editcontent' (16). For all other tables this is ignored. (16 is used)
648 * @return boolean
649 * @access public (used by typo3/alt_clickmenu.php)
650 */
651 public function isPSet($compiledPermissions, $tableName, $actionType = '') {
652 if ($this->isAdmin()) {
653 $result = TRUE;
654 }
655 elseif ($tableName == 'pages') {
656 switch ($actionType) {
657 case 'edit':
658 $result = ($compiledPermissions & 2) !== 0;
659 break;
660 case 'new':
661 // Create new page OR page content
662 $result = ($compiledPermissions & (8 + 16)) !== 0;
663 break;
664 case 'delete':
665 $result = ($compiledPermissions & 4) !== 0;
666 break;
667 case 'editcontent':
668 $result = ($compiledPermissions & 16) !== 0;
669 break;
670 default:
671 $result = FALSE;
672 }
673 } else {
674 $result = ($compiledPermissions & 16) !== 0;
675 }
676 return $result;
677 }
678
679 /**
680 * Returns TRUE if the BE_USER is allowed to *create* shortcuts in the backend modules
681 *
682 * @return boolean
683 */
684 function mayMakeShortcut() {
685 return $this->getTSConfigVal('options.enableBookmarks') && !$this->getTSConfigVal('options.mayNotCreateEditBookmarks');
686 }
687
688 /**
689 * Checking if editing of an existing record is allowed in current workspace if that is offline.
690 * Rules for editing in offline mode:
691 * - record supports versioning and is an offline version from workspace and has the corrent stage
692 * - or record (any) is in a branch where there is a page which is a version from the workspace and where the stage is not preventing records
693 *
694 * @param string $table Table of record
695 * @param array $recData Integer (record uid) or array where fields are at least: pid, t3ver_wsid, t3ver_stage (if versioningWS is set)
696 * @return string String error code, telling the failure state. FALSE=All ok
697 */
698 function workspaceCannotEditRecord($table, $recData) {
699 // Only test offline spaces:
700 if ($this->workspace !== 0) {
701
702 if (!is_array($recData)) {
703 $recData = t3lib_BEfunc::getRecord($table, $recData, 'pid' . ($GLOBALS['TCA'][$table]['ctrl']['versioningWS'] ? ',t3ver_wsid,t3ver_stage' : ''));
704 }
705
706 if (is_array($recData)) {
707 // We are testing a "version" (identified by a pid of -1): it can be edited provided that workspace matches and versioning is enabled for the table.
708 if ((int) $recData['pid'] === -1) {
709 // No versioning, basic error, inconsistency even! Such records should not have a pid of -1!
710 if (!$GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
711 return 'Versioning disabled for table';
712 } elseif ((int) $recData['t3ver_wsid'] !== $this->workspace) { // So does workspace match?
713 return 'Workspace ID of record didn\'t match current workspace';
714 } else {
715 // So is the user allowed to "use" the edit stage within the workspace?
716 return $this->workspaceCheckStageForCurrent(0) ? FALSE : 'User\'s access level did not allow for editing';
717 }
718 } else { // We are testing a "live" record:
719 // For "Live" records, check that PID for table allows editing
720 if ($res = $this->workspaceAllowLiveRecordsInPID($recData['pid'], $table)) {
721 // Live records are OK in this branch, but what about the stage of branch point, if any:
722 // OK
723 return $res > 0 ? FALSE : 'Stage for versioning root point and users access level did not allow for editing';
724 } else {
725 // If not offline and not in versionized branch, output error:
726 return 'Online record was not in versionized branch!';
727 }
728 }
729 } else {
730 return 'No record';
731 }
732 } else {
733 // OK because workspace is 0
734 return FALSE;
735 }
736 }
737
738 /**
739 * Evaluates if a user is allowed to edit the offline version
740 *
741 * @param string $table Table of record
742 * @param array $recData Integer (record uid) or array where fields are at least: pid, t3ver_wsid, t3ver_stage (if versioningWS is set)
743 * @return string String error code, telling the failure state. FALSE=All ok
744 * @see workspaceCannotEditRecord()
745 */
746 function workspaceCannotEditOfflineVersion($table, $recData) {
747 if ($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
748
749 if (!is_array($recData)) {
750 $recData = t3lib_BEfunc::getRecord($table, $recData, 'uid,pid,t3ver_wsid,t3ver_stage');
751 }
752 if (is_array($recData)) {
753 if ((int) $recData['pid'] === -1) {
754 return $this->workspaceCannotEditRecord($table, $recData);
755 } else {
756 return 'Not an offline version';
757 }
758 } else {
759 return 'No record';
760 }
761 } else {
762 return 'Table does not support versioning.';
763 }
764 }
765
766 /**
767 * Check if "live" records from $table may be created or edited in this PID.
768 * If the answer is FALSE it means the only valid way to create or edit records in the PID is by versioning
769 * If the answer is 1 or 2 it means it is OK to create a record, if -1 it means that it is OK in terms of versioning because the element was within a versionized branch but NOT ok in terms of the state the root point had!
770 *
771 * @param integer $pid PID value to check for.
772 * @param string $table Table name
773 * @return mixed Returns FALSE if a live record cannot be created and must be versionized in order to do so. 2 means a) Workspace is "Live" or workspace allows "live edit" of records from non-versionized tables (and the $table is not versionizable). 1 and -1 means the pid is inside a versionized branch where -1 means that the branch-point did NOT allow a new record according to its state.
774 */
775 function workspaceAllowLiveRecordsInPID($pid, $table) {
776
777 // Always for Live workspace AND if live-edit is enabled and tables are completely without versioning it is ok as well.
778 if ($this->workspace === 0 || ($this->workspaceRec['live_edit'] && !$GLOBALS['TCA'][$table]['ctrl']['versioningWS']) || $GLOBALS['TCA'][$table]['ctrl']['versioningWS_alwaysAllowLiveEdit']) {
779 // OK to create for this table.
780 return 2;
781 } else {
782 // If the answer is FALSE it means the only valid way to create or edit records in the PID is by versioning
783 return FALSE;
784 }
785 }
786
787 /**
788 * Evaluates if a record from $table can be created in $pid
789 *
790 * @param integer $pid Page id. This value must be the _ORIG_uid if available: So when you have pages versionized as "page" or "element" you must supply the id of the page version in the workspace!
791 * @param string $table Table name
792 * @return boolean TRUE if OK.
793 */
794 function workspaceCreateNewRecord($pid, $table) {
795 if ($res = $this->workspaceAllowLiveRecordsInPID($pid, $table)) {
796 // If LIVE records cannot be created in the current PID due to workspace restrictions, prepare creation of placeholder-record
797 if ($res < 0) {
798 // Stage for versioning root point and users access level did not allow for editing
799 return FALSE;
800 }
801 } elseif (!$GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
802 // So, if no live records were allowed, we have to create a new version of this record:
803 return FALSE;
804 }
805 return TRUE;
806 }
807
808 /**
809 * Evaluates if auto creation of a version of a record is allowed.
810 *
811 * @param string $table Table of the record
812 * @param integer $id UID of record
813 * @param integer $recpid PID of record
814 * @return boolean TRUE if ok.
815 */
816 function workspaceAllowAutoCreation($table, $id, $recpid) {
817 // Auto-creation of version: In offline workspace, test if versioning is
818 // enabled and look for workspace version of input record.
819 // If there is no versionized record found we will create one and save to that.
820 if ($this->workspace !== 0 // Only in draft workspaces
821 && !$this->workspaceRec['disable_autocreate'] // Auto-creation must not be disabled.
822 && $GLOBALS['TCA'][$table]['ctrl']['versioningWS'] // Table must be versionizable
823 && $recpid >= 0 // The PID of the record must NOT be -1 or less (would indicate that it already was a version!)
824 && !t3lib_BEfunc::getWorkspaceVersionOfRecord($this->workspace, $table, $id, 'uid')) { // There must be no existing version of this record in workspace.
825 return TRUE;
826 } elseif ($this->workspaceRec['disable_autocreate']) {
827 t3lib_div::deprecationLog('Usage of disable_autocreate feature is deprecated since 4.5.');
828 }
829 }
830
831 /**
832 * Checks if an element stage allows access for the user in the current workspace
833 * In live workspace (= 0) access is always granted for any stage.
834 * Admins are always allowed.
835 * An option for custom workspaces allows members to also edit when the stage is "Review"
836 *
837 * @param integer $stage Stage id from an element: -1,0 = editing, 1 = reviewer, >1 = owner
838 * @return boolean TRUE if user is allowed access
839 */
840 function workspaceCheckStageForCurrent($stage) {
841 // Always allow for admins
842 if ($this->isAdmin()) {
843 return TRUE;
844 }
845
846 if ($this->workspace !== 0 && t3lib_extMgm::isLoaded('workspaces')) {
847 $stage = intval($stage);
848 $stat = $this->checkWorkspaceCurrent();
849
850 // Check if custom staging is activated
851 $workspaceRec = t3lib_BEfunc::getRecord('sys_workspace', $stat['uid']);
852 if ($workspaceRec['custom_stages'] > 0 && $stage !== 0 && $stage !== -10) {
853
854 // Get custom stage record
855 $workspaceStageRec = t3lib_BEfunc::getRecord('sys_workspace_stage', $stage);
856 // Check if the user is responsible for the current stage
857 if ((t3lib_div::inList($workspaceStageRec['responsible_persons'], 'be_users_' . $this->user['uid'])
858 && $stat['_ACCESS'] === 'member')
859 || $stat['_ACCESS'] === 'owner') {
860 return TRUE;
861 }
862
863 // Check if the user is in a group which is responsible for the current stage
864 foreach ($this->userGroupsUID as $groupUid) {
865 if ((t3lib_div::inList($workspaceStageRec['responsible_persons'], 'be_groups_' . $groupUid)
866 && $stat['_ACCESS'] === 'member')
867 || $stat['_ACCESS'] === 'owner') {
868 return TRUE;
869 }
870 }
871 // Only owner is allowed to change records which are "ready to publish"
872 } elseif ($stage == -10 || $stage == -20) {
873 if ($stat['_ACCESS'] === 'owner') {
874 return TRUE;
875 } else {
876 return FALSE;
877 }
878 } else {
879 $memberStageLimit = $this->workspaceRec['review_stage_edit'] ? 1 : 0;
880 if (($stage <= $memberStageLimit && $stat['_ACCESS'] === 'member')
881 || ($stage <= 1 && $stat['_ACCESS'] === 'reviewer')
882 || $stat['_ACCESS'] === 'owner') {
883 return TRUE;
884 }
885 }
886 } else {
887 // Always OK for live workspace.
888 return TRUE;
889 }
890 }
891
892 /**
893 * Returns TRUE if the user has access to publish content from the workspace ID given.
894 * Admin-users are always granted access to do this
895 * If the workspace ID is 0 (live) all users have access also
896 * For custom workspaces it depends on whether the user is owner OR like with draft workspace if the user has access to Live workspace.
897 *
898 * @param integer $wsid Workspace UID; 0,1+
899 * @return boolean Returns TRUE if the user has access to publish content from the workspace ID given.
900 */
901 function workspacePublishAccess($wsid) {
902 if ($this->isAdmin()) {
903 return TRUE;
904 }
905
906 // If no access to workspace, of course you cannot publish!
907 $retVal = FALSE;
908
909 $wsAccess = $this->checkWorkspace($wsid);
910 if ($wsAccess) {
911 switch ($wsAccess['uid']) {
912 case 0: // Live workspace
913 // If access to Live workspace, no problem.
914 $retVal = TRUE;
915 break;
916 default: // Custom workspace
917 $retVal = $wsAccess['_ACCESS'] === 'owner' || ($this->checkWorkspace(0) && !($wsAccess['publish_access'] & 2)); // Either be an adminuser OR have access to online workspace which is OK as well as long as publishing access is not limited by workspace option.
918 break;
919 }
920 }
921 return $retVal;
922 }
923
924 /**
925 * Workspace swap-mode access?
926 *
927 * @return boolean Returns TRUE if records can be swapped in the current workspace, otherwise FALSE
928 */
929 function workspaceSwapAccess() {
930 if ($this->workspace > 0 && (int) $this->workspaceRec['swap_modes'] === 2) {
931 return FALSE;
932 } else {
933 return TRUE;
934 }
935 }
936
937 /**
938 * Workspace Versioning type access. Check wether the requsted type of versioning (element/page/branch) is allowd in current workspace
939 * (element/pages/branches type of versioning can/could be set on custom workspaces on filed "vtype")
940 *
941 * @todo workspacecleanup: this seems mostly obsolete and should be removed
942 * @param integer $type Versioning type to evaluation: -1, 0, >1
943 * 0 = page (deprecated)
944 * -1 = element
945 * >1 = branch (deprecated), indicating the "nesting" level
946 * @return boolean TRUE if OK
947 * @deprecated since TYPO3 4.4, will be removed in TYPO3 6.0 as only element versioning is supported now
948 */
949 function workspaceVersioningTypeAccess($type) {
950 t3lib_div::logDeprecatedFunction();
951
952 $type = t3lib_utility_Math::forceIntegerInRange($type, -1);
953
954 // only element versioning is allowed now
955 return $type == -1;
956 }
957
958 /**
959 * Finding "closest" versioning type, used for creation of new records.
960 *
961 * @see workspaceVersioningTypeAccess() for hints on $type
962 * @param integer $type Versioning type to evaluation: -1, 0, >1
963 * @return integer Returning versioning type
964 * @deprecated since TYPO3 4.4, will be removed in TYPO3 6.0 as only element versioning is supported now
965 */
966 function workspaceVersioningTypeGetClosest($type) {
967 t3lib_div::logDeprecatedFunction();
968
969 $type = t3lib_utility_Math::forceIntegerInRange($type, -1);
970
971 if ($this->workspace > 0) {
972 switch ((int) $type) {
973 case -1:
974 $type = -1;
975 break;
976 case 0:
977 $type = $this->workspaceVersioningTypeAccess($type) ? $type : -1;
978 break;
979 default:
980 $type = $this->workspaceVersioningTypeAccess($type) ? $type : ($this->workspaceVersioningTypeAccess(0) ? 0 : -1);
981 break;
982 }
983 }
984 return $type;
985 }
986
987 /*************************************
988 *
989 * Miscellaneous functions
990 *
991 *************************************/
992
993 /**
994 * Returns the value/properties of a TS-object as given by $objectString, eg. 'options.dontMountAdminMounts'
995 * Nice (general!) function for returning a part of a TypoScript array!
996 *
997 * @param string $objectString Pointer to an "object" in the TypoScript array, fx. 'options.dontMountAdminMounts'
998 * @param array $config Optional TSconfig array: If array, then this is used and not $this->userTS. If not array, $this->userTS is used.
999 * @return array An array with two keys, "value" and "properties" where "value" is a string with the value of the objectsting and "properties" is an array with the properties of the objectstring.
1000 */
1001 function getTSConfig($objectString, $config = '') {
1002 if (!is_array($config)) {
1003 // Getting Root-ts if not sent
1004 $config = $this->userTS;
1005 }
1006 $TSConf = array();
1007 $parts = explode('.', $objectString, 2);
1008 $key = $parts[0];
1009 if (trim($key)) {
1010 if (count($parts) > 1 && trim($parts[1])) {
1011 // Go on, get the next level
1012 if (is_array($config[$key . '.'])) {
1013 $TSConf = $this->getTSConfig($parts[1], $config[$key . '.']);
1014 }
1015 } else {
1016 $TSConf['value'] = $config[$key];
1017 $TSConf['properties'] = $config[$key . '.'];
1018 }
1019 }
1020 return $TSConf;
1021 }
1022
1023 /**
1024 * Returns the "value" of the $objectString from the BE_USERS "User TSconfig" array
1025 *
1026 * @param string $objectString Object string, eg. "somestring.someproperty.somesubproperty"
1027 * @return string The value for that object string (object path)
1028 * @see getTSConfig()
1029 */
1030 function getTSConfigVal($objectString) {
1031 $TSConf = $this->getTSConfig($objectString);
1032 return $TSConf['value'];
1033 }
1034
1035 /**
1036 * Returns the "properties" of the $objectString from the BE_USERS "User TSconfig" array
1037 *
1038 * @param string $objectString Object string, eg. "somestring.someproperty.somesubproperty"
1039 * @return array The properties for that object string (object path) - if any
1040 * @see getTSConfig()
1041 */
1042 function getTSConfigProp($objectString) {
1043 $TSConf = $this->getTSConfig($objectString);
1044 return $TSConf['properties'];
1045 }
1046
1047 /**
1048 * Returns TRUE if $item is in $in_list
1049 *
1050 * @param string $in_list Comma list with items, no spaces between items!
1051 * @param string $item The string to find in the list of items
1052 * @return string Boolean
1053 * @deprecated since TYPO3 4.7, should be removed in TYPO3 6.1, use equivalent function t3lib_div::inList()
1054 */
1055 function inList($in_list, $item) {
1056 t3lib_div::logDeprecatedFunction();
1057 return t3lib_div::inList($in_list, $item);
1058 }
1059
1060 /**
1061 * Returns an array with the webmounts.
1062 * If no webmounts, and empty array is returned.
1063 * NOTICE: Deleted pages WILL NOT be filtered out! So if a mounted page has been deleted it is STILL coming out as a webmount. This is not checked due to performance.
1064 *
1065 * @return array
1066 */
1067 function returnWebmounts() {
1068 return (string) ($this->groupData['webmounts']) != '' ? explode(',', $this->groupData['webmounts']) : array();
1069 }
1070
1071
1072 /**
1073 * Returns TRUE or FALSE, depending if an alert popup (a javascript confirmation) should be shown
1074 * call like $GLOBALS['BE_USER']->jsConfirmation($BITMASK)
1075 *
1076 * 1 - typeChange
1077 * 2 - copy/move/paste
1078 * 4 - delete
1079 * 8 - frontend editing
1080 * 128 - other (not used yet)
1081 *
1082 * @param integer $bitmask Bitmask
1083 * @return boolean TRUE if the confirmation should be shown
1084 */
1085 function jsConfirmation($bitmask) {
1086 $alertPopup = $GLOBALS['BE_USER']->getTSConfig('options.alertPopups');
1087 if (empty($alertPopup['value'])) {
1088 // Default: show all warnings
1089 $alertPopup = 255;
1090 } else {
1091 $alertPopup = (int) $alertPopup['value'];
1092 }
1093 // Show confirmation
1094 if (($alertPopup & $bitmask) == $bitmask) {
1095 return 1;
1096 } else { // don't show confirmation
1097 return 0;
1098 }
1099 }
1100
1101 /*************************************
1102 *
1103 * Authentication methods
1104 *
1105 *************************************/
1106
1107 /**
1108 * Initializes a lot of stuff like the access-lists, database-mountpoints and filemountpoints
1109 * This method is called by ->backendCheckLogin() (from extending class t3lib_beuserauth) if the backend user login has verified OK.
1110 * Generally this is required initialization of a backend user.
1111 *
1112 * @return void
1113 * @access private
1114 * @see t3lib_TSparser
1115 */
1116 function fetchGroupData() {
1117 if ($this->user['uid']) {
1118
1119 // Get lists for the be_user record and set them as default/primary values.
1120 // Enabled Backend Modules
1121 $this->dataLists['modList'] = $this->user['userMods'];
1122 // Add Allowed Languages
1123 $this->dataLists['allowed_languages'] = $this->user['allowed_languages'];
1124 // Set user value for workspace permissions.
1125 $this->dataLists['workspace_perms'] = $this->user['workspace_perms'];
1126 // Database mountpoints
1127 $this->dataLists['webmount_list'] = $this->user['db_mountpoints'];
1128 // File mountpoints
1129 $this->dataLists['filemount_list'] = $this->user['file_mountpoints'];
1130 // Fileoperation permissions
1131 $this->dataLists['fileoper_perms'] = (int) $this->user['fileoper_perms'];
1132
1133 // Setting default User TSconfig:
1134 $this->TSdataArray[] = $this->addTScomment('From $GLOBALS["TYPO3_CONF_VARS"]["BE"]["defaultUserTSconfig"]:') .
1135 $GLOBALS['TYPO3_CONF_VARS']['BE']['defaultUserTSconfig'];
1136
1137 // Default TSconfig for admin-users
1138 if ($this->isAdmin()) {
1139 $this->TSdataArray[] = $this->addTScomment('"admin" user presets:') . '
1140 admPanel.enable.all = 1
1141 ';
1142 if (t3lib_extMgm::isLoaded('sys_note')) {
1143 $this->TSdataArray[] = '
1144 // Setting defaults for sys_note author / email...
1145 TCAdefaults.sys_note.author = ' . $this->user['realName'] . '
1146 TCAdefaults.sys_note.email = ' . $this->user['email'] . '
1147 ';
1148 }
1149 }
1150
1151 // BE_GROUPS:
1152 // Get the groups...
1153 // 240203: Since the group-field never contains any references to groups with a prepended table name
1154 // we think it's safe to just intExplode and re-implode - which should be much faster than the other function call.
1155 $grList = $GLOBALS['TYPO3_DB']->cleanIntList($this->user[$this->usergroup_column]);
1156 if ($grList) {
1157 // Fetch groups will add a lot of information to the internal arrays: modules, accesslists, TSconfig etc. Refer to fetchGroups() function.
1158 $this->fetchGroups($grList);
1159 }
1160
1161 // Add the TSconfig for this specific user:
1162 $this->TSdataArray[] = $this->addTScomment('USER TSconfig field') . $this->user['TSconfig'];
1163 // Check include lines.
1164 $this->TSdataArray = t3lib_TSparser::checkIncludeLines_array($this->TSdataArray);
1165 // Imploding with "[global]" will make sure that non-ended confinements with braces are ignored.
1166 $this->userTS_text = implode(LF . '[GLOBAL]' . LF, $this->TSdataArray);
1167
1168 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['TSconfigConditions'] && !$this->userTS_dontGetCached) {
1169 // Perform TS-Config parsing with condition matching
1170 $parseObj = t3lib_div::makeInstance('t3lib_TSparser_TSconfig');
1171 $res = $parseObj->parseTSconfig($this->userTS_text, 'userTS');
1172 if ($res) {
1173 $this->userTS = $res['TSconfig'];
1174 $this->userTSUpdated = ($res['cached'] ? 0 : 1);
1175 }
1176 } else {
1177 // Parsing the user TSconfig (or getting from cache)
1178 $hash = md5('userTS:' . $this->userTS_text);
1179 $cachedContent = t3lib_BEfunc::getHash($hash);
1180 if (isset($cachedContent) && !$this->userTS_dontGetCached) {
1181 $this->userTS = unserialize($cachedContent);
1182 } else {
1183 $parseObj = t3lib_div::makeInstance('t3lib_TSparser');
1184 $parseObj->parse($this->userTS_text);
1185 $this->userTS = $parseObj->setup;
1186 t3lib_BEfunc::storeHash($hash, serialize($this->userTS), 'BE_USER_TSconfig');
1187 // Update UC:
1188 $this->userTSUpdated = 1;
1189 }
1190 }
1191
1192 // Processing webmounts
1193 // Admin's always have the root mounted
1194 if ($this->isAdmin() && !$this->getTSConfigVal('options.dontMountAdminMounts')) {
1195 $this->dataLists['webmount_list'] = '0,' . $this->dataLists['webmount_list'];
1196 }
1197
1198 // The lists are cleaned for duplicates
1199 $this->groupData['webmounts'] = t3lib_div::uniqueList($this->dataLists['webmount_list']);
1200 $this->groupData['pagetypes_select'] = t3lib_div::uniqueList($this->dataLists['pagetypes_select']);
1201 $this->groupData['tables_select'] = t3lib_div::uniqueList($this->dataLists['tables_modify'] . ',' . $this->dataLists['tables_select']);
1202 $this->groupData['tables_modify'] = t3lib_div::uniqueList($this->dataLists['tables_modify']);
1203 $this->groupData['non_exclude_fields'] = t3lib_div::uniqueList($this->dataLists['non_exclude_fields']);
1204 $this->groupData['explicit_allowdeny'] = t3lib_div::uniqueList($this->dataLists['explicit_allowdeny']);
1205 $this->groupData['allowed_languages'] = t3lib_div::uniqueList($this->dataLists['allowed_languages']);
1206 $this->groupData['custom_options'] = t3lib_div::uniqueList($this->dataLists['custom_options']);
1207 $this->groupData['modules'] = t3lib_div::uniqueList($this->dataLists['modList']);
1208 $this->groupData['fileoper_perms'] = $this->dataLists['fileoper_perms'];
1209 $this->groupData['workspace_perms'] = $this->dataLists['workspace_perms'];
1210
1211 // Populating the $this->userGroupsUID -array with the groups in the order in which they were LAST included.!!
1212 $this->userGroupsUID = array_reverse(array_unique(array_reverse($this->includeGroupArray)));
1213
1214 // Finally this is the list of group_uid's in the order they are parsed (including subgroups!) and without duplicates (duplicates are presented with their last entrance in the list, which thus reflects the order of the TypoScript in TSconfig)
1215 $this->groupList = implode(',', $this->userGroupsUID);
1216 $this->setCachedList($this->groupList);
1217
1218 // Checking read access to webmounts:
1219 if (trim($this->groupData['webmounts']) !== '') {
1220 $webmounts = explode(',', $this->groupData['webmounts']); // Explode mounts
1221 // Selecting all webmounts with permission clause for reading
1222 $MProws = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid', 'pages', 'deleted=0 AND uid IN (' . $this->groupData['webmounts'] . ') AND ' . $this->getPagePermsClause(1), '', '', '', 'uid');
1223 foreach ($webmounts as $idx => $mountPointUid) {
1224 // If the mount ID is NOT found among selected pages, unset it:
1225 if ($mountPointUid > 0 && !isset($MProws[$mountPointUid])) {
1226 unset($webmounts[$idx]);
1227 }
1228 }
1229 // Implode mounts in the end.
1230 $this->groupData['webmounts'] = implode(',', $webmounts);
1231 }
1232
1233 // Setting up workspace situation (after webmounts are processed!):
1234 $this->workspaceInit();
1235 }
1236 }
1237
1238 /**
1239 * Fetches the group records, subgroups and fills internal arrays.
1240 * Function is called recursively to fetch subgroups
1241 *
1242 * @param string $grList Commalist of be_groups uid numbers
1243 * @param string $idList List of already processed be_groups-uids so the function will not fall into a eternal recursion.
1244 * @return void
1245 * @access private
1246 */
1247 function fetchGroups($grList, $idList = '') {
1248 // Fetching records of the groups in $grList (which are not blocked by lockedToDomain either):
1249 $lockToDomain_SQL = ' AND (lockToDomain=\'\' OR lockToDomain IS NULL OR lockToDomain=\'' . t3lib_div::getIndpEnv('HTTP_HOST') . '\')';
1250 $whereSQL = 'deleted=0 AND hidden=0 AND pid=0 AND uid IN (' . $grList . ')' . $lockToDomain_SQL;
1251
1252 // Hook for manipulation of the WHERE sql sentence which controls which BE-groups are included
1253 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['fetchGroupQuery'])) {
1254 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['fetchGroupQuery'] as $classRef) {
1255 $hookObj = t3lib_div::getUserObj($classRef);
1256 if (method_exists($hookObj, 'fetchGroupQuery_processQuery')) {
1257 $whereSQL = $hookObj->fetchGroupQuery_processQuery($this, $grList, $idList, $whereSQL);
1258 }
1259 }
1260 }
1261
1262 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', $this->usergroup_table, $whereSQL);
1263
1264 // The userGroups array is filled
1265 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
1266 $this->userGroups[$row['uid']] = $row;
1267 }
1268 $GLOBALS['TYPO3_DB']->sql_free_result($res);
1269
1270 // Traversing records in the correct order
1271 $include_staticArr = t3lib_div::intExplode(',', $grList);
1272 // Traversing list
1273 foreach ($include_staticArr as $key => $uid) {
1274
1275 // Get row:
1276 $row = $this->userGroups[$uid];
1277 // Must be an array and $uid should not be in the idList, because then it is somewhere previously in the grouplist
1278 if (is_array($row) && !t3lib_div::inList($idList, $uid)) {
1279
1280 // Include sub groups
1281 if (trim($row['subgroup'])) {
1282 // Make integer list
1283 $theList = implode(',', t3lib_div::intExplode(',', $row['subgroup']));
1284 // Call recursively, pass along list of already processed groups so they are not recursed again.
1285 $this->fetchGroups($theList, $idList . ',' . $uid);
1286 }
1287 // Add the group uid, current list, TSconfig to the internal arrays.
1288 $this->includeGroupArray[] = $uid;
1289 $this->includeHierarchy[] = $idList;
1290 $this->TSdataArray[] = $this->addTScomment('Group "' . $row['title'] . '" [' . $row['uid'] . '] TSconfig field:') . $row['TSconfig'];
1291
1292 // Mount group database-mounts
1293 if (($this->user['options'] & 1) == 1) {
1294 $this->dataLists['webmount_list'] .= ',' . $row['db_mountpoints'];
1295 }
1296
1297 // Mount group file-mounts
1298 if (($this->user['options'] & 2) == 2) {
1299 $this->dataLists['filemount_list'] .= ',' . $row['file_mountpoints'];
1300 }
1301
1302 // The lists are made: groupMods, tables_select, tables_modify, pagetypes_select, non_exclude_fields, explicit_allowdeny, allowed_languages, custom_options
1303 if ($row['inc_access_lists'] == 1) {
1304 $this->dataLists['modList'] .= ',' . $row['groupMods'];
1305 $this->dataLists['tables_select'] .= ',' . $row['tables_select'];
1306 $this->dataLists['tables_modify'] .= ',' . $row['tables_modify'];
1307 $this->dataLists['pagetypes_select'] .= ',' . $row['pagetypes_select'];
1308 $this->dataLists['non_exclude_fields'] .= ',' . $row['non_exclude_fields'];
1309 $this->dataLists['explicit_allowdeny'] .= ',' . $row['explicit_allowdeny'];
1310 $this->dataLists['allowed_languages'] .= ',' . $row['allowed_languages'];
1311 $this->dataLists['custom_options'] .= ',' . $row['custom_options'];
1312 }
1313
1314 // Setting fileoperation permissions
1315 $this->dataLists['fileoper_perms'] |= (int) $row['fileoper_perms'];
1316
1317 // Setting workspace permissions:
1318 $this->dataLists['workspace_perms'] |= $row['workspace_perms'];
1319
1320 // If this function is processing the users OWN group-list (not subgroups) AND if the ->firstMainGroup is not set, then the ->firstMainGroup will be set.
1321 if (!strcmp($idList, '') && !$this->firstMainGroup) {
1322 $this->firstMainGroup = $uid;
1323 }
1324 }
1325 }
1326
1327 // ****************
1328 // HOOK: fetchGroups_postProcessing
1329 // ****************
1330 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['fetchGroups_postProcessing'])) {
1331 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['fetchGroups_postProcessing'] as $_funcRef) {
1332 $_params = array();
1333 t3lib_div::callUserFunction($_funcRef, $_params, $this);
1334 }
1335 }
1336 }
1337
1338 /**
1339 * Updates the field be_users.usergroup_cached_list if the groupList of the user has changed/is different from the current list.
1340 * The field "usergroup_cached_list" contains the list of groups which the user is a member of. After authentication (where these functions are called...) one can depend on this list being a representation of the exact groups/subgroups which the BE_USER has membership with.
1341 *
1342 * @param string $cList The newly compiled group-list which must be compared with the current list in the user record and possibly stored if a difference is detected.
1343 * @return void
1344 * @access private
1345 */
1346 function setCachedList($cList) {
1347 if ((string) $cList != (string) $this->user['usergroup_cached_list']) {
1348 $GLOBALS['TYPO3_DB']->exec_UPDATEquery('be_users', 'uid=' . intval($this->user['uid']), array('usergroup_cached_list' => $cList));
1349 }
1350 }
1351
1352 /***********************
1353 * FILE MOUNT FUNCTIONS
1354 **********************/
1355
1356 /**
1357 * Sets up all file storages for a user.
1358 * Needs to be called AFTER the groups have been loaded.
1359 *
1360 * @return void
1361 */
1362 protected function initializeFileStorages() {
1363 $this->fileStorages = array();
1364 /** @var $storageRepository t3lib_file_Repository_StorageRepository */
1365 $storageRepository = t3lib_div::makeInstance('t3lib_file_Repository_StorageRepository');
1366
1367 // Admin users have all file storages visible, without any filters
1368 if ($this->isAdmin()) {
1369 $storageObjects = $storageRepository->findAll();
1370 foreach ($storageObjects as $storageObject) {
1371 $this->fileStorages[$storageObject->getUid()] = $storageObject;
1372 }
1373 } else {
1374 // If userHomePath is set, we attempt to mount it
1375 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['userHomePath']) {
1376 list($userHomeStorageUid, $userHomeFilter) = explode(':', $GLOBALS['TYPO3_CONF_VARS']['BE']['userHomePath'], 2);
1377 $userHomeStorageUid = intval($userHomeStorageUid);
1378 if ($userHomeStorageUid > 0) {
1379 $storageObject = $storageRepository->findByUid($userHomeStorageUid);
1380 // First try and mount with [uid]_[username]
1381 $userHomeFilterIdentifier = $userHomeFilter . $this->user['uid'] . '_' . $this->user['username'] . $GLOBALS['TYPO3_CONF_VARS']['BE']['userUploadDir'];
1382 $didMount = $storageObject->injectFileMount($userHomeFilterIdentifier);
1383 // If that failed, try and mount with only [uid]
1384 if (!$didMount) {
1385 $userHomeFilterIdentifier = $userHomeFilter . $this->user['uid'] . '_' . $this->user['username'] . $GLOBALS['TYPO3_CONF_VARS']['BE']['userUploadDir'];
1386 $storageObject->injectFileMount($userHomeFilterIdentifier);
1387 }
1388 $this->fileStorages[$storageObject->getUid()] = $storageObject;
1389 }
1390 }
1391
1392 // Mount group home-dirs
1393 if (($this->user['options'] & 2) == 2 && $GLOBALS['TYPO3_CONF_VARS']['BE']['groupHomePath'] != '') {
1394 // If groupHomePath is set, we attempt to mount it
1395 list($groupHomeStorageUid, $groupHomeFilter) = explode(':', $GLOBALS['TYPO3_CONF_VARS']['BE']['groupHomePath'], 2);
1396 $groupHomeStorageUid = intval($groupHomeStorageUid);
1397 if ($groupHomeStorageUid > 0) {
1398 $storageObject = $storageRepository->findByUid($groupHomeStorageUid);
1399 foreach ($this->userGroups as $groupUid => $groupData) {
1400 $groupHomeFilterIdentifier = $groupHomeFilter . $groupData['uid'];
1401 $storageObject->injectFileMount($groupHomeFilterIdentifier);
1402 }
1403 $this->fileStorages[$storageObject->getUid()] = $storageObject;
1404 }
1405 }
1406
1407 // Processing filemounts (both from the user and the groups)
1408 $this->dataLists['filemount_list'] = t3lib_div::uniqueList($this->dataLists['filemount_list']);
1409 if ($this->dataLists['filemount_list']) {
1410 $orderBy = $GLOBALS['TCA']['sys_filemounts']['ctrl']['default_sortby'] ? $GLOBALS['TYPO3_DB']->stripOrderBy($GLOBALS['TCA']['sys_filemounts']['ctrl']['default_sortby']) : 'sorting';
1411 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
1412 '*',
1413 'sys_filemounts',
1414 'deleted=0 AND hidden=0 AND pid=0 AND uid IN (' . $this->dataLists['filemount_list'] . ')',
1415 '',
1416 $orderBy
1417 );
1418
1419 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
1420 $storageObject = $storageRepository->findByUid($row['base']);
1421 $storageObject->injectFileMount($row['path'], $row);
1422 $this->fileStorages[$storageObject->getUid()] = $storageObject;
1423 }
1424 $GLOBALS['TYPO3_DB']->sql_free_result($res);
1425 }
1426 }
1427
1428 // Injects the users' permissions to each storage
1429 foreach ($this->fileStorages as $storageObject) {
1430 $storagePermissions = $this->getFilePermissionsForStorage($storageObject);
1431 $storageObject->injectUserPermissions($storagePermissions);
1432 }
1433
1434 // more narrowing down through the workspace
1435 $this->initializeFileStoragesForWorkspace();
1436
1437 // this has to be called always in order to set certain filters
1438 // @todo Should be in BE_USER object then
1439 $GLOBALS['BE_USER']->evaluateUserSpecificFileFilterSettings();
1440 }
1441
1442 /**
1443 * Returns an array with the filemounts for the user.
1444 * Each filemount is represented with an array of a "name", "path" and "type".
1445 * If no filemounts an empty array is returned.
1446 *
1447 * @api
1448 * @return t3lib_file_Storage[]
1449 */
1450 public function getFileStorages() {
1451 // Initializing file mounts after the groups are fetched
1452 if ($this->fileStorages === NULL) {
1453 $this->initializeFileStorages();
1454 }
1455
1456 return $this->fileStorages;
1457 }
1458
1459 /**
1460 * Adds filters based on what the user has set
1461 * this should be done in this place, and called whenever needed,
1462 * but only when needed
1463 *
1464 * @return void
1465 * @todo Should be in BE_USER object then
1466 */
1467 public function evaluateUserSpecificFileFilterSettings() {
1468 // Add the option for also displaying the non-hidden files
1469 if ($this->uc['showHiddenFilesAndFolders']) {
1470 t3lib_file_Utility_FilenameFilters::setShowHiddenFilesAndFolders(TRUE);
1471 }
1472 }
1473
1474 /**
1475 * Returns the information about file permissions
1476 * previously, this was stored in the DB fields (fileoper_perms)
1477 * but is now handled via userTSconfig
1478 *
1479 * permissions.file.default {
1480 * addFile = 1
1481 * readFile = 1
1482 * editFile = 1
1483 * writeFile = 1
1484 * uploadFile = 1
1485 * copyFile = 1
1486 * moveFile = 1
1487 * renameFile = 1
1488 * unzipFile = 1
1489 * removeFile = 1
1490 *
1491 * addFolder = 1
1492 * browseFolder = 1
1493 * moveFolder = 1
1494 * writeFolder = 1
1495 * renameFolder = 1
1496 * removeFolder = 1
1497 * removeSubfolders = 1
1498 * }
1499 *
1500 * # overwrite settings for a specific storageObject
1501 * permissions.file.storage.StorageUid {
1502 * readFile = 0
1503 * removeSubfolders = 1
1504 * }
1505 *
1506 * Please note that these permissions only apply, if the storage has the
1507 * capabilities (browseable, writable), and if the driver allows for writing etc
1508 *
1509 * @api
1510 * @return array
1511 */
1512 public function getFilePermissions() {
1513 if (!isset($this->filePermissions)) {
1514 $defaultOptions = array(
1515 'addFile' => TRUE, // new option
1516 'readFile' => TRUE, // new option, generic check of the user rights
1517 'editFile' => TRUE, // new option
1518 'writeFile' => TRUE, // new option, generic check of the user rights
1519 'uploadFile' => TRUE,
1520 'copyFile' => TRUE,
1521 'moveFile' => TRUE,
1522 'renameFile' => TRUE,
1523 'unzipFile' => TRUE,
1524 'removeFile' => TRUE,
1525 'addFolder' => TRUE,
1526 'browseFolder' => TRUE, // new option,, generic check of the user rights
1527 'moveFolder' => TRUE,
1528 'renameFolder' => TRUE,
1529 'writeFolder' => TRUE, // new option, generic check of the user rights
1530 'removeFolder' => TRUE,
1531 'removeSubfolders' => TRUE // was "delete recursively" previously
1532 );
1533
1534 if (!$this->isAdmin()) {
1535 $this->filePermissions = $this->getTSConfig('permissions.file.default');
1536 if (!is_array($this->filePermissions)) {
1537 $oldFileOperationPermissions = $this->getFileoperationPermissions();
1538
1539 // Lower permissions if the old file operation permissions are not set
1540 if ($oldFileOperationPermissions ^ 1) {
1541 $defaultOptions['addFile'] = FALSE;
1542 $defaultOptions['uploadFile'] = FALSE;
1543 $defaultOptions['copyFile'] = FALSE;
1544 $defaultOptions['moveFile'] = FALSE;
1545 $defaultOptions['renameFile'] = FALSE;
1546 $defaultOptions['removeFile'] = FALSE;
1547 }
1548 if ($oldFileOperationPermissions ^ 2) {
1549 $defaultOptions['unzipFile'] = FALSE;
1550 }
1551 if ($oldFileOperationPermissions ^ 4) {
1552 $defaultOptions['addFolder'] = FALSE;
1553 $defaultOptions['moveFolder'] = FALSE;
1554 $defaultOptions['renameFolder'] = FALSE;
1555 $defaultOptions['removeFolder'] = FALSE;
1556 }
1557 if ($oldFileOperationPermissions ^ 8) {
1558 $defaultOptions['copyFolder'] = FALSE;
1559 }
1560 if ($oldFileOperationPermissions ^ 16) {
1561 $defaultOptions['removeSubfolders'] = FALSE;
1562 }
1563 }
1564 }
1565
1566 $this->filePermissions = $defaultOptions;
1567 }
1568
1569 return $this->filePermissions;
1570 }
1571
1572 /**
1573 * Gets the file permissions for a storage
1574 * by merging any storage-specific permissions for a
1575 * storage with the default settings
1576 *
1577 * @api
1578 * @param t3lib_file_Storage $storageObject
1579 * @return array
1580 */
1581 public function getFilePermissionsForStorage(t3lib_file_Storage $storageObject) {
1582 $defaultFilePermissions = $this->getFilePermissions();
1583 $storageFilePermissions = $this->getTSConfig('permissions.file.storage.' . $storageObject->getUid());
1584
1585 if (is_array($storageFilePermissions) && count($storageFilePermissions)) {
1586 return array_merge($storageFilePermissions, $defaultFilePermissions);
1587 } else {
1588 return $defaultFilePermissions;
1589 }
1590 }
1591
1592 /**
1593 * Returns a t3lib_file_Folder object that is used for uploading
1594 * files by default.
1595 * This is used for RTE and its magic images, as well as uploads
1596 * in the TCEforms fields, unless otherwise configured (will be added in the future)
1597 *
1598 * the default upload folder for a user is the defaultFolder on the first
1599 * filestorage/filemount that the user can access
1600 * however, you can set the users' upload folder like this:
1601 *
1602 * options.defaultUploadFolder = 3:myfolder/yourfolder/
1603 *
1604 * @return t3lib_file_Folder|boolean The default upload folder for this user
1605 */
1606 public function getDefaultUploadFolder() {
1607 $uploadFolder = $this->getTSConfigVal('options.defaultUploadFolder');
1608 if ($uploadFolder) {
1609 $uploadFolder = t3lib_file_Factory::getInstance()->getFolderObjectFromCombinedIdentifier($uploadFolder);
1610 } else {
1611 $storages = $this->getFileStorages();
1612 if (count($storages) > 0) {
1613 /** @var $firstStorage t3lib_file_Storage */
1614 $firstStorage = reset($storages);
1615 $uploadFolder = $firstStorage->getDefaultFolder();
1616 }
1617 }
1618
1619 if ($uploadFolder instanceof t3lib_file_Folder) {
1620 return $uploadFolder;
1621 } else {
1622 return FALSE;
1623 }
1624 }
1625
1626 /**
1627 * Returns an integer bitmask that represents the permissions for file operations.
1628 * Permissions of the user and groups the user is a member of were combined by a logical OR.
1629 *
1630 * Meaning of each bit:
1631 * 1 - Files: Upload,Copy,Move,Delete,Rename
1632 * 2 - Files: Unzip
1633 * 4 - Directory: Move,Delete,Rename,New
1634 * 8 - Directory: Copy
1635 * 16 - Directory: Delete recursively (rm -Rf)
1636 *
1637 * @return integer File operation permission bitmask
1638 * @deprecated since TYPO3 6.0, use the TSconfig settings instead
1639 */
1640 public function getFileoperationPermissions() {
1641 t3lib_div::logDeprecatedFunction();
1642
1643 if ($this->isAdmin()) {
1644 return 31;
1645 } else {
1646 return $this->groupData['fileoper_perms'];
1647 }
1648 }
1649
1650 /**
1651 * Adds a filemount to the users array of filemounts, $this->groupData['filemounts'][hash_key] = Array ('name'=>$name, 'path'=>$path, 'type'=>$type);
1652 * Is a part of the authentication proces of the user.
1653 * A final requirement for a path being mounted is that a) it MUST return TRUE on is_dir(), b) must contain either PATH_site+'fileadminDir' OR 'lockRootPath' - if lockRootPath is set - as first part of string!
1654 * Paths in the mounted information will always be absolute and have a trailing slash.
1655 *
1656 * @param string $title Will be the (root)name of the filemount in the folder tree
1657 * @param string $altTitle Will be the (root)name of the filemount IF $title is not TRUE (blank or zero)
1658 * @param string $path Is the path which should be mounted. Will accept backslash in paths on windows servers (will substituted with forward slash). The path should be 1) relative to TYPO3_CONF_VARS[BE][fileadminDir] if $webspace is set, otherwise absolute.
1659 * @param boolean $webspace If $webspace is set, the $path is relative to 'fileadminDir' in TYPO3_CONF_VARS, otherwise $path is absolute. 'fileadminDir' must be set to allow mounting of relative paths.
1660 * @param string $type Type of filemount; Can be blank (regular) or "user" / "group" (for user and group filemounts presumably). Probably sets the icon first and foremost.
1661 * @return boolean Returns "1" if the requested filemount was mounted, otherwise no return value.
1662 * @deprecated since TYPO3 6.0, will be removed in TYPO3 6.1, all data is stored in $this->fileStorages now, see initializeFileStorages()
1663 * @access private
1664 */
1665 function addFileMount($title, $altTitle, $path, $webspace, $type) {
1666 t3lib_div::logDeprecatedFunction();
1667
1668 // Return FALSE if fileadminDir is not set and we try to mount a relative path
1669 if ($webspace && !$GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir']) {
1670 return FALSE;
1671 }
1672
1673 // Trimming and pre-processing
1674 $path = trim($path);
1675 // with WINDOWS convert backslash to slash!!
1676 if ($this->OS == 'WIN') {
1677 $path = str_replace('\\', '/', $path);
1678 }
1679 // If the path is TRUE and validates as a valid path string:
1680 if ($path && t3lib_div::validPathStr($path)) {
1681 // normalize path: remove leading '/' and './', and trailing '/' and '/.'
1682 $path = trim($path);
1683 $path = preg_replace('#^\.?/|/\.?$#', '', $path);
1684
1685 // There must be some chars in the path
1686 if ($path) {
1687 // Fileadmin dir, absolute
1688 $fdir = PATH_site . $GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'];
1689 if ($webspace) {
1690 $path = $fdir . $path; // PATH_site + fileadmin dir is prepended
1691 } else {
1692 // With WINDOWS no prepending!!
1693 if ($this->OS != 'WIN') {
1694 // With WINDOWS no prepending!!
1695 $path = '/' . $path;
1696 }
1697 }
1698 $path .= '/';
1699
1700 // We now have a path with slash after and slash before (if unix)
1701 if (@is_dir($path) &&
1702 (($GLOBALS['TYPO3_CONF_VARS']['BE']['lockRootPath'] && t3lib_div::isFirstPartOfStr($path, $GLOBALS['TYPO3_CONF_VARS']['BE']['lockRootPath'])) || t3lib_div::isFirstPartOfStr($path, $fdir))) {
1703 // Alternative title?
1704 $name = $title ? $title : $altTitle;
1705 // Adds the filemount. The same filemount with same name, type and path cannot be set up twice because of the hash string used as key.
1706 $this->groupData['filemounts'][md5($name . '|' . $path . '|' . $type)] = array('name' => $name, 'path' => $path, 'type' => $type);
1707 // Return TRUE - went well, success!
1708 return 1;
1709 }
1710 }
1711 }
1712 }
1713
1714
1715 /**
1716 * Returns an array with the filemounts for the user. Each filemount is represented with an array of a "name", "path" and "type".
1717 * If no filemounts an empty array is returned.
1718 *
1719 * @return array
1720 * @deprecated since TYPO3 6.0, will be removed in TYPO3 6.1 as getFileStorages() should be the one to be used
1721 */
1722 function returnFilemounts() {
1723 t3lib_div::logDeprecatedFunction();
1724
1725 // initialize the file storages in order to set some default settings in any time
1726 $this->getFileStorages();
1727
1728 return $this->groupData['filemounts'];
1729 }
1730
1731 /**
1732 * Creates a TypoScript comment with the string text inside.
1733 *
1734 * @param string $str The text to wrap in comment prefixes and delimiters.
1735 * @return string TypoScript comment with the string text inside.
1736 */
1737 function addTScomment($str) {
1738 $delimiter = '# ***********************************************';
1739
1740 $out = $delimiter . LF;
1741 $lines = t3lib_div::trimExplode(LF, $str);
1742 foreach ($lines as $v) {
1743 $out .= '# ' . $v . LF;
1744 }
1745 $out .= $delimiter . LF;
1746 return $out;
1747 }
1748
1749 /************************************
1750 *
1751 * Workspaces
1752 *
1753 ************************************/
1754
1755 /**
1756 * Initializing workspace.
1757 * Called from within this function, see fetchGroupData()
1758 *
1759 * @return void
1760 * @see fetchGroupData()
1761 */
1762 function workspaceInit() {
1763
1764 // Initializing workspace by evaluating and setting the workspace, possibly updating it in the user record!
1765 $this->setWorkspace($this->user['workspace_id']);
1766
1767 // Limiting the DB mountpoints if there any selected in the workspace record
1768 $this->initializeDbMountpointsInWorkspace();
1769
1770 if ($allowed_languages = $this->getTSConfigVal('options.workspaces.allowed_languages.' . $this->workspace)) {
1771 $this->groupData['allowed_languages'] = $allowed_languages;
1772 $this->groupData['allowed_languages'] = t3lib_div::uniqueList($this->groupData['allowed_languages']);
1773 }
1774 }
1775
1776 /**
1777 * Limiting the DB mountpoints if there any selected in the workspace record
1778 *
1779 * @return void
1780 */
1781 protected function initializeDbMountpointsInWorkspace() {
1782 $dbMountpoints = trim($this->workspaceRec['db_mountpoints']);
1783 if ($this->workspace > 0 && $dbMountpoints != '') {
1784 $filteredDbMountpoints = array();
1785 // Notice: We cannot call $this->getPagePermsClause(1); as usual because the group-list is not available at this point. But bypassing is fine because all we want here is check if the workspace mounts are inside the current webmounts rootline.
1786 // The actual permission checking on page level is done elsewhere as usual anyway before the page tree is rendered.
1787 $readPerms = '1=1';
1788
1789 // Traverse mount points of the
1790 $dbMountpoints = t3lib_div::intExplode(',', $dbMountpoints);
1791 foreach ($dbMountpoints as $mpId) {
1792 if ($this->isInWebMount($mpId, $readPerms)) {
1793 $filteredDbMountpoints[] = $mpId;
1794 }
1795 }
1796
1797 // Re-insert webmounts:
1798 $filteredDbMountpoints = array_unique($filteredDbMountpoints);
1799 $this->groupData['webmounts'] = implode(',', $filteredDbMountpoints);
1800 }
1801 }
1802
1803 /**
1804 * Adds more limitations for users who are no admins
1805 * this was previously in workspaceInit but has now been moved to "
1806 *
1807 * @return void
1808 */
1809 protected function initializeFileStoragesForWorkspace() {
1810 // Filtering the file mountpoints
1811 // if there some selected in the workspace record
1812 if ($this->workspace > 0) {
1813 $storageFiltersInWorkspace = trim($this->workspaceRec['file_mountpoints']);
1814
1815 // no custom filemounts that should serve as filter or user is admin
1816 // so all user mountpoints are re-applied
1817 if (!$this->isAdmin() && $storageFiltersInWorkspace !== '') {
1818 // empty the fileStorages (will be re-applied later)
1819 $existingFileStoragesOfUser = $this->fileStorages;
1820 $this->fileStorages = array();
1821
1822 $storageRepository = t3lib_div::makeInstance('t3lib_file_Repository_StorageRepository');
1823
1824 // Fetching all filemounts from the workspace
1825 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
1826 '*',
1827 'sys_filemounts',
1828 'deleted = 0 AND hidden = 0 AND pid = 0 AND uid IN (' . $GLOBALS['TYPO3_DB']->cleanIntList($storageFiltersInWorkspace) . ')'
1829 );
1830
1831 // add every filemount of this workspace record
1832 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
1833 // get the added entry, and check if it was in the users' original filemounts
1834 // if not, remove it from the new filemount list again
1835 // see self::addFileMount
1836
1837 // TODO: check if the filter is narrowing down the existing user
1838 $storageObject = $storageRepository->findByUid($row['base']);
1839 if (isset($existingFileStoragesOfUser[$storageObject->getUid()])) {
1840 $storageObject->injectFileMount($row['path']);
1841 $this->fileStorages[$storageObject->getUid()] = $storageObject;
1842 }
1843 }
1844 $GLOBALS['TYPO3_DB']->sql_free_result($res);
1845 }
1846 }
1847 }
1848
1849 /**
1850 * Checking if a workspace is allowed for backend user
1851 *
1852 * @param mixed $wsRec If integer, workspace record is looked up, if array it is seen as a Workspace record with at least uid, title, members and adminusers columns. Can be faked for workspaces uid 0 and -1 (online and offline)
1853 * @param string $fields List of fields to select. Default fields are: uid,title,adminusers,members,reviewers,publish_access,stagechg_notification
1854 * @return array TRUE if access. Output will also show how access was granted. Admin users will have a true output regardless of input.
1855 */
1856 function checkWorkspace($wsRec, $fields = 'uid,title,adminusers,members,reviewers,publish_access,stagechg_notification') {
1857 $retVal = FALSE;
1858
1859 // If not array, look up workspace record:
1860 if (!is_array($wsRec)) {
1861 switch ((string) $wsRec) {
1862 case '0':
1863 $wsRec = array('uid' => $wsRec);
1864 break;
1865 default:
1866 if (t3lib_extMgm::isLoaded('workspaces')) {
1867 $wsRec = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow(
1868 $fields,
1869 'sys_workspace',
1870 'pid=0 AND uid=' . intval($wsRec) .
1871 t3lib_BEfunc::deleteClause('sys_workspace'),
1872 '',
1873 'title'
1874 );
1875 }
1876 break;
1877 }
1878 }
1879
1880 // If wsRec is set to an array, evaluate it:
1881 if (is_array($wsRec)) {
1882 if ($this->isAdmin()) {
1883 return array_merge($wsRec, array('_ACCESS' => 'admin'));
1884 } else {
1885
1886 switch ((string) $wsRec['uid']) {
1887 case '0':
1888 $retVal = ($this->groupData['workspace_perms'] & 1) ? array_merge($wsRec, array('_ACCESS' => 'online')) : FALSE;
1889 break;
1890 default:
1891 // Checking if the guy is admin:
1892 if (t3lib_div::inList($wsRec['adminusers'], 'be_users_' . $this->user['uid'])) {
1893 return array_merge($wsRec, array('_ACCESS' => 'owner'));
1894 }
1895 // Checking if he is owner through a user group of his:
1896 foreach ($this->userGroupsUID as $groupUid) {
1897 if (t3lib_div::inList($wsRec['adminusers'], 'be_groups_' . $groupUid)) {
1898 return array_merge($wsRec, array('_ACCESS' => 'owner'));
1899 }
1900 }
1901 // Checking if he is reviewer user:
1902 if (t3lib_div::inList($wsRec['reviewers'], 'be_users_' . $this->user['uid'])) {
1903 return array_merge($wsRec, array('_ACCESS' => 'reviewer'));
1904 }
1905 // Checking if he is reviewer through a user group of his:
1906 foreach ($this->userGroupsUID as $groupUid) {
1907 if (t3lib_div::inList($wsRec['reviewers'], 'be_groups_' . $groupUid)) {
1908 return array_merge($wsRec, array('_ACCESS' => 'reviewer'));
1909 }
1910 }
1911 // Checking if he is member as user:
1912 if (t3lib_div::inList($wsRec['members'], 'be_users_' . $this->user['uid'])) {
1913 return array_merge($wsRec, array('_ACCESS' => 'member'));
1914 }
1915 // Checking if he is member through a user group of his:
1916 foreach ($this->userGroupsUID as $groupUid) {
1917 if (t3lib_div::inList($wsRec['members'], 'be_groups_' . $groupUid)) {
1918 return array_merge($wsRec, array('_ACCESS' => 'member'));
1919 }
1920 }
1921 break;
1922 }
1923 }
1924 }
1925
1926 return $retVal;
1927 }
1928
1929 /**
1930 * Uses checkWorkspace() to check if current workspace is available for user. This function caches the result and so can be called many times with no performance loss.
1931 *
1932 * @return array See checkWorkspace()
1933 * @see checkWorkspace()
1934 */
1935 function checkWorkspaceCurrent() {
1936 if (!isset($this->checkWorkspaceCurrent_cache)) {
1937 $this->checkWorkspaceCurrent_cache = $this->checkWorkspace($this->workspace);
1938 }
1939 return $this->checkWorkspaceCurrent_cache;
1940 }
1941
1942 /**
1943 * Setting workspace ID
1944 *
1945 * @param integer $workspaceId ID of workspace to set for backend user. If not valid the default workspace for BE user is found and set.
1946 * @return void
1947 */
1948 function setWorkspace($workspaceId) {
1949
1950 // Check workspace validity and if not found, revert to default workspace.
1951 if ($this->workspaceRec = $this->checkWorkspace($workspaceId, '*')) {
1952 // Set workspace ID internally
1953 $this->workspace = (int) $workspaceId;
1954 } else {
1955 $this->workspace = (int) $this->getDefaultWorkspace();
1956 $this->workspaceRec = $this->checkWorkspace($this->workspace, '*');
1957 }
1958
1959 // Unset access cache:
1960 unset($this->checkWorkspaceCurrent_cache);
1961
1962 // If ID is different from the stored one, change it:
1963 if (strcmp($this->workspace, $this->user['workspace_id'])) {
1964 $this->user['workspace_id'] = $this->workspace;
1965 $GLOBALS['TYPO3_DB']->exec_UPDATEquery('be_users', 'uid=' . intval($this->user['uid']), array('workspace_id' => $this->user['workspace_id']));
1966 $this->simplelog('User changed workspace to "' . $this->workspace . '"');
1967 }
1968 }
1969
1970 /**
1971 * Setting workspace preview state for user:
1972 *
1973 * @param boolean $previewState State of user preview.
1974 * @return void
1975 */
1976 function setWorkspacePreview($previewState) {
1977 $this->user['workspace_preview'] = $previewState;
1978 $GLOBALS['TYPO3_DB']->exec_UPDATEquery('be_users', 'uid=' . intval($this->user['uid']), array('workspace_preview' => $this->user['workspace_preview']));
1979 }
1980
1981 /**
1982 * Return default workspace ID for user,
1983 * If EXT:workspaces is not installed the user will be pushed the the
1984 * Live workspace
1985 *
1986 * @return integer Default workspace id. If no workspace is available it will be "-99"
1987 */
1988 function getDefaultWorkspace() {
1989
1990 $defaultWorkspace = -99;
1991
1992 if (!t3lib_extMgm::isLoaded('workspaces') || $this->checkWorkspace(0)) { // Check online
1993 $defaultWorkspace = 0;
1994 } elseif ($this->checkWorkspace(-1)) { // Check offline
1995 $defaultWorkspace = -1;
1996 } elseif (t3lib_extMgm::isLoaded('workspaces')) { // Traverse custom workspaces:
1997 $workspaces = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid,title,adminusers,members,reviewers', 'sys_workspace', 'pid=0' . t3lib_BEfunc::deleteClause('sys_workspace'), '', 'title');
1998 foreach ($workspaces as $rec) {
1999 if ($this->checkWorkspace($rec)) {
2000 $defaultWorkspace = $rec['uid'];
2001 break;
2002 }
2003 }
2004 }
2005 return $defaultWorkspace;
2006 }
2007
2008 /************************************
2009 *
2010 * Logging
2011 *
2012 ************************************/
2013
2014 /**
2015 * Writes an entry in the logfile/table
2016 * Documentation in "TYPO3 Core API"
2017 *
2018 * @param integer $type Denotes which module that has submitted the entry. See "TYPO3 Core API". Use "4" for extensions.
2019 * @param integer $action Denotes which specific operation that wrote the entry. Use "0" when no sub-categorizing applies
2020 * @param integer $error Flag. 0 = message, 1 = error (user problem), 2 = System Error (which should not happen), 3 = security notice (admin)
2021 * @param integer $details_nr The message number. Specific for each $type and $action. This will make it possible to translate errormessages to other languages
2022 * @param string $details Default text that follows the message (in english!). Possibly translated by identification through type/action/details_nr
2023 * @param array $data Data that follows the log. Might be used to carry special information. If an array the first 5 entries (0-4) will be sprintf'ed with the details-text
2024 * @param string $tablename Table name. Special field used by tce_main.php.
2025 * @param integer $recuid Record UID. Special field used by tce_main.php.
2026 * @param integer $recpid Record PID. Special field used by tce_main.php. OBSOLETE
2027 * @param integer $event_pid The page_uid (pid) where the event occurred. Used to select log-content for specific pages.
2028 * @param string $NEWid Special field used by tce_main.php. NEWid string of newly created records.
2029 * @param integer $userId Alternative Backend User ID (used for logging login actions where this is not yet known).
2030 * @return integer Log entry ID.
2031 */
2032 function writelog($type, $action, $error, $details_nr, $details, $data, $tablename = '', $recuid = '', $recpid = '', $event_pid = -1, $NEWid = '', $userId = 0) {
2033
2034 $fields_values = array(
2035 'userid' => $userId ? $userId : intval($this->user['uid']),
2036 'type' => intval($type),
2037 'action' => intval($action),
2038 'error' => intval($error),
2039 'details_nr' => intval($details_nr),
2040 'details' => $details,
2041 'log_data' => serialize($data),
2042 'tablename' => $tablename,
2043 'recuid' => intval($recuid),
2044 'IP' => t3lib_div::getIndpEnv('REMOTE_ADDR'),
2045 'tstamp' => $GLOBALS['EXEC_TIME'],
2046 'event_pid' => intval($event_pid),
2047 'NEWid' => $NEWid,
2048 'workspace' => $this->workspace
2049 );
2050
2051 $GLOBALS['TYPO3_DB']->exec_INSERTquery('sys_log', $fields_values);
2052 return $GLOBALS['TYPO3_DB']->sql_insert_id();
2053 }
2054
2055 /**
2056 * Simple logging function
2057 *
2058 * @param string $message Log message
2059 * @param string $extKey Option extension key / module name
2060 * @param integer $error Error level. 0 = message, 1 = error (user problem), 2 = System Error (which should not happen), 3 = security notice (admin)
2061 * @return integer Log entry UID
2062 */
2063 function simplelog($message, $extKey = '', $error = 0) {
2064 return $this->writelog(
2065 4,
2066 0,
2067 $error,
2068 0,
2069 ($extKey ? '[' . $extKey . '] ' : '') . $message,
2070 array()
2071 );
2072 }
2073
2074 /**
2075 * Sends a warning to $email if there has been a certain amount of failed logins during a period.
2076 * If a login fails, this function is called. It will look up the sys_log to see if there has been more than $max failed logins the last $secondsBack seconds (default 3600). If so, an email with a warning is sent to $email.
2077 *
2078 * @param string $email Email address
2079 * @param integer $secondsBack Number of sections back in time to check. This is a kind of limit for how many failures an hour for instance.
2080 * @param integer $max Max allowed failures before a warning mail is sent
2081 * @return void
2082 * @access private
2083 */
2084 function checkLogFailures($email, $secondsBack = 3600, $max = 3) {
2085
2086 if ($email) {
2087
2088 // Get last flag set in the log for sending
2089 $theTimeBack = $GLOBALS['EXEC_TIME'] - $secondsBack;
2090 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
2091 'tstamp',
2092 'sys_log',
2093 'type=255 AND action=4 AND tstamp>' . intval($theTimeBack),
2094 '',
2095 'tstamp DESC',
2096 '1'
2097 );
2098 if ($testRow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
2099 $theTimeBack = $testRow['tstamp'];
2100 }
2101 $GLOBALS['TYPO3_DB']->sql_free_result($res);
2102
2103 // Check for more than $max number of error failures with the last period.
2104 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
2105 '*',
2106 'sys_log',
2107 'type=255 AND action=3 AND error<>0 AND tstamp>' . intval($theTimeBack),
2108 '',
2109 'tstamp'
2110 );
2111 if ($GLOBALS['TYPO3_DB']->sql_num_rows($res) > $max) {
2112 // OK, so there were more than the max allowed number of login failures - so we will send an email then.
2113 $subject = 'TYPO3 Login Failure Warning (at ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] . ')';
2114 $email_body = 'There have been some attempts (' . $GLOBALS['TYPO3_DB']->sql_num_rows($res) . ') to login at the TYPO3
2115 site "' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] . '" (' . t3lib_div::getIndpEnv('HTTP_HOST') . ').
2116
2117 This is a dump of the failures:
2118
2119 ';
2120 while ($testRows = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
2121 $theData = unserialize($testRows['log_data']);
2122 $email_body .= date($GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] . ' ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'], $testRows['tstamp']) . ': ' . @sprintf($testRows['details'], '' . $theData[0], '' . $theData[1], '' . $theData[2]);
2123 $email_body .= LF;
2124 }
2125 $GLOBALS['TYPO3_DB']->sql_free_result($res);
2126
2127 $from = t3lib_utility_Mail::getSystemFrom();
2128 /** @var $mail t3lib_mail_Message */
2129 $mail = t3lib_div::makeInstance('t3lib_mail_Message');
2130 $mail->setTo($email)
2131 ->setFrom($from)
2132 ->setSubject($subject)
2133 ->setBody($email_body);
2134 $mail->send();
2135 // Logout written to log
2136 $this->writelog(255, 4, 0, 3, 'Failure warning (%s failures within %s seconds) sent by email to %s', array($GLOBALS['TYPO3_DB']->sql_num_rows($res), $secondsBack, $email));
2137 }
2138 }
2139 }
2140 }
2141
2142 ?>