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