@silexlabs/silex 3.0.1-0 → 3.0.1

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 (250) hide show
  1. package/.env.default +3 -2
  2. package/.silex.js +39 -1
  3. package/README.md +37 -35
  4. package/dist/client/client/api.d.ts +8 -1
  5. package/dist/client/client/assetUrl.d.ts +54 -0
  6. package/dist/client/client/config.d.ts +60 -1
  7. package/dist/client/client/events.d.ts +19 -10
  8. package/dist/client/client/grapesjs/PublicationManager.d.ts +6 -8
  9. package/dist/client/client/grapesjs/PublicationUi.d.ts +8 -10
  10. package/dist/client/client/grapesjs/breadcrumbs.d.ts +1 -0
  11. package/dist/client/client/grapesjs/css-props.d.ts +6 -0
  12. package/dist/client/client/grapesjs/footer.d.ts +2 -0
  13. package/dist/client/client/grapesjs/img.d.ts +6 -0
  14. package/dist/client/client/grapesjs/index.d.ts +6 -2
  15. package/dist/client/client/grapesjs/internal-links.d.ts +1 -1
  16. package/dist/client/client/grapesjs/keymaps.d.ts +123 -0
  17. package/dist/client/client/grapesjs/new-page-dialog.d.ts +1 -1
  18. package/dist/client/client/grapesjs/page-panel.d.ts +7 -1
  19. package/dist/client/client/grapesjs/project-bar.d.ts +20 -0
  20. package/dist/client/client/grapesjs/resize-panel.d.ts +1 -0
  21. package/dist/client/client/grapesjs/rich-text.d.ts +4 -1
  22. package/dist/client/client/grapesjs/semantic.d.ts +1 -1
  23. package/dist/client/client/grapesjs/settings-sections.d.ts +16 -0
  24. package/dist/client/client/grapesjs/settings.d.ts +3 -1
  25. package/dist/client/client/grapesjs/storage.d.ts +1 -0
  26. package/dist/client/client/grapesjs/symbolDialogs.d.ts +1 -1
  27. package/dist/client/client/grapesjs/view-buttons.d.ts +1 -0
  28. package/dist/client/client/index.d.ts +5 -0
  29. package/dist/client/client/publication-transformers.d.ts +42 -0
  30. package/dist/client/client/publish-custom-code-block.d.ts +4 -0
  31. package/dist/client/client/publish-fonts.d.ts +4 -0
  32. package/dist/client/client/utils.d.ts +22 -1
  33. package/dist/client/constants.d.ts +3 -0
  34. package/dist/client/css/admin.css +1107 -411
  35. package/dist/client/css/admin.css.map +1 -0
  36. package/dist/client/grapesjs-filter-styles/dist/index.d.ts +4 -0
  37. package/dist/client/grapesjs-fonts/dist/index.d.ts +4 -0
  38. package/dist/client/grapesjs-fonts/src/commands.d.ts +3 -0
  39. package/dist/client/grapesjs-fonts/src/fonts.d.ts +4 -0
  40. package/dist/client/grapesjs-storage-rate-limit/dist/index.d.ts +4 -0
  41. package/dist/client/grapesjs-ui-suggest-classes/dist/index.d.ts +4 -0
  42. package/dist/client/index.html +3 -2
  43. package/dist/client/js/main.js +305 -170
  44. package/dist/client/js/main.js.LICENSE.txt +21 -1
  45. package/dist/client/page.d.ts +1 -1
  46. package/dist/client/silex-lib/src/ts/client/api.d.ts +90 -0
  47. package/dist/client/silex-lib/src/ts/client/assetUrl.d.ts +54 -0
  48. package/dist/client/silex-lib/src/ts/client/config.d.ts +102 -0
  49. package/dist/client/silex-lib/src/ts/client/events.d.ts +23 -0
  50. package/dist/client/silex-lib/src/ts/client/expose.d.ts +8 -0
  51. package/dist/client/silex-lib/src/ts/client/grapesjs/LoginDialog.d.ts +15 -0
  52. package/dist/client/silex-lib/src/ts/client/grapesjs/PublicationManager.d.ts +73 -0
  53. package/dist/client/silex-lib/src/ts/client/grapesjs/PublicationUi.d.ts +59 -0
  54. package/dist/client/silex-lib/src/ts/client/grapesjs/blocks.d.ts +1 -0
  55. package/dist/client/silex-lib/src/ts/client/grapesjs/breadcrumbs.d.ts +1 -0
  56. package/dist/client/silex-lib/src/ts/client/grapesjs/css-props.d.ts +6 -0
  57. package/dist/client/silex-lib/src/ts/client/grapesjs/footer.d.ts +2 -0
  58. package/dist/client/silex-lib/src/ts/client/grapesjs/img.d.ts +6 -0
  59. package/dist/client/silex-lib/src/ts/client/grapesjs/index.d.ts +9 -0
  60. package/dist/client/silex-lib/src/ts/client/grapesjs/internal-links.d.ts +1 -0
  61. package/dist/client/silex-lib/src/ts/client/grapesjs/keymaps.d.ts +123 -0
  62. package/dist/client/silex-lib/src/ts/client/grapesjs/new-page-dialog.d.ts +2 -0
  63. package/dist/client/silex-lib/src/ts/client/grapesjs/openImport.d.ts +9 -0
  64. package/dist/client/silex-lib/src/ts/client/grapesjs/page-panel.d.ts +9 -0
  65. package/dist/client/silex-lib/src/ts/client/grapesjs/project-bar.d.ts +21 -0
  66. package/dist/client/silex-lib/src/ts/client/grapesjs/resize-panel.d.ts +1 -0
  67. package/dist/client/silex-lib/src/ts/client/grapesjs/rich-text.d.ts +4 -0
  68. package/dist/client/silex-lib/src/ts/client/grapesjs/semantic.d.ts +1 -0
  69. package/dist/client/silex-lib/src/ts/client/grapesjs/settings-sections.d.ts +16 -0
  70. package/dist/client/silex-lib/src/ts/client/grapesjs/settings.d.ts +4 -0
  71. package/dist/client/silex-lib/src/ts/client/grapesjs/storage.d.ts +2 -0
  72. package/dist/client/silex-lib/src/ts/client/grapesjs/symbolDialogs.d.ts +3 -0
  73. package/dist/client/silex-lib/src/ts/client/grapesjs/view-buttons.d.ts +1 -0
  74. package/dist/client/silex-lib/src/ts/client/index.d.ts +10 -0
  75. package/dist/client/silex-lib/src/ts/client/publication-transformers.d.ts +42 -0
  76. package/dist/client/silex-lib/src/ts/client/publish-custom-code-block.d.ts +4 -0
  77. package/dist/client/silex-lib/src/ts/client/publish-fonts.d.ts +4 -0
  78. package/dist/client/silex-lib/src/ts/client/utils.d.ts +22 -0
  79. package/dist/client/silex-lib/src/ts/constants.d.ts +32 -0
  80. package/dist/client/silex-lib/src/ts/page.d.ts +2 -0
  81. package/dist/client/silex-lib/src/ts/types.d.ts +339 -0
  82. package/dist/client/types.d.ts +59 -35
  83. package/dist/plugins/client/client/events.d.ts +19 -10
  84. package/dist/plugins/client/client/events.js +11 -0
  85. package/dist/plugins/client/client/events.js.map +1 -1
  86. package/dist/plugins/client/plugins/client/template.js +119 -71
  87. package/dist/plugins/client/plugins/client/template.js.map +1 -1
  88. package/dist/plugins/client/plugins/client/website-info.d.ts +1 -0
  89. package/dist/plugins/client/plugins/client/website-info.js +72 -0
  90. package/dist/plugins/client/plugins/client/website-info.js.map +1 -0
  91. package/dist/plugins/client/types.d.ts +59 -35
  92. package/dist/plugins/client/types.js +23 -0
  93. package/dist/plugins/client/types.js.map +1 -1
  94. package/dist/plugins/server/constants.d.ts +3 -0
  95. package/dist/plugins/server/constants.js +28 -1
  96. package/dist/plugins/server/constants.js.map +1 -1
  97. package/dist/plugins/server/plugins/server/Cache.js +9 -5
  98. package/dist/plugins/server/plugins/server/Cache.js.map +1 -1
  99. package/dist/plugins/server/plugins/server/DownloadConnector.d.ts +29 -0
  100. package/dist/plugins/server/plugins/server/DownloadConnector.js +127 -0
  101. package/dist/plugins/server/plugins/server/DownloadConnector.js.map +1 -0
  102. package/dist/plugins/server/plugins/server/FtpConnector.d.ts +5 -2
  103. package/dist/plugins/server/plugins/server/FtpConnector.js +38 -23
  104. package/dist/plugins/server/plugins/server/FtpConnector.js.map +1 -1
  105. package/dist/plugins/server/plugins/server/GitlabConnector.d.ts +63 -9
  106. package/dist/plugins/server/plugins/server/GitlabConnector.js +367 -91
  107. package/dist/plugins/server/plugins/server/GitlabConnector.js.map +1 -1
  108. package/dist/plugins/server/plugins/server/GitlabHostingConnector.d.ts +16 -0
  109. package/dist/plugins/server/plugins/server/GitlabHostingConnector.js +209 -0
  110. package/dist/plugins/server/plugins/server/GitlabHostingConnector.js.map +1 -0
  111. package/dist/plugins/server/plugins/server/SslPlugin.js +6 -6
  112. package/dist/plugins/server/plugins/server/SslPlugin.js.map +1 -1
  113. package/dist/plugins/server/plugins/server/StaticPlugin.d.ts +8 -1
  114. package/dist/plugins/server/plugins/server/StaticPlugin.js +5 -31
  115. package/dist/plugins/server/plugins/server/StaticPlugin.js.map +1 -1
  116. package/dist/plugins/server/server/config.js +8 -9
  117. package/dist/plugins/server/server/config.js.map +1 -1
  118. package/dist/plugins/server/server/connectors/FsHosting.d.ts +3 -2
  119. package/dist/plugins/server/server/connectors/FsHosting.js +18 -7
  120. package/dist/plugins/server/server/connectors/FsHosting.js.map +1 -1
  121. package/dist/plugins/server/server/connectors/FsStorage.d.ts +3 -1
  122. package/dist/plugins/server/server/connectors/FsStorage.js +35 -28
  123. package/dist/plugins/server/server/connectors/FsStorage.js.map +1 -1
  124. package/dist/plugins/server/server/connectors/connectors.d.ts +7 -7
  125. package/dist/plugins/server/server/connectors/connectors.js +6 -7
  126. package/dist/plugins/server/server/connectors/connectors.js.map +1 -1
  127. package/dist/plugins/server/server/events.js +16 -0
  128. package/dist/plugins/server/server/events.js.map +1 -1
  129. package/dist/plugins/server/server/jobs.d.ts +6 -0
  130. package/dist/plugins/server/server/jobs.js +12 -8
  131. package/dist/plugins/server/server/jobs.js.map +1 -1
  132. package/dist/plugins/server/server/utils/validation.js +1 -2
  133. package/dist/plugins/server/server/utils/validation.js.map +1 -1
  134. package/dist/plugins/server/types.d.ts +59 -35
  135. package/dist/plugins/server/types.js +24 -1
  136. package/dist/plugins/server/types.js.map +1 -1
  137. package/dist/server/constants.js +27 -1
  138. package/dist/server/plugins/server/Cache.js +9 -5
  139. package/dist/server/server/api/api.js +1 -1
  140. package/dist/server/server/api/connectorApi.js +154 -25
  141. package/dist/server/server/api/publicationApi.js +17 -25
  142. package/dist/server/server/api/websiteApi.js +30 -8
  143. package/dist/server/server/cli.js +9 -6
  144. package/dist/server/server/config.js +8 -9
  145. package/dist/server/server/connectors/FsHosting.js +17 -7
  146. package/dist/server/server/connectors/FsStorage.js +34 -28
  147. package/dist/server/server/connectors/connectors.js +6 -7
  148. package/dist/server/server/express.js +4 -5
  149. package/dist/server/server/index.js +5 -1
  150. package/dist/server/server/jobs.js +11 -8
  151. package/dist/server/server/utils/validation.js +1 -2
  152. package/dist/server/types.js +13 -1
  153. package/package.json +56 -42
  154. package/public/assets/download.png +0 -0
  155. package/public/assets/favicon.ico +0 -0
  156. package/public/assets/favicon.png +0 -0
  157. package/public/assets/ftp.png +0 -0
  158. package/public/assets/gitlab.png +0 -0
  159. package/public/assets/gitlab2.png +0 -0
  160. package/public/assets/laptop.png +0 -0
  161. package/public/assets/logo-silex-small.png +0 -0
  162. package/public/silex.js +5 -23
  163. package/src/css/_variables.scss +23 -20
  164. package/src/css/dialog.scss +27 -1
  165. package/src/css/fonts.scss +31 -0
  166. package/src/css/footer.scss +12 -0
  167. package/src/css/grapesjs-light.scss +180 -0
  168. package/src/css/grapesjs-plugins.scss +227 -0
  169. package/src/css/grapesjs.scss +84 -10
  170. package/src/css/index.scss +12 -1
  171. package/src/css/light.scss +7 -0
  172. package/src/css/project-bar.scss +32 -16
  173. package/src/css/silex.scss +79 -5
  174. package/src/css/website-info.scss +14 -0
  175. package/src/html/index.pug +3 -3
  176. package/src/ts/client/api.ts +38 -14
  177. package/src/ts/client/assetUrl.test.ts +164 -0
  178. package/src/ts/client/assetUrl.ts +254 -0
  179. package/src/ts/client/config.ts +133 -5
  180. package/src/ts/client/events.ts +21 -9
  181. package/src/ts/client/grapesjs/LoginDialog.ts +4 -2
  182. package/src/ts/client/grapesjs/PublicationManager.ts +180 -54
  183. package/src/ts/client/grapesjs/PublicationUi.ts +122 -86
  184. package/src/ts/client/grapesjs/breadcrumbs.ts +79 -0
  185. package/src/ts/client/grapesjs/css-props.ts +910 -0
  186. package/src/ts/client/grapesjs/eleventy.ts +11 -8
  187. package/{dist/plugins/client/page.js → src/ts/client/grapesjs/footer.ts} +24 -13
  188. package/src/ts/client/grapesjs/img.ts +147 -0
  189. package/src/ts/client/grapesjs/index.ts +234 -52
  190. package/src/ts/client/grapesjs/internal-links.test.ts +2 -1
  191. package/src/ts/client/grapesjs/internal-links.ts +14 -7
  192. package/src/ts/client/grapesjs/keymaps.ts +207 -0
  193. package/src/ts/client/grapesjs/new-page-dialog.ts +2 -3
  194. package/src/ts/client/grapesjs/page-panel.ts +92 -7
  195. package/src/ts/client/grapesjs/project-bar.ts +116 -72
  196. package/src/ts/client/grapesjs/resize-panel.ts +20 -0
  197. package/src/ts/client/grapesjs/rich-text.ts +21 -13
  198. package/src/ts/client/grapesjs/semantic.ts +3 -3
  199. package/src/ts/client/grapesjs/settings-sections.ts +117 -0
  200. package/src/ts/client/grapesjs/settings.ts +174 -96
  201. package/src/ts/client/grapesjs/storage.ts +98 -33
  202. package/src/ts/client/grapesjs/symbolDialogs.ts +3 -4
  203. package/src/ts/{plugins/server/AuthPlugin.ts → client/grapesjs/view-buttons.ts} +23 -19
  204. package/src/ts/client/index.ts +35 -16
  205. package/src/ts/client/publication-transformers.test.ts +207 -0
  206. package/src/ts/client/publication-transformers.ts +280 -0
  207. package/{dist/plugins/client/client/utils.js → src/ts/client/publish-custom-code-block.ts} +14 -8
  208. package/src/ts/client/publish-fonts.ts +36 -0
  209. package/src/ts/client/utils.ts +53 -1
  210. package/src/ts/constants.ts +31 -0
  211. package/src/ts/page.ts +2 -2
  212. package/src/ts/plugins/client/template.ts +125 -75
  213. package/src/ts/plugins/client/website-info.ts +84 -0
  214. package/src/ts/plugins/server/Cache.ts +6 -2
  215. package/src/ts/plugins/server/DownloadConnector.ts +146 -0
  216. package/src/ts/plugins/server/FtpConnector.ts +47 -21
  217. package/src/ts/plugins/server/GitlabConnector.ts +423 -108
  218. package/src/ts/plugins/server/GitlabHostingConnector.ts +215 -0
  219. package/src/ts/plugins/server/SslPlugin.ts +5 -5
  220. package/src/ts/plugins/server/StaticPlugin.ts +3 -30
  221. package/src/ts/server/api/connectorApi.ts +165 -28
  222. package/src/ts/server/api/publicationApi.ts +17 -24
  223. package/src/ts/server/api/websiteApi.ts +57 -14
  224. package/src/ts/server/cli.ts +7 -5
  225. package/src/ts/server/config.ts +9 -10
  226. package/src/ts/server/connectors/FsHosting.ts +14 -8
  227. package/src/ts/server/connectors/FsStorage.ts +40 -7
  228. package/src/ts/server/connectors/connectors.ts +8 -6
  229. package/src/ts/server/events.ts +17 -0
  230. package/src/ts/server/express.ts +2 -2
  231. package/src/ts/server/index.ts +6 -0
  232. package/src/ts/server/jobs.ts +14 -2
  233. package/src/ts/server/utils/BackwardCompat.ts +530 -529
  234. package/src/ts/types.ts +78 -34
  235. package/dist/plugins/client/client/utils.d.ts +0 -1
  236. package/dist/plugins/client/client/utils.js.map +0 -1
  237. package/dist/plugins/client/page.d.ts +0 -2
  238. package/dist/plugins/client/page.js.map +0 -1
  239. package/dist/plugins/client/plugins/client/publicationRenderer.d.ts +0 -2
  240. package/dist/plugins/client/plugins/client/publicationRenderer.js +0 -128
  241. package/dist/plugins/client/plugins/client/publicationRenderer.js.map +0 -1
  242. package/dist/plugins/server/plugins/server/AuthPlugin.d.ts +0 -1
  243. package/dist/plugins/server/plugins/server/AuthPlugin.js +0 -38
  244. package/dist/plugins/server/plugins/server/AuthPlugin.js.map +0 -1
  245. package/dist/plugins/server/plugins/server/HooksPlugin.d.ts +0 -7
  246. package/dist/plugins/server/plugins/server/HooksPlugin.js +0 -74
  247. package/dist/plugins/server/plugins/server/HooksPlugin.js.map +0 -1
  248. package/src/example-client-config.js +0 -64
  249. package/src/ts/plugins/client/publicationRenderer.ts +0 -128
  250. package/src/ts/plugins/server/HooksPlugin.ts +0 -76
@@ -16,12 +16,15 @@
16
16
  */
17
17
 
18
18
  import { getPageSlug } from '../../page'
19
- import { ApiConnectorLoggedInPostMessage, ApiConnectorLoginQuery, ApiPublicationPublishBody, ApiPublicationPublishQuery, ApiPublicationPublishResponse, ApiPublicationStatusQuery, ApiPublicationStatusResponse, ConnectorData, ConnectorType, ConnectorUser, JobData, JobStatus, PublicationData, PublicationJobData, PublicationSettings, WebsiteData, WebsiteId, WebsiteSettings } from '../../types'
20
- import { Editor, ProjectData } from 'grapesjs'
19
+ import { ApiConnectorLoggedInPostMessage, ApiConnectorLoginQuery, ApiPublicationPublishBody, ClientSideFile, ClientSideFileType, ConnectorData, ConnectorType, ConnectorUser, JobStatus, Initiator, PublicationData, PublicationJobData, PublicationSettings, WebsiteData, WebsiteFile, WebsiteId, WebsiteSettings } from '../../types'
20
+ import { Editor } from 'grapesjs'
21
21
  import { PublicationUi } from './PublicationUi'
22
22
  import { getUser, logout, publicationStatus, publish } from '../api'
23
23
  import { API_CONNECTOR_LOGIN, API_CONNECTOR_PATH, API_PATH } from '../../constants'
24
24
  import { ClientEvent } from '../events'
25
+ import { resetRenderComponents, resetRenderCssRules, transformPermalink, transformFiles, transformPath, renderComponents, renderCssRules } from '../publication-transformers'
26
+ import { displayedToStored } from '../assetUrl'
27
+ import { hashString } from '../utils'
25
28
 
26
29
  /**
27
30
  * @fileoverview Publication manager for Silex
@@ -74,6 +77,9 @@ function jobStatusToPublicationStatus(status: JobStatus): PublicationStatus {
74
77
  throw new Error(`Unknown job status ${status}`)
75
78
  }
76
79
 
80
+ // Orging and path, should we use config.rootUrl?
81
+ const SERVER_URL = window.location.origin + window.location.pathname.replace(/\/$/, '')
82
+
77
83
  // The publication manager class
78
84
  // This class is responsible for the publication dialog and for the publication process
79
85
  // It is added to the editor instance as editor.PublicationManager
@@ -146,6 +152,27 @@ export class PublicationManager {
146
152
  }
147
153
 
148
154
  async goLogin(connector: ConnectorData) {
155
+ let preventDefault = false
156
+ this.editor.trigger(ClientEvent.PUBLISH_LOGIN_START, { connector, publicationManager: this, preventDefault: () => preventDefault = true })
157
+ if(preventDefault) {
158
+ this.status = PublicationStatus.STATUS_NONE
159
+ this.dialog && this.dialog.displayPending(this.job, this.status)
160
+ return
161
+ }
162
+ // Check if the user is already logged in
163
+ if(connector.isLoggedIn) {
164
+ this.settings = {
165
+ ...this.settings, // In case there are options
166
+ connector,
167
+ }
168
+ this.status = PublicationStatus.STATUS_NONE
169
+ // Save the website with the new settings
170
+ // WIP: prevent saving during publication
171
+ // await this.editor.store(null)
172
+ // Display the dialog
173
+ this.dialog && this.dialog.displayPending(this.job, this.status)
174
+ return
175
+ }
149
176
  this.settings = {}
150
177
  this.status = PublicationStatus.STATUS_LOGGED_OUT
151
178
  this.dialog && this.dialog.displayPending(this.job, this.status)
@@ -153,8 +180,8 @@ export class PublicationManager {
153
180
  connectorId: connector.connectorId,
154
181
  type: connector.type,
155
182
  }
156
- window.open(connector.oauthUrl || `${API_PATH}${API_CONNECTOR_PATH}${API_CONNECTOR_LOGIN}?connectorId=${params.connectorId}&type=${params.type}`, '_blank')
157
- return new Promise(resolve => {
183
+ window.open(connector.oauthUrl || `${SERVER_URL}${API_PATH}${API_CONNECTOR_PATH}${API_CONNECTOR_LOGIN}?connectorId=${params.connectorId}&type=${params.type}`, '_blank')
184
+ return new Promise<void>((resolve, reject) => {
158
185
  const onMessage = async (event) => {
159
186
  const data = event.data as ApiConnectorLoggedInPostMessage
160
187
  if (data?.type === 'login') {
@@ -163,6 +190,7 @@ export class PublicationManager {
163
190
  this.status = PublicationStatus.STATUS_LOGGED_OUT
164
191
  this.settings = {}
165
192
  this.dialog && this.dialog.displayError(data.message, this.job, this.status)
193
+ reject(new Error(data.message))
166
194
  } else {
167
195
  this.editor.trigger(ClientEvent.PUBLISH_LOGIN_END)
168
196
  //const uesr = await getUser({type: connector.type, connectorId: data.connectorId})
@@ -170,10 +198,12 @@ export class PublicationManager {
170
198
  this.settings.options = data.options
171
199
  this.status = PublicationStatus.STATUS_NONE
172
200
  // Save the website with the new settings
173
- this.editor.store(null)
201
+ // WIP: prevent saving during publication
202
+ // await this.editor.store(null)
174
203
  // Display the dialog
175
204
  this.dialog && this.dialog.displayPending(this.job, this.status)
176
205
  //await this.startPublication()
206
+ resolve()
177
207
  }
178
208
  }
179
209
  }
@@ -193,49 +223,111 @@ export class PublicationManager {
193
223
  }
194
224
  }
195
225
 
196
- /**
197
- * Start the publication process
198
- * This is the command "publish"
199
- */
200
- async startPublication() {
201
- if (this.status === PublicationStatus.STATUS_PENDING) throw new Error('Publication is already in progress')
202
- this.status = PublicationStatus.STATUS_PENDING
203
- this.job = null
204
- this.dialog && this.dialog.displayPending(this.job, this.status)
205
- this.editor.trigger(ClientEvent.PUBLISH_START)
206
- const projectData = this.editor.getProjectData() as WebsiteData
207
- const siteSettings = this.editor.getModel().get('settings') as WebsiteSettings
226
+ async getPublicationData(projectData, siteSettings, preventDefault: () => void): Promise<PublicationData> {
227
+ // Data to publish
228
+ // See assetUrl.ts which is a default transformer, always present
229
+ this.setPublicationTransformers()
208
230
  // Build the files structure
209
- const files = await this.getFiles(siteSettings)
210
-
231
+ const files: ClientSideFile[] = (await this.getHtmlFiles(siteSettings, preventDefault))
232
+ .flatMap(file => ([{
233
+ path: file.htmlPath, // Already "transformed" in getHtmlFiles
234
+ content: file.html,
235
+ type: ClientSideFileType.HTML,
236
+ } as ClientSideFile, {
237
+ path: file.cssPath, // Already "transformed" in getHtmlFiles
238
+ content: file.css,
239
+ type: ClientSideFileType.CSS,
240
+ } as ClientSideFile]))
241
+ .concat(projectData.assets
242
+ .map(asset => {
243
+ // TODO: is this needed?
244
+ // // Remove /assets that is added by grapesjs
245
+ // const initialPath = asset.src
246
+ // .replace(/^\/assets/, '')
247
+ // Transform the file paths with the transformers
248
+ const path = transformPath(this.editor, asset.src, ClientSideFileType.ASSET)
249
+ //const src = transformPermalink(this.editor, asset.src, ClientSideFileType.ASSET)
250
+ // This is done in transformPermalink and transformPath but other transformers may change it
251
+ // So we do this only using displayedToStored for the path
252
+ // As path is used to download the asset
253
+ const src = displayedToStored(asset.src)
254
+ // Remove the /asset prefix to keep only the file name
255
+ .replace(/^\/assets\//, '')
256
+ return {
257
+ ...asset,
258
+ path,
259
+ src,
260
+ type: ClientSideFileType.ASSET, // Replaces grapesjs's 'image' type
261
+ } as ClientSideFile
262
+ }))
211
263
  // Create the data to send to the server
212
264
  const data: PublicationData = {
213
265
  ...projectData,
266
+ assets: projectData.assets.map(asset => ({
267
+ ...asset,
268
+ src: displayedToStored(asset.src),
269
+ })),
214
270
  settings: siteSettings,
215
271
  publication: this.settings,
216
272
  files,
217
273
  }
218
- this.editor.trigger(ClientEvent.PUBLISH_DATA, data)
219
- const storageUser = this.editor.getModel().get('user') as ConnectorUser
220
- if(!storageUser) throw new Error('User not logged in to a storage connector')
221
- if(!this.settings.connector?.connectorId) throw new Error('User not logged in to a hosting connector')
274
+ this.resetPublicationTransformers()
275
+ // Let plugins transform the data
276
+ transformFiles(this.editor, data)
277
+ this.editor.trigger(ClientEvent.PUBLISH_DATA, { data, preventDefault, publicationManager: this })
278
+ // Return the data
279
+ return data
280
+ }
281
+
282
+ /**
283
+ * Start the publication process
284
+ * This is the command "publish"
285
+ */
286
+ async startPublication() {
222
287
  try {
288
+ if (this.status === PublicationStatus.STATUS_PENDING) throw new Error('Publication is already in progress')
289
+ this.status = PublicationStatus.STATUS_PENDING
290
+ this.job = null
291
+ this.dialog && this.dialog.displayPending(this.job, this.status)
292
+ // Get the data to publish, clone the objects because plugins can change it
293
+ const projectData = { ...this.editor.getProjectData() as WebsiteData }
294
+ const siteSettings = { ...this.editor.getModel().get('settings') as WebsiteSettings }
295
+ let preventDefaultStart = false
296
+ this.editor.trigger(ClientEvent.PUBLISH_START, {projectData, siteSettings, preventDefault: () => preventDefaultStart = true, publicationManager: this })
297
+ if(preventDefaultStart) {
298
+ this.status = PublicationStatus.STATUS_NONE
299
+ this.dialog && this.dialog.displayPending(this.job, this.status)
300
+ return
301
+ }
302
+ // Get the data to publish
303
+ let preventDefaultData = false
304
+ const data = await this.getPublicationData(projectData, siteSettings, () => preventDefaultData = true)
305
+ if(preventDefaultData) {
306
+ this.status = PublicationStatus.STATUS_NONE
307
+ this.dialog && this.dialog.displayPending(this.job, this.status)
308
+ return
309
+ }
310
+ // User and where to publish
311
+ const storageUser = this.editor.getModel().get('user') as ConnectorUser
312
+ if(!storageUser) throw new Error('User not logged in to a storage connector')
313
+ if(!this.settings.connector?.connectorId) throw new Error('User not logged in to a hosting connector')
223
314
  const websiteId = this.options.websiteId
315
+ const storageId = storageUser.storage.connectorId
316
+ // Use the publication API
224
317
  const [url, job] = await publish({
225
318
  websiteId,
226
319
  hostingId: this.settings.connector.connectorId,
227
- storageId: storageUser.storage.connectorId,
320
+ storageId,
228
321
  data: data as ApiPublicationPublishBody,
229
322
  options: this.settings.options,
230
323
  })
324
+
325
+ // in gitlab pages situation, getUrl from gitlabHostingConnector gives publication url obtained with Gitlab API pages
326
+ console.info('Gitlab url: ', url)
327
+ // could be used in an future UI
328
+
231
329
  this.job = job
232
330
  this.status = jobStatusToPublicationStatus(this.job.status)
233
- // Save the publication settings
234
- // Useless because the url is useful after publish only
235
- //if(this.settings.url !== url) {
236
- // this.settings.url = url
237
- // await this.editor.store(null)
238
- //}
239
331
  this.trackProgress()
240
332
  } catch (e) {
241
333
  console.error('publish error', e)
@@ -253,37 +345,61 @@ export class PublicationManager {
253
345
  }
254
346
  }
255
347
 
256
- async getFiles(siteSettings: WebsiteSettings) {
257
- return this.editor.Pages.getAll().map(page => {
258
- const pageSettings = page.get('settings') as WebsiteSettings
348
+ async getHtmlFiles(siteSettings: WebsiteSettings, preventDefault): Promise<WebsiteFile[]> {
349
+ return Promise.all(this.editor.Pages.getAll().map(async page => {
350
+ // Clone the settings because plugins can change them
351
+ siteSettings = { ...siteSettings }
352
+ const pageSettings = { ...page.get('settings') as WebsiteSettings }
353
+ // Utility function to get a setting from the page or the site settings
259
354
  function getSetting(name) {
260
355
  return (pageSettings || {})[name] || (siteSettings || [])[name] || ''
261
356
  }
262
- const component = page.getMainComponent()
263
- const slug = page.get('slug') || getPageSlug(page.get('name') || page.get('type'))
357
+
358
+ // Get the content from GrapesJS
359
+ const body = page.getMainComponent()
360
+ const cssContent = this.editor.getCss({ component: body })
361
+ console.time(`getHtml ${page.getId()} ${page.get('name')}`)
362
+ const htmlContent = this.editor.getHtml({ component: body })
363
+ console.timeEnd(`getHtml ${page.getId()} ${page.get('name')}`)
364
+
365
+ // Transform the file paths
366
+ const slug = getPageSlug(page.get('name'))
367
+ const cssInitialPath = `/css/${slug}-${await hashString(cssContent)}.css`
368
+ const htmlInitialPath = `/${slug}.html`
369
+ const cssPermalink = transformPermalink(this.editor, cssInitialPath, ClientSideFileType.CSS, Initiator.HTML)
370
+ const cssPath = transformPath(this.editor, cssInitialPath, ClientSideFileType.CSS)
371
+ const htmlPath = transformPath(this.editor, htmlInitialPath, ClientSideFileType.HTML)
372
+
373
+ // Let plugins transform the data
374
+ this.editor.trigger(ClientEvent.PUBLISH_PAGE, { page, siteSettings, pageSettings, preventDefault, publicationManager: this })
375
+
376
+ // Useful data for HTML result
377
+ const title = getSetting('title')
378
+ const favicon = getSetting('favicon')
379
+ // Return the HTML file
264
380
  return {
265
- html: `
266
- <!DOCTYPE html>
267
- <html lang="${getSetting('lang')}">
268
- <head>
269
- <link rel="stylesheet" href="/css/${slug}.css" />
270
- ${siteSettings?.head || ''}
271
- ${pageSettings?.head || ''}
272
- <title>${getSetting('title')}</title>
273
- <link rel="icon" href="${getSetting('favicon')}" />
274
- ${['description', 'og:title', 'og:description', 'og:image']
275
- .map(prop => `<meta property="${prop}" content="${getSetting(prop)}"/>`)
381
+ html: `<!DOCTYPE html>
382
+ <html lang="${getSetting('lang')}">
383
+ <head>
384
+ <meta charset="UTF-8">
385
+ <link rel="stylesheet" href="${cssPermalink}" />
386
+ ${siteSettings?.head || ''}
387
+ ${pageSettings?.head || ''}
388
+ ${ title ? `<title>${ title }</title>` : '' }
389
+ ${ favicon ? `<link rel="icon" href="${favicon}" />` : '' }
390
+ ${['description', 'og:title', 'og:description', 'og:image']
391
+ .filter(prop => !!getSetting(prop))
392
+ .map(prop => `<meta name="${prop}" property="${prop}" content="${getSetting(prop)}"/>`)
276
393
  .join('\n')
277
394
  }
278
- </head>
279
- ${this.editor.getHtml({ component })}
280
- </html>
281
- `,
282
- css: this.editor.getCss({ component }),
283
- cssPath: `/${slug}.css`,
284
- htmlPath: `/${slug}.html`,
395
+ </head>
396
+ ${htmlContent}
397
+ </html>`,
398
+ css: cssContent,
399
+ cssPath,
400
+ htmlPath,
285
401
  }
286
- })
402
+ }))
287
403
  }
288
404
 
289
405
  async trackProgress() {
@@ -304,4 +420,14 @@ export class PublicationManager {
304
420
  }
305
421
  this.dialog && this.dialog.displayPending(this.job, this.status)
306
422
  }
423
+
424
+ private setPublicationTransformers() {
425
+ renderComponents(this.editor)
426
+ renderCssRules(this.editor)
427
+ }
428
+
429
+ private resetPublicationTransformers() {
430
+ resetRenderComponents(this.editor)
431
+ resetRenderCssRules(this.editor)
432
+ }
307
433
  }
@@ -21,6 +21,9 @@ import { unsafeHTML } from 'lit-html/directives/unsafe-html.js'
21
21
  import { cmdPublicationLogin, cmdPublicationLogout, cmdPublicationStart, PublicationStatus, PublishableEditor } from './PublicationManager'
22
22
  import { ConnectorData, ConnectorType, PublicationJobData, PublicationSettings } from '../../types'
23
23
  import { connectorList } from '../api'
24
+ import { defaultKms } from './keymaps'
25
+ import { titleCase } from '../utils'
26
+ import { ClientEvent } from '../events'
24
27
 
25
28
  /**
26
29
  * @fileoverview define the publication dialog
@@ -36,6 +39,12 @@ import { connectorList } from '../api'
36
39
  // Constants
37
40
  export const cmdPublish = 'publish-open-dialog'
38
41
 
42
+ // **
43
+ // Semantic Icons
44
+ const svgConnector = '<svg width="100%" height="100%" viewBox="0 0 44 23" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;"><g><g><path d="M2,11.427c0,0 3.846,-2.655 7.376,0c3.531,2.656 7.45,0 7.45,0" style="fill:none;stroke:#a291ff;stroke-width:4px;"/></g><path d="M32.165,6.424l9.156,0" style="fill:none;stroke:#a291ff;stroke-width:4px;"/><path d="M32.165,2l0,18.854" style="fill:none;stroke:#a291ff;stroke-width:4px;"/><path d="M32.165,16.43l9.156,0" style="fill:none;stroke:#a291ff;stroke-width:4px;"/><path d="M26.253,20.854c-5.203,0 -9.427,-4.224 -9.427,-9.427c-0,-5.203 4.224,-9.427 9.427,-9.427l5.912,-0l0,18.854l-5.912,0Z" style="fill:none;stroke:#a291ff;stroke-width:4px;"/></g></svg>'
45
+ const svgSuccess = '<svg viewBox="0 0 40 28" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;"><path d="M3,12.969l11.868,11.869l21.838,-21.838" style="fill:none;stroke:#37bf76;stroke-width:6px;"/></svg>'
46
+ const svgError = '<svg viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;"><g><path d="M3,28.915l25.915,-25.915" style="fill:none;stroke:#cf4443;stroke-width:6px;"/><path d="M28.915,28.915l-25.915,-25.915" style="fill:none;stroke:#cf4443;stroke-width:6px;"/></g></svg>'
47
+
39
48
  // **
40
49
  // Types
41
50
  export type PublicationDialogOptions = {
@@ -71,6 +80,8 @@ export class PublicationUi {
71
80
 
72
81
  public settings: PublicationSettings
73
82
 
83
+ private sender = null
84
+
74
85
  /**
75
86
  * Initialize the dialog and the publish button
76
87
  */
@@ -80,8 +91,12 @@ export class PublicationUi {
80
91
  // Add the publish command to the editor
81
92
  const openDialog = () => this.openDialog()
82
93
  const closeDialog = () => this.closeDialog()
94
+ const setSender = sender => this.sender = sender
83
95
  editor.Commands.add(cmdPublish, {
84
- run(editor: PublishableEditor) { openDialog() },
96
+ run(editor: PublishableEditor, sender) {
97
+ setSender(sender)
98
+ openDialog()
99
+ },
85
100
  stop(editor: PublishableEditor) { closeDialog() },
86
101
  })
87
102
  }
@@ -106,12 +121,9 @@ export class PublicationUi {
106
121
 
107
122
  // **
108
123
  // Functions to open and close the dialog
109
- getDialogElements(): { el: HTMLElement, primary: HTMLElement, secondary: HTMLElement } {
110
- const el = document.querySelector('#publish-dialog') as HTMLElement
111
- const primary = el?.querySelector('#publish-button--primary') as HTMLElement
112
- const secondary = el?.querySelector('#publish-button--secondary') as HTMLElement
113
- if(!el || !primary || !secondary) throw new Error('Publication dialog elements not found')
114
- return { el, primary, secondary }
124
+ userContent: TemplateResult | null = null
125
+ setUserContent(content: TemplateResult) {
126
+ this.userContent = content
115
127
  }
116
128
  createDialogElements(): HTMLElement {
117
129
  // Create the dialog element
@@ -119,13 +131,16 @@ export class PublicationUi {
119
131
  el.id = 'publish-dialog'
120
132
  el.className = 'silex-dialog-inline silex-dialog gjs-two-color'
121
133
  document.body.append(el)
122
- // Create the publish button
123
- this.editor.Panels.addButton(this.options.appendTo, {
124
- id: 'publish-button',
125
- className: 'silex-button--size publish-button',
126
- command: cmdPublish,
127
- attributes: { title: 'Publish' },
128
- label: '<span class="fa-solid fa-upload"></span><span class="silex-button--small">Publish</span>',
134
+ // Let the other buttons be added, we want to be last
135
+ setTimeout(() => {
136
+ // Create the publish button
137
+ this.editor.Panels.addButton(this.options.appendTo, {
138
+ id: 'publish-button',
139
+ className: 'silex-button--size publish-button',
140
+ command: cmdPublish,
141
+ attributes: { title: `Publish (${titleCase(defaultKms.kmOpenPublish.keys, '+')})` },
142
+ label: '<span class="fa-solid fa-upload"></span><span class="silex-button--small">Publish</span>',
143
+ })
129
144
  })
130
145
  return el
131
146
  }
@@ -143,6 +158,8 @@ export class PublicationUi {
143
158
  `, this.el)
144
159
  if (this.isOpen) {
145
160
  this.el.classList.remove('silex-dialog-hide')
161
+ const primaryBtn = this.el.querySelector('.silex-button--primary') as HTMLElement
162
+ if (primaryBtn) primaryBtn.focus()
146
163
  } else {
147
164
  this.el.classList.add('silex-dialog-hide')
148
165
  }
@@ -152,79 +169,83 @@ export class PublicationUi {
152
169
  }
153
170
  async renderOpenDialog(job: PublicationJobData, status: PublicationStatus): Promise<TemplateResult> {
154
171
  return html`
172
+ <header>
173
+ <span>${unsafeHTML(svgConnector)} Connected to <span class="connector">${this.settings.connector.displayName}</span></span>
174
+ ${this.settings.connector.disableLogout ? '' : html`
175
+ <button
176
+ class="silex-button silex-button--secondary"
177
+ id="publish-button--secondary"
178
+ @click=${() => this.editor.Commands.run(cmdPublicationLogout)}
179
+ >Disconnect
180
+ </button>`}
181
+ </header>
155
182
  <main>
156
- ${this.isPending(status) ? html`
157
- <p>Publication in progress</p>
158
- ` : ''}
159
- ${this.isReady(status) ? html`
160
- <p>You are connected to ${this.settings.connector.displayName}</p><p>Click on the publish button to publish your website</p>
161
- ${this.settings.options && Object.entries(this.settings.options).length && html`<p>Publication options: <ul>${ Object.entries(this.settings.options).map(([key, value]) => html`<li>${key}: ${value}</li>`) }</ul></p>`}
162
- ` : ''}
163
- ${this.isSuccess(status) ? html`
164
- <p>Publication success</p>
165
- ${this.settings.url ? html`<p><a href="${this.settings.url}" target="_blank">Click here to view the published website</a></p>` : ''}
166
- ` : ''}
167
- ${this.isError(status) || this.isLoggedOut(status) ? html`
168
- <p>Publication error</p>
169
- <div>${unsafeHTML(this.errorMessage)}</div>
170
- ` : ''}
171
- ${job && job.message ? html`
172
- <p>${unsafeHTML(job.message)}</p>
173
- ` : ''}
174
- ${this.isPending(status) ? html`
175
- <progress
176
- value=""
177
- style="width: 100%;"
178
- ></progress>
179
- ` : ''}
180
- ${job && job.logs?.length && job.logs[0].length ? html`
181
- <br><details>
182
- <summary>Logs</summary>
183
- <pre style="
184
- max-width: 100%;
185
- max-height: 50vh;
186
- overflow: auto;
187
- font-size: x-small;
188
- "
189
- >${unsafeHTML(cleanupLogEntry(job.logs))}
190
- </pre>
191
- </details>
192
- ` : ''}
193
- ${job && job.errors?.length && job.errors[0].length ? html`
194
- <br><details>
195
- <summary>Errors</summary>
196
- <pre style="
197
- max-width: 100%;
198
- max-height: 50vh;
199
- overflow: auto;
200
- font-size: x-small;
201
- "
202
- >${unsafeHTML(cleanupLogEntry(job.errors))}
203
- </pre>
204
- </details>
205
- ` : ''}
183
+ ${this.userContent || html`
184
+ ${this.isPending(status) ? html`
185
+ <p>Publication in progress...</p>
186
+ ` : ''}
187
+ ${this.isReady(status) ? html`
188
+ <p>Click on the button below to publish your website.</p>
189
+ ${this.settings.options && Object.entries(this.settings.options).length && html`<p>Publication options: <ul>${ Object.entries(this.settings.options).map(([key, value]) => html`<li>${key}: ${value}</li>`) }</ul></p>`}
190
+ ` : ''}
191
+ ${this.isSuccess(status) ? html`
192
+ <h3 class="status">Publication success ${unsafeHTML(svgSuccess)}</h3>
193
+ ${this.settings.options?.websiteUrl ? html`<p><a href="${this.settings.options.websiteUrl}" target="_blank">Click here to view the published website</a></p>` : ''}
194
+ ` : ''}
195
+ ${this.isError(status) || this.isLoggedOut(status) ? html`
196
+ <h3 class="status">Publication error ${unsafeHTML(svgError)}</h3>
197
+ <details open>
198
+ <summary>Details</summary>
199
+ ${unsafeHTML(this.errorMessage)}
200
+ </details>
201
+ ` : ''}
202
+ ${job?.message ? html`
203
+ <details open>
204
+ <summary>Details</summary>
205
+ ${unsafeHTML(job.message)}
206
+ </details>
207
+ ` : ''}
208
+ ${this.isPending(status) ? html`
209
+ <progress
210
+ value=""
211
+ style="width: 100%;"
212
+ ></progress>
213
+ ` : ''}
214
+ ${job?.logs?.length && job.logs[0].length ? html`
215
+ <details>
216
+ <summary>Logs</summary>
217
+ <div class="logs">${unsafeHTML(cleanupLogEntry(job.logs))}</div>
218
+ </details>
219
+ ` : ''}
220
+ ${job?.errors?.length && job.errors[0].length ? html`
221
+ <details>
222
+ <summary>Errors</summary>
223
+ <pre style="
224
+ max-width: 100%;
225
+ font-size: x-small;
226
+ "
227
+ >${unsafeHTML(cleanupLogEntry(job.errors))}
228
+ </pre>
229
+ </details>
230
+ ` : ''}
231
+ ${this.isPending(status) || this.isLoggedOut(status) ? '' : html`
232
+ <button
233
+ class="silex-button silex-button--primary"
234
+ id="publish-button--primary"
235
+ @click=${() => this.editor.Commands.run(cmdPublicationStart)}
236
+ >Publish</button>
237
+ `}
238
+ `}
206
239
  </main>
207
240
  <footer>
208
- ${this.isPending(status) || this.isLoggedOut(status) ? '' : html`
209
- <button
210
- class="silex-button silex-button--primary"
211
- id="publish-button--primary"
212
- @click=${() => this.editor.Commands.run(cmdPublicationStart)}
213
- >Publish</button>
214
- `}
215
241
  ${this.isLoggedOut(status) ? html`
216
242
  <button
217
243
  class="silex-button silex-button--primary"
218
244
  id="publish-button--primary"
219
245
  @click=${() => this.editor.Commands.run(cmdPublicationLogin, this.settings.connector)}
220
- >Login</button>
221
- `: this.settings.connector.disableLogout ? '' : html`
222
- <button
223
- class="silex-button silex-button--secondary"
224
- id="publish-button--secondary"
225
- @click=${() => this.editor.Commands.run(cmdPublicationLogout)}
226
- >Logout</button>
227
- `}
246
+ >Connect</button>
247
+ `: ''}
248
+ <a href="https://docs.silex.me/en/user/publish" target="_blank">Help</a>
228
249
  <button
229
250
  class="silex-button silex-button--secondary"
230
251
  id="publish-button--secondary"
@@ -237,27 +258,35 @@ export class PublicationUi {
237
258
  try {
238
259
  render(html`<main><p>Loading</p></main>`, this.el)
239
260
  const hostingConnectors = await connectorList({ type: ConnectorType.HOSTING })
240
- const loggedConnector: ConnectorData = hostingConnectors.find(connector => connector.isLoggedIn)
241
- if (loggedConnector) {
242
- this.settings.connector = loggedConnector
261
+ const loggedConnectors: ConnectorData[] = hostingConnectors.filter(connector => connector.isLoggedIn)
262
+ if (hostingConnectors.length === 1 && loggedConnectors.length === 1) {
263
+ this.settings.connector = loggedConnectors[0]
243
264
  return this.renderOpenDialog(null, PublicationStatus.STATUS_NONE)
244
265
  }
266
+ //const loggedConnector: ConnectorData = hostingConnectors.find(connector => connector.isLoggedIn)
267
+ //if (loggedConnector) {
268
+ // this.settings.connector = loggedConnector
269
+ // return this.renderOpenDialog(null, PublicationStatus.STATUS_NONE)
270
+ //}
245
271
  return html`
246
272
  <main>
247
- <p>You need to login to publish your website</p>
273
+ <p>You need to select a hosting connector to publish your website.</p>
248
274
  ${this.isError(status) || this.isLoggedOut(status) ? html`
249
275
  <p>Login error</p>
250
276
  <div>${unsafeHTML(this.errorMessage)}</div>
251
277
  ` : ''}
252
- ${hostingConnectors.map(connector => html`
278
+ <div class="buttons">
279
+ ${hostingConnectors.map(connector => html`
253
280
  <button
254
- class="silex-button silex-button--primary"
281
+ class="silex-button"
255
282
  id="publish-button--primary"
256
283
  @click=${() => this.editor.Commands.run(cmdPublicationLogin, connector)}
257
- >Login with ${connector.connectorId}</button>
284
+ >${connector.displayName}</button>
258
285
  `)}
286
+ </div>
259
287
  </main>
260
288
  <footer>
289
+ <a href="https://docs.silex.me/en/user/publish" target="_blank">Help</a>
261
290
  <button
262
291
  class="silex-button silex-button--secondary"
263
292
  id="publish-button--secondary"
@@ -276,6 +305,7 @@ export class PublicationUi {
276
305
  <p>Something went wrong: ${err.message}</p>
277
306
  </main>
278
307
  <footer>
308
+ <a href="https://docs.silex.me/en/user/publish" target="_blank">Help</a>
279
309
  <button
280
310
  class="silex-button silex-button--secondary"
281
311
  id="publish-button--secondary"
@@ -297,6 +327,11 @@ export class PublicationUi {
297
327
  async closeDialog() {
298
328
  this.isOpen = false
299
329
  this.renderDialog(null, null)
330
+ if (this.sender && typeof this.sender.set === 'function') {
331
+ this.sender.set('active', 0)
332
+ } // Deactivate the button to make it ready to be clicked again
333
+ this.sender = null
334
+ this.editor.trigger(ClientEvent.PUBLICATION_UI_CLOSE, { publicationUi: this })
300
335
  }
301
336
  async toggleDialog() {
302
337
  if (this.isOpen) this.closeDialog()
@@ -326,5 +361,6 @@ export class PublicationUi {
326
361
  // Publication
327
362
  //this.editor.Commands.run(cmdPublicationStart)
328
363
  this.renderDialog(null, PublicationStatus.STATUS_NONE)
364
+ this.editor.trigger(ClientEvent.PUBLICATION_UI_OPEN, { publicationUi: this })
329
365
  }
330
366
  }