PageRenderTime 58ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/Classes/Aloha/Save.php

https://bitbucket.org/pixelant/aloha
PHP | 442 lines | 260 code | 67 blank | 115 comment | 42 complexity | 9cb3e72d6bf924f9a0e303f0e51e194c MD5 | raw file
  1. <?php
  2. /***************************************************************
  3. * Copyright notice
  4. *
  5. * (c) 2011 Georg Ringer <typo3@ringerge.org>
  6. * All rights reserved
  7. *
  8. * This script is part of the TYPO3 project. The TYPO3 project is
  9. * free software; you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License as published by
  11. * the Free Software Foundation; either version 2 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * The GNU General Public License can be found at
  15. * http://www.gnu.org/copyleft/gpl.html.
  16. *
  17. * This script is distributed in the hope that it will be useful,
  18. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. * GNU General Public License for more details.
  21. *
  22. * This copyright notice MUST APPEAR in all copies of the script!
  23. ***************************************************************/
  24. /**
  25. * Save the changes by using TCEmain
  26. *
  27. * @package TYPO3
  28. * @subpackage tx_aloha
  29. */
  30. class Tx_Aloha_Aloha_Save {
  31. /**
  32. * @var t3lib_tcemain
  33. */
  34. protected $tce;
  35. /**
  36. * @var t3lib_frontendedit
  37. */
  38. protected $t3lib_frontendedit;
  39. /**
  40. * If set, a javascript reload is added to the response
  41. *
  42. * @var boolean
  43. */
  44. protected $forceReload = FALSE;
  45. protected $table;
  46. protected $uid;
  47. protected $field;
  48. /**
  49. * Save method, can be none, direct or intermediate
  50. * @var string
  51. */
  52. protected $saveMethod;
  53. public function __construct() {
  54. $this->tce = t3lib_div::makeInstance('t3lib_TCEmain');
  55. $this->tce->stripslashes_values = 0;
  56. $this->t3lib_frontendedit = t3lib_div::makeInstance('t3lib_frontendedit');
  57. $configurationArray = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['aloha']);
  58. $this->saveMethod = $configurationArray['saveMethod'];
  59. }
  60. /**
  61. * Initial function which is called by an additional page type
  62. * Calls correct function to edit/hide/delete/... record
  63. *
  64. * @param string $content
  65. * @param array $conf plugin configuration
  66. * @return sring
  67. */
  68. public function start($content, $conf) {
  69. $request = t3lib_div::_POST();
  70. $response = '';
  71. // aborting save
  72. if ($this->saveMethod === 'none') {
  73. return 'This is like saving';
  74. }
  75. try {
  76. // @todo check if this workaround can be solved differently
  77. $GLOBALS['PAGES_TYPES']['default']['allowedTables'] = 'pages,tt_content';
  78. if ($request['action'] === 'discardSavings') {
  79. $response = $this->discardSavings();
  80. } elseif($request['action'] === 'commitSavings') {
  81. $response = $this->commitSavings();
  82. } else {
  83. $this->init($request);
  84. switch ($request['action']) {
  85. case 'save':
  86. if ($this->saveMethod == 'direct') {
  87. $response = $this->directSave($request);
  88. } elseif ($this->saveMethod == 'intermediate') {
  89. $response = $this->intermediateSave($request);
  90. } else {
  91. throw new Exception(Tx_Aloha_Utility_Helper::ll('error.saveMethod'));
  92. }
  93. break;
  94. case 'up':
  95. $response = $this->move('up');
  96. break;
  97. case 'down':
  98. $response = $this->move('down');
  99. break;
  100. case 'hide':
  101. $response = $this->changeVisibility(1);
  102. break;
  103. case 'unhide':
  104. $response = $this->changeVisibility(0);
  105. break;
  106. case 'delete':
  107. $response = $this->delete();
  108. break;
  109. default:
  110. $errorMsg = sprintf(Tx_Aloha_Utility_Helper::ll('error.response-action'), $request['action']);
  111. throw new Exception($errorMsg);
  112. }
  113. }
  114. // Add JS reload if needed
  115. if ($this->forceReload) {
  116. $response .= '<script type="text/javascript">window.location.reload();</script>';
  117. }
  118. } catch (Exception $e) {
  119. $response = $e->getMessage();
  120. header('HTTP/1.1 404 Not Found');
  121. }
  122. return $response;
  123. }
  124. private function commitSavings() {
  125. $elements = $GLOBALS['BE_USER']->uc['aloha'][$GLOBALS['TSFE']->id];
  126. if (!is_array($elements) || count($elements) == 0) {
  127. $response = 'nothing to be saved';
  128. } else {
  129. foreach($elements as $element) {
  130. $this->directSave(unserialize($element), TRUE);
  131. }
  132. $GLOBALS['BE_USER']->uc['aloha'][$GLOBALS['TSFE']->id] = array();
  133. $GLOBALS['BE_USER']->writeUC();
  134. $response = 'all done
  135. <script>
  136. window.alohaQuery("#count").text("0").removeClass("tobesaved");
  137. </script>';
  138. }
  139. return $response;
  140. }
  141. private function intermediateSave(array $request) {
  142. $GLOBALS['BE_USER']->uc['aloha'][$GLOBALS['TSFE']->id][$request['identifier']] = serialize($request);
  143. $GLOBALS['BE_USER']->writeUC();
  144. $countOfElements = Tx_Aloha_Utility_Integration::getCountOfUnsavedElements($GLOBALS['TSFE']->id);
  145. $response = 'Press Save button for a real save. <script>
  146. window.alohaQuery("#count").text("' . $test. $countOfElements . '").' . ($countOfElements > 0 ? 'add' : 'remove') . 'Class("tobesaved");
  147. window.alohaQuery("#aloha-saveButton").show();
  148. </script>';
  149. return $response;
  150. }
  151. private function discardSavings() {
  152. $elements = $GLOBALS['BE_USER']->uc['aloha'][$GLOBALS['TSFE']->id];
  153. if (!is_array($elements) || count($elements) == 0) {
  154. $response = 'No staged changes, nothing to do. you are fine!';
  155. } else {
  156. $GLOBALS['BE_USER']->uc['aloha'][$GLOBALS['TSFE']->id] = array();
  157. $GLOBALS['BE_USER']->writeUC();
  158. $this->forceReload = TRUE;
  159. $response = 'Changes have been discard<script>
  160. window.alohaQuery("#count").text("0").removeClass("tobesaved");
  161. </script>';
  162. }
  163. return $response;
  164. }
  165. /**
  166. * Hide/Unhide record
  167. *
  168. * @param integer $visibility
  169. * @return string
  170. */
  171. private function changeVisibility($visibility) {
  172. $this->forceReload = TRUE;
  173. if ($visibility == 0) {
  174. $this->t3lib_frontendedit->doUnhide($this->table, $this->uid);
  175. return Tx_Aloha_Utility_Helper::ll('response.action.unhide');
  176. } elseif ($visibility == 1) {
  177. $this->t3lib_frontendedit->doHide($this->table, $this->uid);
  178. return Tx_Aloha_Utility_Helper::ll('response.action.hide');
  179. }
  180. }
  181. /**
  182. * Delete a record
  183. *
  184. * @return string
  185. */
  186. private function delete() {
  187. $this->forceReload = TRUE;
  188. $this->t3lib_frontendedit->doDelete($this->table, $this->uid);
  189. return Tx_Aloha_Utility_Helper::ll('response.action.delete');
  190. }
  191. /**
  192. * Move a table, either up or down (set in $direction)
  193. *
  194. * @param string $direction
  195. * @return string
  196. */
  197. private function move($direction) {
  198. $this->forceReload = TRUE;
  199. if ($direction === 'down') {
  200. $this->t3lib_frontendedit->doDown($this->table, $this->uid);
  201. return Tx_Aloha_Utility_Helper::ll('response.action.moveDown');
  202. } elseif($direction === 'up') {
  203. $this->t3lib_frontendedit->doUp($this->table, $this->uid);
  204. return Tx_Aloha_Utility_Helper::ll('response.action.moveUp');
  205. } else {
  206. throw new Exception(sprintf(Tx_Aloha_Utility_Helper::ll('error.move-action.wrong-direction'), htmlspecialchars($direction)));
  207. }
  208. }
  209. /**
  210. * True main function which starts to call tcemain
  211. *
  212. * @param array $request POST request
  213. * @return string
  214. */
  215. public function directSave(array $request, $initAgain = FALSE) {
  216. // PIXELANT HACK - this function needs to be public
  217. // @todo do that nice again
  218. if ($initAgain) {
  219. $this->init($request);
  220. }
  221. if (is_array($GLOBALS['TYPO3_CONF_VARS']['Aloha']['Classes/Save/Save.php']['requestPreProcess'])) {
  222. $finished = FALSE;
  223. foreach ($GLOBALS['TYPO3_CONF_VARS']['Aloha']['Classes/Save/Save.php']['requestPreProcess'] as $classData) {
  224. if (!$finished) {
  225. $hookObject = t3lib_div::getUserObj($classData);
  226. if (!($hookObject instanceof Tx_Aloha_Interfaces_RequestPreProcess)) {
  227. throw new UnexpectedValueException(
  228. $classData . ' must implement interface Tx_Aloha_Interfaces_RequestPreProcess',
  229. 1274563549
  230. );
  231. }
  232. $request = $hookObject->preProcess($request, $finished, $this);
  233. }
  234. }
  235. }
  236. /*
  237. Aloha automatically encodes entities, but typo3 automatically encodes them too,
  238. so we have to decode them from Aloha otherwise we would encode twice.
  239. CHANGED from html_entity_decode to urldecode, after problem with encoding on some servers which broke content and flexform.
  240. */
  241. $htmlEntityDecode = true;
  242. $request['content'] = Tx_Aloha_Utility_Integration::rteModification($this->table, $this->field, $this->uid, $GLOBALS['TSFE']->id, $request['content']);
  243. // in case we want to write to flexform
  244. $fields = explode('-', $this->field, 2);
  245. if ($this->table == 'tt_content' && $fields[0] == 'pi_flexform' && !is_null($fields[1]))
  246. {
  247. $this->field = 'pi_flexform';
  248. $request['content'] = $this->processFlexform($request,$fields);
  249. }
  250. // in case we changed header
  251. if ($this->table == 'tt_content' && $this->field == 'header' && substr( $request['content'], 0, 2 ) === "<h")
  252. {
  253. $request['content'] = $this->processHeader($request);
  254. }
  255. if ($htmlEntityDecode) {
  256. $request['content'] = urldecode($request['content']);
  257. // Try to remove invalid utf-8 characters so content won't break if there are invalid characters in content
  258. $request['content'] = iconv("UTF-8", "UTF-8//IGNORE", $request['content']);
  259. }
  260. if (!empty($request)) {
  261. $data = array(
  262. $this->table => array(
  263. $this->uid => array(
  264. $this->field => $request['content']
  265. )
  266. )
  267. );
  268. }
  269. $this->tce->start($data, array());
  270. $this->tce->process_datamap();
  271. return Tx_Aloha_Utility_Helper::ll('response.action.save');
  272. }
  273. /**
  274. * Initialize everything
  275. *
  276. * @param array $request POST request
  277. * @return void
  278. */
  279. private function init(array $request) {
  280. // request is only allowed for POST request and a BE_USER is available
  281. if (empty($request)) {
  282. throw new BadFunctionCallException(Tx_Aloha_Utility_Helper::ll('error.request.no-post'));
  283. } elseif (!Tx_Aloha_Utility_Access::isEnabled()) {
  284. throw new BadFunctionCallException(Tx_Aloha_Utility_Helper::ll('error.request.not-allowed'));
  285. }
  286. $split = explode('--', $request['identifier']);
  287. if (count($split) != 3) {
  288. throw new Exception(Tx_Aloha_Utility_Helper::ll('error.request.identifier'));
  289. } elseif(empty($split[0])) {
  290. throw new Exception(Tx_Aloha_Utility_Helper::ll('error.request.table'));
  291. } elseif(empty($split[1])) {
  292. throw new Exception(Tx_Aloha_Utility_Helper::ll('error.request.field'));
  293. } elseif (!ctype_digit($split[2])) {
  294. throw new Exception(Tx_Aloha_Utility_Helper::ll('error.request.uid'));
  295. }
  296. $this->table = $split[0];
  297. $this->field = $split[1];
  298. $this->uid = (int)$split[2];
  299. $this->content = $request['content'];
  300. $this->record = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('*', $this->table, 'uid=' . $this->uid);
  301. }
  302. /**
  303. * Wrapper function to get uid of record
  304. *
  305. * @return integer
  306. */
  307. public function getUid() {
  308. return $this->uid;
  309. }
  310. /**
  311. * Wrapper function to get field name
  312. *
  313. * @return string
  314. */
  315. public function getField() {
  316. return $this->field;
  317. }
  318. /**
  319. * Wrapper function to get the tablename
  320. * @return string
  321. */
  322. public function getTable() {
  323. return $this->table;
  324. }
  325. /**
  326. * Wrapper function to get record
  327. *
  328. * @return array
  329. */
  330. public function getRecord() {
  331. return $this->record;
  332. }
  333. private function processFlexform($request,$fields) {
  334. $xml = new SimpleXMLElement($this->record['pi_flexform']);
  335. // @TODO: Maybe give possibility for fields to have html tags
  336. $fieldAllowedTags = '<sup><sub>';
  337. foreach ($xml->xpath('//T3FlexForms/data/sheet/language/field[@index = "'.$fields[1].'"]') as $entry) {
  338. $content = trim($request['content']);
  339. $content = strip_tags(urldecode($content), $fieldAllowedTags);
  340. // Try to remove invalid characters so save won't break xml if there are invalid characters in string
  341. $content = iconv("UTF-8", "UTF-8//IGNORE", $content);
  342. $node = dom_import_simplexml($entry->value);
  343. $node->nodeValue = "";
  344. $node->appendChild($node->ownerDocument->createCDATASection($content));
  345. }
  346. $request['content'] = $xml->saveXml();
  347. return $request['content'];
  348. }
  349. private function processHeader($request) {
  350. $content = urldecode(strip_tags($request['content']));
  351. switch (substr( $request['content'], 0, 4 ))
  352. {
  353. case '<h1>':
  354. $request['content'] = 1;
  355. break;
  356. case '<h2>':
  357. $request['content'] = 2;
  358. break;
  359. case '<h3>':
  360. $request['content'] = 3;
  361. break;
  362. case '<h4>':
  363. $request['content'] = 4;
  364. break;
  365. default:
  366. $request['content'] = 0;
  367. break;
  368. }
  369. $request['identifier'] = $this->table . '--header_layout--' . $this->uid;
  370. $this->directSave($request,TRUE);
  371. $this->field = 'header';
  372. return $content;
  373. }
  374. }
  375. ?>