PageRenderTime 46ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/files/lib/data/page/revision/PageRevisionAction.class.php

https://gitlab.com/fsteltenkamp/Fireball
PHP | 316 lines | 236 code | 43 blank | 37 comment | 56 complexity | 89589f06da7d187b041d4817477678fb MD5 | raw file
  1. <?php
  2. namespace cms\data\page\revision;
  3. use cms\data\content\Content;
  4. use cms\data\content\ContentAction;
  5. use cms\data\content\ContentList;
  6. use wcf\data\object\type\ObjectTypeCache;
  7. use cms\data\page\Page;
  8. use cms\data\page\PageAction;
  9. use wcf\data\package\PackageCache;
  10. use wcf\data\AbstractDatabaseObjectAction;
  11. use wcf\data\DatabaseObject;
  12. use wcf\system\language\I18nHandler;
  13. use wcf\system\language\LanguageFactory;
  14. use wcf\system\WCF;
  15. /**
  16. * Executes page revision-related actions.
  17. *
  18. * @author Florian Frantzen
  19. * @copyright 2013 - 2015 codeQuake
  20. * @license GNU Lesser General Public License <http://www.gnu.org/licenses/lgpl-3.0.txt>
  21. * @package de.codequake.cms
  22. */
  23. class PageRevisionAction extends AbstractDatabaseObjectAction {
  24. /**
  25. * @see \wcf\data\AbstractDatabaseObjectAction::$className
  26. */
  27. protected $className = 'cms\data\page\revision\PageRevisionEditor';
  28. /**
  29. * @see \wcf\data\AbstractDatabaseObjectAction::$permissionsDelete
  30. */
  31. protected $permissionsDelete = array('admin.cms.page.canAddPage');
  32. /**
  33. * @see \wcf\data\AbstractDatabaseObjectAction::$requireACP
  34. */
  35. protected $requireACP = array('delete', 'restore');
  36. /**
  37. * Validates permissions to restore a specific revision.
  38. */
  39. public function validateRestore() {
  40. WCF::getSession()->checkPermissions(array('admin.cms.page.canAddPage'));
  41. // validate 'objectIDs' parameter
  42. $this->getSingleObject();
  43. }
  44. /**
  45. * Restores a specific revision.
  46. */
  47. public function restore() {
  48. // get available languages
  49. $availableLanguages = LanguageFactory::getInstance()->getLanguages();
  50. $revision = $this->getSingleObject();
  51. WCF::getDB()->beginTransaction();
  52. $pageData = base64_decode($revision->data);
  53. $pageData = @unserialize($pageData);
  54. $i18nfields = array();
  55. // save i18n
  56. foreach ($pageData as $key => $data) {
  57. if (($key == 'title' || $key == 'description' || $key == 'metaDescription' || $key == 'metaKeywords') && is_array($data)) {
  58. $langData = array();
  59. foreach ($availableLanguages as $lang) {
  60. if (isset($data[$lang->countryCode])) {
  61. $langData[$lang->languageID] = $data[$lang->countryCode];
  62. } else {
  63. $langData[$lang->languageID] = '';
  64. }
  65. }
  66. I18nHandler::getInstance()->setValues($key, $langData);
  67. $i18nfields[] = $key;
  68. $pageData[$key] = '';
  69. }
  70. }
  71. // restore page
  72. $pageAction = new PageAction(array($revision->pageID), 'update', array('data' => $pageData));
  73. $pageAction->executeAction();
  74. // save i18n
  75. foreach ($pageData as $key => $data) {
  76. if (($key == 'title' || $key == 'description' || $key == 'metaDescription' || $key == 'metaKeywords') && in_array($key, $i18nfields)) {
  77. $this->saveI18nValue(new Page($revision->pageID), 'page', $key);
  78. }
  79. }
  80. // restore contents
  81. $contentData = base64_decode($revision->contentData);
  82. $contentData = @unserialize($contentData);
  83. $contentList = new ContentList();
  84. $contentList->getConditionBuilder()->add('content.pageID = ?', array($revision->pageID));
  85. $contentList->readObjects();
  86. $existingContentIDs = $contentList->getObjectIDs();
  87. $oldContents = array();
  88. foreach ($contentData as $data) {
  89. $oldContents[$data['contentID']] = $data;
  90. }
  91. // delete contents that where created after the revision
  92. $orphanedElementIDs = array_diff($existingContentIDs, array_keys($oldContents));
  93. if (!empty($orphanedElementIDs)) {
  94. $contentAction = new ContentAction($orphanedElementIDs, 'delete');
  95. $contentAction->executeAction();
  96. }
  97. foreach ($oldContents as $oldContent) {
  98. $objectType = ObjectTypeCache::getInstance()->getObjectType($oldContent['contentTypeID']);
  99. if (in_array($oldContent['contentID'], $existingContentIDs)) {
  100. // content this exists => update
  101. $i18ntitle = false;
  102. if (is_array($oldContent['title'])) {
  103. $langData = array();
  104. foreach ($availableLanguages as $lang) {
  105. if (isset($oldContent['title'][$lang->countryCode])) {
  106. $langData[$lang->languageID] = $oldContent['title'][$lang->countryCode];
  107. } else {
  108. $langData[$lang->languageID] = '';
  109. }
  110. }
  111. I18nHandler::getInstance()->setValues($key, $langData);
  112. $i18ntitle = true;
  113. $oldContent['title'] = '';
  114. }
  115. $i18nfields = array();
  116. foreach ($objectType->getProcessor()->multilingualFields as $field) {
  117. if (isset($oldContent['contentData'][$field]) && is_array($oldContent['contentData'][$field])) {
  118. $langData = array();
  119. foreach ($availableLanguages as $lang) {
  120. if (isset($oldContent['contentData'][$field][$lang->countryCode])) {
  121. $langData[$lang->languageID] = $oldContent['contentData'][$field][$lang->countryCode];
  122. } else {
  123. $langData[$lang->languageID] = '';
  124. }
  125. }
  126. I18nHandler::getInstance()->setValues($field, $langData);
  127. $i18nfields[] = $field;
  128. $oldContent['contentData'][$field] = '';
  129. }
  130. }
  131. $contentAction = new ContentAction(array($oldContent['contentID']), 'update', array('data' => $oldContent));
  132. $contentAction->executeAction();
  133. if ($i18ntitle)
  134. $this->saveI18nValue(new Content($oldContent['contentID']), 'content', 'title');
  135. foreach ($objectType->getProcessor()->multilingualFields as $field) {
  136. if (in_array($field, $i18nfields))
  137. $this->saveI18nValue(new Content($oldContent['contentID']), 'content', $field, true);
  138. }
  139. } else {
  140. // content was deleted => re-create
  141. $i18ntitle = false;
  142. if (is_array($oldContent['title'])) {
  143. $langData = array();
  144. foreach ($availableLanguages as $lang) {
  145. if (isset($oldContent['title'][$lang->countryCode])) {
  146. $langData[$lang->languageID] = $oldContent['title'][$lang->countryCode];
  147. } else {
  148. $langData[$lang->languageID] = '';
  149. }
  150. }
  151. I18nHandler::getInstance()->setValues($field, $langData);
  152. $i18ntitle = true;
  153. $oldContent['title'] = '';
  154. }
  155. $i18nfields = array();
  156. foreach ($objectType->getProcessor()->multilingualFields as $field) {
  157. if (isset($oldContent['contentData'][$field])) {
  158. $langData = array();
  159. foreach ($availableLanguages as $lang) {
  160. if (isset($oldContent['contentData'][$field][$lang->countryCode])) {
  161. $langData[$lang->languageID] = $oldContent['contentData'][$field][$lang->countryCode];
  162. } else {
  163. $langData[$lang->languageID] = '';
  164. }
  165. }
  166. I18nHandler::getInstance()->setValues($key, $langData);
  167. $i18nfields[] = $field;
  168. $oldContent['contentData'][$field] = '';
  169. }
  170. }
  171. $contentAction = new ContentAction(array($oldContent['contentID']), 'create', array('data' => $oldContent));
  172. $contentAction->executeAction();
  173. if ($i18ntitle)
  174. $this->saveI18nValue(new Content($oldContent['contentID']), 'content', 'title');
  175. foreach ($objectType->getProcessor()->multilingualFields as $field) {
  176. if (in_array($field, $i18nfields))
  177. $this->saveI18nValue(new Content($oldContent['contentID']), 'content', $field, true);
  178. }
  179. }
  180. }
  181. WCF::getDB()->commitTransaction();
  182. }
  183. private function saveI18nValue(DatabaseObject $object, $type, $columnName, $inContentData = false) {
  184. if (!I18nHandler::getInstance()->isPlainValue($columnName)) {
  185. I18nHandler::getInstance()->save(
  186. $columnName,
  187. 'cms.'.$type.'.'.$columnName. $object->{$type.'ID'},
  188. 'cms.'.$type,
  189. PackageCache::getInstance()->getPackageID('de.codequake.cms')
  190. );
  191. $editorName = '\\cms\\data\\'.$type.'\\'.ucfirst($type).'Editor';
  192. if ($object !== null) {
  193. $editor = new $editorName($object);
  194. if ($inContentData) {
  195. $tmpContentData = $object->contentData;
  196. if ($this->is_serialized($tmpContentData)) {
  197. $tmpContentData = unserialize($tmpContentData);
  198. $tmpContentData['text'] = 'cms.'.$type.'.'.$columnName. $object->{$type.'ID'};
  199. } else if (is_array($tmpContentData)) {
  200. $tmpContentData['text'] = 'cms.'.$type.'.'.$columnName. $object->{$type.'ID'};
  201. } else {
  202. $tmpContentData = array();
  203. $tmpContentData['text'] = 'cms.'.$type.'.'.$columnName. $object->{$type.'ID'};
  204. }
  205. $tmpContentData = serialize($tmpContentData);
  206. $editor->update(array(
  207. 'contentData' => $tmpContentData
  208. ));
  209. } else {
  210. $editor->update(array(
  211. $columnName => 'cms.'.$type.'.'.$columnName. $object->{$type.'ID'}
  212. ));
  213. }
  214. }
  215. }
  216. }
  217. private function is_serialized($value, &$result = null) {
  218. // Bit of a give away this one
  219. if (!is_string($value))
  220. return false;
  221. // Serialized false, return true. unserialize() returns false on an
  222. // invalid string or it could return false if the string is serialized
  223. // false, eliminate that possibility.
  224. if ($value === 'b:0;') {
  225. $result = false;
  226. return true;
  227. }
  228. $length = strlen($value);
  229. $end = '';
  230. switch ($value[0]) {
  231. case 's':
  232. if ($value[$length - 2] !== '"')
  233. return false;
  234. case 'b':
  235. case 'i':
  236. case 'd':
  237. // This looks odd but it is quicker than isset()ing
  238. $end .= ';';
  239. case 'a':
  240. case 'O':
  241. $end .= '}';
  242. if ($value[1] !== ':')
  243. return false;
  244. switch ($value[2]) {
  245. case 0:
  246. case 1:
  247. case 2:
  248. case 3:
  249. case 4:
  250. case 5:
  251. case 6:
  252. case 7:
  253. case 8:
  254. case 9:
  255. break;
  256. default:
  257. return false;
  258. }
  259. case 'N':
  260. $end .= ';';
  261. if ($value[$length - 1] !== $end[0])
  262. return false;
  263. break;
  264. default:
  265. return false;
  266. }
  267. if (($result = @unserialize($value)) === false) {
  268. $result = null;
  269. return false;
  270. }
  271. return true;
  272. }
  273. }