@silexlabs/silex 3.0.0-alpha.97 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. package/.env.default +2 -1
  2. package/README.md +31 -9
  3. package/dist/client/client/api.d.ts +5 -0
  4. package/dist/client/client/assetUrl.d.ts +3 -0
  5. package/dist/client/client/config.d.ts +31 -0
  6. package/dist/client/client/events.d.ts +7 -1
  7. package/dist/client/client/grapesjs/PublicationManager.d.ts +2 -1
  8. package/dist/client/client/grapesjs/css-props.d.ts +6 -0
  9. package/dist/client/client/grapesjs/img.d.ts +6 -0
  10. package/dist/client/client/grapesjs/index.d.ts +2 -2
  11. package/dist/client/client/grapesjs/new-page-dialog.d.ts +1 -1
  12. package/dist/client/client/grapesjs/page-panel.d.ts +1 -1
  13. package/dist/client/client/grapesjs/project-bar.d.ts +16 -0
  14. package/dist/client/client/grapesjs/resize-panel.d.ts +1 -0
  15. package/dist/client/client/grapesjs/rich-text.d.ts +4 -1
  16. package/dist/client/client/grapesjs/semantic.d.ts +1 -1
  17. package/dist/client/client/grapesjs/settings-sections.d.ts +15 -0
  18. package/dist/client/client/grapesjs/settings.d.ts +3 -1
  19. package/dist/client/client/grapesjs/symbolDialogs.d.ts +1 -1
  20. package/dist/client/client/grapesjs/view-buttons.d.ts +1 -0
  21. package/dist/client/client/index.d.ts +10 -0
  22. package/dist/client/client/publication-transformers.d.ts +4 -3
  23. package/dist/client/client/publish-custom-code-block.d.ts +4 -0
  24. package/dist/client/client/publish-fonts.d.ts +4 -0
  25. package/dist/client/client/utils.d.ts +4 -0
  26. package/dist/client/constants.d.ts +1 -0
  27. package/dist/client/css/admin.css +273 -32
  28. package/dist/client/index.html +2 -1
  29. package/dist/client/js/main.js +164 -123
  30. package/dist/client/js/main.js.LICENSE.txt +15 -1
  31. package/dist/client/types.d.ts +12 -7
  32. package/dist/plugins/client/client/events.d.ts +7 -1
  33. package/dist/plugins/client/client/events.js +7 -1
  34. package/dist/plugins/client/client/events.js.map +1 -1
  35. package/dist/plugins/client/plugins/client/template.js +108 -58
  36. package/dist/plugins/client/plugins/client/template.js.map +1 -1
  37. package/dist/plugins/client/types.d.ts +12 -7
  38. package/dist/plugins/client/types.js +6 -0
  39. package/dist/plugins/client/types.js.map +1 -1
  40. package/dist/plugins/server/constants.d.ts +1 -0
  41. package/dist/plugins/server/constants.js +2 -1
  42. package/dist/plugins/server/constants.js.map +1 -1
  43. package/dist/plugins/server/plugins/server/FtpConnector.d.ts +1 -0
  44. package/dist/plugins/server/plugins/server/FtpConnector.js +12 -1
  45. package/dist/plugins/server/plugins/server/FtpConnector.js.map +1 -1
  46. package/dist/plugins/server/plugins/server/GitlabConnector.d.ts +3 -0
  47. package/dist/plugins/server/plugins/server/GitlabConnector.js +72 -35
  48. package/dist/plugins/server/plugins/server/GitlabConnector.js.map +1 -1
  49. package/dist/plugins/server/server/config.js +3 -3
  50. package/dist/plugins/server/server/config.js.map +1 -1
  51. package/dist/plugins/server/server/connectors/FsHosting.d.ts +1 -0
  52. package/dist/plugins/server/server/connectors/FsHosting.js +13 -1
  53. package/dist/plugins/server/server/connectors/FsHosting.js.map +1 -1
  54. package/dist/plugins/server/server/connectors/FsStorage.d.ts +2 -1
  55. package/dist/plugins/server/server/connectors/FsStorage.js +29 -25
  56. package/dist/plugins/server/server/connectors/FsStorage.js.map +1 -1
  57. package/dist/plugins/server/server/connectors/connectors.d.ts +1 -0
  58. package/dist/plugins/server/server/jobs.js +4 -2
  59. package/dist/plugins/server/server/jobs.js.map +1 -1
  60. package/dist/plugins/server/types.d.ts +12 -7
  61. package/dist/plugins/server/types.js +7 -1
  62. package/dist/plugins/server/types.js.map +1 -1
  63. package/dist/server/constants.js +2 -1
  64. package/dist/server/server/api/publicationApi.js +3 -5
  65. package/dist/server/server/api/websiteApi.js +22 -3
  66. package/dist/server/server/cli.js +9 -6
  67. package/dist/server/server/config.js +3 -3
  68. package/dist/server/server/connectors/FsHosting.js +13 -1
  69. package/dist/server/server/connectors/FsStorage.js +28 -25
  70. package/dist/server/server/jobs.js +4 -2
  71. package/dist/server/types.js +7 -1
  72. package/package.json +34 -30
  73. package/public/assets/favicon.ico +0 -0
  74. package/public/assets/favicon.png +0 -0
  75. package/public/assets/logo-silex-small.png +0 -0
  76. package/src/css/_variables.scss +5 -3
  77. package/src/css/dialog.scss +22 -1
  78. package/src/css/fonts.scss +31 -0
  79. package/src/css/footer.scss +1 -1
  80. package/src/css/grapesjs-plugins.scss +34 -0
  81. package/src/css/grapesjs.scss +26 -11
  82. package/src/css/index.scss +1 -0
  83. package/src/css/project-bar.scss +11 -7
  84. package/src/css/silex.scss +58 -5
  85. package/src/html/index.pug +2 -2
  86. package/src/ts/client/api.ts +14 -9
  87. package/src/ts/client/assetUrl.test.ts +39 -0
  88. package/src/ts/client/assetUrl.ts +24 -5
  89. package/src/ts/client/config.ts +85 -4
  90. package/src/ts/client/events.ts +15 -9
  91. package/src/ts/client/grapesjs/PublicationManager.ts +71 -38
  92. package/src/ts/client/grapesjs/PublicationUi.ts +13 -11
  93. package/src/ts/client/grapesjs/css-props.ts +544 -0
  94. package/src/ts/client/grapesjs/eleventy.ts +2 -5
  95. package/src/ts/client/grapesjs/footer.ts +24 -9
  96. package/src/ts/client/grapesjs/img.ts +147 -0
  97. package/src/ts/client/grapesjs/index.ts +63 -25
  98. package/src/ts/client/grapesjs/internal-links.test.ts +2 -1
  99. package/src/ts/client/grapesjs/internal-links.ts +0 -1
  100. package/src/ts/client/grapesjs/new-page-dialog.ts +2 -3
  101. package/src/ts/client/grapesjs/page-panel.ts +28 -3
  102. package/src/ts/client/grapesjs/project-bar.ts +83 -64
  103. package/src/ts/client/grapesjs/resize-panel.ts +20 -0
  104. package/src/ts/client/grapesjs/rich-text.ts +21 -13
  105. package/src/ts/client/grapesjs/semantic.ts +3 -3
  106. package/src/ts/client/grapesjs/settings-sections.ts +117 -0
  107. package/src/ts/client/grapesjs/settings.ts +120 -77
  108. package/src/ts/client/grapesjs/symbolDialogs.ts +2 -3
  109. package/src/ts/client/grapesjs/view-buttons.ts +42 -0
  110. package/src/ts/client/index.ts +13 -6
  111. package/src/ts/client/publication-transformers.test.ts +13 -5
  112. package/src/ts/client/publication-transformers.ts +60 -47
  113. package/{dist/plugins/client/client/utils.js → src/ts/client/publish-custom-code-block.ts} +14 -8
  114. package/{dist/plugins/client/page.js → src/ts/client/publish-fonts.ts} +19 -14
  115. package/src/ts/client/utils.ts +18 -0
  116. package/src/ts/constants.ts +1 -0
  117. package/src/ts/plugins/client/template.ts +112 -60
  118. package/src/ts/plugins/server/FtpConnector.ts +14 -2
  119. package/src/ts/plugins/server/GitlabConnector.ts +71 -32
  120. package/src/ts/server/api/publicationApi.ts +5 -5
  121. package/src/ts/server/api/websiteApi.ts +39 -9
  122. package/src/ts/server/cli.ts +7 -5
  123. package/src/ts/server/config.ts +4 -4
  124. package/src/ts/server/connectors/FsHosting.ts +11 -5
  125. package/src/ts/server/connectors/FsStorage.ts +33 -4
  126. package/src/ts/server/connectors/connectors.ts +1 -1
  127. package/src/ts/server/jobs.ts +4 -2
  128. package/src/ts/types.ts +10 -7
  129. package/dist/plugins/client/client/api.d.ts +0 -85
  130. package/dist/plugins/client/client/api.js +0 -114
  131. package/dist/plugins/client/client/api.js.map +0 -1
  132. package/dist/plugins/client/client/assetUrl.d.ts +0 -51
  133. package/dist/plugins/client/client/assetUrl.js +0 -224
  134. package/dist/plugins/client/client/assetUrl.js.map +0 -1
  135. package/dist/plugins/client/client/config.d.ts +0 -58
  136. package/dist/plugins/client/client/config.js +0 -90
  137. package/dist/plugins/client/client/config.js.map +0 -1
  138. package/dist/plugins/client/client/grapesjs/LoginDialog.d.ts +0 -15
  139. package/dist/plugins/client/client/grapesjs/LoginDialog.js +0 -195
  140. package/dist/plugins/client/client/grapesjs/LoginDialog.js.map +0 -1
  141. package/dist/plugins/client/client/grapesjs/PublicationManager.d.ts +0 -72
  142. package/dist/plugins/client/client/grapesjs/PublicationManager.js +0 -356
  143. package/dist/plugins/client/client/grapesjs/PublicationManager.js.map +0 -1
  144. package/dist/plugins/client/client/grapesjs/PublicationUi.d.ts +0 -62
  145. package/dist/plugins/client/client/grapesjs/PublicationUi.js +0 -336
  146. package/dist/plugins/client/client/grapesjs/PublicationUi.js.map +0 -1
  147. package/dist/plugins/client/client/grapesjs/blocks.d.ts +0 -1
  148. package/dist/plugins/client/client/grapesjs/blocks.js +0 -37
  149. package/dist/plugins/client/client/grapesjs/blocks.js.map +0 -1
  150. package/dist/plugins/client/client/grapesjs/breadcrumbs.d.ts +0 -1
  151. package/dist/plugins/client/client/grapesjs/breadcrumbs.js +0 -75
  152. package/dist/plugins/client/client/grapesjs/breadcrumbs.js.map +0 -1
  153. package/dist/plugins/client/client/grapesjs/footer.d.ts +0 -2
  154. package/dist/plugins/client/client/grapesjs/footer.js +0 -28
  155. package/dist/plugins/client/client/grapesjs/footer.js.map +0 -1
  156. package/dist/plugins/client/client/grapesjs/index.d.ts +0 -5
  157. package/dist/plugins/client/client/grapesjs/index.js +0 -313
  158. package/dist/plugins/client/client/grapesjs/index.js.map +0 -1
  159. package/dist/plugins/client/client/grapesjs/internal-links.d.ts +0 -1
  160. package/dist/plugins/client/client/grapesjs/internal-links.js +0 -219
  161. package/dist/plugins/client/client/grapesjs/internal-links.js.map +0 -1
  162. package/dist/plugins/client/client/grapesjs/new-page-dialog.d.ts +0 -2
  163. package/dist/plugins/client/client/grapesjs/new-page-dialog.js +0 -83
  164. package/dist/plugins/client/client/grapesjs/new-page-dialog.js.map +0 -1
  165. package/dist/plugins/client/client/grapesjs/openImport.d.ts +0 -9
  166. package/dist/plugins/client/client/grapesjs/openImport.js +0 -74
  167. package/dist/plugins/client/client/grapesjs/openImport.js.map +0 -1
  168. package/dist/plugins/client/client/grapesjs/page-panel.d.ts +0 -3
  169. package/dist/plugins/client/client/grapesjs/page-panel.js +0 -113
  170. package/dist/plugins/client/client/grapesjs/page-panel.js.map +0 -1
  171. package/dist/plugins/client/client/grapesjs/project-bar.d.ts +0 -1
  172. package/dist/plugins/client/client/grapesjs/project-bar.js +0 -104
  173. package/dist/plugins/client/client/grapesjs/project-bar.js.map +0 -1
  174. package/dist/plugins/client/client/grapesjs/rich-text.d.ts +0 -1
  175. package/dist/plugins/client/client/grapesjs/rich-text.js +0 -31
  176. package/dist/plugins/client/client/grapesjs/rich-text.js.map +0 -1
  177. package/dist/plugins/client/client/grapesjs/semantic.d.ts +0 -1
  178. package/dist/plugins/client/client/grapesjs/semantic.js +0 -110
  179. package/dist/plugins/client/client/grapesjs/semantic.js.map +0 -1
  180. package/dist/plugins/client/client/grapesjs/settings.d.ts +0 -2
  181. package/dist/plugins/client/client/grapesjs/settings.js +0 -198
  182. package/dist/plugins/client/client/grapesjs/settings.js.map +0 -1
  183. package/dist/plugins/client/client/grapesjs/storage.d.ts +0 -1
  184. package/dist/plugins/client/client/grapesjs/storage.js +0 -126
  185. package/dist/plugins/client/client/grapesjs/storage.js.map +0 -1
  186. package/dist/plugins/client/client/grapesjs/symbolDialogs.d.ts +0 -3
  187. package/dist/plugins/client/client/grapesjs/symbolDialogs.js +0 -36
  188. package/dist/plugins/client/client/grapesjs/symbolDialogs.js.map +0 -1
  189. package/dist/plugins/client/client/publication-transformers.d.ts +0 -41
  190. package/dist/plugins/client/client/publication-transformers.js +0 -238
  191. package/dist/plugins/client/client/publication-transformers.js.map +0 -1
  192. package/dist/plugins/client/client/utils.d.ts +0 -2
  193. package/dist/plugins/client/client/utils.js.map +0 -1
  194. package/dist/plugins/client/constants.d.ts +0 -29
  195. package/dist/plugins/client/constants.js +0 -46
  196. package/dist/plugins/client/constants.js.map +0 -1
  197. package/dist/plugins/client/page.d.ts +0 -2
  198. package/dist/plugins/client/page.js.map +0 -1
  199. package/dist/plugins/client/plugins/client/11ty.d.ts +0 -17
  200. package/dist/plugins/client/plugins/client/11ty.js +0 -86
  201. package/dist/plugins/client/plugins/client/11ty.js.map +0 -1
  202. package/dist/plugins/client/plugins/client/publicationRenderer.d.ts +0 -2
  203. package/dist/plugins/client/plugins/client/publicationRenderer.js +0 -170
  204. package/dist/plugins/client/plugins/client/publicationRenderer.js.map +0 -1
  205. package/src/ts/plugins/client/11ty.ts +0 -104
  206. package/src/ts/plugins/client/publicationRenderer.ts +0 -174
@@ -16,7 +16,7 @@
16
16
  */
17
17
 
18
18
  import { Component, CssRule, Editor, ObjectStrings, Page } from 'grapesjs'
19
- import { ClientSideFile, ClientSideFileType, ClientSideFileWithPermalink, PublicationData } from '../types'
19
+ import { ClientSideFile, ClientSideFileType, ClientSideFileWithPermalink, Initiator, PublicationData } from '../types'
20
20
  import { onAll } from './utils'
21
21
 
22
22
  /**
@@ -47,11 +47,42 @@ export interface PublicationTransformer {
47
47
  // Transform files after they are rendered and before they are published
48
48
  transformFile?(file: ClientSideFile): ClientSideFile
49
49
  // Define files URLs
50
- transformPermalink?(link: string, type: ClientSideFileType): string
50
+ transformPermalink?(link: string, type: ClientSideFileType, initiator: Initiator): string
51
51
  // Define where files are published
52
52
  transformPath?(path: string, type: ClientSideFileType): string
53
53
  }
54
54
 
55
+ export const publicationTransformerDefault: PublicationTransformer = {
56
+ // Override how components render at publication by grapesjs
57
+ renderComponent(component: Component, toHtml: () => string): string | undefined {
58
+ return toHtml()
59
+ },
60
+ // Override how styles render at publication by grapesjs
61
+ renderCssRule(rule: CssRule, initialRule: () => ObjectStrings): ObjectStrings | undefined {
62
+ return initialRule()
63
+ },
64
+ // Define where files are published
65
+ transformPath(path: string, type: ClientSideFileType): string {
66
+ return path
67
+ },
68
+ // Define files URLs
69
+ transformPermalink(link: string, type: ClientSideFileType, initiator: Initiator): string {
70
+ switch(initiator) {
71
+ case Initiator.HTML:
72
+ return link
73
+ case Initiator.CSS:
74
+ // In case of a link from a CSS file, we need to go up one level
75
+ return `../${link.replace(/^\//, '')}`
76
+ default:
77
+ throw new Error(`Unknown initiator ${initiator}`)
78
+ }
79
+ },
80
+ // Define how files are named
81
+ transformFile(file: ClientSideFile): ClientSideFile {
82
+ return file
83
+ }
84
+ }
85
+
55
86
  export function validatePublicationTransformer(transformer: PublicationTransformer): void {
56
87
  // List all the properties
57
88
  const allowedProperties = [
@@ -77,7 +108,6 @@ export function validatePublicationTransformer(transformer: PublicationTransform
77
108
  })
78
109
  }
79
110
 
80
-
81
111
  /**
82
112
  * Alter the components rendering
83
113
  * Exported for unit tests
@@ -97,7 +127,7 @@ export function renderComponents(editor: Editor) {
97
127
  c[ATTRIBUTE_METHOD_STORE_HREF] = href
98
128
  c.set('attributes', {
99
129
  ...c.get('attributes'),
100
- href: transformPermalink(editor, href, ClientSideFileType.HTML),
130
+ href: transformPermalink(editor, href, ClientSideFileType.HTML, Initiator.HTML),
101
131
  })
102
132
  }
103
133
  // Handle both c.attributes.src and c.attributes.attributes.src
@@ -105,7 +135,7 @@ export function renderComponents(editor: Editor) {
105
135
  // Especially when the component is not on the current page, we need c.attributes.attributes.src
106
136
  if(c.get('attributes').src) {
107
137
  c[ATTRIBUTE_METHOD_STORE_SRC] = c.get('attributes').src
108
- const src = transformPermalink(editor, c.get('attributes').src, ClientSideFileType.ASSET)
138
+ const src = transformPermalink(editor, c.get('attributes').src, ClientSideFileType.ASSET, Initiator.HTML)
109
139
  c.set('attributes', {
110
140
  ...c.get('attributes'),
111
141
  src,
@@ -113,17 +143,12 @@ export function renderComponents(editor: Editor) {
113
143
  }
114
144
  if(c.get('src')) {
115
145
  c[ATTRIBUTE_METHOD_STORE_ATTRIBUTES_SRC] = c.get('src')
116
- const src = transformPermalink(editor, c.get('src'), ClientSideFileType.ASSET)
146
+ const src = transformPermalink(editor, c.get('src'), ClientSideFileType.ASSET, Initiator.HTML)
117
147
  c.set('src', src)
118
148
  }
119
149
  c.toHTML = () => {
120
150
  return config.publicationTransformers.reduce((html: string, transformer: PublicationTransformer) => {
121
- try {
122
- return transformer.renderComponent ? transformer.renderComponent(c, () => html) ?? html : html
123
- } catch (e) {
124
- console.error('Publication transformer: error rendering component', c, e)
125
- return html
126
- }
151
+ return transformer.renderComponent ? transformer.renderComponent(c, () => html) ?? html : html
127
152
  }, initialToHTML())
128
153
  }
129
154
  c.getStyle = () => transformBgImage(editor, initialGetStyle())
@@ -144,17 +169,13 @@ export function renderCssRules(editor: Editor) {
144
169
  const initialGetStyle = style.getStyle.bind(style)
145
170
  style[ATTRIBUTE_METHOD_STORE_CSS] = style.getStyle
146
171
  style.getStyle = () => {
147
- try {
148
- const result = config.publicationTransformers.reduce((s: CssRule, transformer: PublicationTransformer) => {
149
- return {
150
- ...transformer.renderCssRule ? transformer.renderCssRule(s, initialGetStyle) ?? s : s,
151
- }
152
- }, transformBgImage(editor, initialGetStyle()))
153
- return result
154
- } catch (e) {
155
- console.error('Publication transformer: error rendering style', style, e)
156
- return initialGetStyle()
157
- }
172
+ const initialStyle = transformBgImage(editor, initialGetStyle())
173
+ const result = config.publicationTransformers.reduce((s: CssRule, transformer: PublicationTransformer) => {
174
+ return {
175
+ ...transformer.renderCssRule ? transformer.renderCssRule(s, () => initialStyle) ?? s : s,
176
+ }
177
+ }, initialStyle)
178
+ return result
158
179
  }
159
180
  }
160
181
  })
@@ -164,12 +185,19 @@ export function renderCssRules(editor: Editor) {
164
185
  * Transform background image url according to the transformed path of assets
165
186
  */
166
187
  export function transformBgImage(editor: Editor, style: ObjectStrings): ObjectStrings {
167
- const url = style['background-image']
168
- const bgUrl = url?.match(/url\(["']?(.*?)["']?\)/)?.pop()
169
- if (bgUrl) {
188
+ const cssValue = style['background-image']
189
+ if (cssValue) {
190
+ const newCssValue = cssValue.replace(/url\(([^)]+)\)/g, (match, url) => {
191
+ // Support URLs with or without quotes
192
+ const cleanUrl = url.replace(/['"]/g, '')
193
+ // Transform URLs
194
+ const newUrl = transformPermalink(editor, cleanUrl, ClientSideFileType.ASSET, Initiator.CSS)
195
+ // Return the new URL with url keyword
196
+ return `url(${newUrl})`
197
+ })
170
198
  return {
171
199
  ...style,
172
- 'background-image': `url(${transformPermalink(editor, bgUrl, ClientSideFileType.ASSET)})`,
200
+ 'background-image': newCssValue,
173
201
  }
174
202
  }
175
203
  return style
@@ -183,13 +211,8 @@ export function transformFiles(editor: Editor, data: PublicationData) {
183
211
  const config = editor.getModel().get('config')
184
212
  data.files = config.publicationTransformers.reduce((files: ClientSideFile[], transformer: PublicationTransformer) => {
185
213
  return files.map((file, idx) => {
186
- try {
187
- const page = data.pages[idx] ?? null
188
- return transformer.transformFile ? transformer.transformFile(file) as ClientSideFile ?? file : file
189
- } catch (e) {
190
- console.error('Publication transformer: error transforming file', file, e)
191
- return file
192
- }
214
+ const page = data.pages[idx] ?? null
215
+ return transformer.transformFile ? transformer.transformFile(file) as ClientSideFile ?? file : file
193
216
  })
194
217
  }, data.files)
195
218
  }
@@ -198,27 +221,17 @@ export function transformFiles(editor: Editor, data: PublicationData) {
198
221
  * Transform files paths
199
222
  * Exported for unit tests
200
223
  */
201
- export function transformPermalink(editor: Editor, path: string, type: ClientSideFileType): string {
224
+ export function transformPermalink(editor: Editor, path: string, type: ClientSideFileType, initiator: Initiator): string {
202
225
  const config = editor.getModel().get('config')
203
226
  return config.publicationTransformers.reduce((result: string, transformer: PublicationTransformer) => {
204
- try {
205
- return transformer.transformPermalink ? transformer.transformPermalink(result, type) ?? result : result
206
- } catch (e) {
207
- console.error('Publication transformer: error transforming path', result, e)
208
- return result
209
- }
227
+ return transformer.transformPermalink ? transformer.transformPermalink(result, type, initiator) ?? result : result
210
228
  }, path)
211
229
  }
212
230
 
213
231
  export function transformPath(editor: Editor, path: string, type: ClientSideFileType): string {
214
232
  const config = editor.getModel().get('config')
215
233
  return config.publicationTransformers.reduce((result: string, transformer: PublicationTransformer) => {
216
- try {
217
- return transformer.transformPath ? transformer.transformPath(result, type) ?? result : result
218
- } catch (e) {
219
- console.error('Publication transformer: error transforming path', result, e)
220
- return result
221
- }
234
+ return transformer.transformPath ? transformer.transformPath(result, type) ?? result : result
222
235
  }, path)
223
236
  }
224
237
 
@@ -14,12 +14,18 @@
14
14
  * You should have received a copy of the GNU Affero General Public License
15
15
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
16
16
  */
17
- // Browse all elements of all pages
18
- export function onAll(editor, cbk) {
19
- editor.Pages.getAll()
20
- .forEach(page => {
21
- page.getMainComponent()
22
- .onAll(c => cbk(c));
23
- });
17
+
18
+ /**
19
+ * @fileoverview Make sure custom script blocks include script tags when published
20
+ */
21
+
22
+ export default async function (config) {
23
+ config.addPublicationTransformers({
24
+ renderComponent(component, toHtml) {
25
+ if (component.get('type') === 'custom-code' && component.get('custom-code-plugin__code')) {
26
+ return component.get('custom-code-plugin__code')
27
+ }
28
+ return toHtml()
29
+ },
30
+ })
24
31
  }
25
- //# sourceMappingURL=utils.js.map
@@ -14,18 +14,23 @@
14
14
  * You should have received a copy of the GNU Affero General Public License
15
15
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
16
16
  */
17
- // Page related functions
18
- // This is used on the client and the server
19
- export function getPageSlug(pageName) {
20
- return (pageName || 'index')
21
- .toLowerCase()
22
- .replace(/[^a-z0-9 -]/g, '')
23
- // Collapse whitespace and replace by -
24
- .replace(/\s+/g, '-')
25
- // Collapse dashes
26
- .replace(/-+/g, '-');
27
- }
28
- export function getPageLink(pageName) {
29
- return `./${getPageSlug(pageName)}.html`;
17
+
18
+ import { ClientSideFile, ClientSideFileType, ClientSideFileWithContent } from '../types'
19
+ import { cmdGetHtml } from '@silexlabs/grapesjs-fonts'
20
+
21
+ /**
22
+ * @fileoverview Make sure custom script blocks include script tags when published
23
+ */
24
+
25
+ export default async function (config) {
26
+ config.addPublicationTransformers({
27
+ transformFile(file: ClientSideFile): ClientSideFile {
28
+ const fileWithContent = file as ClientSideFileWithContent
29
+ if(fileWithContent.type === ClientSideFileType.HTML && fileWithContent.content) {
30
+ const htmlLinks = config.getEditor().runCommand(cmdGetHtml)
31
+ fileWithContent.content = fileWithContent.content.replace(/<\/head>/, `${htmlLinks}</head>`)
32
+ }
33
+ return file
34
+ },
35
+ })
30
36
  }
31
- //# sourceMappingURL=page.js.map
@@ -26,3 +26,21 @@ export function onAll(editor: Editor, cbk: (c: Component) => void) {
26
26
  .onAll(c => cbk(c))
27
27
  })
28
28
  }
29
+
30
+ /**
31
+ * SHA256 hash a string
32
+ */
33
+ export async function hashString(str: string): Promise<string> {
34
+ // Convert the string to an ArrayBuffer
35
+ const encoder = new TextEncoder()
36
+ const data = encoder.encode(str)
37
+
38
+ // Hash the data with SHA-256
39
+ const hashBuffer = await crypto.subtle.digest('SHA-256', data)
40
+
41
+ // Convert the ArrayBuffer to hex string
42
+ const hashArray = Array.from(new Uint8Array(hashBuffer))
43
+ const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('')
44
+
45
+ return hashHex
46
+ }
@@ -45,6 +45,7 @@ export const API_WEBSITE_READ = '/'
45
45
  export const API_WEBSITE_WRITE = '/'
46
46
  export const API_WEBSITE_CREATE = '/'
47
47
  export const API_WEBSITE_DELETE = '/'
48
+ export const API_WEBSITE_DUPLICATE = '/duplicate'
48
49
  export const API_WEBSITE_LIST = '/'
49
50
  export const API_WEBSITE_ASSET_READ = '/assets'
50
51
  export const API_WEBSITE_ASSETS_WRITE = '/assets'
@@ -33,9 +33,43 @@ const pluginName = 'template'
33
33
  const templateType = 'templateType'
34
34
  const templateKey = 'template'
35
35
 
36
+ function createCodeEditor(editor, mode = 'htmlmixed', singleLine = false) {
37
+ const options = {
38
+ readOnly: false,
39
+ codeName: mode,
40
+ } as any
41
+ if(singleLine) {
42
+ options.lineWrapping = false
43
+ options.lineNumbers = false
44
+ options.indentWithTabs = false
45
+ } else {
46
+ options.lineNumbers = true
47
+ options.lineWrapping = true
48
+ }
49
+
50
+ return editor.CodeManager.createViewer(options)
51
+ }
52
+
36
53
  export default async (config, opts: any = {}) => {
37
54
  config.on(ClientEvent.GRAPESJS_END, () => {
38
55
  const editor = silex.getEditor()
56
+ // Get the necessary code editors
57
+ const editors = {
58
+ before: createCodeEditor(editor),
59
+ replace: createCodeEditor(editor),
60
+ after: createCodeEditor(editor),
61
+ attributes: createCodeEditor(editor, 'text', true),
62
+ classname: createCodeEditor(editor, 'text', true),
63
+ style: createCodeEditor(editor, 'css', true),
64
+ }
65
+ const is1line = {
66
+ before: false,
67
+ replace: false,
68
+ after: false,
69
+ attributes: true,
70
+ classname: true,
71
+ style: true,
72
+ }
39
73
  // Add the new trait to all component types
40
74
  editor.DomComponents.getTypes().map(type => {
41
75
  const originalType = editor.DomComponents.getType(type.id)
@@ -60,7 +94,7 @@ export default async (config, opts: any = {}) => {
60
94
  function doRender(el) {
61
95
  const template = editor.getSelected()?.get(templateKey) || {}
62
96
  const taStyle = opts.styles?.textarea ?? styleMap({
63
- backgroundColor: 'var(--darkerPrimaryColor)',
97
+ xxbackgroundColor: 'var(--darkerPrimaryColor)',
64
98
  })
65
99
  const sepStyle = opts.styles?.sep ?? styleMap({ height: '10px' })
66
100
  const labels = {
@@ -72,31 +106,61 @@ export default async (config, opts: any = {}) => {
72
106
  style: html`CSS styles`,
73
107
  }
74
108
  render(html`
109
+ <style>
110
+ .CodeMirror {
111
+ height: 100%;
112
+ }
113
+ </style>
75
114
  <div>
76
115
  <h3>Template</h3>
77
116
  <p>This will be inserted in the published version</p>
78
117
  </div>
79
- ${['classname', 'attributes', 'style'].map(id => html`
80
- <label>
118
+ ${[
119
+ 'classname',
120
+ 'attributes',
121
+ 'style',
122
+ 'before',
123
+ 'replace',
124
+ 'after',
125
+ ].map(id => html`
126
+ <label data-contain="${id}" class="template-wrapper-${id}">
81
127
  ${labels[id]}
82
- <input
83
- id="template-${id}"
84
- .value=${template[id] || ''}
85
- style=${taStyle}
86
- />
87
128
  </label>
88
129
  `)}
89
- ${['before', 'replace', 'after'].map(id => html`
90
- <label
91
- for="template-${id}"
92
- >${labels[id]}</label>
93
- <textarea
94
- id="template-${id}"
95
- style=${taStyle}
96
- .value=${template[id] || ''}
97
- ></textarea>
98
- `)}
99
130
  `, el)
131
+ el.querySelectorAll('[data-contain]').forEach(container => {
132
+ const id = container.getAttribute('data-contain')
133
+ const codeEditor = editors[id]
134
+ container.appendChild(codeEditor.getElement())
135
+ codeEditor.getElement().style.display = 'block'
136
+ codeEditor.getElement().style.height = is1line[id] ? '' : '200px'
137
+ codeEditor.getElement().style.width = '100%'
138
+ codeEditor.setContent(template[id] ?? '')
139
+ codeEditor.refresh()
140
+ })
141
+ // Make sure we apply the changes when the user presses any key
142
+ // The events should be triggered by the trait manager (call onEvent) but it doesn't work for "delete" key for example
143
+ el.onkeyup = () => {
144
+ applyChanges(editor.getSelected())
145
+ }
146
+ }
147
+
148
+ function applyChanges(component) {
149
+ const template = {
150
+ before: editors.before.getContent(),
151
+ replace: editors.replace.getContent(),
152
+ after: editors.after.getContent(),
153
+ attributes: editors.attributes.getContent(),
154
+ classname: editors.classname.getContent(),
155
+ style: editors.style.getContent(),
156
+ }
157
+
158
+ // Store the new template
159
+ if (Object.values(template).filter(val => !!val && !!cleanup(val)).length > 0) {
160
+ component.set(templateKey, template)
161
+ } else {
162
+ component.set(templateKey)
163
+ }
100
164
  }
101
165
 
102
166
  editor.TraitManager.addType(templateType, {
@@ -113,21 +177,7 @@ export default async (config, opts: any = {}) => {
113
177
  // Update the component based on UI changes
114
178
  // `elInput` is the result HTMLElement you get from `createInput`
115
179
  onEvent({ elInput, component, event }) {
116
- const template = {
117
- before: elInput.querySelector('#template-before').value,
118
- replace: elInput.querySelector('#template-replace').value,
119
- after: elInput.querySelector('#template-after').value,
120
- attributes: elInput.querySelector('#template-attributes').value,
121
- classname: elInput.querySelector('#template-classname').value,
122
- style: elInput.querySelector('#template-style').value,
123
- }
124
-
125
- // Store the new template
126
- if (Object.values(template).filter(val => !!val && !!cleanup(val)).length > 0) {
127
- component.set(templateKey, template)
128
- } else {
129
- component.set(templateKey)
130
- }
180
+ applyChanges(component)
131
181
  },
132
182
  // Update UI on the component change
133
183
  onUpdate({ elInput, component }) {
@@ -154,39 +204,41 @@ export default async (config, opts: any = {}) => {
154
204
  // put back together
155
205
  .join('\n')
156
206
  }
157
- editor.on(ClientEvent.PUBLISH_START, () => {
158
- // Insert templates
159
- onAll(editor, c => {
160
- const template = c.get(templateKey)
161
- const toHTML = c.toHTML
162
- const classes = c.getClasses()
163
- const before = cleanup(template?.before || '')
164
- const replace = cleanup(template?.replace || '')
165
- const after = cleanup(template?.after || '')
166
- const classname = cleanup(template?.classname || '')
167
- const style = cleanup(template?.style || '')
168
- const attributes = cleanup(template?.attributes || '')
169
- // Store the initial method
170
- if (!c.has('tmpHtml')) c.set('tmpHtml', toHTML)
171
- // Override the method
172
- c.toHTML = () => {
173
- return `${before
174
- }${c.get('tagName') ? `<${c.get('tagName')}
207
+ if (opts.publication !== false) {
208
+ editor.on(ClientEvent.PUBLISH_START, () => {
209
+ // Insert templates
210
+ onAll(editor, c => {
211
+ const template = c.get(templateKey)
212
+ const toHTML = c.toHTML
213
+ const classes = c.getClasses()
214
+ const before = cleanup(template?.before || '')
215
+ const replace = cleanup(template?.replace || '')
216
+ const after = cleanup(template?.after || '')
217
+ const classname = cleanup(template?.classname || '')
218
+ const style = cleanup(template?.style || '')
219
+ const attributes = cleanup(template?.attributes || '')
220
+ // Store the initial method
221
+ if (!c.has('tmpHtml')) c.set('tmpHtml', toHTML)
222
+ // Override the method
223
+ c.toHTML = () => {
224
+ return `${before
225
+ }${c.get('tagName') ? `<${c.get('tagName')}
175
226
  ${Object.entries(c.get('attributes') as object).map(([key, value]) => makeAttribute(key, value)).join(' ')}
176
227
  ${classes.length || classname ? `class="${classes.join(' ')} ${classname}"` : ''}
177
228
  ${attributes}
178
229
  ${style ? `style="${style}"` : ''}
179
230
  >` : ''}${replace || c.getInnerHTML()
180
- }${c.get('tagName') ? `</${c.get('tagName')}>` : ''}${after
181
- }`
182
- }
231
+ }${c.get('tagName') ? `</${c.get('tagName')}>` : ''}${after
232
+ }`
233
+ }
234
+ })
183
235
  })
184
- })
185
- editor.on(ClientEvent.PUBLISH_END, () => {
186
- onAll(editor, c => {
187
- // Restore the original method
188
- c.toHTML = c.get('tmpHtml')
236
+ editor.on(ClientEvent.PUBLISH_END, () => {
237
+ onAll(editor, c => {
238
+ // Restore the original method
239
+ c.toHTML = c.get('tmpHtml')
240
+ })
189
241
  })
190
- })
242
+ }
191
243
  })
192
244
  }
@@ -23,9 +23,10 @@ import { WEBSITE_DATA_FILE, WEBSITE_META_DATA_FILE } from '../../constants'
23
23
  import { ConnectorType, ConnectorUser, WebsiteMeta, FileMeta, JobData, JobStatus, WebsiteId, PublicationJobData, WebsiteMetaFileContent, defaultWebsiteData, WebsiteData, ConnectorOptions } from '../../types'
24
24
  import { ServerConfig } from '../../server/config'
25
25
  import { join } from 'path'
26
- import { type } from 'os'
26
+ import { tmpdir } from 'os'
27
27
  import { v4 as uuid } from 'uuid'
28
28
  import { JobManager } from '../../server/jobs'
29
+ import { mkdtemp } from 'fs/promises'
29
30
 
30
31
  /**
31
32
  * @fileoverview FTP connector for Silex
@@ -338,7 +339,6 @@ export default class FtpConnector implements StorageConnector<FtpSession> {
338
339
 
339
340
  async getLoginForm(session: FtpSession, redirectTo: string): Promise<string | null> {
340
341
  const { host, user, pass, port, secure, publicationPath, storageRootPath, websiteUrl } = this.sessionData(session)
341
- requiredParam(type, 'connector type')
342
342
  return `
343
343
  <style>
344
344
  ${formCss}
@@ -491,6 +491,18 @@ export default class FtpConnector implements StorageConnector<FtpSession> {
491
491
  this.closeClient(ftp)
492
492
  }
493
493
 
494
+ async duplicateWebsite(session: FtpSession, websiteId: string): Promise<void> {
495
+ const newWebsiteId = uuid()
496
+ const storageRootPath = this.rootPath(session)
497
+ const ftp = await this.getClient(this.sessionData(session))
498
+ const websitePath = join(storageRootPath, websiteId)
499
+ const newWebsitePath = join(storageRootPath, newWebsiteId)
500
+ const tempDir = await mkdtemp(tmpdir())
501
+ await ftp.downloadToDir(websitePath, tempDir)
502
+ await ftp.uploadFromDir(tempDir, newWebsitePath)
503
+ this.closeClient(ftp)
504
+ }
505
+
494
506
  async writeAssets(
495
507
  session: any,
496
508
  id: WebsiteId,