maglevcms 1.1.5 → 1.1.6

Sign up to get free protection for your applications and to get access to all the features.
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