[CLEANUP] Improve comments in TypoScriptFrontendController
[Packages/TYPO3.CMS.git] / typo3 / sysext / version / Classes / Hook / PreviewHook.php
1 <?php
2 namespace TYPO3\CMS\Version\Hook;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2011-2013 TYPO3 Workspaces Team (http://forge.typo3.org/projects/show/typo3v4-workspaces)
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 * A copy is found in the text file GPL.txt and important notices to the license
19 * from the author is found in LICENSE.txt distributed with these scripts.
20 *
21 *
22 * This script is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * This copyright notice MUST APPEAR in all copies of the script!
28 ***************************************************************/
29
30 use TYPO3\CMS\Core\Utility\GeneralUtility;
31
32 /**
33 * Hook for checking if the preview mode is activated
34 * preview mode = show a page of a workspace without having to log in
35 *
36 * @author Workspaces Team (http://forge.typo3.org/projects/show/typo3v4-workspaces)
37 */
38 class PreviewHook implements \TYPO3\CMS\Core\SingletonInterface {
39
40 /**
41 * the GET parameter to be used
42 *
43 * @var string
44 */
45 protected $previewKey = 'ADMCMD_prev';
46
47 /**
48 * instance of the \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController object
49 *
50 * @var \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController
51 */
52 protected $tsfeObj;
53
54 /**
55 * preview configuration
56 *
57 * @var array
58 */
59 protected $previewConfiguration = FALSE;
60
61 /**
62 * hook to check if the preview is activated
63 * right now, this hook is called at the end of "$TSFE->connectToDB"
64 *
65 * @param array $params (not needed right now)
66 * @param \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController $pObj
67 * @return void
68 */
69 public function checkForPreview($params, &$pObj) {
70 $this->tsfeObj = $pObj;
71 $this->previewConfiguration = $this->getPreviewConfiguration();
72 if (is_array($this->previewConfiguration)) {
73 // In case of a keyword-authenticated preview,
74 // re-initialize the TSFE object:
75 // because the GET variables are taken from the preview
76 // configuration
77 $this->tsfeObj = GeneralUtility::makeInstance(
78 'TYPO3\\CMS\\Frontend\\Controller\\TypoScriptFrontendController',
79 $GLOBALS['TYPO3_CONF_VARS'],
80 GeneralUtility::_GP('id'),
81 GeneralUtility::_GP('type'),
82 GeneralUtility::_GP('no_cache'),
83 GeneralUtility::_GP('cHash'),
84 GeneralUtility::_GP('jumpurl'),
85 GeneralUtility::_GP('MP'),
86 GeneralUtility::_GP('RDCT')
87 );
88 $GLOBALS['TSFE'] = $this->tsfeObj;
89 // Configuration after initialization of TSFE object.
90 // Basically this unsets the BE cookie if any and forces
91 // the BE user set according to the preview configuration.
92 // @previouslyknownas TSFE->ADMCMD_preview_postInit
93 // Clear cookies:
94 unset($_COOKIE['be_typo_user']);
95 }
96 }
97
98 /**
99 * hook after the regular BE user has been initialized
100 * if there is no BE user login, but a preview configuration
101 * the BE user of the preview configuration gets initialized
102 *
103 * @param array $params holding the BE_USER object
104 * @param \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController $pObj
105 * @return void
106 */
107 public function initializePreviewUser(&$params, &$pObj) {
108 if ((is_null($params['BE_USER']) || $params['BE_USER'] === FALSE) && $this->previewConfiguration !== FALSE && $this->previewConfiguration['BEUSER_uid'] > 0) {
109 // New backend user object
110 $BE_USER = GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\FrontendBackendUserAuthentication');
111 $BE_USER->userTS_dontGetCached = 1;
112 $BE_USER->OS = TYPO3_OS;
113 $BE_USER->setBeUserByUid($this->previewConfiguration['BEUSER_uid']);
114 $BE_USER->unpack_uc('');
115 if ($BE_USER->user['uid']) {
116 $BE_USER->fetchGroupData();
117 $pObj->beUserLogin = TRUE;
118 } else {
119 $BE_USER = NULL;
120 $pObj->beUserLogin = FALSE;
121 $_SESSION['TYPO3-TT-start'] = FALSE;
122 }
123 $params['BE_USER'] = $BE_USER;
124 }
125 // if there is a valid BE user, and the full workspace should be
126 // previewed, the workspacePreview option shouldbe set
127 $workspaceUid = $this->previewConfiguration['fullWorkspace'];
128 if ($pObj->beUserLogin && is_object($params['BE_USER']) && \TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($workspaceUid)) {
129 if ($workspaceUid == 0 || $workspaceUid >= -1 && $params['BE_USER']->checkWorkspace($workspaceUid)) {
130 // Check Access to workspace. Live (0) is OK to preview for all.
131 $pObj->workspacePreview = (int)$workspaceUid;
132 } else {
133 // No preview, will default to "Live" at the moment
134 $pObj->workspacePreview = -99;
135 }
136 }
137 }
138
139 /**
140 * Looking for a ADMCMD_prev code, looks it up if found and returns configuration data.
141 * Background: From the backend a request to the frontend to show a page, possibly with
142 * workspace preview can be "recorded" and associated with a keyword.
143 * When the frontend is requested with this keyword the associated request parameters are
144 * restored from the database AND the backend user is loaded - only for that request.
145 * The main point is that a special URL valid for a limited time,
146 * eg. http://localhost/typo3site/index.php?ADMCMD_prev=035d9bf938bd23cb657735f68a8cedbf will
147 * open up for a preview that doesn't require login. Thus it's useful for sending in an email
148 * to someone without backend account.
149 * This can also be used to generate previews of hidden pages, start/endtimes, usergroups and
150 * those other settings from the Admin Panel - just not implemented yet.
151 *
152 * @throws \Exception
153 * @return array Preview configuration array from sys_preview record.
154 */
155 public function getPreviewConfiguration() {
156 $inputCode = $this->getPreviewInputCode();
157 // If inputcode is available, look up the settings
158 if ($inputCode) {
159 // "log out"
160 if ($inputCode == 'LOGOUT') {
161 setcookie($this->previewKey, '', 0, GeneralUtility::getIndpEnv('TYPO3_SITE_PATH'));
162 if ($this->tsfeObj->TYPO3_CONF_VARS['FE']['workspacePreviewLogoutTemplate']) {
163 $templateFile = PATH_site . $this->tsfeObj->TYPO3_CONF_VARS['FE']['workspacePreviewLogoutTemplate'];
164 if (@is_file($templateFile)) {
165 $message = GeneralUtility::getUrl(PATH_site . $this->tsfeObj->TYPO3_CONF_VARS['FE']['workspacePreviewLogoutTemplate']);
166 } else {
167 $message = '<strong>ERROR!</strong><br>Template File "'
168 . $this->tsfeObj->TYPO3_CONF_VARS['FE']['workspacePreviewLogoutTemplate']
169 . '" configured with $TYPO3_CONF_VARS["FE"]["workspacePreviewLogoutTemplate"] not found. Please contact webmaster about this problem.';
170 }
171 } else {
172 $message = 'You logged out from Workspace preview mode. Click this link to <a href="%1$s">go back to the website</a>';
173 }
174 $returnUrl = GeneralUtility::sanitizeLocalUrl(GeneralUtility::_GET('returnUrl'));
175 die(sprintf($message, htmlspecialchars(preg_replace('/\\&?' . $this->previewKey . '=[[:alnum:]]+/', '', $returnUrl))));
176 }
177 // Look for keyword configuration record:
178 $where = 'keyword=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($inputCode, 'sys_preview') . ' AND endtime>' . $GLOBALS['EXEC_TIME'];
179 $previewData = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('*', 'sys_preview', $where);
180 // Get: Backend login status, Frontend login status
181 // - Make sure to remove fe/be cookies (temporarily);
182 // BE already done in ADMCMD_preview_postInit()
183 if (is_array($previewData)) {
184 if (!count(GeneralUtility::_POST())) {
185 // Unserialize configuration:
186 $previewConfig = unserialize($previewData['config']);
187 // For full workspace preview we only ADD a get variable
188 // to set the preview of the workspace - so all other Get
189 // vars are accepted. Hope this is not a security problem.
190 // Still posting is not allowed and even if a backend user
191 // get initialized it shouldn't lead to situations where
192 // users can use those credentials.
193 if ($previewConfig['fullWorkspace']) {
194 // Set the workspace preview value:
195 GeneralUtility::_GETset($previewConfig['fullWorkspace'], 'ADMCMD_previewWS');
196 // If ADMCMD_prev is set the $inputCode value cannot come
197 // from a cookie and we set that cookie here. Next time it will
198 // be found from the cookie if ADMCMD_prev is not set again...
199 if (GeneralUtility::_GP($this->previewKey)) {
200 // Lifetime is 1 hour, does it matter much?
201 // Requires the user to click the link from their email again if it expires.
202 SetCookie($this->previewKey, GeneralUtility::_GP($this->previewKey), 0, GeneralUtility::getIndpEnv('TYPO3_SITE_PATH'));
203 }
204 return $previewConfig;
205 } elseif (GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . 'index.php?' . $this->previewKey . '=' . $inputCode === GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL')) {
206 // Set GET variables
207 $GET_VARS = '';
208 parse_str($previewConfig['getVars'], $GET_VARS);
209 GeneralUtility::_GETset($GET_VARS);
210 // Return preview keyword configuration
211 return $previewConfig;
212 } else {
213 // This check is to prevent people from setting additional
214 // GET vars via realurl or other URL path based ways of passing parameters.
215 throw new \Exception(htmlspecialchars('Request URL did not match "'
216 . GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . 'index.php?' . $this->previewKey . '='
217 . $inputCode . '"', 1294585190));
218 }
219 } else {
220 throw new \Exception('POST requests are incompatible with keyword preview.', 1294585191);
221 }
222 } else {
223 throw new \Exception('ADMCMD command could not be executed! (No keyword configuration found)', 1294585192);
224 }
225 }
226 return FALSE;
227 }
228
229 /**
230 * returns the input code value from the admin command variable
231 *
232 * @return input code
233 */
234 protected function getPreviewInputCode() {
235 $inputCode = GeneralUtility::_GP($this->previewKey);
236 // If no inputcode and a cookie is set, load input code from cookie:
237 if (!$inputCode && $_COOKIE[$this->previewKey]) {
238 $inputCode = $_COOKIE[$this->previewKey];
239 }
240 return $inputCode;
241 }
242
243 /**
244 * Set preview keyword, eg:
245 * $previewUrl = GeneralUtility::getIndpEnv('TYPO3_SITE_URL').'index.php?ADMCMD_prev='.$this->compilePreviewKeyword('id='.$pageId.'&L='.$language.'&ADMCMD_view=1&ADMCMD_editIcons=1&ADMCMD_previewWS='.$this->workspace, $GLOBALS['BE_USER']->user['uid'], 120);
246 *
247 * todo for sys_preview:
248 * - Add a comment which can be shown to previewer in frontend in some way (plus maybe ability to write back, take other action?)
249 * - Add possibility for the preview keyword to work in the backend as well: So it becomes a quick way to a certain action of sorts?
250 *
251 * @param string $getVarsStr Get variables to preview, eg. 'id=1150&L=0&ADMCMD_view=1&ADMCMD_editIcons=1&ADMCMD_previewWS=8'
252 * @param string $backendUserUid 32 byte MD5 hash keyword for the URL: "?ADMCMD_prev=[keyword]
253 * @param int $ttl Time-To-Live for keyword
254 * @param int|NULL $fullWorkspace Which workspace to preview. Workspace UID, -1 or >0. If set, the getVars is ignored in the frontend, so that string can be empty
255 * @return string Returns keyword to use in URL for ADMCMD_prev=
256 */
257 public function compilePreviewKeyword($getVarsStr, $backendUserUid, $ttl = 172800, $fullWorkspace = NULL) {
258 $fieldData = array(
259 'keyword' => md5(uniqid(microtime())),
260 'tstamp' => $GLOBALS['EXEC_TIME'],
261 'endtime' => $GLOBALS['EXEC_TIME'] + $ttl,
262 'config' => serialize(array(
263 'fullWorkspace' => $fullWorkspace,
264 'getVars' => $getVarsStr,
265 'BEUSER_uid' => $backendUserUid
266 ))
267 );
268 $GLOBALS['TYPO3_DB']->exec_INSERTquery('sys_preview', $fieldData);
269 return $fieldData['keyword'];
270 }
271
272 /**
273 * easy function to just return the number of hours
274 * a preview link is valid, based on the TSconfig value "options.workspaces.previewLinkTTLHours"
275 * by default, it's 48hs
276 *
277 * @return integer the hours as a number
278 */
279 public function getPreviewLinkLifetime() {
280 $ttlHours = (int)$GLOBALS['BE_USER']->getTSConfigVal('options.workspaces.previewLinkTTLHours');
281 return $ttlHours ? $ttlHours : 24 * 2;
282 }
283
284 }