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