/** * Returns RichTextElement as class name if RTE widget should be rendered. * * @return string|void New class name or void if this resolver does not change current class name. */ public function resolve() { $table = $this->data['tableName']; $fieldName = $this->data['fieldName']; $row = $this->data['databaseRow']; $parameterArray = $this->data['parameterArray']; $backendUser = $this->getBackendUserAuthentication(); if (!$parameterArray['fieldConf']['config']['readOnly'] && $backendUser->isRTE()) { // @todo: Most of this stuff is prepared by data providers within $this->data already $specialConfiguration = BackendUtility::getSpecConfParts($parameterArray['fieldConf']['defaultExtras']); // If "richtext" is within defaultExtras if (isset($specialConfiguration['richtext'])) { // Operates by reference on $row! 'pid' is changed ... BackendUtility::fixVersioningPid($table, $row); list($recordPid, $tsConfigPid) = BackendUtility::getTSCpidCached($table, $row['uid'], $row['pid']); // If the pid-value is not negative (that is, a pid could NOT be fetched) if ($tsConfigPid >= 0) { // Fetch page ts config and do some magic with it to find out if RTE is disabled on TS level. $rteSetup = $backendUser->getTSConfig('RTE', BackendUtility::getPagesTSconfig($recordPid)); $rteTcaTypeValue = $this->data['recordTypeValue']; $rteSetupConfiguration = BackendUtility::RTEsetup($rteSetup['properties'], $table, $fieldName, $rteTcaTypeValue); if (!$rteSetupConfiguration['disabled']) { // Finally, we're sure the editor should really be rendered ... return RichtextElement::class; } } } } return null; }
/** * Fetch existing record from database * * @param array $result * @return array * @throws \UnexpectedValueException */ public function addData(array $result) { if ($result['command'] !== 'edit') { return $result; } $databaseRow = $this->getRecordFromDatabase($result['tableName'], $result['vanillaUid']); if (!array_key_exists('pid', $databaseRow)) { throw new \UnexpectedValueException('Parent record does not have a pid field', 1437663061); } // Warning: By reference! In case the record is in a workspace, the -1 in pid will be // changed to the real pid of the life record here. BackendUtility::fixVersioningPid($result['tableName'], $databaseRow); $result['databaseRow'] = $databaseRow; return $result; }
/** * Returns RichTextElement as class name if RTE widget should be rendered. * * @return string|void New class name or void if this resolver does not change current class name. */ public function resolve() { $table = $this->globalOptions['table']; $fieldName = $this->globalOptions['fieldName']; $row = $this->globalOptions['databaseRow']; $parameterArray = $this->globalOptions['parameterArray']; $backendUser = $this->getBackendUserAuthentication(); if ( // Whole thing is not read only empty($this->globalOptions['renderReadonly']) // This field is not read only && !$parameterArray['fieldConf']['config']['readOnly'] // If RTE is generally enabled by user settings and RTE object registry can return something valid && $backendUser->isRTE() ) { $specialConfiguration = BackendUtility::getSpecConfParts($parameterArray['fieldConf']['defaultExtras']); // $parameters is a key=>value array from "defaultExtras" pipe separated rte_transform string $parameters = BackendUtility::getSpecConfParametersFromArray($specialConfiguration['rte_transform']['parameters']); if ( // If "richtext" is within defaultExtras isset($specialConfiguration['richtext']) // rte_transform[flag=foo] means RTE should only be rendered if the value of db row field "foo" can be interpreted as TRUE && (!$parameters['flag'] || !$row[$parameters['flag']]) ) { // Operates by reference on $row! 'pid' is changed ... BackendUtility::fixVersioningPid($table, $row); list($recordPid, $tsConfigPid) = BackendUtility::getTSCpidCached($table, $row['uid'], $row['pid']); // If the pid-value is not negative (that is, a pid could NOT be fetched) if ($tsConfigPid >= 0) { // Fetch page ts config and do some magic with it to find out if RTE is disabled on TS level. $rteSetup = $backendUser->getTSConfig('RTE', BackendUtility::getPagesTSconfig($recordPid)); $rteTcaTypeValue = BackendUtility::getTCAtypeValue($table, $row); $rteSetupConfiguration = BackendUtility::RTEsetup($rteSetup['properties'], $table, $fieldName, $rteTcaTypeValue); if (!$rteSetupConfiguration['disabled']) { // Finally, we're sure the editor should really be rendered ... return RichtextElement::class; } } } } return NULL; }
/** * Renders the ckeditor element * * @return array * @throws \InvalidArgumentException */ public function render() : array { $resultArray = $this->initializeResultArray(); $row = $this->data['databaseRow']; BackendUtility::fixVersioningPid($this->data['tableName'], $row); $this->pidOfVersionedMotherRecord = (int) $row['pid']; $resourcesPath = PathUtility::getAbsoluteWebPath(ExtensionManagementUtility::extPath('rte_ckeditor', 'Resources/Public/')); $table = $this->data['tableName']; $row = $this->data['databaseRow']; $parameterArray = $this->data['parameterArray']; $defaultExtras = BackendUtility::getSpecConfParts($parameterArray['fieldConf']['defaultExtras']); BackendUtility::fixVersioningPid($table, $row); $fieldId = $this->sanitizeFieldId($parameterArray['itemFormElName']); $resultArray['html'] = $this->renderWizards([$this->getHtml($fieldId)], $parameterArray['fieldConf']['config']['wizards'], $table, $row, $this->data['fieldName'], $parameterArray, $parameterArray['itemFormElName'], $defaultExtras, true); $vanillaRteTsConfig = $this->getBackendUserAuthentication()->getTSConfig('RTE', BackendUtility::getPagesTSconfig($this->data['effectivePid'])); $this->rteConfiguration = BackendUtility::RTEsetup($vanillaRteTsConfig['properties'], $table, $this->data['fieldName'], $this->data['recordTypeValue']); $resultArray['requireJsModules'] = []; $resultArray['requireJsModules'][] = ['ckeditor' => $this->getCkEditorRequireJsModuleCode($resourcesPath, $fieldId)]; return $resultArray; }
/** * This will render a <textarea> OR RTE area form field, * possibly with various control/validation features * * @return array As defined in initializeResultArray() of AbstractNode */ public function render() { $table = $this->globalOptions['table']; $fieldName = $this->globalOptions['fieldName']; $row = $this->globalOptions['databaseRow']; $parameterArray = $this->globalOptions['parameterArray']; $resultArray = $this->initializeResultArray(); $backendUser = $this->getBackendUserAuthentication(); $validationConfig = array(); // "Extra" configuration; Returns configuration for the field based on settings found in the "types" fieldlist. Traditionally, this is where RTE configuration has been found. $specialConfiguration = BackendUtility::getSpecConfParts($parameterArray['fieldConf']['defaultExtras']); // Setting up the altItem form field, which is a hidden field containing the value $altItem = '<input type="hidden" name="' . htmlspecialchars($parameterArray['itemFormElName']) . '" value="' . htmlspecialchars($parameterArray['itemFormElValue']) . '" />'; BackendUtility::fixVersioningPid($table, $row); list($recordPid, $tsConfigPid) = BackendUtility::getTSCpidCached($table, $row['uid'], $row['pid']); // If the pid-value is not negative (that is, a pid could NOT be fetched) $rteSetup = $backendUser->getTSConfig('RTE', BackendUtility::getPagesTSconfig($recordPid)); $rteTcaTypeValue = BackendUtility::getTCAtypeValue($table, $row); $rteSetupConfiguration = BackendUtility::RTEsetup($rteSetup['properties'], $table, $fieldName, $rteTcaTypeValue); // Get RTE object, draw form and set flag: $rteObject = BackendUtility::RTEgetObj(); $dummyFormEngine = new FormEngine(); $rteResult = $rteObject->drawRTE($dummyFormEngine, $table, $fieldName, $row, $parameterArray, $specialConfiguration, $rteSetupConfiguration, $rteTcaTypeValue, '', $tsConfigPid, $this->globalOptions, $this->initializeResultArray(), $this->getValidationDataAsDataAttribute($validationConfig)); // This is a compat layer for "other" RTE's: If the result is not an array, it is the html string, // otherwise it is a structure similar to our casual return array // @todo: This interface needs a full re-definition, RTE should probably be its own type in the // @todo: end, and other RTE implementations could then just override this. if (is_array($rteResult)) { $html = $rteResult['html']; $rteResult['html'] = ''; $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $rteResult); } else { $html = $rteResult; } // Wizard $html = $this->renderWizards(array($html, $altItem), $parameterArray['fieldConf']['config']['wizards'], $table, $row, $fieldName, $parameterArray, $parameterArray['itemFormElName'], $specialConfiguration, TRUE); $resultArray['html'] = $html; return $resultArray; }
/** * Checks access for element * * @param string $table Table name * @param int $uid Record uid * @return bool */ protected function checkEditAccess($table, $uid) { $record = BackendUtility::getRecord($table, $uid); BackendUtility::fixVersioningPid($table, $record); if (is_array($record)) { // If pages: if ($table === 'pages') { $calculatedPermissions = $this->getBackendUserAuthentication()->calcPerms($record); $hasAccess = $calculatedPermissions & Permission::PAGE_EDIT; } else { // Fetching pid-record first. $calculatedPermissions = $this->getBackendUserAuthentication()->calcPerms(BackendUtility::getRecord('pages', $record['pid'])); $hasAccess = $calculatedPermissions & Permission::CONTENT_EDIT; } // Check internals regarding access: if ($hasAccess) { $hasAccess = $this->getBackendUserAuthentication()->recordEditAccessInternals($table, $record); } } else { $hasAccess = false; } return (bool) $hasAccess; }
/** * A function which can be used for load a batch of records from $table into internal memory of this object. * The function is also used to produce proper default data for new records * Ultimately the function will call renderRecord() * * @param string $table Table name, must be found in $GLOBALS['TCA'] * @param string $idList Comma list of id values. If $idList is "prev" then the value from $this->prevPageID is used. NOTICE: If $operation is "new", then negative ids are meant to point to a "previous" record and positive ids are PID values for new records. Otherwise (for existing records that is) it is straight forward table/id pairs. * @param string $operation If "new", then a record with default data is returned. Further, the $id values are meant to be PID values (or if negative, pointing to a previous record). If NOT new, then the table/ids are just pointing to an existing record! * @return void * @see renderRecord() */ public function fetchRecord($table, $idList, $operation) { if ((string) $idList === 'prev') { $idList = $this->prevPageID; } if ($GLOBALS['TCA'][$table]) { // For each ID value (int) we $ids = GeneralUtility::trimExplode(',', $idList, TRUE); foreach ($ids as $id) { // If ID is not blank: if ((string) $id !== '') { // For new records to be created, find default values: if ($operation == 'new') { // Default values: // Used to store default values as found here: $newRow = array(); // Default values as set in userTS: $TCAdefaultOverride = $GLOBALS['BE_USER']->getTSConfigProp('TCAdefaults'); if (is_array($TCAdefaultOverride[$table . '.'])) { foreach ($TCAdefaultOverride[$table . '.'] as $theF => $theV) { if (isset($GLOBALS['TCA'][$table]['columns'][$theF])) { $newRow[$theF] = $theV; } } } if ($id < 0) { $record = BackendUtility::getRecord($table, abs($id), 'pid'); $pid = $record['pid']; unset($record); } else { $pid = (int) $id; } $pageTS = BackendUtility::getPagesTSconfig($pid); if (isset($pageTS['TCAdefaults.'])) { $TCAPageTSOverride = $pageTS['TCAdefaults.']; if (is_array($TCAPageTSOverride[$table . '.'])) { foreach ($TCAPageTSOverride[$table . '.'] as $theF => $theV) { if (isset($GLOBALS['TCA'][$table]['columns'][$theF])) { $newRow[$theF] = $theV; } } } } // Default values as submitted: if (!empty($this->defVals[$table]) && is_array($this->defVals[$table])) { foreach ($this->defVals[$table] as $theF => $theV) { if (isset($GLOBALS['TCA'][$table]['columns'][$theF])) { $newRow[$theF] = $theV; } } } // Fetch default values if a previous record exists if ($id < 0 && $GLOBALS['TCA'][$table]['ctrl']['useColumnsForDefaultValues']) { // Fetches the previous record: $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', $table, 'uid=' . abs($id) . BackendUtility::deleteClause($table)); if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { // Gets the list of fields to copy from the previous record. $fArr = GeneralUtility::trimExplode(',', $GLOBALS['TCA'][$table]['ctrl']['useColumnsForDefaultValues'], TRUE); foreach ($fArr as $theF) { if (isset($GLOBALS['TCA'][$table]['columns'][$theF]) && !isset($newRow[$theF])) { $newRow[$theF] = $row[$theF]; } } } $GLOBALS['TYPO3_DB']->sql_free_result($res); } // Finally, call renderRecord: $this->renderRecord($table, uniqid('NEW', TRUE), $id, $newRow); } else { $id = (int) $id; // Fetch database values $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', $table, 'uid=' . $id . BackendUtility::deleteClause($table)); if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { BackendUtility::fixVersioningPid($table, $row); $this->renderRecord($table, $id, $row['pid'], $row); $this->lockRecord($table, $id, $table === 'tt_content' ? $row['pid'] : 0); } $GLOBALS['TYPO3_DB']->sql_free_result($res); } } } } $this->emitFetchRecordPostProcessingSignal(); }
public function readPageAccess($id, $perms_clause) { if ((string) $id != '') { $id = intval($id); if (!$id) { if ($GLOBALS['BE_USER']->isAdmin()) { $path = '/'; $pageinfo['_thePath'] = $path; return $pageinfo; } } else { $pageinfo = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecord('pages', $id, '*', $perms_clause ? ' AND ' . $perms_clause : ''); if ($pageinfo['uid'] && $GLOBALS['BE_USER']->isInWebMount($id, $perms_clause)) { \TYPO3\CMS\Backend\Utility\BackendUtility::workspaceOL('pages', $pageinfo); \TYPO3\CMS\Backend\Utility\BackendUtility::fixVersioningPid('pages', $pageinfo); list($pageinfo['_thePath'], $pageinfo['_thePathFull']) = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecordPath(intval($pageinfo['uid']), $perms_clause, 15, 1000); return $pageinfo; } } } return false; }
/** * Initializes language icons etc. * * @param string $table Table name * @param array $row Record * @param string $sys_language_uid Sys language uid OR ISO language code prefixed with "v", eg. "vDA * @return string * @todo Define visibility */ public function getLanguageIcon($table, $row, $sys_language_uid) { $mainKey = $table . ':' . $row['uid']; if (!isset($this->cachedLanguageFlag[$mainKey])) { BackendUtility::fixVersioningPid($table, $row); list($tscPID) = $this->getTSCpid($table, $row['uid'], $row['pid']); /** @var $t8Tools \TYPO3\CMS\Backend\Configuration\TranslationConfigurationProvider */ $t8Tools = GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\Configuration\\TranslationConfigurationProvider'); $this->cachedLanguageFlag[$mainKey] = $t8Tools->getSystemLanguages($tscPID, $this->backPath); } // Convert sys_language_uid to sys_language_uid if input was in fact a string (ISO code expected then) if (!MathUtility::canBeInterpretedAsInteger($sys_language_uid)) { foreach ($this->cachedLanguageFlag[$mainKey] as $rUid => $cD) { if ('v' . $cD['ISOcode'] === $sys_language_uid) { $sys_language_uid = $rUid; } } } $out = ''; if ($this->cachedLanguageFlag[$mainKey][$sys_language_uid]['flagIcon']) { $out .= IconUtility::getSpriteIcon($this->cachedLanguageFlag[$mainKey][$sys_language_uid]['flagIcon']); $out .= ' '; } elseif ($this->cachedLanguageFlag[$mainKey][$sys_language_uid]['title']) { $out .= '[' . $this->cachedLanguageFlag[$mainKey][$sys_language_uid]['title'] . ']'; $out .= ' '; } return $out; }
/** * Returns the path (visually) of a page $uid, fx. "/First page/Second page/Another subpage" * Each part of the path will be limited to $titleLimit characters * Deleted pages are filtered out. * * @param integer Page uid for which to create record path * @param string $clause is additional where clauses, eg. * @param integer Title limit * @param integer Title limit of Full title (typ. set to 1000 or so) * @return mixed Path of record (string) OR array with short/long title if $fullTitleLimit is set. */ public static function getRecordPath($uid, $clause = '', $titleLimit = 1000, $fullTitleLimit = 0) { $loopCheck = 100; $output = $fullOutput = '/'; while ($uid != 0 && $loopCheck > 0) { $loopCheck--; $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid,pid,title,deleted,t3ver_oid,t3ver_wsid', 'pages', 'uid=' . intval($uid) . (strlen(trim($clause)) ? ' AND ' . $clause : '')); if (is_resource($res)) { $row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res); $GLOBALS['TYPO3_DB']->sql_free_result($res); \TYPO3\CMS\Backend\Utility\BackendUtility::workspaceOL('pages', $row); if (is_array($row)) { \TYPO3\CMS\Backend\Utility\BackendUtility::fixVersioningPid('pages', $row); $uid = $row['pid']; $output = '/' . htmlspecialchars(\TYPO3\CMS\Core\Utility\GeneralUtility::fixed_lgd_cs($row['title'], $titleLimit)) . $output; if ($row['deleted']) { $output = '<span class="deletedPath">' . $output . '</span>'; } if ($fullTitleLimit) { $fullOutput = '/' . htmlspecialchars(\TYPO3\CMS\Core\Utility\GeneralUtility::fixed_lgd_cs($row['title'], $fullTitleLimit)) . $fullOutput; } } else { break; } } else { break; } } if ($fullTitleLimit) { return array($output, $fullOutput); } else { return $output; } }
/** * Checks the page access rights (Code for access check mostly taken from alt_doc.php) * as well as the table access rights of the user. * * @param string $cmd The command that should be performed ('new' or 'edit') * @param string $table The table to check access for * @param string $theUid The record uid of the table * @return boolean Returns TRUE is the user has access, or FALSE if not * @todo Define visibility */ public function checkAccess($cmd, $table, $theUid) { // Checking if the user has permissions? (Only working as a precaution, because the final permission check is always down in TCE. But it's good to notify the user on beforehand...) // First, resetting flags. $hasAccess = 0; // Admin users always have access: if ($GLOBALS['BE_USER']->isAdmin()) { return TRUE; } // If the command is to create a NEW record...: if ($cmd == 'new') { // If the pid is numerical, check if it's possible to write to this page: if (MathUtility::canBeInterpretedAsInteger($this->inlineFirstPid)) { $calcPRec = BackendUtility::getRecord('pages', $this->inlineFirstPid); if (!is_array($calcPRec)) { return FALSE; } // Permissions for the parent page $CALC_PERMS = $GLOBALS['BE_USER']->calcPerms($calcPRec); // If pages: if ($table == 'pages') { // Are we allowed to create new subpages? $hasAccess = $CALC_PERMS & 8 ? 1 : 0; } else { // Are we allowed to edit content on this page? $hasAccess = $CALC_PERMS & 16 ? 1 : 0; } } else { $hasAccess = 1; } } else { // Edit: $calcPRec = BackendUtility::getRecord($table, $theUid); BackendUtility::fixVersioningPid($table, $calcPRec); if (is_array($calcPRec)) { // If pages: if ($table == 'pages') { $CALC_PERMS = $GLOBALS['BE_USER']->calcPerms($calcPRec); $hasAccess = $CALC_PERMS & 2 ? 1 : 0; } else { // Fetching pid-record first. $CALC_PERMS = $GLOBALS['BE_USER']->calcPerms(BackendUtility::getRecord('pages', $calcPRec['pid'])); $hasAccess = $CALC_PERMS & 16 ? 1 : 0; } // Check internals regarding access: $isRootLevelRestrictionIgnored = BackendUtility::isRootLevelRestrictionIgnored($table); if ($hasAccess || (int) $calcPRec['pid'] === 0 && $isRootLevelRestrictionIgnored) { $hasAccess = $GLOBALS['BE_USER']->recordEditAccessInternals($table, $calcPRec); } } } if (!$GLOBALS['BE_USER']->check('tables_modify', $table)) { $hasAccess = 0; } if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tceforms_inline.php']['checkAccess']) && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tceforms_inline.php']['checkAccess'])) { foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tceforms_inline.php']['checkAccess'] as $_funcRef) { $_params = array('table' => $table, 'uid' => $theUid, 'cmd' => $cmd, 'hasAccess' => $hasAccess); $hasAccess = GeneralUtility::callUserFunction($_funcRef, $_params, $this); } } if (!$hasAccess) { $deniedAccessReason = $GLOBALS['BE_USER']->errorMsg; if ($deniedAccessReason) { debug($deniedAccessReason); } } return $hasAccess ? TRUE : FALSE; }
/** * Send an email notification to users in workspace * * @param array $stat Workspace access array from \TYPO3\CMS\Core\Authentication\BackendUserAuthentication::checkWorkspace() * @param int $stageId New Stage number: 0 = editing, 1= just ready for review, 10 = ready for publication, -1 = rejected! * @param string $table Table name of element (or list of element names if $id is zero) * @param int $id Record uid of element (if zero, then $table is used as reference to element(s) alone) * @param string $comment User comment sent along with action * @param DataHandler $tcemainObj TCEmain object * @param array $notificationAlternativeRecipients List of recipients to notify instead of be_users selected by sys_workspace, list is generated by workspace extension module * @return void */ protected function notifyStageChange(array $stat, $stageId, $table, $id, $comment, DataHandler $tcemainObj, array $notificationAlternativeRecipients = array()) { $workspaceRec = BackendUtility::getRecord('sys_workspace', $stat['uid']); // So, if $id is not set, then $table is taken to be the complete element name! $elementName = $id ? $table . ':' . $id : $table; if (!is_array($workspaceRec)) { return; } // Get the new stage title from workspaces library, if workspaces extension is installed if (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('workspaces')) { $stageService = GeneralUtility::makeInstance(\TYPO3\CMS\Workspaces\Service\StagesService::class); $newStage = $stageService->getStageTitle((int) $stageId); } else { // @todo CONSTANTS SHOULD BE USED - tx_service_workspace_workspaces // @todo use localized labels // Compile label: switch ((int) $stageId) { case 1: $newStage = 'Ready for review'; break; case 10: $newStage = 'Ready for publishing'; break; case -1: $newStage = 'Element was rejected!'; break; case 0: $newStage = 'Rejected element was noticed and edited'; break; default: $newStage = 'Unknown state change!?'; } } if (count($notificationAlternativeRecipients) == 0) { // Compile list of recipients: $emails = array(); switch ((int) $stat['stagechg_notification']) { case 1: switch ((int) $stageId) { case 1: $emails = $this->getEmailsForStageChangeNotification($workspaceRec['reviewers']); break; case 10: $emails = $this->getEmailsForStageChangeNotification($workspaceRec['adminusers'], TRUE); break; case -1: // List of elements to reject: $allElements = explode(',', $elementName); // Traverse them, and find the history of each foreach ($allElements as $elRef) { list($eTable, $eUid) = explode(':', $elRef); $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('log_data,tstamp,userid', 'sys_log', 'action=6 and details_nr=30 AND tablename=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($eTable, 'sys_log') . ' AND recuid=' . (int) $eUid, '', 'uid DESC'); // Find all implicated since the last stage-raise from editing to review: foreach ($rows as $dat) { $data = unserialize($dat['log_data']); $emails = $this->getEmailsForStageChangeNotification($dat['userid'], TRUE) + $emails; if ($data['stage'] == 1) { break; } } } break; case 0: $emails = $this->getEmailsForStageChangeNotification($workspaceRec['members']); break; default: $emails = $this->getEmailsForStageChangeNotification($workspaceRec['adminusers'], TRUE); } break; case 10: $emails = $this->getEmailsForStageChangeNotification($workspaceRec['adminusers'], TRUE); $emails = $this->getEmailsForStageChangeNotification($workspaceRec['reviewers']) + $emails; $emails = $this->getEmailsForStageChangeNotification($workspaceRec['members']) + $emails; break; default: // Do nothing } } else { $emails = $notificationAlternativeRecipients; } // prepare and then send the emails if (count($emails)) { // Path to record is found: list($elementTable, $elementUid) = explode(':', $elementName); $elementUid = (int) $elementUid; $elementRecord = BackendUtility::getRecord($elementTable, $elementUid); $recordTitle = BackendUtility::getRecordTitle($elementTable, $elementRecord); if ($elementTable == 'pages') { $pageUid = $elementUid; } else { BackendUtility::fixVersioningPid($elementTable, $elementRecord); $pageUid = $elementUid = $elementRecord['pid']; } // fetch the TSconfig settings for the email // old way, options are TCEMAIN.notificationEmail_body/subject $TCEmainTSConfig = $tcemainObj->getTCEMAIN_TSconfig($pageUid); // new way, options are // pageTSconfig: tx_version.workspaces.stageNotificationEmail.subject // userTSconfig: page.tx_version.workspaces.stageNotificationEmail.subject $pageTsConfig = BackendUtility::getPagesTSconfig($pageUid); $emailConfig = $pageTsConfig['tx_version.']['workspaces.']['stageNotificationEmail.']; $markers = array('###RECORD_TITLE###' => $recordTitle, '###RECORD_PATH###' => BackendUtility::getRecordPath($elementUid, '', 20), '###SITE_NAME###' => $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'], '###SITE_URL###' => GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . TYPO3_mainDir, '###WORKSPACE_TITLE###' => $workspaceRec['title'], '###WORKSPACE_UID###' => $workspaceRec['uid'], '###ELEMENT_NAME###' => $elementName, '###NEXT_STAGE###' => $newStage, '###COMMENT###' => $comment, '###USER_REALNAME###' => $tcemainObj->BE_USER->user['realName'], '###USER_FULLNAME###' => $tcemainObj->BE_USER->user['realName'], '###USER_USERNAME###' => $tcemainObj->BE_USER->user['username']); // add marker for preview links if workspace extension is loaded if (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('workspaces')) { $this->workspaceService = GeneralUtility::makeInstance(\TYPO3\CMS\Workspaces\Service\WorkspaceService::class); // only generate the link if the marker is in the template - prevents database from getting to much entries if (GeneralUtility::isFirstPartOfStr($emailConfig['message'], 'LLL:')) { $tempEmailMessage = $GLOBALS['LANG']->sL($emailConfig['message']); } else { $tempEmailMessage = $emailConfig['message']; } if (strpos($tempEmailMessage, '###PREVIEW_LINK###') !== FALSE) { $markers['###PREVIEW_LINK###'] = $this->workspaceService->generateWorkspacePreviewLink($elementUid); } unset($tempEmailMessage); $markers['###SPLITTED_PREVIEW_LINK###'] = $this->workspaceService->generateWorkspaceSplittedPreviewLink($elementUid, TRUE); } // Hook for preprocessing of the content for formmails: if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/version/class.tx_version_tcemain.php']['notifyStageChange-postModifyMarkers'])) { foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/version/class.tx_version_tcemain.php']['notifyStageChange-postModifyMarkers'] as $_classRef) { $_procObj =& GeneralUtility::getUserObj($_classRef); $markers = $_procObj->postModifyMarkers($markers, $this); } } // send an email to each individual user, to ensure the // multilanguage version of the email $emailRecipients = array(); // an array of language objects that are needed // for emails with different languages $languageObjects = array($GLOBALS['LANG']->lang => $GLOBALS['LANG']); // loop through each recipient and send the email foreach ($emails as $recipientData) { // don't send an email twice if (isset($emailRecipients[$recipientData['email']])) { continue; } $emailSubject = $emailConfig['subject']; $emailMessage = $emailConfig['message']; $emailRecipients[$recipientData['email']] = $recipientData['email']; // check if the email needs to be localized // in the users' language if (GeneralUtility::isFirstPartOfStr($emailSubject, 'LLL:') || GeneralUtility::isFirstPartOfStr($emailMessage, 'LLL:')) { $recipientLanguage = $recipientData['lang'] ? $recipientData['lang'] : 'default'; if (!isset($languageObjects[$recipientLanguage])) { // a LANG object in this language hasn't been // instantiated yet, so this is done here /** @var $languageObject \TYPO3\CMS\Lang\LanguageService */ $languageObject = GeneralUtility::makeInstance(\TYPO3\CMS\Lang\LanguageService::class); $languageObject->init($recipientLanguage); $languageObjects[$recipientLanguage] = $languageObject; } else { $languageObject = $languageObjects[$recipientLanguage]; } if (GeneralUtility::isFirstPartOfStr($emailSubject, 'LLL:')) { $emailSubject = $languageObject->sL($emailSubject); } if (GeneralUtility::isFirstPartOfStr($emailMessage, 'LLL:')) { $emailMessage = $languageObject->sL($emailMessage); } } $emailSubject = \TYPO3\CMS\Core\Html\HtmlParser::substituteMarkerArray($emailSubject, $markers, '', TRUE, TRUE); $emailMessage = \TYPO3\CMS\Core\Html\HtmlParser::substituteMarkerArray($emailMessage, $markers, '', TRUE, TRUE); // Send an email to the recipient /** @var $mail \TYPO3\CMS\Core\Mail\MailMessage */ $mail = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Mail\MailMessage::class); if (!empty($recipientData['realName'])) { $recipient = array($recipientData['email'] => $recipientData['realName']); } else { $recipient = $recipientData['email']; } $mail->setTo($recipient)->setSubject($emailSubject)->setFrom(\TYPO3\CMS\Core\Utility\MailUtility::getSystemFrom())->setBody($emailMessage); $mail->send(); } $emailRecipients = implode(',', $emailRecipients); $tcemainObj->newlog2('Notification email for stage change was sent to "' . $emailRecipients . '"', $table, $id); } }
/** * This will render a <textarea> OR RTE area form field, * possibly with various control/validation features * * @return array As defined in initializeResultArray() of AbstractNode */ public function render() { $table = $this->data['tableName']; $fieldName = $this->data['fieldName']; $row = $this->data['databaseRow']; $parameterArray = $this->data['parameterArray']; $backendUser = $this->getBackendUserAuthentication(); $this->resultArray = $this->initializeResultArray(); $this->defaultExtras = BackendUtility::getSpecConfParts($parameterArray['fieldConf']['defaultExtras']); $this->pidOfPageRecord = $table === 'pages' && MathUtility::canBeInterpretedAsInteger($row['uid']) ? (int) $row['uid'] : (int) $row['pid']; BackendUtility::fixVersioningPid($table, $row); $this->pidOfVersionedMotherRecord = (int) $row['pid']; $this->vanillaRteTsConfig = $backendUser->getTSConfig('RTE', BackendUtility::getPagesTSconfig($this->pidOfPageRecord)); $this->processedRteConfiguration = BackendUtility::RTEsetup($this->vanillaRteTsConfig['properties'], $table, $fieldName, $this->data['recordTypeValue']); $this->client = $this->clientInfo(); $this->domIdentifier = preg_replace('/[^a-zA-Z0-9_:.-]/', '_', $parameterArray['itemFormElName']); $this->domIdentifier = htmlspecialchars(preg_replace('/^[^a-zA-Z]/', 'x', $this->domIdentifier)); $this->initializeLanguageRelatedProperties(); // Get skin file name from Page TSConfig if any $skinFilename = trim($this->processedRteConfiguration['skin']) ?: 'EXT:rtehtmlarea/Resources/Public/Css/Skin/htmlarea.css'; $skinFilename = $this->getFullFileName($skinFilename); $skinDirectory = dirname($skinFilename); // jQuery UI Resizable style sheet and main skin stylesheet $this->resultArray['stylesheetFiles'][] = $skinDirectory . '/jquery-ui-resizable.css'; $this->resultArray['stylesheetFiles'][] = $skinFilename; $this->enableRegisteredPlugins(); // Configure toolbar $this->setToolbar(); // Check if some plugins need to be disabled $this->setPlugins(); // Merge the list of enabled plugins with the lists from the previous RTE editing areas on the same form $this->pluginEnabledCumulativeArray = $this->pluginEnabledArray; $this->addInstanceJavaScriptRegistration(); $this->addOnSubmitJavaScriptCode(); // Add RTE JavaScript $this->loadRequireModulesForRTE(); // Create language labels $this->createJavaScriptLanguageLabelsFromFiles(); // Get RTE init JS code $this->resultArray['additionalJavaScriptPost'][] = $this->getRteInitJsCode(); $html = $this->getMainHtml(); $this->resultArray['html'] = $this->renderWizards(array($html), $parameterArray['fieldConf']['config']['wizards'], $table, $row, $fieldName, $parameterArray, $parameterArray['itemFormElName'], $this->defaultExtras, true); return $this->resultArray; }
/** * Checking if the RTE is available/enabled for a certain table/field and if so, it returns TRUE. * Used to determine if the RTE button should be displayed. * * @param string $table Table name * @param array $row Record row (needed, if there are RTE dependencies based on other fields in the record) * @param string $field Field name * @return boolean Returns TRUE if the rich text editor would be enabled/available for the field name specified. * @todo Define visibility */ public function isRTEforField($table, $row, $field) { $specConf = $this->getSpecConfForField($table, $row, $field); if (!count($specConf)) { return FALSE; } $p = BackendUtility::getSpecConfParametersFromArray($specConf['rte_transform']['parameters']); if (isset($specConf['richtext']) && (!$p['flag'] || !$row[$p['flag']])) { BackendUtility::fixVersioningPid($table, $row); list($tscPID, $thePidValue) = BackendUtility::getTSCpid($table, $row['uid'], $row['pid']); // If the pid-value is not negative (that is, a pid could NOT be fetched) if ($thePidValue >= 0) { if (!isset($this->rteSetup[$tscPID])) { $this->rteSetup[$tscPID] = $this->getBackendUser()->getTSConfig('RTE', BackendUtility::getPagesTSconfig($tscPID)); } $RTEtypeVal = BackendUtility::getTCAtypeValue($table, $row); $thisConfig = BackendUtility::RTEsetup($this->rteSetup[$tscPID]['properties'], $table, $field, $RTEtypeVal); if (!$thisConfig['disabled']) { return TRUE; } } } return FALSE; }
/** * The data structure is located in a record. This method resolves the record and * returns an array to identify that record. * * The example setup below looks in current row for a tx_templavoila_ds value. If not found, * it will search the rootline (the table is a tree, typically pages) until a value in * tx_templavoila_next_ds or tx_templavoila_ds is found. That value should then be an * integer, that points to a record in tx_templavoila_datastructure, and then the data * structure is found in field dataprot: * * fieldTca = [ * 'config' => [ * 'type' => 'flex', * 'ds_pointerField' => 'tx_templavoila_ds', * 'ds_pointerField_searchParent' => 'pid', * 'ds_pointerField_searchParent_subField' => 'tx_templavoila_next_ds', * 'ds_tableField' => 'tx_templavoila_datastructure:dataprot', * ] * ] * * More simple scenario without tree traversal and having a valid data structure directly * located in field theFlexDataStructureField. * * fieldTca = [ * 'config' => [ * 'type' => 'flex', * 'ds_pointerField' => 'theFlexDataStructureField', * ] * ] * * Example return array: * [ * 'type' => 'record', * 'tableName' => 'tx_templavoila_datastructure', * 'uid' => 42, * 'fieldName' => 'dataprot', * ]; * * @param array $fieldTca Full TCA of the field in question that has type=flex set * @param string $tableName The table name of the TCA field * @param string $fieldName The field name * @param array $row The data row * @return array Identifier as array, see example above * @throws InvalidParentRowException * @throws InvalidParentRowLoopException * @throws InvalidParentRowRootException * @throws InvalidPointerFieldValueException * @throws InvalidTcaException */ protected function getDataStructureIdentifierFromRecord(array $fieldTca, string $tableName, string $fieldName, array $row) : array { $pointerFieldName = $finalPointerFieldName = $fieldTca['config']['ds_pointerField']; if (!array_key_exists($pointerFieldName, $row)) { // Pointer field does not exist in row at all -> throw throw new InvalidTcaException('No data structure for field "' . $fieldName . '" in table "' . $tableName . '" found, no "ds" array' . ' configured and given row does not have a field with ds_pointerField name "' . $pointerFieldName . '".', 1464115059); } $pointerValue = $row[$pointerFieldName]; // If set, this is typically set to "pid" $parentFieldName = $fieldTca['config']['ds_pointerField_searchParent'] ?? null; $pointerSubFieldName = $fieldTca['config']['ds_pointerField_searchParent_subField'] ?? null; if (!$pointerValue && $parentFieldName) { // Fetch rootline until a valid pointer value is found $handledUids = []; while (!$pointerValue) { $handledUids[$row['uid']] = 1; $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($tableName); $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class)); $queryBuilder->select('uid', $parentFieldName, $pointerFieldName); if (!empty($pointerSubFieldName)) { $queryBuilder->addSelect($pointerSubFieldName); } $queryStatement = $queryBuilder->from($tableName)->where($queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($row[$parentFieldName], \PDO::PARAM_INT)))->execute(); if ($queryStatement->rowCount() !== 1) { throw new InvalidParentRowException('The data structure for field "' . $fieldName . '" in table "' . $tableName . '" has to be looked up' . ' in field "' . $pointerFieldName . '". That field had no valid value, so a lookup in parent record' . ' with uid "' . $row[$parentFieldName] . '" was done. This row however does not exist or was deleted.', 1463833794); } $row = $queryStatement->fetch(); if (isset($handledUids[$row[$parentFieldName]])) { // Row has been fetched before already -> loop detected! throw new InvalidParentRowLoopException('The data structure for field "' . $fieldName . '" in table "' . $tableName . '" has to be looked up' . ' in field "' . $pointerFieldName . '". That field had no valid value, so a lookup in parent record' . ' with uid "' . $row[$parentFieldName] . '" was done. A loop of records was detected, the tree is broken.', 1464110956); } BackendUtility::workspaceOL($tableName, $row); BackendUtility::fixVersioningPid($tableName, $row, true); // New pointer value: This is the "subField" value if given, else the field value // ds_pointerField_searchParent_subField is the "template on next level" structure from templavoila if ($pointerSubFieldName && $row[$pointerSubFieldName]) { $finalPointerFieldName = $pointerSubFieldName; $pointerValue = $row[$pointerSubFieldName]; } else { $pointerValue = $row[$pointerFieldName]; } if (!$pointerValue && ((int) $row[$parentFieldName] === 0 || $row[$parentFieldName] === null)) { // If on root level and still no valid pointer found -> exception throw new InvalidParentRowRootException('The data structure for field "' . $fieldName . '" in table "' . $tableName . '" has to be looked up' . ' in field "' . $pointerFieldName . '". That field had no valid value, so a lookup in parent record' . ' with uid "' . $row[$parentFieldName] . '" was done. Root node with uid "' . $row['uid'] . '"' . ' was fetched and still no valid pointer field value was found.', 1464112555); } } } if (!$pointerValue) { // Still no valid pointer value -> exception, This still can be a data integrity issue, so throw a catchable exception throw new InvalidPointerFieldValueException('No data structure for field "' . $fieldName . '" in table "' . $tableName . '" found, no "ds" array' . ' configured and data structure could be found by resolving parents. This is probably a TCA misconfiguration.', 1464114011); } // Ok, finally we have the field value. This is now either a data structure directly, or a pointer to a file, // or the value can be interpreted as integer (is an uid) and "ds_tableField" is set, so this is the table, uid and field // where the final data structure can be found. if (MathUtility::canBeInterpretedAsInteger($pointerValue)) { if (!isset($fieldTca['config']['ds_tableField'])) { throw new InvalidTcaException('Invalid data structure pointer for field "' . $fieldName . '" in table "' . $tableName . '", the value' . 'resolved to "' . $pointerValue . '" . which is an integer, so "ds_tableField" must be configured', 1464115639); } if (substr_count($fieldTca['config']['ds_tableField'], ':') !== 1) { // ds_tableField must be of the form "table:field" throw new InvalidTcaException('Invalid TCA configuration for field "' . $fieldName . '" in table "' . $tableName . '", the setting' . '"ds_tableField" must be of the form "tableName:fieldName"', 1464116002); } list($foreignTableName, $foreignFieldName) = GeneralUtility::trimExplode(':', $fieldTca['config']['ds_tableField']); $dataStructureIdentifier = ['type' => 'record', 'tableName' => $foreignTableName, 'uid' => (int) $pointerValue, 'fieldName' => $foreignFieldName]; } else { $dataStructureIdentifier = ['type' => 'record', 'tableName' => $tableName, 'uid' => (int) $row['uid'], 'fieldName' => $finalPointerFieldName]; } return $dataStructureIdentifier; }
/** * Main function, rendering the document with the iFrame with the RTE in. * * @return void */ public function main() { // Translate id to the workspace version: if ($versionedRecord = BackendUtility::getWorkspaceVersionOfRecord($this->getBackendUserAuthentication()->workspace, $this->P['table'], $this->P['uid'], 'uid')) { $this->P['uid'] = $versionedRecord['uid']; } // If all parameters are available: if ($this->P['table'] && $this->P['field'] && $this->P['uid'] && $this->checkEditAccess($this->P['table'], $this->P['uid'])) { // Getting the raw record (we need only the pid-value from here...) $rawRecord = BackendUtility::getRecord($this->P['table'], $this->P['uid']); BackendUtility::fixVersioningPid($this->P['table'], $rawRecord); // override the default jumpToUrl $this->doc->JScodeArray['jumpToUrl'] = ' function jumpToUrl(URL,formEl) { if (document.editform) { if (!TBE_EDITOR.isFormChanged()) { window.location.href = URL; } else if (formEl) { if (formEl.type=="checkbox") formEl.checked = formEl.checked ? 0 : 1; } } else { window.location.href = URL; } } '; // Setting JavaScript of the pid value for viewing: if ($this->popView) { $this->doc->JScode = $this->doc->wrapScriptTags(BackendUtility::viewOnClick($rawRecord['pid'], '', BackendUtility::BEgetRootLine($rawRecord['pid']))); } // Initialize FormEngine - for rendering the field: /** @var FormEngine $formEngine */ $formEngine = GeneralUtility::makeInstance(FormEngine::class); // SPECIAL: Disables all wizards - we are NOT going to need them. $formEngine->disableWizards = 1; // Fetching content of record: /** @var DataPreprocessor $dataPreprocessor */ $dataPreprocessor = GeneralUtility::makeInstance(DataPreprocessor::class); $dataPreprocessor->lockRecords = 1; $dataPreprocessor->fetchRecord($this->P['table'], $this->P['uid'], ''); // Getting the processed record content out: $processedRecord = reset($dataPreprocessor->regTableItems_data); $processedRecord['uid'] = $this->P['uid']; $processedRecord['pid'] = $rawRecord['pid']; // TSconfig, setting width: $fieldTSConfig = FormEngineUtility::getTSconfigForTableRow($this->P['table'], $processedRecord, $this->P['field']); if ((string) $fieldTSConfig['RTEfullScreenWidth'] !== '') { $width = $fieldTSConfig['RTEfullScreenWidth']; } else { $width = '100%'; } // Get the form field and wrap it in the table with the buttons: $formContent = $formEngine->getSoloField($this->P['table'], $processedRecord, $this->P['field']); $formContent = ' <!-- RTE wizard: --> <table border="0" cellpadding="0" cellspacing="0" width="' . $width . '" id="typo3-rtewizard"> <tr> <td width="' . $width . '" colspan="2" id="c-formContent">' . $formContent . '</td> <td></td> </tr> </table>'; // Adding hidden fields: $formContent .= '<input type="hidden" name="redirect" value="' . htmlspecialchars($this->R_URI) . '" /> <input type="hidden" name="_serialNumber" value="' . md5(microtime()) . '" />' . FormEngine::getHiddenTokenField('tceAction'); // Finally, add the whole setup: $this->content .= $formEngine->printNeededJSFunctions_top() . $formContent . $formEngine->printNeededJSFunctions(); } else { // ERROR: $this->content .= $this->doc->section($this->getLanguageService()->getLL('forms_title'), '<span class="text-danger">' . $this->getLanguageService()->getLL('table_noData', TRUE) . '</span>', 0, 1); } // Setting up the buttons and markers for docHeader $docHeaderButtons = $this->getButtons(); $markers['CONTENT'] = $this->content; // Build the <body> for the module $this->content = $this->doc->startPage(''); $this->content .= $this->doc->moduleBody(array(), $docHeaderButtons, $markers); $this->content .= $this->doc->endPage(); $this->content = $this->doc->insertStylesAndJS($this->content); }
/** * Return record path (visually formatted, using BackendUtility::getRecordPath() ) * * @param string $table Table name * @param array $rec Record array * @return string The record path. * @see BackendUtility::getRecordPath() */ public function getRecordPath($table, $rec) { BackendUtility::fixVersioningPid($table, $rec); list($tscPID, $thePidValue) = BackendUtility::getTSCpidCached($table, $rec['uid'], $rec['pid']); if ($thePidValue >= 0) { return BackendUtility::getRecordPath($tscPID, $this->readPerms(), 15); } return ''; }
/** * Returns an array with record properties, like header and pid, based on the row * * @param string $table Table name * @param array $row Input row * @return array|NULL Output array */ public function getRecordPropertiesFromRow($table, $row) { if ($GLOBALS['TCA'][$table]) { BackendUtility::fixVersioningPid($table, $row); $out = array('header' => BackendUtility::getRecordTitle($table, $row), 'pid' => $row['pid'], 'event_pid' => $this->eventPid($table, isset($row['_ORIG_pid']) ? $row['t3ver_oid'] : $row['uid'], $row['pid']), 't3ver_state' => $GLOBALS['TCA'][$table]['ctrl']['versioningWS'] ? $row['t3ver_state'] : '', '_ORIG_pid' => $row['_ORIG_pid']); return $out; } return null; }
/** * Creates the editing form with TCEforms, based on the input from GPvars. * * @return string HTML form elements wrapped in tables */ public function makeEditForm() { // Initialize variables: $this->elementsData = array(); $this->errorC = 0; $this->newC = 0; $thePrevUid = ''; $editForm = ''; $trData = NULL; $beUser = $this->getBackendUser(); // Traverse the GPvar edit array // Tables: foreach ($this->editconf as $table => $conf) { if (is_array($conf) && $GLOBALS['TCA'][$table] && $beUser->check('tables_modify', $table)) { // Traverse the keys/comments of each table (keys can be a commalist of uids) foreach ($conf as $cKey => $cmd) { if ($cmd == 'edit' || $cmd == 'new') { // Get the ids: $ids = GeneralUtility::trimExplode(',', $cKey, TRUE); // Traverse the ids: foreach ($ids as $theUid) { // Checking if the user has permissions? (Only working as a precaution, // because the final permission check is always down in TCE. But it's // good to notify the user on beforehand...) // First, resetting flags. $hasAccess = 1; $deniedAccessReason = ''; $deleteAccess = 0; $this->viewId = 0; // If the command is to create a NEW record...: if ($cmd == 'new') { // NOTICE: the id values in this case points to the page uid onto which the // record should be create OR (if the id is negativ) to a record from the // same table AFTER which to create the record. if ((int) $theUid) { // Find parent page on which the new record reside // Less than zero - find parent page if ($theUid < 0) { $calcPRec = BackendUtility::getRecord($table, abs($theUid)); $calcPRec = BackendUtility::getRecord('pages', $calcPRec['pid']); } else { // always a page $calcPRec = BackendUtility::getRecord('pages', abs($theUid)); } // Now, calculate whether the user has access to creating new records on this position: if (is_array($calcPRec)) { // Permissions for the parent page $CALC_PERMS = $beUser->calcPerms($calcPRec); if ($table == 'pages') { // If pages: $hasAccess = $CALC_PERMS & Permission::PAGE_NEW ? 1 : 0; $this->viewId = 0; } else { $hasAccess = $CALC_PERMS & Permission::CONTENT_EDIT ? 1 : 0; $this->viewId = $calcPRec['uid']; } } } // Don't save this document title in the document selector if the document is new. $this->dontStoreDocumentRef = 1; } else { // Edit: $calcPRec = BackendUtility::getRecord($table, $theUid); BackendUtility::fixVersioningPid($table, $calcPRec); if (is_array($calcPRec)) { if ($table == 'pages') { // If pages: $CALC_PERMS = $beUser->calcPerms($calcPRec); $hasAccess = $CALC_PERMS & Permission::PAGE_EDIT ? 1 : 0; $deleteAccess = $CALC_PERMS & Permission::PAGE_DELETE ? 1 : 0; $this->viewId = $calcPRec['uid']; } else { // Fetching pid-record first $CALC_PERMS = $beUser->calcPerms(BackendUtility::getRecord('pages', $calcPRec['pid'])); $hasAccess = $CALC_PERMS & Permission::CONTENT_EDIT ? 1 : 0; $deleteAccess = $CALC_PERMS & Permission::CONTENT_EDIT ? 1 : 0; $this->viewId = $calcPRec['pid']; // Adding "&L=xx" if the record being edited has a languageField with a value larger than zero! if ($GLOBALS['TCA'][$table]['ctrl']['languageField'] && $calcPRec[$GLOBALS['TCA'][$table]['ctrl']['languageField']] > 0) { $this->viewId_addParams = '&L=' . $calcPRec[$GLOBALS['TCA'][$table]['ctrl']['languageField']]; } } // Check internals regarding access: $isRootLevelRestrictionIgnored = BackendUtility::isRootLevelRestrictionIgnored($table); if ($hasAccess || (int) $calcPRec['pid'] === 0 && $isRootLevelRestrictionIgnored) { $hasAccess = $beUser->recordEditAccessInternals($table, $calcPRec); $deniedAccessReason = $beUser->errorMsg; } } else { $hasAccess = 0; } } if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/alt_doc.php']['makeEditForm_accessCheck'])) { foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/alt_doc.php']['makeEditForm_accessCheck'] as $_funcRef) { $_params = array('table' => $table, 'uid' => $theUid, 'cmd' => $cmd, 'hasAccess' => $hasAccess); $hasAccess = GeneralUtility::callUserFunction($_funcRef, $_params, $this); } } // AT THIS POINT we have checked the access status of the editing/creation of // records and we can now proceed with creating the form elements: if ($hasAccess) { /** @var DataPreprocessor $trData */ $prevPageID = is_object($trData) ? $trData->prevPageID : ''; $trData = GeneralUtility::makeInstance(DataPreprocessor::class); $trData->addRawData = TRUE; $trData->defVals = $this->defVals; $trData->lockRecords = 1; $trData->prevPageID = $prevPageID; // 'new' $trData->fetchRecord($table, $theUid, $cmd == 'new' ? 'new' : ''); $rec = reset($trData->regTableItems_data); $rec['uid'] = $cmd == 'new' ? uniqid('NEW', TRUE) : $theUid; if ($cmd == 'new') { $rec['pid'] = $theUid == 'prev' ? $thePrevUid : $theUid; } $this->elementsData[] = array('table' => $table, 'uid' => $rec['uid'], 'pid' => $rec['pid'], 'cmd' => $cmd, 'deleteAccess' => $deleteAccess); // Now, render the form: if (is_array($rec)) { // Setting visual path / title of form: $this->generalPathOfForm = $this->tceforms->getRecordPath($table, $rec); if (!$this->storeTitle) { $this->storeTitle = $this->recTitle ? htmlspecialchars($this->recTitle) : BackendUtility::getRecordTitle($table, $rec, TRUE); } // Setting variables in TCEforms object: if (is_array($this->overrideVals) && is_array($this->overrideVals[$table])) { $this->tceforms->hiddenFieldListArr = array_keys($this->overrideVals[$table]); } // Create form for the record (either specific list of fields or the whole record): $panel = ''; if ($this->columnsOnly) { if (is_array($this->columnsOnly)) { $panel .= $this->tceforms->getListedFields($table, $rec, $this->columnsOnly[$table]); } else { $panel .= $this->tceforms->getListedFields($table, $rec, $this->columnsOnly); } } else { $panel .= $this->tceforms->getMainFields($table, $rec); } $panel = $this->tceforms->wrapTotal($panel, $rec, $table); // Setting the pid value for new records: if ($cmd == 'new') { $panel .= '<input type="hidden" name="data[' . $table . '][' . $rec['uid'] . '][pid]" value="' . $rec['pid'] . '" />'; $this->newC++; } // Display "is-locked" message: if ($lockInfo = BackendUtility::isRecordLocked($table, $rec['uid'])) { /** @var $flashMessage \TYPO3\CMS\Core\Messaging\FlashMessage */ $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, htmlspecialchars($lockInfo['msg']), '', FlashMessage::WARNING); /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */ $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class); /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */ $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier(); $defaultFlashMessageQueue->enqueue($flashMessage); } // Combine it all: $editForm .= $panel; } $thePrevUid = $rec['uid']; } else { $this->errorC++; $editForm .= $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.noEditPermission', TRUE) . '<br /><br />' . ($deniedAccessReason ? 'Reason: ' . htmlspecialchars($deniedAccessReason) . '<br /><br />' : ''); } } } } } } return $editForm; }
/** * Checks access for element * * @param string $table Table name * @param integer $uid Record uid * @return void * @todo Define visibility */ public function checkEditAccess($table, $uid) { $calcPRec = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecord($table, $uid); \TYPO3\CMS\Backend\Utility\BackendUtility::fixVersioningPid($table, $calcPRec); if (is_array($calcPRec)) { // If pages: if ($table == 'pages') { $CALC_PERMS = $GLOBALS['BE_USER']->calcPerms($calcPRec); $hasAccess = $CALC_PERMS & 2 ? TRUE : FALSE; } else { // Fetching pid-record first. $CALC_PERMS = $GLOBALS['BE_USER']->calcPerms(\TYPO3\CMS\Backend\Utility\BackendUtility::getRecord('pages', $calcPRec['pid'])); $hasAccess = $CALC_PERMS & 16 ? TRUE : FALSE; } // Check internals regarding access: if ($hasAccess) { $hasAccess = $GLOBALS['BE_USER']->recordEditAccessInternals($table, $calcPRec); } } else { $hasAccess = FALSE; } return $hasAccess; }
/** * Returns the path (visually) of a page $uid, fx. "/First page/Second page/Another subpage" * Each part of the path will be limited to $titleLimit characters * Deleted pages are filtered out. * * @param int $uid Page uid for which to create record path * @param string $clause is additional where clauses, eg. * @param int $titleLimit Title limit * @param int $fullTitleLimit Title limit of Full title (typ. set to 1000 or so) * @return mixed Path of record (string) OR array with short/long title if $fullTitleLimit is set. */ public static function getRecordPath($uid, $clause = '', $titleLimit = 1000, $fullTitleLimit = 0) { if ($clause !== '' || (int) $titleLimit !== 1000 || (int) $fullTitleLimit !== 0) { GeneralUtility::deprecationLog('The arguments "clause", "tileLimit" and "fullTitleLimit" ' . 'have been deprecated since TYPO3 CMS 8 and will be removed in TYPO3 CMS 9'); } $uid = (int) $uid; $output = $fullOutput = '/'; if ($uid === 0) { return $output; } $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages'); $queryBuilder->getRestrictions()->removeAll(); $clause = trim($clause); $loopCheck = 100; while ($loopCheck > 0) { $loopCheck--; $queryBuilder->select('uid', 'pid', 'title', 'deleted', 't3ver_oid', 't3ver_wsid')->from('pages')->where($queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT))); if (!empty($clause)) { $queryBuilder->andWhere($clause); } $row = $queryBuilder->execute()->fetch(); if ($row !== false) { BackendUtility::workspaceOL('pages', $row); if (is_array($row)) { BackendUtility::fixVersioningPid('pages', $row); $uid = (int) $row['pid']; $output = '/' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($row['title'], $titleLimit)) . $output; if ($row['deleted']) { $output = '<span class="text-danger">' . $output . '</span>'; } if ($fullTitleLimit) { $fullOutput = '/' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($row['title'], $fullTitleLimit)) . $fullOutput; } } else { break; } } else { break; } } if ($fullTitleLimit) { return [$output, $fullOutput]; } else { return $output; } }
/** * Returns the path (visually) of a page $uid, fx. "/First page/Second page/Another subpage" * Each part of the path will be limited to $titleLimit characters * Deleted pages are filtered out. * * @param int $uid Page uid for which to create record path * @param string $clause is additional where clauses, eg. * @param int $titleLimit Title limit * @param int $fullTitleLimit Title limit of Full title (typ. set to 1000 or so) * @return mixed Path of record (string) OR array with short/long title if $fullTitleLimit is set. */ public static function getRecordPath($uid, $clause = '', $titleLimit = 1000, $fullTitleLimit = 0) { $uid = (int) $uid; $output = $fullOutput = '/'; if ($uid === 0) { return $output; } $databaseConnection = static::getDatabaseConnection(); $clause = trim($clause) !== '' ? ' AND ' . $clause : ''; $loopCheck = 100; while ($loopCheck > 0) { $loopCheck--; $res = $databaseConnection->exec_SELECTquery('uid,pid,title,deleted,t3ver_oid,t3ver_wsid', 'pages', 'uid=' . $uid . $clause); if ($res !== FALSE) { $row = $databaseConnection->sql_fetch_assoc($res); $databaseConnection->sql_free_result($res); BackendUtility::workspaceOL('pages', $row); if (is_array($row)) { BackendUtility::fixVersioningPid('pages', $row); $uid = (int) $row['pid']; $output = '/' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($row['title'], $titleLimit)) . $output; if ($row['deleted']) { $output = '<span class="text-danger">' . $output . '</span>'; } if ($fullTitleLimit) { $fullOutput = '/' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($row['title'], $fullTitleLimit)) . $fullOutput; } } else { break; } } else { break; } } if ($fullTitleLimit) { return array($output, $fullOutput); } else { return $output; } }
/** * Gets the cached page record for the rootline. * * @param int $uid Page id for which to create the root line. * @param string $clause Can be used to select other criteria. It would * typically be where-clauses that stops the process if we meet a page, * the user has no reading access to. * @param bool $workspaceOl If true, version overlay is applied. This must * be requested specifically because it is usually only wanted when the * rootline is used for visual output while for permission checking you * want the raw thing! * * @return array Cached page record for the rootline */ protected static function getCategoryForRootline($uid, $clause, $workspaceOl) { $database = self::getDatabaseConnection(); static $getPageForRootlineCache = array(); $ident = $uid . '-' . $clause . '-' . $workspaceOl; if (is_array($getPageForRootlineCache[$ident])) { $row = $getPageForRootlineCache[$ident]; } else { $res = $database->exec_SELECTquery('mm.uid_foreign AS pid, tx_commerce_categories.uid, tx_commerce_categories.hidden, tx_commerce_categories.title, tx_commerce_categories.ts_config, tx_commerce_categories.t3ver_oid, tx_commerce_categories.perms_userid, tx_commerce_categories.perms_groupid, tx_commerce_categories.perms_user, tx_commerce_categories.perms_group, tx_commerce_categories.perms_everybody', 'tx_commerce_categories INNER JOIN pages ON tx_commerce_categories.pid = pages.uid INNER JOIN tx_commerce_categories_parent_category_mm AS mm ON tx_commerce_categories.uid = mm.uid_local', 'tx_commerce_categories.uid = ' . (int) $uid . ' ' . \TYPO3\CMS\Backend\Utility\BackendUtility::deleteClause('tx_commerce_categories') . ' ' . $clause); $row = $database->sql_fetch_assoc($res); if ($row) { if ($workspaceOl) { \TYPO3\CMS\Backend\Utility\BackendUtility::workspaceOL('tx_commerce_categories', $row); } if (is_array($row)) { \TYPO3\CMS\Backend\Utility\BackendUtility::fixVersioningPid('tx_commerce_categories', $row); $getPageForRootlineCache[$ident] = $row; } } $database->sql_free_result($res); } return $row; }
/** * Checks the page access rights (Code for access check mostly taken from alt_doc.php) * as well as the table access rights of the user. * * @param string $cmd The command that sould be performed ('new' or 'edit') * @param string $table The table to check access for * @param string $theUid The record uid of the table * @return boolean Returns TRUE is the user has access, or FALSE if not * @todo Define visibility */ public function checkAccess($cmd, $table, $theUid) { // Checking if the user has permissions? (Only working as a precaution, because the final permission check is always down in TCE. But it's good to notify the user on beforehand...) // First, resetting flags. $hasAccess = 0; $deniedAccessReason = ''; // Admin users always have acces: if ($GLOBALS['BE_USER']->isAdmin()) { return TRUE; } // If the command is to create a NEW record...: if ($cmd == 'new') { // If the pid is numerical, check if it's possible to write to this page: if (MathUtility::canBeInterpretedAsInteger($this->inlineFirstPid)) { $calcPRec = BackendUtility::getRecord('pages', $this->inlineFirstPid); if (!is_array($calcPRec)) { return FALSE; } // Permissions for the parent page $CALC_PERMS = $GLOBALS['BE_USER']->calcPerms($calcPRec); // If pages: if ($table == 'pages') { // Are we allowed to create new subpages? $hasAccess = $CALC_PERMS & 8 ? 1 : 0; } else { // Are we allowed to edit content on this page? $hasAccess = $CALC_PERMS & 16 ? 1 : 0; } } else { $hasAccess = 1; } } else { // Edit: $calcPRec = BackendUtility::getRecord($table, $theUid); BackendUtility::fixVersioningPid($table, $calcPRec); if (is_array($calcPRec)) { // If pages: if ($table == 'pages') { $CALC_PERMS = $GLOBALS['BE_USER']->calcPerms($calcPRec); $hasAccess = $CALC_PERMS & 2 ? 1 : 0; } else { // Fetching pid-record first. $CALC_PERMS = $GLOBALS['BE_USER']->calcPerms(BackendUtility::getRecord('pages', $calcPRec['pid'])); $hasAccess = $CALC_PERMS & 16 ? 1 : 0; } // Check internals regarding access: if ($hasAccess) { $hasAccess = $GLOBALS['BE_USER']->recordEditAccessInternals($table, $calcPRec); } } } if (!$GLOBALS['BE_USER']->check('tables_modify', $table)) { $hasAccess = 0; } if (!$hasAccess) { $deniedAccessReason = $GLOBALS['BE_USER']->errorMsg; if ($deniedAccessReason) { debug($deniedAccessReason); } } return $hasAccess ? TRUE : FALSE; }
/** * Checks the page access rights (Code for access check mostly * taken from alt_doc.php) as well as the table access rights * of the user. * * @param string $table The table to check access * @param string $row The record uid of the table * * @return bool Returns true is the user has access, or false if not * @see tx_recycler */ public function checkAccess($table, $row) { // Checking if the user has permissions? // (Only working as a precaution, because the final permission check // is always down in TCE. But it's good to notify the user on beforehand...) // First, resetting flags. $hasAccess = 0; $backendUser = $this->getBackendUser(); $calcPermissionRecord = $row; BackendUtility::fixVersioningPid($table, $calcPermissionRecord); if (is_array($calcPermissionRecord)) { // If pages: if ($table == 'pages') { $hasAccess = $backendUser->calcPerms($calcPermissionRecord) & 2 ? 1 : 0; } else { // Fetching pid-record first. $hasAccess = $backendUser->calcPerms(BackendUtility::getRecord('pages', $calcPermissionRecord['pid'])) & 16 ? 1 : 0; } } if ($hasAccess) { $hasAccess = $backendUser->isInWebMount($calcPermissionRecord['pid'], '1=1'); } return $hasAccess ? TRUE : FALSE; }