@silexlabs/silex 3.3.1-3 → 3.3.1-5
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.
- package/README.md +27 -2
- package/dist/client/css/admin.css +26 -9
- package/dist/client/index.html +3 -3
- package/dist/client/js/main.js +285 -217
- package/dist/client/js/main.js.LICENSE.txt +2 -13
- package/dist/client/page.d.ts +1 -1
- package/dist/client/types.d.ts +1 -17
- package/dist/plugins/client/types.d.ts +1 -17
- package/dist/plugins/client/types.js.map +1 -1
- package/dist/plugins/server/page.d.ts +1 -1
- package/dist/plugins/server/page.js.map +1 -1
- package/dist/plugins/server/plugins/server/FtpConnector.js +53 -8
- package/dist/plugins/server/plugins/server/FtpConnector.js.map +1 -1
- package/dist/plugins/server/plugins/server/GitlabConnector.js +106 -85
- package/dist/plugins/server/plugins/server/GitlabConnector.js.map +1 -1
- package/dist/plugins/server/server/connectors/FsStorage.js +50 -4
- package/dist/plugins/server/server/connectors/FsStorage.js.map +1 -1
- package/dist/plugins/server/server/utils/websiteDataSerialization.d.ts +20 -0
- package/dist/plugins/server/server/utils/websiteDataSerialization.js +127 -0
- package/dist/plugins/server/server/utils/websiteDataSerialization.js.map +1 -0
- package/dist/plugins/server/types.d.ts +1 -17
- package/dist/plugins/server/types.js.map +1 -1
- package/dist/server/page.js +14 -0
- package/dist/server/server/cli.js +0 -0
- package/dist/server/server/connectors/FsStorage.js +40 -4
- package/dist/server/server/utils/websiteDataSerialization.js +85 -0
- package/package.json +13 -13
- package/src/css/_variables.scss +4 -0
- package/src/css/grapesjs-plugins.scss +5 -0
- package/src/css/grapesjs.scss +8 -2
- package/src/css/loader.scss +9 -6
- package/src/css/page-panel.scss +3 -2
- package/src/css/project-bar.scss +0 -1
- package/src/html/index.pug +1 -1
- package/src/ts/client/assetUrl.ts +1 -1
- package/src/ts/client/grapesjs/PublicationUi.ts +1 -1
- package/src/ts/client/grapesjs/css-props.ts +4 -3
- package/src/ts/client/grapesjs/flex.ts +8 -4
- package/src/ts/client/grapesjs/img.ts +9 -3
- package/src/ts/client/grapesjs/index.ts +9 -11
- package/src/ts/client/grapesjs/li.ts +3 -1
- package/src/ts/client/grapesjs/sectors.ts +3 -1
- package/src/ts/client/grapesjs/storage.ts +25 -30
- package/src/ts/client/index.ts +2 -3
- package/src/ts/page.ts +1 -1
- package/src/ts/plugins/server/FtpConnector.test.ts +2 -1
- package/src/ts/plugins/server/FtpConnector.ts +61 -9
- package/src/ts/plugins/server/GitlabConnector.ts +112 -102
- package/src/ts/server/connectors/FsStorage.test.ts +6 -2
- package/src/ts/server/connectors/FsStorage.ts +60 -5
- package/src/ts/server/utils/websiteDataSerialization.test.ts +59 -0
- package/src/ts/server/utils/websiteDataSerialization.ts +143 -0
- package/src/ts/types.ts +2 -20
- package/dist/client/css/admin.css.map +0 -1
- package/dist/client/grapesjs-filter-styles/dist/index.d.ts +0 -4
- package/dist/client/grapesjs-fonts/dist/index.d.ts +0 -4
- package/dist/client/grapesjs-fonts/src/commands.d.ts +0 -3
- package/dist/client/grapesjs-fonts/src/fonts.d.ts +0 -4
- package/dist/client/grapesjs-storage-rate-limit/dist/index.d.ts +0 -4
- package/dist/client/grapesjs-ui-suggest-classes/dist/index.d.ts +0 -4
- package/dist/client/silex-lib/src/ts/client/api.d.ts +0 -90
- package/dist/client/silex-lib/src/ts/client/assetUrl.d.ts +0 -46
- package/dist/client/silex-lib/src/ts/client/config.d.ts +0 -102
- package/dist/client/silex-lib/src/ts/client/events.d.ts +0 -23
- package/dist/client/silex-lib/src/ts/client/expose.d.ts +0 -8
- package/dist/client/silex-lib/src/ts/client/grapesjs/LoginDialog.d.ts +0 -15
- package/dist/client/silex-lib/src/ts/client/grapesjs/PublicationManager.d.ts +0 -74
- package/dist/client/silex-lib/src/ts/client/grapesjs/PublicationUi.d.ts +0 -59
- package/dist/client/silex-lib/src/ts/client/grapesjs/blocks.d.ts +0 -1
- package/dist/client/silex-lib/src/ts/client/grapesjs/breadcrumbs.d.ts +0 -1
- package/dist/client/silex-lib/src/ts/client/grapesjs/css-props.d.ts +0 -6
- package/dist/client/silex-lib/src/ts/client/grapesjs/flex.d.ts +0 -7
- package/dist/client/silex-lib/src/ts/client/grapesjs/footer.d.ts +0 -2
- package/dist/client/silex-lib/src/ts/client/grapesjs/img.d.ts +0 -6
- package/dist/client/silex-lib/src/ts/client/grapesjs/index.d.ts +0 -9
- package/dist/client/silex-lib/src/ts/client/grapesjs/internal-links.d.ts +0 -1
- package/dist/client/silex-lib/src/ts/client/grapesjs/keymaps.d.ts +0 -123
- package/dist/client/silex-lib/src/ts/client/grapesjs/li.d.ts +0 -7
- package/dist/client/silex-lib/src/ts/client/grapesjs/new-page-dialog.d.ts +0 -2
- package/dist/client/silex-lib/src/ts/client/grapesjs/openImport.d.ts +0 -9
- package/dist/client/silex-lib/src/ts/client/grapesjs/page-panel.d.ts +0 -9
- package/dist/client/silex-lib/src/ts/client/grapesjs/project-bar.d.ts +0 -21
- package/dist/client/silex-lib/src/ts/client/grapesjs/resize-panel.d.ts +0 -1
- package/dist/client/silex-lib/src/ts/client/grapesjs/rich-text.d.ts +0 -4
- package/dist/client/silex-lib/src/ts/client/grapesjs/scroll.d.ts +0 -7
- package/dist/client/silex-lib/src/ts/client/grapesjs/sectors.d.ts +0 -11
- package/dist/client/silex-lib/src/ts/client/grapesjs/semantic.d.ts +0 -1
- package/dist/client/silex-lib/src/ts/client/grapesjs/settings-sections.d.ts +0 -16
- package/dist/client/silex-lib/src/ts/client/grapesjs/settings.d.ts +0 -4
- package/dist/client/silex-lib/src/ts/client/grapesjs/storage.d.ts +0 -3
- package/dist/client/silex-lib/src/ts/client/grapesjs/symbolDialogs.d.ts +0 -3
- package/dist/client/silex-lib/src/ts/client/grapesjs/upload-progress.d.ts +0 -2
- package/dist/client/silex-lib/src/ts/client/grapesjs/view-buttons.d.ts +0 -1
- package/dist/client/silex-lib/src/ts/client/index.d.ts +0 -10
- package/dist/client/silex-lib/src/ts/client/publication-transformers.d.ts +0 -42
- package/dist/client/silex-lib/src/ts/client/publish-custom-code-block.d.ts +0 -4
- package/dist/client/silex-lib/src/ts/client/publish-fonts.d.ts +0 -4
- package/dist/client/silex-lib/src/ts/client/utils.d.ts +0 -22
- package/dist/client/silex-lib/src/ts/constants.d.ts +0 -33
- package/dist/client/silex-lib/src/ts/page.d.ts +0 -2
- package/dist/client/silex-lib/src/ts/types.d.ts +0 -341
- package/src/ts/client/grapesjs/dynamic-plugins.ts +0 -63
- package/src/ts/client/grapesjs/plugin-options.ts +0 -243
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@silexlabs/silex",
|
|
3
3
|
"description": "Free and easy website builder for everyone.",
|
|
4
|
-
"version": "3.3.1-
|
|
4
|
+
"version": "3.3.1-5",
|
|
5
5
|
"version:backwardcompat": "3.0.0",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Alex Hoyau",
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"dev": "run-p watch start:debug",
|
|
80
80
|
"watch": "npm-watch",
|
|
81
81
|
"start": "node dist/server/server/",
|
|
82
|
-
"start:debug": "cross-env
|
|
82
|
+
"start:debug": "cross-env SILEX_DEBUG=true nodemon --watch dist/server dist/server/server/",
|
|
83
83
|
"start:preview": "http-server silex/hosting -o",
|
|
84
84
|
"prepublishOnly": "$npm_execpath run lint && $npm_execpath test && $npm_execpath run build",
|
|
85
85
|
"preversion": "$npm_execpath run prepublishOnly",
|
|
@@ -100,21 +100,21 @@
|
|
|
100
100
|
"lint:fix": "$npm_execpath run lint -- --fix",
|
|
101
101
|
"test": "node --experimental-vm-modules `node_modules jest`/jest/bin/jest.js",
|
|
102
102
|
"test:watch": "$npm_execpath test -- --watch",
|
|
103
|
-
"prepare": "husky
|
|
103
|
+
"prepare": "husky && $npm_execpath run build"
|
|
104
104
|
},
|
|
105
105
|
"dependencies": {
|
|
106
106
|
"@fontsource/ubuntu": "^5.0.13",
|
|
107
107
|
"@fortawesome/fontawesome-free": "6.6.0",
|
|
108
|
-
"@silexlabs/grapesjs-advanced-selector": "1.2.1-
|
|
109
|
-
"@silexlabs/grapesjs-filter-styles": "1.1.0",
|
|
110
|
-
"@silexlabs/grapesjs-fonts": "1.1.
|
|
111
|
-
"@silexlabs/grapesjs-keymaps-dialog": "1.1.0",
|
|
112
|
-
"@silexlabs/grapesjs-loading": "1.1.0",
|
|
113
|
-
"@silexlabs/grapesjs-notifications": "0.1.0",
|
|
114
|
-
"@silexlabs/grapesjs-storage-rate-limit": "1.1.0",
|
|
115
|
-
"@silexlabs/grapesjs-symbols": "1.2.1-
|
|
116
|
-
"@silexlabs/grapesjs-ui-suggest-classes": "1.1.0",
|
|
117
|
-
"@silexlabs/silex-plugins": "1.1.0",
|
|
108
|
+
"@silexlabs/grapesjs-advanced-selector": "1.2.1-5",
|
|
109
|
+
"@silexlabs/grapesjs-filter-styles": "1.1.1-0",
|
|
110
|
+
"@silexlabs/grapesjs-fonts": "1.1.1-1",
|
|
111
|
+
"@silexlabs/grapesjs-keymaps-dialog": "1.1.1-0",
|
|
112
|
+
"@silexlabs/grapesjs-loading": "1.1.1-0",
|
|
113
|
+
"@silexlabs/grapesjs-notifications": "0.1.1-0",
|
|
114
|
+
"@silexlabs/grapesjs-storage-rate-limit": "1.1.1-0",
|
|
115
|
+
"@silexlabs/grapesjs-symbols": "1.2.1-1",
|
|
116
|
+
"@silexlabs/grapesjs-ui-suggest-classes": "1.1.1-0",
|
|
117
|
+
"@silexlabs/silex-plugins": "1.1.1-0",
|
|
118
118
|
"@types/archiver": "^6.0.2",
|
|
119
119
|
"adm-zip": "0.5.14",
|
|
120
120
|
"archiver": "^7.0.1",
|
package/src/css/_variables.scss
CHANGED
|
@@ -27,9 +27,13 @@ $fontFamily: 'Ubuntu', sans-serif;
|
|
|
27
27
|
|
|
28
28
|
:root {
|
|
29
29
|
--primaryColor: #{$primaryColor};
|
|
30
|
+
--gjs-primary-color: #{$primaryColor} !important;
|
|
30
31
|
--secondaryColor: #{$secondaryColor};
|
|
32
|
+
--gjs-secondary-color: #{$secondaryColor} !important;
|
|
31
33
|
--tertiaryColor: #{$tertiaryColor};
|
|
34
|
+
--gjs-tertiary-color: #{$tertiaryColor} !important;
|
|
32
35
|
--quaternaryColor: #{$quaternaryColor};
|
|
36
|
+
--gjs-quaternary-color: #{$quaternaryColor} !important;
|
|
33
37
|
--darkerPrimaryColor: #{$darkerPrimaryColor};
|
|
34
38
|
--lighterPrimaryColor: #{$lighterPrimaryColor};
|
|
35
39
|
--viewsPanelWidth: #{$viewsPanelWidth};
|
|
@@ -33,6 +33,11 @@
|
|
|
33
33
|
border-right-color: $secondaryColor;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
// Hide tooltip info buttons for border properties that interfere with the visual squares
|
|
37
|
+
.gjs-radio-item-label:after {
|
|
38
|
+
display: none !important;
|
|
39
|
+
}
|
|
40
|
+
|
|
36
41
|
// grapesjs-notifications
|
|
37
42
|
.gjs-editor .gjs-notification {
|
|
38
43
|
max-height: initial;
|
package/src/css/grapesjs.scss
CHANGED
|
@@ -130,6 +130,12 @@
|
|
|
130
130
|
.gjs-sm-icon[title]:not([title=""])::after {
|
|
131
131
|
content: "?";
|
|
132
132
|
margin-left: 0.5em;
|
|
133
|
+
padding: 0.1em 0.3em;
|
|
134
|
+
transition: background 0.5s;
|
|
135
|
+
font-size: x-small;
|
|
136
|
+
}
|
|
137
|
+
.gjs-sm-icon[title]:not([title=""]):hover::after {
|
|
138
|
+
background: var(--gjs-main-dark-color);
|
|
133
139
|
}
|
|
134
140
|
|
|
135
141
|
/*
|
|
@@ -214,9 +220,9 @@
|
|
|
214
220
|
}
|
|
215
221
|
.gjs-sm-sector .gjs-sm-sector-title::before,
|
|
216
222
|
.gjs-sm-sector .gjs-sm-sector-title::after {
|
|
217
|
-
content: '
|
|
223
|
+
content: 'o';
|
|
218
224
|
display: none;
|
|
219
|
-
margin-left: 0.
|
|
225
|
+
margin-left: 0.4em;
|
|
220
226
|
order: 5;
|
|
221
227
|
}
|
|
222
228
|
.gjs-sm-sector:has(.gjs-sm-label.gjs-four-color) .gjs-sm-sector-title::before {
|
package/src/css/loader.scss
CHANGED
|
@@ -22,20 +22,23 @@
|
|
|
22
22
|
height: 0;
|
|
23
23
|
position: absolute;
|
|
24
24
|
background-color: variables.$primaryColor;
|
|
25
|
+
outline: 1px solid grey;
|
|
26
|
+
border: none;
|
|
25
27
|
border-radius: 0.125rem;
|
|
26
28
|
transform: translateY(-5px);
|
|
27
29
|
transition: all 0.3s 0.3s ease;
|
|
30
|
+
/* CHROME */
|
|
28
31
|
&::-webkit-progress-bar {
|
|
29
|
-
background-color: variables.$
|
|
30
|
-
border-radius: 0.125rem;
|
|
32
|
+
background-color: variables.$primaryColor; // dark background (track)
|
|
31
33
|
}
|
|
34
|
+
|
|
32
35
|
&::-webkit-progress-value {
|
|
33
|
-
background-color: variables.$
|
|
34
|
-
border-radius: 0.125rem;
|
|
36
|
+
background-color: variables.$secondaryColor; // light foreground (filled)
|
|
35
37
|
}
|
|
38
|
+
|
|
39
|
+
/* FIREFOX */
|
|
36
40
|
&::-moz-progress-bar {
|
|
37
|
-
background-color: variables.$secondaryColor;
|
|
38
|
-
border-radius: 0.125rem;
|
|
41
|
+
background-color: variables.$secondaryColor; // light foreground (filled)
|
|
39
42
|
}
|
|
40
43
|
}
|
|
41
44
|
}
|
package/src/css/page-panel.scss
CHANGED
|
@@ -2,16 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
.page-panel-container {
|
|
4
4
|
.pages__wrapper {
|
|
5
|
-
|
|
5
|
+
display: contents !important;
|
|
6
|
+
height: fit-content;
|
|
6
7
|
}
|
|
7
8
|
.pages {
|
|
8
|
-
height: 100%;
|
|
9
9
|
display: flex;
|
|
10
10
|
flex-direction: column;
|
|
11
11
|
.pages__main {
|
|
12
12
|
flex: 1; // take the available space in height
|
|
13
13
|
border: 1px solid $darkerPrimaryColor;
|
|
14
14
|
background-color: $primaryColor;
|
|
15
|
+
min-height: 20vh;
|
|
15
16
|
}
|
|
16
17
|
.pages__page {
|
|
17
18
|
display: flex;
|
package/src/css/project-bar.scss
CHANGED
package/src/html/index.pug
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { API_PATH, API_WEBSITE_ASSET_READ, API_WEBSITE_PATH } from '../constants'
|
|
2
|
-
import { Asset, ClientSideFileType,
|
|
2
|
+
import { Asset, ClientSideFileType, ConnectorId, Style, WebsiteId } from '../types'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* @fileoverview Helpers to manage assets URL
|
|
@@ -129,7 +129,7 @@ export class PublicationUi {
|
|
|
129
129
|
// Create the dialog element
|
|
130
130
|
const el = document.createElement('div')
|
|
131
131
|
el.id = 'publish-dialog'
|
|
132
|
-
el.className = 'silex-dialog-inline silex-dialog gjs-two-color'
|
|
132
|
+
el.className = 'silex-dialog-inline silex-dialog gjs-two-color silex-dialog-hide'
|
|
133
133
|
document.body.append(el)
|
|
134
134
|
// Let the other buttons be added, we want to be last
|
|
135
135
|
setTimeout(() => {
|
|
@@ -128,19 +128,20 @@ export default (editor: Editor, opts) => {
|
|
|
128
128
|
name: 'Content',
|
|
129
129
|
property: 'content',
|
|
130
130
|
type: 'text',
|
|
131
|
-
defaults: '',
|
|
131
|
+
defaults: '"Content in double quotes"',
|
|
132
132
|
info: 'Generates content for an element.',
|
|
133
133
|
full: true,
|
|
134
134
|
visible: false,
|
|
135
135
|
}],
|
|
136
136
|
shouldShow: async () => {
|
|
137
137
|
return new Promise<boolean>((resolve, reject) => {
|
|
138
|
+
|
|
138
139
|
requestAnimationFrame(() => {
|
|
139
140
|
try {
|
|
140
141
|
const state = editor.StyleManager
|
|
141
142
|
.getSelected()
|
|
142
|
-
|
|
143
|
-
if (['before', 'after'].includes(state)) {
|
|
143
|
+
?.get('state')
|
|
144
|
+
if (state && ['before', 'after'].includes(state)) {
|
|
144
145
|
return resolve(true)
|
|
145
146
|
}
|
|
146
147
|
return resolve(false)
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
16
|
*/
|
|
17
17
|
import { Component, Editor } from 'grapesjs'
|
|
18
|
-
import { SectorConfig, registerSector } from './sectors'
|
|
19
18
|
|
|
20
19
|
/**
|
|
21
20
|
* @fileoverview This plugin adds CSS properties for the UL, OL and LI elements
|
|
@@ -54,12 +53,16 @@ export default (editor: Editor) => {
|
|
|
54
53
|
// })
|
|
55
54
|
// }
|
|
56
55
|
// })
|
|
57
|
-
//
|
|
58
|
-
// This is a workaround grapesjs behavior: https://github.com/GrapesJS/grapesjs/issues/3123
|
|
59
|
-
sector.on('change:visible', () => applyVisibility())
|
|
56
|
+
// Listen to the events that should trigger a visibility check
|
|
60
57
|
editor.on('component:selected', comp => applyVisibility(comp))
|
|
58
|
+
// `change:visible` is a workaround grapesjs behavior: https://github.com/GrapesJS/grapesjs/issues/3123
|
|
59
|
+
editor.on('change:visible', applyVisibility)
|
|
60
|
+
// `style:property:update` is useful for instantly display the flex sector when selecting display: inline-flex;
|
|
61
|
+
editor.on('style:property:update', () => applyVisibility())
|
|
61
62
|
function applyVisibility(comp: Component = editor.getSelected()) {
|
|
63
|
+
editor.off('change:visible', applyVisibility) // Avoid infinite loop
|
|
62
64
|
requestAnimationFrame(() => {
|
|
65
|
+
if(!comp) return // No selection yet
|
|
63
66
|
const computedDisplay = comp?.view?.el && getComputedStyle(comp.view.el)['display'] as string || ''
|
|
64
67
|
const isFlex = computedDisplay === 'flex' || computedDisplay === 'inline-flex'
|
|
65
68
|
const parent = comp.parent()
|
|
@@ -78,6 +81,7 @@ export default (editor: Editor) => {
|
|
|
78
81
|
sector.getProperties()
|
|
79
82
|
.forEach(p => p.set('visible', FLEX_ITEM_PROPS.includes(p.getId())))
|
|
80
83
|
}
|
|
84
|
+
editor.on('change:visible', applyVisibility)
|
|
81
85
|
})
|
|
82
86
|
}
|
|
83
87
|
})
|
|
@@ -144,9 +144,15 @@ export default (editor: Editor, opts) => {
|
|
|
144
144
|
}
|
|
145
145
|
})
|
|
146
146
|
// Watch changes made by grapesjs and revert them if needed
|
|
147
|
-
|
|
148
|
-
if(sector.get('visible') !== showSector)
|
|
149
|
-
|
|
147
|
+
function doubleCheck() {
|
|
148
|
+
if(sector.get('visible') !== showSector){
|
|
149
|
+
sector.off('change:visible', doubleCheck)
|
|
150
|
+
sector.set('visible', showSector)
|
|
151
|
+
sector.on('change:visible', doubleCheck)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
sector.on('change:visible', doubleCheck)
|
|
155
|
+
|
|
150
156
|
// Add a new image type that handles the storage URL vs displayed URL
|
|
151
157
|
domc.addType('image', {
|
|
152
158
|
...imgType,
|
|
@@ -49,7 +49,7 @@ import rateLimitPlugin from '@silexlabs/grapesjs-storage-rate-limit'
|
|
|
49
49
|
import borderPugin from 'grapesjs-style-border'
|
|
50
50
|
import backgroundPlugin from 'grapesjs-style-bg'
|
|
51
51
|
import resizePanelPlugin from './resize-panel'
|
|
52
|
-
import notificationsPlugin, {
|
|
52
|
+
import notificationsPlugin, { NOTIFICATION_CHANGED } from '@silexlabs/grapesjs-notifications'
|
|
53
53
|
import keymapsDialogPlugin, { cmdKeymapsDialog } from '@silexlabs/grapesjs-keymaps-dialog'
|
|
54
54
|
|
|
55
55
|
import { pagePanelPlugin, cmdTogglePages, cmdAddPage } from './page-panel'
|
|
@@ -428,16 +428,14 @@ export async function initEditor(config: EditorConfig) {
|
|
|
428
428
|
// Add the notifications container
|
|
429
429
|
document.body.querySelector('.notifications-container')?.appendChild(notificationContainer)
|
|
430
430
|
// Mark the button as dirty when there are notifications
|
|
431
|
-
|
|
432
|
-
editor.on(
|
|
433
|
-
'notifications
|
|
434
|
-
()
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
}
|
|
440
|
-
)
|
|
431
|
+
let notificationCount = 0
|
|
432
|
+
editor.on(NOTIFICATION_CHANGED, (notifications) => {
|
|
433
|
+
const notificationButton = editor.Panels.getPanel(PROJECT_BAR_PANEL_ID).view?.el.querySelector('.notifications-btn')
|
|
434
|
+
notificationCount = Array.isArray(notifications) ? notifications.length : 0
|
|
435
|
+
notificationCount > 0
|
|
436
|
+
? notificationButton?.classList.add('project-bar__dirty')
|
|
437
|
+
: notificationButton?.classList.remove('project-bar__dirty')
|
|
438
|
+
})
|
|
441
439
|
|
|
442
440
|
// GrapesJs editor is ready
|
|
443
441
|
resolve(editor)
|
|
@@ -68,7 +68,9 @@ export default (editor: Editor) => {
|
|
|
68
68
|
defaults: '',
|
|
69
69
|
},
|
|
70
70
|
],
|
|
71
|
-
shouldShow: comp =>
|
|
71
|
+
shouldShow: comp => {
|
|
72
|
+
return comp && ['ul', 'ol', 'li'].includes(comp.get('tagName')?.toLowerCase() || '')
|
|
73
|
+
},
|
|
72
74
|
},
|
|
73
75
|
]
|
|
74
76
|
|
|
@@ -46,12 +46,14 @@ export function registerSector(editor: Editor, config: SectorConfig, at?: number
|
|
|
46
46
|
async function applyVisibility(comp: Component = editor.getSelected()) {
|
|
47
47
|
const shouldShow = await config.shouldShow(comp)
|
|
48
48
|
if (sector.get('visible') !== shouldShow) {
|
|
49
|
+
sector.off('change:visible', applyVisibility)
|
|
49
50
|
sector.set('visible', shouldShow)
|
|
51
|
+
sector.on('change:visible', applyVisibility)
|
|
50
52
|
// sector.getProperties()
|
|
51
53
|
// .forEach(p => p.set('visible', shouldShow))
|
|
52
54
|
}
|
|
53
55
|
}
|
|
54
56
|
|
|
55
|
-
sector.on('change:visible',
|
|
57
|
+
sector.on('change:visible', applyVisibility)
|
|
56
58
|
editor.on('component:selected', comp => applyVisibility(comp))
|
|
57
59
|
}
|
|
@@ -21,7 +21,7 @@ import { cmdLogin, eventLoggedIn, eventLoggedOut, getCurrentUser, updateUser } f
|
|
|
21
21
|
import { addTempDataToAssetUrl, addTempDataToStyles, removeTempDataFromAssetUrl, removeTempDataFromStyles } from '../assetUrl'
|
|
22
22
|
import { PublicationStatus, PublishableEditor } from './PublicationManager'
|
|
23
23
|
import { ClientEvent } from '../events'
|
|
24
|
-
import { ProjectData } from 'grapesjs'
|
|
24
|
+
import { Page, PageProperties, ProjectData } from 'grapesjs'
|
|
25
25
|
import { html, render, TemplateResult } from 'lit-html'
|
|
26
26
|
import { unsafeHTML } from 'lit-html/directives/unsafe-html.js'
|
|
27
27
|
|
|
@@ -62,10 +62,11 @@ export const storagePlugin = (editor: PublishableEditor) => {
|
|
|
62
62
|
// Add useful commands
|
|
63
63
|
editor.Commands.add(cmdPauseAutoSave, {
|
|
64
64
|
run: () => {
|
|
65
|
-
if(!editor.StorageManager.isAutosave())
|
|
65
|
+
if(!editor.StorageManager.isAutosave()) console.warn('Autosave is not enabled')
|
|
66
66
|
editor.StorageManager.setAutosave(false)
|
|
67
67
|
},
|
|
68
68
|
stop: () => {
|
|
69
|
+
if(editor.StorageManager.isAutosave()) console.warn('Autosave is already enabled')
|
|
69
70
|
editor.editor.set('changesCount', 0)
|
|
70
71
|
editor.UndoManager.clear()
|
|
71
72
|
editor.StorageManager.setAutosave(true)
|
|
@@ -80,23 +81,34 @@ export const storagePlugin = (editor: PublishableEditor) => {
|
|
|
80
81
|
const data = await websiteLoad({ websiteId: options.id, connectorId: user.storage.connectorId }) as WebsiteData
|
|
81
82
|
if (data.assets) data.assets = addTempDataToAssetUrl(data.assets, options.id, user.storage.connectorId)
|
|
82
83
|
if (data.styles) data.styles = addTempDataToStyles(data.styles, options.id, user.storage.connectorId)
|
|
83
|
-
//setTimeout(() => progressiveLoadPages(editor, data))
|
|
84
84
|
if (!data.pages) {
|
|
85
85
|
// This happens when the website was just created
|
|
86
86
|
// Let grapesjs create the pages in the frontend
|
|
87
87
|
return data
|
|
88
88
|
} else {
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
editor.runCommand(cmdPauseAutoSave)
|
|
90
|
+
const { pages, ...rest } = data
|
|
91
|
+
// Load any additional project data, e.g. symbols, but not the ones we progressive load
|
|
92
|
+
loader && render(getLoaderHtml('Loading styles, assets and symbols', 0, pages.length + 1), loader)
|
|
93
|
+
// Add to the project, everything but pages
|
|
94
|
+
editor.loadProjectData(rest)
|
|
95
|
+
// Add the pages to the project
|
|
96
|
+
await progressiveLoadPages(editor, pages)
|
|
97
|
+
await nextFrame()
|
|
98
|
+
// Select the first page
|
|
99
|
+
const firstPage = editor.Pages.getAll()[0]
|
|
100
|
+
if (firstPage) editor.Pages.select(firstPage)
|
|
101
|
+
loader && render(getLoaderHtml('Starting', 1, 1), loader)
|
|
102
|
+
await nextFrame()
|
|
103
|
+
editor.once('canvas:frame:load', () => editor.stopCommand(cmdPauseAutoSave))
|
|
104
|
+
return data
|
|
91
105
|
}
|
|
92
106
|
} catch (err) {
|
|
93
107
|
editor.UndoManager.clear()
|
|
94
108
|
if (err.httpStatusCode === 401) {
|
|
95
109
|
editor.once(eventLoggedIn, async () => {
|
|
96
110
|
try {
|
|
97
|
-
editor.runCommand(cmdPauseAutoSave)
|
|
98
111
|
await editor.Storage.load(options)
|
|
99
|
-
editor.once('canvas:frame:load', () => editor.stopCommand(cmdPauseAutoSave))
|
|
100
112
|
} catch (err) {
|
|
101
113
|
console.error('connectorPlugin load error', err)
|
|
102
114
|
throw err
|
|
@@ -115,7 +127,7 @@ export const storagePlugin = (editor: PublishableEditor) => {
|
|
|
115
127
|
return await this.addToQueue(myData, options)
|
|
116
128
|
},
|
|
117
129
|
|
|
118
|
-
async addToQueue(data: WebsiteData, options) {
|
|
130
|
+
async addToQueue(data: WebsiteData, options: { id: WebsiteId, connectorId: ConnectorId }) {
|
|
119
131
|
// Handle concurrent saving
|
|
120
132
|
if (lastPendingSaving && lastPendingSaving !== data) {
|
|
121
133
|
// Cancel previous saving
|
|
@@ -125,7 +137,7 @@ export const storagePlugin = (editor: PublishableEditor) => {
|
|
|
125
137
|
return this.doStore(lastPendingSaving, options)
|
|
126
138
|
},
|
|
127
139
|
|
|
128
|
-
async doStore(data, options) {
|
|
140
|
+
async doStore(data: WebsiteData, options: { id: WebsiteId, connectorId: ConnectorId }) {
|
|
129
141
|
if (editor.PublicationManager.status === PublicationStatus.STATUS_PENDING) {
|
|
130
142
|
// Publication is pending, save is delayed
|
|
131
143
|
return new Promise((resolve) => {
|
|
@@ -217,32 +229,15 @@ export const storagePlugin = (editor: PublishableEditor) => {
|
|
|
217
229
|
}
|
|
218
230
|
}
|
|
219
231
|
|
|
220
|
-
async function progressiveLoadPages(editor: PublishableEditor,
|
|
232
|
+
async function progressiveLoadPages(editor: PublishableEditor, pages: Page[]) {
|
|
221
233
|
editor.Pages.getAll().forEach(page => editor.Pages.remove(page))
|
|
222
|
-
const max = data.pages.length + 1
|
|
223
234
|
let i = 0
|
|
224
|
-
for (const page of
|
|
225
|
-
loader && render(getLoaderHtml(`Loading page <strong>${++i}</strong> / ${
|
|
235
|
+
for (const page of pages) {
|
|
236
|
+
loader && render(getLoaderHtml(`Loading page <strong>${++i}</strong> / ${pages.length + 1}`, i, pages.length + 1), loader)
|
|
226
237
|
//await new Promise(resolve => setTimeout(resolve, 1000 * 60 * 1000))
|
|
227
238
|
await nextFrame()
|
|
228
239
|
const newPage = editor.Pages.add({
|
|
229
240
|
...page,
|
|
230
|
-
})
|
|
241
|
+
} as PageProperties)
|
|
231
242
|
}
|
|
232
|
-
|
|
233
|
-
// Charger les styles, assets, etc. après les pages
|
|
234
|
-
loader && render(getLoaderHtml('Loading styles and assets', data.pages.length + 1, max), loader)
|
|
235
|
-
if (data.styles) editor.setStyle(data.styles)
|
|
236
|
-
if (data.assets) editor.AssetManager.add(data.assets)
|
|
237
|
-
|
|
238
|
-
await nextFrame()
|
|
239
|
-
setTimeout(() => {
|
|
240
|
-
// FIXME: Why is this setTimeout needed?
|
|
241
|
-
editor.trigger('storage:end:load', data)
|
|
242
|
-
editor.trigger('canvas:frame:load', editor)
|
|
243
|
-
})
|
|
244
|
-
|
|
245
|
-
// Sélectionner la première page
|
|
246
|
-
const firstPage = editor.Pages.getAll()[0]
|
|
247
|
-
if (firstPage) editor.Pages.select(firstPage)
|
|
248
243
|
}
|
package/src/ts/client/index.ts
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
*
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
+
import { Editor } from 'grapesjs'
|
|
24
25
|
import { DEV_MESSAGE } from '../constants'
|
|
25
26
|
import { ClientConfig } from './config'
|
|
26
27
|
import { ClientEvent } from './events'
|
|
@@ -85,9 +86,7 @@ export async function start(options = {}): Promise<void> {
|
|
|
85
86
|
|
|
86
87
|
// Load the site
|
|
87
88
|
try {
|
|
88
|
-
editor.runCommand(cmdPauseAutoSave)
|
|
89
89
|
await editor.load(null)
|
|
90
|
-
editor.once('canvas:frame:load', () => editor.stopCommand(cmdPauseAutoSave))
|
|
91
90
|
} catch(e) {
|
|
92
91
|
if(e.httpStatusCode === 401) {
|
|
93
92
|
// Unauthorized, will try to login
|
|
@@ -103,7 +102,7 @@ export async function start(options = {}): Promise<void> {
|
|
|
103
102
|
}
|
|
104
103
|
}
|
|
105
104
|
|
|
106
|
-
function loaded(editor) {
|
|
105
|
+
function loaded(editor: Editor) {
|
|
107
106
|
document.querySelector('.silex-loader').classList.add('silex-dialog-hide')
|
|
108
107
|
document.querySelector('#gjs').classList.remove('silex-dialog-hide')
|
|
109
108
|
config.emit(ClientEvent.STARTUP_END, { editor, config })
|
package/src/ts/page.ts
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
// Page related functions
|
|
19
19
|
// This is used on the client and the server
|
|
20
|
-
export function getPageSlug(pageName) {
|
|
20
|
+
export function getPageSlug(pageName: string | undefined) {
|
|
21
21
|
return (pageName || 'index')
|
|
22
22
|
.toLowerCase()
|
|
23
23
|
.replace(/[^a-z0-9 -]/g, '')
|
|
@@ -2,6 +2,7 @@ import { ServerConfig } from '../../server/config'
|
|
|
2
2
|
import { expect, jest, beforeEach, afterEach, it, describe } from '@jest/globals'
|
|
3
3
|
import { ConnectorType, WebsiteData, WebsiteId } from '../../types'
|
|
4
4
|
import { Readable } from 'stream'
|
|
5
|
+
import { WEBSITE_DATA_FILE } from '../../constants'
|
|
5
6
|
|
|
6
7
|
const mocks = {
|
|
7
8
|
access: jest.fn(),
|
|
@@ -64,7 +65,7 @@ describe('FtpConnector website', () => {
|
|
|
64
65
|
await connector.updateWebsite(dummySession, dummyId, dummyWebsite)
|
|
65
66
|
expect(mocks.uploadFrom).toHaveBeenCalledTimes(1)
|
|
66
67
|
expect(mocks.uploadFrom.mock.calls[0][0]).toBeInstanceOf(Readable)
|
|
67
|
-
expect(mocks.uploadFrom.mock.calls[0][1]).toBe(`${storageRootPath}/${dummyId}
|
|
68
|
+
expect(mocks.uploadFrom.mock.calls[0][1]).toBe(`${storageRootPath}/${dummyId}/${WEBSITE_DATA_FILE}`)
|
|
68
69
|
})
|
|
69
70
|
})
|
|
70
71
|
|
|
@@ -19,7 +19,7 @@ import { Client } from 'basic-ftp'
|
|
|
19
19
|
import { Readable } from 'stream'
|
|
20
20
|
import { ConnectorFile, ConnectorFileContent, HostingConnector, StatusCallback, StorageConnector, contentToReadable, contentToString, toConnectorData, toConnectorEnum} from '../../server/connectors/connectors'
|
|
21
21
|
import { requiredParam } from '../../server/utils/validation'
|
|
22
|
-
import { WEBSITE_DATA_FILE, WEBSITE_META_DATA_FILE } from '../../constants'
|
|
22
|
+
import { WEBSITE_DATA_FILE, WEBSITE_META_DATA_FILE, WEBSITE_PAGES_FOLDER } 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'
|
|
@@ -28,6 +28,7 @@ import { v4 as uuid } from 'uuid'
|
|
|
28
28
|
import { JobManager } from '../../server/jobs'
|
|
29
29
|
import { mkdtemp, rm, rmdir } from 'fs/promises'
|
|
30
30
|
import { createReadStream } from 'fs'
|
|
31
|
+
import { stringify, split, merge } from '../../server/utils/websiteDataSerialization'
|
|
31
32
|
|
|
32
33
|
/**
|
|
33
34
|
* @fileoverview FTP connector for Silex
|
|
@@ -399,7 +400,7 @@ export default class FtpConnector implements StorageConnector<FtpSession> {
|
|
|
399
400
|
const websiteId = requiredParam<WebsiteId>(id, 'website id')
|
|
400
401
|
const path = join(this.rootPath(session), websiteId, WEBSITE_META_DATA_FILE)
|
|
401
402
|
const ftp = await this.getClient(this.sessionData(session))
|
|
402
|
-
await this.write(ftp, path,
|
|
403
|
+
await this.write(ftp, path, stringify(data))
|
|
403
404
|
this.closeClient(ftp)
|
|
404
405
|
}
|
|
405
406
|
|
|
@@ -442,10 +443,10 @@ export default class FtpConnector implements StorageConnector<FtpSession> {
|
|
|
442
443
|
await this.mkdir(ftp, assetsPath)
|
|
443
444
|
// create website data file
|
|
444
445
|
const websiteDataPath = join(this.rootPath(session), id, WEBSITE_DATA_FILE)
|
|
445
|
-
await this.write(ftp, websiteDataPath,
|
|
446
|
+
await this.write(ftp, websiteDataPath, stringify(defaultWebsiteData))
|
|
446
447
|
// create website meta data file
|
|
447
448
|
const websiteMetaDataPath = join(this.rootPath(session), id, WEBSITE_META_DATA_FILE)
|
|
448
|
-
await this.write(ftp, websiteMetaDataPath,
|
|
449
|
+
await this.write(ftp, websiteMetaDataPath, stringify({}))
|
|
449
450
|
// Clean up
|
|
450
451
|
this.closeClient(ftp)
|
|
451
452
|
// All good
|
|
@@ -469,16 +470,67 @@ export default class FtpConnector implements StorageConnector<FtpSession> {
|
|
|
469
470
|
const storageRootPath = this.rootPath(session)
|
|
470
471
|
const ftp = await this.getClient(this.sessionData(session))
|
|
471
472
|
const websiteDataPath = join(storageRootPath, websiteId, WEBSITE_DATA_FILE)
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
473
|
+
|
|
474
|
+
try {
|
|
475
|
+
const websiteDataStream = await this.read(ftp, websiteDataPath)
|
|
476
|
+
const websiteDataContent = await contentToString(websiteDataStream)
|
|
477
|
+
|
|
478
|
+
// Check if this is the new split format by trying to parse
|
|
479
|
+
const parsedData = JSON.parse(websiteDataContent)
|
|
480
|
+
if (parsedData.fileFormatVersion === '1.0.0') {
|
|
481
|
+
// Use the merge function to reconstruct website data
|
|
482
|
+
const pageLoader = async (pagePath: string): Promise<string> => {
|
|
483
|
+
const fullPath = join(storageRootPath, websiteId, pagePath)
|
|
484
|
+
const pageStream = await this.read(ftp, fullPath)
|
|
485
|
+
return await contentToString(pageStream)
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
const result = await merge(websiteDataContent, pageLoader)
|
|
489
|
+
this.closeClient(ftp)
|
|
490
|
+
return result
|
|
491
|
+
} else {
|
|
492
|
+
// Legacy format, return as is
|
|
493
|
+
this.closeClient(ftp)
|
|
494
|
+
return parsedData
|
|
495
|
+
}
|
|
496
|
+
} catch (error) {
|
|
497
|
+
this.closeClient(ftp)
|
|
498
|
+
throw error
|
|
499
|
+
}
|
|
475
500
|
}
|
|
476
501
|
|
|
477
502
|
async updateWebsite(session: FtpSession, websiteId: string, data: WebsiteData): Promise<void> {
|
|
478
503
|
const storageRootPath = this.rootPath(session)
|
|
479
504
|
const ftp = await this.getClient(this.sessionData(session))
|
|
480
|
-
|
|
481
|
-
|
|
505
|
+
|
|
506
|
+
// Use the split function to create separate files for pages
|
|
507
|
+
const filesToWrite = split(data)
|
|
508
|
+
|
|
509
|
+
// **
|
|
510
|
+
// Delete pages that are not in the new website data
|
|
511
|
+
const pagesPath = join(storageRootPath, websiteId, WEBSITE_PAGES_FOLDER)
|
|
512
|
+
try {
|
|
513
|
+
const existingPageFiles = await this.readdir(ftp, pagesPath)
|
|
514
|
+
const newPageFiles = new Set(
|
|
515
|
+
filesToWrite
|
|
516
|
+
.filter(f => f.path.startsWith(WEBSITE_PAGES_FOLDER))
|
|
517
|
+
.map(f => f.path.replace(`${WEBSITE_PAGES_FOLDER}/`, ''))
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
for (const existingFile of existingPageFiles) {
|
|
521
|
+
if (existingFile.name.endsWith('.json') && !newPageFiles.has(existingFile.name)) {
|
|
522
|
+
await this.unlink(ftp, join(pagesPath, existingFile.name))
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
} catch (error) {
|
|
526
|
+
// Ignore error if pages directory doesn't exist yet
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
for (const file of filesToWrite) {
|
|
530
|
+
const filePath = join(storageRootPath, websiteId, file.path)
|
|
531
|
+
await this.write(ftp, filePath, file.content)
|
|
532
|
+
}
|
|
533
|
+
|
|
482
534
|
this.closeClient(ftp)
|
|
483
535
|
}
|
|
484
536
|
|