maglevcms 1.1.5 → 1.1.6

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1592154952ea460b1a251f422ff5c2a195671024b1dc7cff35244eb0f22121bc
4
- data.tar.gz: '09969af10aa25986f2df6c5ce062f57889489befcc9bbd3d6c122a4fc7d70aba'
3
+ metadata.gz: f0d746d7653900e39f4c0a7b45ba3503a5688d37cc45f242943f7187729f607f
4
+ data.tar.gz: 4d5a6ff5eab5449ada366810c2ffedbde9466600faf1d30c8c4b6c906db24510
5
5
  SHA512:
6
- metadata.gz: f3937431f0c1da7bfed8238baf1f1179145b128537ebe82184a99ed659aeaf01b523c836c926cb87800e61cfd21ae119b35eecdde85ace9cb3d10f6d8ddf43d7
7
- data.tar.gz: a44cf3da138c61c43cdabd1be26c0253242cc3bfeb817bb59d65952ef716c55527138fcba2b76525d19523f8ff4fa8a6cebe095e9fe50154386415aa6b22fd03
6
+ metadata.gz: 6395309dbf50aed228ef2143e48bc4fb34f3184f715d9b70b478487567c56485483253b8e66a951a512e592917732be376815f78591a6fc02fae3e730bede303
7
+ data.tar.gz: 7fd1217aae956ca2b19228fe83c815f9905e18020047db71feccb15c20662ff1b720d9af2dd54874e0217e7ed0f2ef90235e49444c130b6bac24d7b27f92c715
@@ -27,7 +27,7 @@ module Maglev
27
27
 
28
28
  def fetch_maglev_page
29
29
  @fetch_maglev_page ||= maglev_services.fetch_page.call(
30
- path: params[:path],
30
+ path: maglev_page_path_from_params,
31
31
  locale: content_locale,
32
32
  default_locale: default_content_locale,
33
33
  fallback_to_default_locale: fallback_to_default_locale
@@ -72,6 +72,18 @@ module Maglev
72
72
  fetch_maglev_page
73
73
  end
74
74
 
75
+ def maglev_page_path_from_params
76
+ maglev_page_path_segments.join('/')
77
+ end
78
+
79
+ def maglev_page_path_segments
80
+ # we drop the path after the "_" segment
81
+ params[:path].split('/').reduce([]) do |memo, segment|
82
+ return memo if segment == '_'
83
+ memo.push(segment)
84
+ end
85
+ end
86
+
75
87
  def maglev_page_sections
76
88
  fetch_maglev_page_sections
77
89
  end
@@ -9,7 +9,7 @@ export default class extends Controller {
9
9
 
10
10
  if (height < 200) height = 200
11
11
 
12
- this.element.style.height = `${height}px`
12
+ this.element.style.height = `${height}px`
13
13
  }, 500)
14
14
  })
15
15
  }
@@ -2,7 +2,7 @@
2
2
  <nav class="h-full w-full">
3
3
  <div
4
4
  class="flex items-center justify-between h-full w-full animate-pulse"
5
- v-if="!currentPage"
5
+ v-if="isLoading"
6
6
  >
7
7
  <div class="h-6 bg-gray-200 rounded w-1/4 mx-6"></div>
8
8
  <div class="h-6 bg-gray-200 rounded w-1/4 mx-6"></div>
@@ -92,6 +92,9 @@ export default {
92
92
  PreviewToggler,
93
93
  },
94
94
  computed: {
95
+ isLoading() {
96
+ return !this.currentSite || !this.currentPage
97
+ },
95
98
  isListPagesActive() {
96
99
  return this.$route.name === 'listPages'
97
100
  },
@@ -3,10 +3,9 @@
3
3
  v-bind:is="icon"
4
4
  v-bind="$attrs"
5
5
  viewBox="0 0 24 24"
6
- :width="size"
7
- :height="size"
8
6
  class="fill-current"
9
7
  :class="{ 'animate-spin': spin, 'animate-bounce': bounce }"
8
+ :style="{ width: size, height: size }"
10
9
  ></component>
11
10
  </template>
12
11
 
@@ -8,8 +8,12 @@
8
8
  v-model="selectedOption"
9
9
  class="block w-full mt-1 py-2 px-3 rounded bg-gray-100 text-gray-800 focus:outline-none focus:ring placeholder-gray-500"
10
10
  >
11
- <option v-for="option in selectOptions" :key="option" :value="option">
12
- {{ option }}
11
+ <option
12
+ v-for="(option, index) in selectOptions"
13
+ :key="index"
14
+ :value="getOptionValue(option)"
15
+ >
16
+ {{ getOptionLabel(option) }}
13
17
  </option>
14
18
  </select>
15
19
  </div>
@@ -40,5 +44,13 @@ export default {
40
44
  },
41
45
  },
42
46
  },
47
+ methods: {
48
+ getOptionLabel(option) {
49
+ return typeof option === 'object' ? option.label : option
50
+ },
51
+ getOptionValue(option) {
52
+ return typeof option === 'object' ? option.value : option
53
+ },
54
+ },
43
55
  }
44
56
  </script>
@@ -1,11 +1,23 @@
1
1
  <template>
2
2
  <div class="h-full">
3
3
  <header class="flex h-16 border-b border-gray-200">
4
+ <router-link
5
+ v-if="currentSite && currentPage && currentLocale"
6
+ :to="{
7
+ name: 'editPage',
8
+ params: { locale: currentLocale, pageId: pageId },
9
+ }"
10
+ class="w-16 h-full flex justify-center items-center border-r border-gray-200"
11
+ >
12
+ <img v-bind:src="logoUrl" class="w-2/4" />
13
+ </router-link>
4
14
  <div
5
15
  class="w-16 h-full flex justify-center items-center border-r border-gray-200"
16
+ v-else
6
17
  >
7
18
  <img v-bind:src="logoUrl" class="w-2/4" />
8
19
  </div>
20
+
9
21
  <div class="flex flex-grow items-center h-full">
10
22
  <slot name="header"> [Layout] Default header </slot>
11
23
  </div>
@@ -31,6 +43,9 @@ export default {
31
43
  logoUrl() {
32
44
  return this.$store.state.editorSettings.logoUrl
33
45
  },
46
+ pageId() {
47
+ return this.currentPage.pathHash[this.currentLocale]
48
+ },
34
49
  },
35
50
  }
36
51
  </script>
@@ -125,3 +125,17 @@ export const deepMerge = (target, source) => {
125
125
 
126
126
  return result
127
127
  }
128
+
129
+ export const hasChanged = (newObject, oldObject, property) => {
130
+ return (
131
+ !!oldObject[property] &&
132
+ !!newObject[property] &&
133
+ oldObject[property] !== newObject[property]
134
+ )
135
+ }
136
+
137
+ export const hasAnyChanged = (newObject, oldObject, ...properties) => {
138
+ return properties.some((property) =>
139
+ hasChanged(newObject, oldObject, property),
140
+ )
141
+ }
@@ -2,6 +2,7 @@ import Vue from 'vue'
2
2
  import VueRouter from 'vue-router'
3
3
  import routes from './routes'
4
4
  import store from '@/store'
5
+ import { hasAnyChanged } from '@/misc/utils'
5
6
 
6
7
  Vue.use(VueRouter)
7
8
 
@@ -12,8 +13,29 @@ const router = new VueRouter({
12
13
  })
13
14
 
14
15
  router.beforeEach((to, from, next) => {
15
- if (to.params.pageId !== from.params.pageId && from.params.pageId)
16
- store.dispatch('resetPreview') // force the display of the loader
16
+ // The router hasn't found a component to display so get back
17
+ // to the screen without any UI drawer opened.
18
+ if (to.matched.length === 0) {
19
+ return next({
20
+ name: 'editPage',
21
+ params: {
22
+ pageId: store.state.page.pathHash[store.state.locale],
23
+ locale: store.state.locale,
24
+ },
25
+ })
26
+ }
27
+
28
+ // When an user wants to edit another page or to edit the current page
29
+ // in a different locale, the router detects it and dispatch the new
30
+ // page information to the Vuex store.
31
+ // Important: we don't do that at startup because we already have the current page
32
+ // and locale in the state.
33
+ if (hasAnyChanged(to.params, from.params, 'pageId', 'locale'))
34
+ store.dispatch('editPage', {
35
+ id: to.params.pageId,
36
+ locale: to.params.locale,
37
+ })
38
+
17
39
  next()
18
40
  })
19
41
 
@@ -25,6 +25,12 @@ export default [
25
25
  },
26
26
  props: { default: true },
27
27
  },
28
+ ],
29
+ },
30
+ {
31
+ path: '/:locale/:pageId/_',
32
+ component: AppLayout,
33
+ children: [
28
34
  {
29
35
  path: 'foo-test',
30
36
  name: 'test',
@@ -4,7 +4,7 @@ import EditPage from '@/views/pages/edit.vue'
4
4
 
5
5
  export default [
6
6
  {
7
- path: '__pages',
7
+ path: 'pages',
8
8
  name: 'listPages',
9
9
  components: {
10
10
  default: PagePreview,
@@ -3,7 +3,7 @@ import EditStylePane from '@/views/style/edit-pane.vue'
3
3
 
4
4
  export default [
5
5
  {
6
- path: '__style',
6
+ path: 'style',
7
7
  name: 'editStyle',
8
8
  components: {
9
9
  default: PagePreview,
@@ -58,7 +58,7 @@ export default (api) => ({
58
58
  findById: (site, id) => {
59
59
  if (id === 'index') id = site.homePageId
60
60
 
61
- const safeId = String(id).replace('/', '%2F')
61
+ const safeId = String(id).replaceAll('/', '%2F')
62
62
 
63
63
  console.log('[PageService] Fetching page by id', safeId)
64
64
 
@@ -1,15 +1,29 @@
1
1
  import { isBlank } from '@/misc/utils'
2
2
 
3
3
  export default (services) => ({
4
+ // editPage : Action triggered when the user wants to edit another page
5
+ // or to change the locale of the current page.
6
+ editPage({ state, commit, dispatch }, { id, locale }) {
7
+ console.log('editPage', id, locale)
8
+
9
+ // display the loader
10
+ dispatch('resetPreview')
11
+
12
+ if (state.locale !== locale) {
13
+ dispatch('setLocale', locale)
14
+ Promise.all([dispatch('fetchPage', id), dispatch('fetchSite')])
15
+ } else dispatch('fetchPage', id)
16
+ },
17
+
18
+ // Set page
19
+ setPage({ commit }, page) {
20
+ commit('SET_PAGE', page)
21
+ },
4
22
  // Fetch a page from an id (or a path)
5
- fetchPage({ commit, state: { site } }, id) {
6
- setTimeout(
7
- () =>
8
- services.page
9
- .findById(site, id)
10
- .then((page) => commit('SET_PAGE', page)),
11
- 100,
12
- )
23
+ async fetchPage({ commit, state: { site } }, id) {
24
+ return services.page
25
+ .findById(site, id)
26
+ .then((page) => commit('SET_PAGE', page))
13
27
  },
14
28
  // Persist the content of a page (including or not the site content)
15
29
  async persistPage({
@@ -1,6 +1,6 @@
1
1
  export default (services) => ({
2
- fetchSite({ commit }, locally) {
3
- services.site.find(locally).then((site) => {
2
+ async fetchSite({ commit }, locally) {
3
+ return services.site.find(locally).then((site) => {
4
4
  const { style, ...rawSite } = site
5
5
  services.api.setSiteHandle(site.handle)
6
6
  commit('SET_SITE', rawSite)
@@ -19,6 +19,7 @@ const store = new Vuex.Store({
19
19
 
20
20
  store.dispatch('fetchEditorSettings')
21
21
  store.dispatch('fetchSite', true)
22
+ store.dispatch('setPage', window.page)
22
23
  store.dispatch('setTheme', window.theme)
23
24
  store.dispatch('setLocale', window.locale)
24
25
 
@@ -94,10 +94,6 @@ export default {
94
94
  },
95
95
  computed: {
96
96
  ...mapState(['hoveredSection']),
97
- fullpath() {
98
- // TODO: why here? why not in the App.vue instead?
99
- return [this.locale, this.pageId]
100
- },
101
97
  isPageEmpty() {
102
98
  return this.currentSectionList.length === 0
103
99
  },
@@ -136,19 +132,6 @@ export default {
136
132
  this.services.livePreview.start(this.$refs['iframe'])
137
133
  },
138
134
  },
139
- watch: {
140
- fullpath: {
141
- immediate: true,
142
- handler(newFullpath, oldFullpath) {
143
- const [newLocale, newPageId] = newFullpath
144
- const [oldLocale, oldPageId] = oldFullpath || []
145
- if ((newLocale !== oldLocale || newPageId !== oldPageId) && newPageId) {
146
- this.setLocale(newLocale)
147
- Promise.all([this.fetchPage(newPageId), this.fetchSite()])
148
- }
149
- },
150
- },
151
- },
152
135
  }
153
136
  </script>
154
137
 
@@ -1,5 +1,6 @@
1
1
  import * as axios from 'axios'
2
2
  import { debounce } from './utils'
3
+ import runScripts from './run-scripts'
3
4
 
4
5
  const parentDocument = window.parent.document
5
6
  const previewDocument = window.document
@@ -165,6 +166,8 @@ const updatePreviewDocument = async (content, section, insertAt) => {
165
166
  targetElement = previewDocument.querySelector(selector)
166
167
  }
167
168
 
169
+ runScripts(targetElement)
170
+
168
171
  targetElement.scrollIntoView(true)
169
172
  }
170
173
 
@@ -0,0 +1,73 @@
1
+ export default function(element) {
2
+ var scripts;
3
+
4
+ // Get the scripts
5
+ scripts = element.getElementsByTagName("script");
6
+
7
+ // Run them in sequence (remember NodeLists are live)
8
+ continueLoading();
9
+
10
+ function continueLoading() {
11
+ var script, newscript;
12
+
13
+ // While we have a script to load...
14
+ while (scripts.length) {
15
+ // Get it and remove it from the DOM
16
+ script = scripts[0];
17
+ script.parentNode.removeChild(script);
18
+
19
+ // Create a replacement for it
20
+ newscript = document.createElement('script');
21
+
22
+ // External?
23
+ if (script.src) {
24
+ // Yes, we'll have to wait until it's loaded before continuing
25
+ newscript.onerror = continueLoadingOnError;
26
+ newscript.onload = continueLoadingOnLoad;
27
+ newscript.onreadystatechange = continueLoadingOnReady;
28
+ newscript.src = script.src;
29
+ }
30
+ else {
31
+ // No, we can do it right away
32
+ newscript.text = script.text;
33
+ }
34
+
35
+ // Start the script
36
+ document.documentElement.appendChild(newscript);
37
+
38
+ // If it's external, wait for callback
39
+ if (script.src) {
40
+ return;
41
+ }
42
+ }
43
+
44
+ // All scripts loaded
45
+ newscript = undefined;
46
+
47
+ // Callback on most browsers when a script is loaded
48
+ function continueLoadingOnLoad() {
49
+ // Defend against duplicate calls
50
+ if (this === newscript) {
51
+ continueLoading();
52
+ }
53
+ }
54
+
55
+ // Callback on most browsers when a script fails to load
56
+ function continueLoadingOnError() {
57
+ // Defend against duplicate calls
58
+ if (this === newscript) {
59
+ continueLoading();
60
+ }
61
+ }
62
+
63
+ // Callback on IE when a script's loading status changes
64
+ function continueLoadingOnReady() {
65
+
66
+ // Defend against duplicate calls and check whether the
67
+ // script is complete (complete = loaded or error)
68
+ if (this === newscript && this.readyState === "complete") {
69
+ continueLoading();
70
+ }
71
+ }
72
+ }
73
+ }
@@ -31,7 +31,11 @@ class Maglev::Section::Setting
31
31
  end
32
32
 
33
33
  def build_default_link_content(default)
34
- default.is_a?(String) ? { link_type: 'url', href: default } : { link_type: 'url', href: '#' }.merge(default)
34
+ if default.is_a?(String)
35
+ { text: 'Link', link_type: 'url', href: default }
36
+ else
37
+ { text: 'Link', link_type: 'url', href: '#' }.merge(default)
38
+ end
35
39
  end
36
40
 
37
41
  def build_default_checkbox_content(default)
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ json.sections services.get_page_sections.call(page: page)
4
+
5
+ json.key_format! camelize: :lower
6
+ json.deep_format_keys!
7
+
8
+ json.id page.id
9
+ json.title page.title || page.default_title
10
+ json.path page.path || page.default_path
11
+ json.path_hash page.path_hash
12
+ json.visible page.visible
13
+
14
+ json.seo_title page.seo_title
15
+ json.meta_description page.meta_description
16
+ json.og_title page.og_title
17
+ json.og_description page.og_description
18
+ json.og_image_url page.og_image_url
19
+
20
+ json.preview_url services.get_page_fullpath.call(page: page, preview_mode: true, locale: content_locale)
21
+ json.live_url services.get_page_fullpath.call(page: page, preview_mode: false, locale: content_locale)
22
+ json.section_names services.get_page_section_names.call(page: page)
23
+ json.lock_version page.lock_version
24
+ json.translated page.path.present?
@@ -1,24 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- json.sections services.get_page_sections.call(page: @page)
4
-
5
- json.key_format! camelize: :lower
6
- json.deep_format_keys!
7
-
8
- json.id @page.id
9
- json.title @page.title || @page.default_title
10
- json.path @page.path || @page.default_path
11
- json.path_hash @page.path_hash
12
- json.visible @page.visible
13
-
14
- json.seo_title @page.seo_title
15
- json.meta_description @page.meta_description
16
- json.og_title @page.og_title
17
- json.og_description @page.og_description
18
- json.og_image_url @page.og_image_url
19
-
20
- json.preview_url services.get_page_fullpath.call(page: @page, preview_mode: true, locale: content_locale)
21
- json.live_url services.get_page_fullpath.call(page: @page, preview_mode: false, locale: content_locale)
22
- json.section_names services.get_page_section_names.call(page: @page)
23
- json.lock_version @page.lock_version
24
- json.translated @page.path.present?
3
+ json.partial!('show', page: @page)
@@ -12,6 +12,7 @@
12
12
  window.leaveUrl = <%= site_leave_editor_path.to_json.html_safe %>;
13
13
  window.apiBaseUrl = <%= h api_base_path.to_json.html_safe %>;
14
14
  window.site = <%= h render(partial: 'maglev/api/sites/show', formats: :json, locals: { site: maglev_site, home_page_id: maglev_home_page_id }).html_safe %>;
15
+ window.page = <%= h render(partial: 'maglev/api/pages/show', formats: :json, locals: { page: maglev_page }).html_safe %>;
15
16
  window.theme = <%= h render(partial: 'maglev/api/themes/show', formats: :json, locals: { theme: maglev_theme }).html_safe %>;
16
17
  window.logoUrl = <%= editor_logo_url.to_json.html_safe %>;
17
18
  window.primaryColor = <%= editor_primary_hex_color.to_json.html_safe %>;
data/config/routes.rb CHANGED
@@ -42,7 +42,7 @@ Maglev::Engine.routes.draw do
42
42
 
43
43
  # Editor + Preview
44
44
  get 'editor', to: 'editor#show', as: :base_editor
45
- get 'editor/:locale/(*something)', to: 'editor#show', as: :editor
45
+ get 'editor/:locale/(*path)', to: 'editor#show', as: :editor
46
46
  get 'leave_editor', to: 'editor#destroy', as: :leave_editor
47
47
  get 'preview/(*path)', to: 'page_preview#index',
48
48
  defaults: { path: 'index', rendering_mode: :editor },
@@ -109,7 +109,7 @@ module Maglev
109
109
  when 'text' then label
110
110
  when 'image' then '"/theme/image-placeholder.jpg"'
111
111
  when 'checkbox' then true
112
- when 'link' then '"#"'
112
+ when 'link' then '{ text: "Link", url: "#" }'
113
113
  when 'color' then '#E5E7EB'
114
114
  when 'radio', 'select' then 'option_1'
115
115
  when 'icon' then 'default-icon-class'
@@ -30,6 +30,8 @@ settings:
30
30
  # html: true
31
31
  # line_break: true
32
32
  # nb_rows: 5
33
+ <%- when 'link' -%>
34
+ with_text: true
33
35
  <%- when 'select', 'radio' -%>
34
36
  options:
35
37
  - label: Option 1
@@ -59,6 +61,8 @@ blocks: <%- if blocks.blank? -%>[]
59
61
  # html: true
60
62
  # line_break: true
61
63
  # nb_rows: 5
64
+ <%- when 'link' -%>
65
+ with_text: true
62
66
  <%- when 'select', 'radio' -%>
63
67
  options:
64
68
  - label: Option 1
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Maglev
4
- VERSION = '1.1.5'
4
+ VERSION = '1.1.6'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: maglevcms
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.5
4
+ version: 1.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Didier Lafforgue
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-11-20 00:00:00.000000000 Z
11
+ date: 2022-11-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jbuilder
@@ -692,6 +692,7 @@ files:
692
692
  - app/frontend/live-preview-client/index.js
693
693
  - app/frontend/live-preview-client/message.js
694
694
  - app/frontend/live-preview-client/rails.js
695
+ - app/frontend/live-preview-client/run-scripts.js
695
696
  - app/frontend/live-preview-client/utils.js
696
697
  - app/helpers/maglev/admin/sections/previews_helper.rb
697
698
  - app/helpers/maglev/admin/themes_helper.rb
@@ -761,6 +762,7 @@ files:
761
762
  - app/views/maglev/api/assets/show.json.jbuilder
762
763
  - app/views/maglev/api/collection_items/_show.json.jbuilder
763
764
  - app/views/maglev/api/collection_items/index.json.jbuilder
765
+ - app/views/maglev/api/pages/_show.json.jbuilder
764
766
  - app/views/maglev/api/pages/index.json.jbuilder
765
767
  - app/views/maglev/api/pages/show.json.jbuilder
766
768
  - app/views/maglev/api/sites/_show.json.jbuilder