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