[CLEANUP] Replace strlen() with === for zero length check
[Packages/TYPO3.CMS.git] / typo3 / sysext / feedit / Classes / FrontendEditPanel.php
1 <?php
2 namespace TYPO3\CMS\Feedit;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Backend\Utility\IconUtility;
18
19 use TYPO3\CMS\Core\Utility\GeneralUtility;
20
21 /**
22 * View class for the edit panels in frontend editing.
23 */
24 class FrontendEditPanel {
25
26 /**
27 * @var \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
28 */
29 protected $cObj;
30
31 /**
32 * Constructor for the edit panel
33 */
34 public function __construct() {
35 $this->cObj = GeneralUtility::makeInstance(\TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::class);
36 $this->cObj->start(array());
37 }
38
39 /**
40 * Generates the "edit panels" which can be shown for a page or records on a page when the Admin Panel is enabled for a backend users surfing the frontend.
41 * With the "edit panel" the user will see buttons with links to editing, moving, hiding, deleting the element
42 * This function is used for the cObject EDITPANEL and the stdWrap property ".editPanel"
43 *
44 * @param string $content A content string containing the content related to the edit panel. For cObject "EDITPANEL" this is empty but not so for the stdWrap property. The edit panel is appended to this string and returned.
45 * @param array $conf TypoScript configuration properties for the editPanel
46 * @param string $currentRecord The "table:uid" of the record being shown. If empty string then $this->currentRecord is used. For new records (set by $conf['newRecordFromTable']) it's auto-generated to "[tablename]:NEW
47 * @param array $dataArr Alternative data array to use. Default is $this->data
48 * @param string $table
49 * @param string $allow
50 * @param int $newUID
51 * @param array $hiddenFields
52 * @return string The input content string with the editPanel appended. This function returns only an edit panel appended to the content string if a backend user is logged in (and has the correct permissions). Otherwise the content string is directly returned.
53 */
54 public function editPanel($content, array $conf, $currentRecord = '', array $dataArr = array(), $table = '', $allow = '', $newUID = 0, array $hiddenFields = array()) {
55 $hiddenFieldString = $command = '';
56
57 // Special content is about to be shown, so the cache must be disabled.
58 $GLOBALS['TSFE']->set_no_cache('Frontend edit panel is shown', TRUE);
59
60 $formName = 'TSFE_EDIT_FORM_' . substr($GLOBALS['TSFE']->uniqueHash(), 0, 4);
61 $formTag = '<form name="' . $formName . '" id ="' . $formName . '" action="' . htmlspecialchars(GeneralUtility::getIndpEnv('REQUEST_URI')) . '" method="post" enctype="' . htmlspecialchars($GLOBALS['TYPO3_CONF_VARS']['SYS']['form_enctype']) . '" onsubmit="return TBE_EDITOR.checkSubmit(1);">';
62 $sortField = $GLOBALS['TCA'][$table]['ctrl']['sortby'];
63 $labelField = $GLOBALS['TCA'][$table]['ctrl']['label'];
64 $hideField = $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled'];
65
66 $TSFE_EDIT = $GLOBALS['BE_USER']->frontendEdit->TSFE_EDIT;
67 if (is_array($TSFE_EDIT) && $TSFE_EDIT['record'] == $currentRecord && !$TSFE_EDIT['update_close']) {
68 $command = $TSFE_EDIT['cmd'];
69 }
70
71 $panel = '';
72 if (isset($allow['toolbar']) && $GLOBALS['BE_USER']->adminPanel instanceof \TYPO3\CMS\Frontend\View\AdminPanelView) {
73 $panel .= $GLOBALS['BE_USER']->adminPanel->ext_makeToolBar();
74 }
75 if (isset($allow['edit'])) {
76 $icon = IconUtility::getSpriteIcon('actions-document-open', array('title' => $GLOBALS['BE_USER']->extGetLL('p_editRecord')));
77 $panel .= $this->editPanelLinkWrap($icon, $formName, 'edit', $dataArr['_LOCALIZED_UID'] ? $table . ':' . $dataArr['_LOCALIZED_UID'] : $currentRecord);
78 }
79 // Hiding in workspaces because implementation is incomplete
80 if (isset($allow['move']) && $sortField && $GLOBALS['BE_USER']->workspace === 0) {
81 $icon = IconUtility::getSpriteIcon('actions-move-up', array('title' => $GLOBALS['BE_USER']->extGetLL('p_moveUp')));
82 $panel .= $this->editPanelLinkWrap($icon, $formName, 'up');
83 $icon = IconUtility::getSpriteIcon('actions-move-down', array('title' => $GLOBALS['BE_USER']->extGetLL('p_moveDown')));
84 $panel .= $this->editPanelLinkWrap($icon, $formName, 'down');
85 }
86 // Hiding in workspaces because implementation is incomplete
87 // Hiding for localizations because it is unknown what should be the function in that case
88 if (isset($allow['hide']) && $hideField && $GLOBALS['BE_USER']->workspace === 0 && !$dataArr['_LOCALIZED_UID']) {
89 if ($dataArr[$hideField]) {
90 $icon = IconUtility::getSpriteIcon('actions-edit-unhide', array('title' => $GLOBALS['BE_USER']->extGetLL('p_unhide')));
91 $panel .= $this->editPanelLinkWrap($icon, $formName, 'unhide');
92 } else {
93 $icon = IconUtility::getSpriteIcon('actions-edit-hide', array('title' => $GLOBALS['BE_USER']->extGetLL('p_hide')));
94 $panel .= $this->editPanelLinkWrap($icon, $formName, 'hide', '', $GLOBALS['BE_USER']->extGetLL('p_hideConfirm'));
95 }
96 }
97 if (isset($allow['new'])) {
98 if ($table === 'pages') {
99 $icon = IconUtility::getSpriteIcon('actions-page-new', array('title' => $GLOBALS['BE_USER']->extGetLL('p_newSubpage')));
100 $panel .= $this->editPanelLinkWrap($icon, $formName, 'new', $currentRecord, '');
101 } else {
102 $icon = IconUtility::getSpriteIcon('actions-document-new', array('title' => $GLOBALS['BE_USER']->extGetLL('p_newRecordAfter')));
103 $panel .= $this->editPanelLinkWrap($icon, $formName, 'new', $currentRecord, '', $newUID);
104 }
105 }
106 // Hiding in workspaces because implementation is incomplete
107 // Hiding for localizations because it is unknown what should be the function in that case
108 if (isset($allow['delete']) && $GLOBALS['BE_USER']->workspace === 0 && !$dataArr['_LOCALIZED_UID']) {
109 $icon = IconUtility::getSpriteIcon('actions-edit-delete', array('title' => $GLOBALS['BE_USER']->extGetLL('p_delete')));
110 $panel .= $this->editPanelLinkWrap($icon, $formName, 'delete', '', $GLOBALS['BE_USER']->extGetLL('p_deleteConfirm'));
111 }
112 // Final
113 $labelTxt = $this->cObj->stdWrap($conf['label'], $conf['label.']);
114 foreach ((array)$hiddenFields as $name => $value) {
115 $hiddenFieldString .= '<input type="hidden" name="TSFE_EDIT[' . htmlspecialchars($name) . ']" value="' . htmlspecialchars($value) . '"/>' . LF;
116 }
117
118 $panel = '<!-- BE_USER Edit Panel: -->
119 ' . $formTag . $hiddenFieldString . '
120 <input type="hidden" name="TSFE_EDIT[cmd]" value="" />
121 <input type="hidden" name="TSFE_EDIT[record]" value="' . $currentRecord . '" />
122 <div class="typo3-editPanel">'
123 . $panel .
124 ($labelTxt ? '<div class="typo3-editPanel-label">' . sprintf($labelTxt, htmlspecialchars(GeneralUtility::fixed_lgd_cs($dataArr[$labelField], 50))) . '</div>' : '') . '
125 </div>
126 </form>';
127
128 // Wrap the panel
129 if ($conf['innerWrap']) {
130 $panel = $this->cObj->wrap($panel, $conf['innerWrap']);
131 }
132 if ($conf['innerWrap.']) {
133 $panel = $this->cObj->stdWrap($panel, $conf['innerWrap.']);
134 }
135
136 // Wrap the complete panel
137 if ($conf['outerWrap']) {
138 $panel = $this->cObj->wrap($panel, $conf['outerWrap']);
139 }
140 if ($conf['outerWrap.']) {
141 $panel = $this->cObj->stdWrap($panel, $conf['outerWrap.']);
142 }
143 if ($conf['printBeforeContent']) {
144 $finalOut = $panel . $content;
145 } else {
146 $finalOut = $content . $panel;
147 }
148
149 $hidden = $this->isDisabled($table, $dataArr) ? ' typo3-feedit-element-hidden' : '';
150 $outerWrapConfig = isset($conf['stdWrap.'])
151 ? $conf['stdWrap.']
152 : array('wrap' => '<div class="typo3-feedit-element' . $hidden . '">|</div>');
153 $finalOut = $this->cObj->stdWrap($finalOut, $outerWrapConfig);
154
155 return $finalOut;
156 }
157
158 /**
159 * Adds an edit icon to the content string. The edit icon links to alt_doc.php with proper parameters for editing the table/fields of the context.
160 * This implements TYPO3 context sensitive editing facilities. Only backend users will have access (if properly configured as well).
161 *
162 * @param string $content The content to which the edit icons should be appended
163 * @param string $params The parameters defining which table and fields to edit. Syntax is [tablename]:[fieldname],[fieldname],[fieldname],... OR [fieldname],[fieldname],[fieldname],... (basically "[tablename]:" is optional, default table is the one of the "current record" used in the function). The fieldlist is sent as "&columnsOnly=" parameter to alt_doc.php
164 * @param array $conf TypoScript properties for configuring the edit icons.
165 * @param string $currentRecord The "table:uid" of the record being shown. If empty string then $this->currentRecord is used. For new records (set by $conf['newRecordFromTable']) it's auto-generated to "[tablename]:NEW
166 * @param array $dataArr Alternative data array to use. Default is $this->data
167 * @param string $addUrlParamStr Additional URL parameters for the link pointing to alt_doc.php
168 * @param string $table
169 * @param int $editUid
170 * @param string $fieldList
171 * @return string The input content string, possibly with edit icons added (not necessarily in the end but just after the last string of normal content.
172 */
173 public function editIcons($content, $params, array $conf = array(), $currentRecord = '', array $dataArr = array(), $addUrlParamStr = '', $table, $editUid, $fieldList) {
174 // Special content is about to be shown, so the cache must be disabled.
175 $GLOBALS['TSFE']->set_no_cache('Display frontend edit icons', TRUE);
176 $style = $conf['styleAttribute'] ? ' style="' . htmlspecialchars($conf['styleAttribute']) . '"' : '';
177 $iconTitle = $this->cObj->stdWrap($conf['iconTitle'], $conf['iconTitle.']);
178 $iconImg = $conf['iconImg'] ? $conf['iconImg'] : '<img ' . \TYPO3\CMS\Backend\Utility\IconUtility::skinImg(TYPO3_mainDir, 'gfx/edit_fe.gif', 'width="11" height="12" border="0" align="top" ') . ' title="' . htmlspecialchars($iconTitle, ENT_COMPAT, 'UTF-8', FALSE) . '"' . $style . ' class="frontEndEditIcons" alt="" />';
179 $nV = GeneralUtility::_GP('ADMCMD_view') ? 1 : 0;
180 $adminURL = GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . TYPO3_mainDir;
181 $icon = $this->editPanelLinkWrap_doWrap($iconImg, $adminURL . 'alt_doc.php?edit[' . $table . '][' . $editUid . ']=edit&columnsOnly=' . rawurlencode($fieldList) . '&noView=' . $nV . $addUrlParamStr);
182 if ($conf['beforeLastTag'] < 0) {
183 $content = $icon . $content;
184 } elseif ($conf['beforeLastTag'] > 0) {
185 $cBuf = rtrim($content);
186 $secureCount = 30;
187 while ($secureCount && substr($cBuf, -1) == '>' && substr($cBuf, -4) != '</a>') {
188 $cBuf = rtrim(preg_replace('/<[^<]*>$/', '', $cBuf));
189 $secureCount--;
190 }
191 $content = $cBuf !== '' && $secureCount
192 ? substr($content, 0, strlen($cBuf)) . $icon . substr($content, strlen($cBuf))
193 : $icon . $content;
194 } else {
195 $content .= $icon;
196 }
197 return $content;
198 }
199
200 /**
201 * Helper function for editPanel() which wraps icons in the panel in a link with the action of the panel.
202 * The links are for some of them not simple hyperlinks but onclick-actions which submits a little form which the panel is wrapped in.
203 *
204 * @param string $string The string to wrap in a link, typ. and image used as button in the edit panel.
205 * @param string $formName The name of the form wrapping the edit panel.
206 * @param string $cmd The command of the link. There is a predefined list available: edit, new, up, down etc.
207 * @param string $currentRecord The "table:uid" of the record being processed by the panel.
208 * @param string $confirm Text string with confirmation message; If set a confirm box will be displayed before carrying out the action (if Yes is pressed)
209 * @param int|string $nPid "New pid" - for new records
210 * @return string A <a> tag wrapped string.
211 */
212 protected function editPanelLinkWrap($string, $formName, $cmd, $currentRecord = '', $confirm = '', $nPid = '') {
213 $nV = GeneralUtility::_GP('ADMCMD_view') ? 1 : 0;
214 $adminURL = GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . TYPO3_mainDir;
215 if ($cmd == 'edit') {
216 $rParts = explode(':', $currentRecord);
217 $out = $this->editPanelLinkWrap_doWrap($string, $adminURL . 'alt_doc.php?edit[' . $rParts[0] . '][' . $rParts[1] . ']=edit&noView=' . $nV, $currentRecord);
218 } elseif ($cmd == 'new') {
219 $rParts = explode(':', $currentRecord);
220 if ($rParts[0] == 'pages') {
221 $out = $this->editPanelLinkWrap_doWrap($string, $adminURL . 'db_new.php?id=' . $rParts[1] . '&pagesOnly=1', $currentRecord);
222 } else {
223 if (!(int)$nPid) {
224 $nPid = \TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($rParts[1]) ? -$rParts[1] : $GLOBALS['TSFE']->id;
225 }
226 $out = $this->editPanelLinkWrap_doWrap($string, $adminURL . 'alt_doc.php?edit[' . $rParts[0] . '][' . $nPid . ']=new&noView=' . $nV, $currentRecord);
227 }
228 } else {
229 if ($confirm && $GLOBALS['BE_USER']->jsConfirmation(8)) {
230 // Gets htmlspecialchared later
231 $cf1 = 'if (confirm(' . GeneralUtility::quoteJSvalue($confirm, TRUE) . ')) {';
232 $cf2 = '}';
233 } else {
234 $cf1 = ($cf2 = '');
235 }
236 $out = '<a href="#" onclick="' . htmlspecialchars(($cf1 . 'document.' . $formName . '[\'TSFE_EDIT[cmd]\'].value=\'' . $cmd . '\'; document.' . $formName . '.submit();' . $cf2 . ' return false;')) . '">' . $string . '</a>';
237 }
238 return $out;
239 }
240
241 /**
242 * Creates a link to a script (eg. typo3/alt_doc.php or typo3/db_new.php)
243 * which either opens in the current frame OR in a pop-up window.
244 *
245 * @param string $string The string to wrap in a link, typ. and image used as button in the edit panel.
246 * @param string $url The URL of the link. Should be absolute if supposed to work with <base> path set.
247 * @return string A <a> tag wrapped string.
248 * @see editPanelLinkWrap()
249 */
250 protected function editPanelLinkWrap_doWrap($string, $url) {
251 // Open in the current frame?
252 if ($GLOBALS['BE_USER']->adminPanel->extGetFeAdminValue('edit', 'editNoPopup')) {
253 $href = htmlspecialchars($url . '&returnUrl=' . rawurlencode(GeneralUtility::getIndpEnv('REQUEST_URI')));
254 return '<a href="' . $href . '" class="frontEndEditIconLinks">' . $string . '</a>';
255 } else {
256 $onclick = 'vHWin=window.open(' . GeneralUtility::quoteJSvalue($url . '&returnUrl=close.html') .
257 ',\'FEquickEditWindow\',\'width=690,height=500,status=0,menubar=0,scrollbars=1,resizable=1\');vHWin.focus();return false;';
258 return '<a href="#" onclick="' . htmlspecialchars($onclick) . '" class="frontEndEditIconLinks">' . $string . '</a>';
259 }
260 }
261
262 /**
263 * Returns TRUE if the input table/row would be hidden in the frontend, according to the current time and simulate user group
264 *
265 * @param string $table The table name
266 * @param array $row The data record
267 * @return bool
268 */
269 protected function isDisabled($table, array $row) {
270 $status = FALSE;
271 if (
272 $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled'] &&
273 $row[$GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled']] ||
274 $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['fe_group'] &&
275 $GLOBALS['TSFE']->simUserGroup &&
276 $row[$GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['fe_group']] == $GLOBALS['TSFE']->simUserGroup ||
277 $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['starttime'] &&
278 $row[$GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['starttime']] > $GLOBALS['EXEC_TIME'] ||
279 $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['endtime'] &&
280 $row[$GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['endtime']] &&
281 $row[$GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['endtime']] < $GLOBALS['EXEC_TIME']
282 ) {
283 $status = TRUE;
284 }
285
286 return $status;
287 }
288
289 }