Fred.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. <?php
  2. /*
  3. * This file is part of the Fred package.
  4. *
  5. * Copyright (c) MODX, LLC
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. $corePath = $modx->getOption('fred.core_path', null, $modx->getOption('core_path', null, MODX_CORE_PATH) . 'components/fred/');
  11. /** @var Fred $fred */
  12. $fred = $modx->getService(
  13. 'fred',
  14. 'Fred',
  15. $corePath . 'model/fred/',
  16. array(
  17. 'core_path' => $corePath
  18. )
  19. );
  20. switch ($modx->event->name) {
  21. case 'OnDocFormPrerender':
  22. if(!empty($resource) && !empty($fred->getTheme($resource->template))) {
  23. //Disable ContentBlocks
  24. $isContentBlocks = $resource->getProperty('_isContentBlocks', 'contentblocks', null);
  25. if($isContentBlocks !== false){
  26. $resource->setProperty('_isContentBlocks', false, 'contentblocks');
  27. $resource->save();
  28. }
  29. $data = $resource->getProperty('data', 'fred');
  30. $fingerprint = !empty($data['fingerprint']) ? $data['fingerprint'] : '';
  31. //Load Open in Fred button
  32. $modx->lexicon->load('fred:default');
  33. $modx->controller->addLexiconTopic('fred:default');
  34. $modx->controller->addHtml("
  35. <script>
  36. Ext.ComponentMgr.onAvailable('modx-resource-content', function(right) {
  37. right.on('beforerender', function() {
  38. var content = Ext.getCmp('ta'),
  39. contentvalue = content.getValue(),
  40. panel = Ext.getCmp('modx-page-update-resource');
  41. content.destroy();
  42. right.insert(0,{
  43. xtype: 'button'
  44. ,fieldLabel: _('fred.open_in_fred')
  45. ,hideLabel: true
  46. ,cls: 'primary-button'
  47. ,style: {padding: '10px 15px'}
  48. ,html: _('fred.open_in_fred')
  49. ,handler: function(){
  50. window.open(panel.config.preview_url)
  51. }
  52. });
  53. right.insert(1,{
  54. xtype: 'textarea'
  55. ,hideLabel: true
  56. ,anchor: '100%'
  57. ,grow: true
  58. ,style: {marginTop:'15px'}
  59. ,disabled: true
  60. ,value: contentvalue
  61. });
  62. });
  63. right.on('afterrender', function() {
  64. var panel = Ext.getCmp('modx-panel-resource');
  65. panel.on('success', function(){
  66. location.reload();
  67. });
  68. var fingerprint = document.createElement('input');
  69. fingerprint.setAttribute('type', 'hidden');
  70. fingerprint.setAttribute('name', 'fingerprint');
  71. fingerprint.setAttribute('value', '" . $fingerprint . "');
  72. panel.form.el.dom.appendChild(fingerprint);
  73. });
  74. });
  75. </script>");
  76. }
  77. break;
  78. case 'OnLoadWebDocument':
  79. $theme = $fred->getTheme($modx->resource->template);
  80. if (!empty($theme)) {
  81. $themeUri = $theme->getThemeFolderUri();
  82. $modx->setPlaceholder('+fred.theme_dir', $themeUri);
  83. $modx->setOption('fred.theme_dir', $themeUri);
  84. }
  85. break;
  86. case 'OnWebPagePrerender':
  87. $theme = $fred->getTheme($modx->resource->template);
  88. if (!empty($theme)) {
  89. if (!$modx->user) return;
  90. if (!($modx->user->hasSessionContext('mgr') || $modx->user->hasSessionContext($modx->resource->context_key))) return;
  91. if (!$modx->hasPermission('fred')) return;
  92. $modx->lexicon->load('fred:fe');
  93. $fredMode = 1;
  94. if (isset($_SESSION['fred'])) {
  95. $fredMode = intval($_SESSION['fred']);
  96. }
  97. if (isset($_GET['fred'])) {
  98. $fredMode = intval($_GET['fred']);
  99. }
  100. if ($fredMode === 4) {
  101. $fredMode = 0;
  102. $_SESSION['fred'] = 0;
  103. }
  104. if ($fredMode === 1) {
  105. $_SESSION['fred'] = 1;
  106. }
  107. $get = $_GET;
  108. unset($get[$modx->getOption('request_param_alias', [], 'q')]);
  109. if ($fredMode === 0) {
  110. $button = "<a href=\"" . str_replace('&amp;', '&', $modx->makeUrl($modx->resource->id, '', array_merge($get, ['fred' => 1]) , 'abs')) . "\" title=\"" . $modx->lexicon('fred.fe.turn_on_fred') . "\" role=\"button\" style=\"display: block; text-decoration: none; background-color: #4D4D4D; background-image: url(&quot;data:image/svg+xml, %3Csvg xmlns='http://www.w3.org/2000/svg' width='48' height='48' viewBox='-14.584 -8.583 48 48'%3E%3ClinearGradient id='a' gradientUnits='userSpaceOnUse' x1='2164.318' y1='-2519.043' x2='2047.448' y2='-2442.941' gradientTransform='matrix(.1471 0 0 -.1471 -290.574 -365.794)'%3E%3Cstop offset='0' stop-color='%2380c3e6'/%3E%3Cstop offset='1' stop-color='%233380c2'/%3E%3C/linearGradient%3E%3Cpath fill='url(%23a)' d='M22.035 11.468l9.602-15.406H8.488L5.236 1.17z'/%3E%3Cpath opacity='.15' d='M5.236 1.17l1.702-2.633 15.097 12.931z'/%3E%3ClinearGradient id='b' gradientUnits='userSpaceOnUse' x1='2184.769' y1='-2694.977' x2='2097.394' y2='-2637.275' gradientTransform='matrix(.1471 0 0 -.1471 -290.574 -365.794)'%3E%3Cstop offset='0' stop-color='%23f38649'/%3E%3Cstop offset='.185' stop-color='%23f28147'/%3E%3Cstop offset='.409' stop-color='%23ef7242'/%3E%3Cstop offset='.654' stop-color='%23ea5a3a'/%3E%3Cstop offset='.911' stop-color='%23e4382e'/%3E%3Cstop offset='1' stop-color='%23e12a29'/%3E%3C/linearGradient%3E%3Cpath fill='url(%23b)' d='M27.068 39.416V16.268l-4.957-3.176L11.583 29.74z'/%3E%3Cpath opacity='.15' d='M11.583 29.74l2.632 1.625 7.896-18.273z'/%3E%3ClinearGradient id='c' gradientUnits='userSpaceOnUse' x1='1896.918' y1='-2571.592' x2='2064.08' y2='-2467.137' gradientTransform='matrix(.1471 0 0 -.1471 -290.574 -365.794)'%3E%3Cstop offset='0' stop-color='%2342ab4a'/%3E%3Cstop offset='1' stop-color='%23add155'/%3E%3C/linearGradient%3E%3Cpath fill='url(%23c)' d='M-10.25-8.583v23.148l5.419 3.175 26.866-6.272z'/%3E%3ClinearGradient id='d' gradientUnits='userSpaceOnUse' x1='1933.576' y1='-2758.292' x2='2069.413' y2='-2540.907' gradientTransform='matrix(.1471 0 0 -.1471 -290.574 -365.794)'%3E%3Cstop offset='0' stop-color='%2342ab4a'/%3E%3Cstop offset='1' stop-color='%23add155'/%3E%3C/linearGradient%3E%3Cpath fill='url(%23d)' d='M-4.985 19.52l-9.599 15.093H8.565l13.546-21.521z'/%3E%3C/svg%3E&quot;); height: 48px; width: 48px; border-radius: 50%; outline: 0; border: none; background-repeat: no-repeat; background-size: 60%; background-position: center; box-shadow: rgba(18,55,100,0.34) 0 4px 8px; margin: 0; padding: 0; text-indent: -9999px; cursor: pointer; position: fixed; z-index: 10010; margin: 32px; bottom: 0; left: 0; opacity: 0.8; transition: .3s; box-sizing: border-box;\" onmouseover=\"this.style.transform='scale(1.05)';\" onmouseout=\"this.style.transform='initial'\";>Fred</a>";
  111. $modx->resource->_output = preg_replace('/(<\/body>(?:<\/body>)?)/i', "{$button}\r\n$1", $modx->resource->_output);
  112. return;
  113. }
  114. if ($fredMode === 3) {
  115. $modx->resource->_output = '';
  116. return;
  117. }
  118. $html = Wa72\HtmlPageDom\HtmlPageCrawler::create($modx->resource->_output);
  119. $dzs = $html->filter('[data-fred-dropzone]');
  120. $dzs->each(function(Wa72\HtmlPageDom\HtmlPageCrawler $node, $i) {
  121. $node->setInnerHtml('');
  122. });
  123. $modx->resource->_output = $html->saveHTML();
  124. if ($fredMode === 2) {
  125. return;
  126. }
  127. $scripts = $html->filter('script');
  128. $scripts->each(function(Wa72\HtmlPageDom\HtmlPageCrawler $node, $i) {
  129. $newNode = Wa72\HtmlPageDom\HtmlPageCrawler::create('<script-fred></script-fred>');
  130. $attrs = $node->getNode(0)->attributes;
  131. foreach ($attrs as $attr) {
  132. $newNode->attr($attr->nodeName, $attr->nodeValue);
  133. }
  134. $newNode->setAttribute('data-fred-script', $node->getInnerHtml());
  135. $node->replaceWith($newNode);
  136. });
  137. $modx->resource->_output = $html->saveHTML();
  138. $beforeRenderResults = $modx->invokeEvent('FredBeforeRender');
  139. $includes = '';
  140. $beforeRender = '';
  141. $lexicons = [];
  142. foreach ($beforeRenderResults as $result) {
  143. if ($result['includes']) {
  144. $includes .= $result['includes'];
  145. }
  146. if ($result['beforeRender']) {
  147. $beforeRender .= $result['beforeRender'];
  148. }
  149. if ($result['lexicons'] && is_array($result['lexicons'])) {
  150. $lexicons = array_merge($lexicons, $result['lexicons']);
  151. }
  152. }
  153. /** @var FredElementRTEConfig[] $rteConfigs */
  154. $rteConfigs = $modx->getIterator('FredElementRTEConfig');
  155. $rteConfigString = [];
  156. foreach ($rteConfigs as $rteConfig) {
  157. if (empty($rteConfig->get('data'))) continue;
  158. $rteConfigString[] = $rteConfig->name . ':' . $rteConfig->data;
  159. }
  160. $rteConfigString = implode(',', $rteConfigString);
  161. $payload = [
  162. 'iss' => $modx->user->id,
  163. 'resource' => $modx->resource->id,
  164. 'context' => $modx->resource->context_key,
  165. 'queryParams' => $_GET
  166. ];
  167. $jwt = \Firebase\JWT\JWT::encode($payload, $fred->getSecret());
  168. $fredContent = '
  169. <script type="text/javascript" src="' . $fred->getOption('webAssetsUrl') . 'fred.min.js"></script>
  170. <link rel="stylesheet" href="' . $fred->getOption('webAssetsUrl') . 'fred.css" type="text/css" />
  171. ' . $includes . '
  172. <script>
  173. var fred = new Fred({
  174. theme: ' . $theme->id . ',
  175. assetsUrl: "' . $fred->getOption('webAssetsUrl') . '",
  176. managerUrl: "' . MODX_MANAGER_URL . '",
  177. fredOffUrl: "' . str_replace('&amp;', '&', $modx->makeUrl($modx->resource->id, '', array_merge($get, ['fred' => 4]) , 'abs')) . '",
  178. contextKey: "' . $modx->resource->context_key. '",
  179. launcherPosition: "' . $fred->getOption('launcher_position') . '",
  180. iconEditor: "' . $fred->getOption('icon_editor') . '",
  181. imageEditor: "' . $fred->getOption('image_editor') . '",
  182. rte: "' . $fred->getOption('rte') . '",
  183. rteConfig: {' . $rteConfigString . '},
  184. jwt: "' . $jwt . '",
  185. resource: {
  186. "id": ' . $modx->resource->id . ',
  187. "parent": ' . $modx->resource->parent . ',
  188. "previewUrl": "' . str_replace('&amp;', '&', $modx->makeUrl($modx->resource->id, '', array_merge($get, ['fred' => 2]) , 'abs')) . '",
  189. "emptyUrl": "' . str_replace('&amp;', '&', $modx->makeUrl($modx->resource->id, '', array_merge($get, ['fred' => 3]) , 'abs')) . '",
  190. "save": ' . (int)$modx->resource->checkPolicy('save') . ',
  191. "delete": ' . (int)$modx->resource->checkPolicy('delete') . ',
  192. "undelete": ' . (int)$modx->resource->checkPolicy('undelete') . ',
  193. "publish": ' . (int)$modx->resource->checkPolicy('publish') . ',
  194. "unpublish": ' . (int)$modx->resource->checkPolicy('unpublish') . ',
  195. },
  196. permission: {
  197. "fred_settings": ' . (int)$modx->hasPermission('fred_settings') . ',
  198. "fred_settings_advanced": ' . (int)$modx->hasPermission('fred_settings_advanced') . ',
  199. "fred_settings_tags": ' . (int)$modx->hasPermission('fred_settings_tags') . ',
  200. "fred_settings_tvs": ' . (int)$modx->hasPermission('fred_settings_tvs') . ',
  201. "fred_elements": ' . (int)$modx->hasPermission('fred_elements') . ',
  202. "fred_blueprints": ' . (int)$modx->hasPermission('fred_blueprints') . ',
  203. "fred_element_screenshot": ' . (int)$modx->hasPermission('fred_element_screenshot') . ',
  204. "fred_element_move": ' . (int)$modx->hasPermission('fred_element_move') . ',
  205. "fred_element_delete": ' . (int)$modx->hasPermission('fred_element_delete') . ',
  206. "fred_blueprint_categories_save": ' . (int)$modx->hasPermission('fred_blueprint_categories_save') . ',
  207. "fred_blueprint_categories_create_public": ' . (int)$modx->hasPermission('fred_blueprint_categories_create_public') . ',
  208. "fred_blueprints_save": ' . (int)$modx->hasPermission('fred_blueprints_save') . ',
  209. "fred_blueprints_create_public": ' . (int)$modx->hasPermission('fred_blueprints_create_public') . ',
  210. "save_document": ' . (int)$modx->hasPermission('save_document') . ',
  211. "delete_document" : ' . (int)$modx->hasPermission('delete_document') . ',
  212. "undelete_document" : ' . (int)$modx->hasPermission('undelete_document') . ',
  213. "publish_document" : ' . (int)$modx->hasPermission('publish_document') . ',
  214. "unpublish_document" : ' . (int)$modx->hasPermission('unpublish_document') . ',
  215. "new_document" : ' . (int)$modx->hasPermission('new_document') . ',
  216. "resource_duplicate" : ' . (int)$modx->hasPermission('resource_duplicate') . ',
  217. "new_document_in_root" : ' . (int)$modx->hasPermission('new_document_in_root') . '
  218. },
  219. lexicons: ' . json_encode($lexicons) . ',
  220. beforeRender: function() {
  221. ' . $beforeRender . '
  222. }
  223. });
  224. </script>';
  225. $modx->resource->_output = preg_replace('/(<\/head>(?:<\/head>)?)/i', "{$fredContent}\r\n$1", $modx->resource->_output);
  226. }
  227. break;
  228. case 'OnBeforeDocFormSave':
  229. if ($mode !== 'upd') return;
  230. if (empty($fred->getTheme($resource->template))) return;
  231. $data = $resource->getProperty('data', 'fred');
  232. if (!empty($data['fingerprint'])) {
  233. if (empty($resource->fingerprint)) {
  234. $modx->event->_output = 'No fingerprint was provided.';
  235. return;
  236. }
  237. if ($data['fingerprint'] !== $resource->fingerprint) {
  238. $modx->event->_output = 'Your page is outdated, please reload the page.';
  239. return;
  240. }
  241. }
  242. break;
  243. case 'OnDocFormSave':
  244. if ($mode !== 'upd') return;
  245. if (empty($fred->getTheme($resource->template))) return;
  246. $renderResource = new \Fred\RenderResource($resource, $modx);
  247. $renderResource->render();
  248. break;
  249. case 'OnTemplateRemove':
  250. /** @var modTemplate $template */
  251. $templateId = $template->id;
  252. if (!empty($templateId)) {
  253. /** @var FredThemedTemplate $themedTemplate */
  254. $themedTemplate = $modx->getObject('FredThemedTemplate', ['template' => $templateId]);
  255. if ($themedTemplate) {
  256. $themedTemplate->remove();
  257. }
  258. }
  259. break;
  260. case 'OnTVInputRenderList':
  261. $modx->event->output($corePath . 'elements/tvs/input/');
  262. break;
  263. case 'OnTVInputPropertiesList':
  264. $modx->event->output($corePath . 'elements/tvs/input/options/');
  265. break;
  266. case 'OnManagerPageBeforeRender':
  267. $modx->controller->addLexiconTopic('fred:default');
  268. break;
  269. }
  270. return;