maglevcms 1.1.5 → 1.1.7

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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/concerns/maglev/fetchers_concern.rb +14 -1
  3. data/app/controllers/concerns/maglev/standalone_sections_concern.rb +6 -0
  4. data/app/frontend/admin/controllers/iframe_controller.js +1 -1
  5. data/app/frontend/editor/components/dynamic-form/dynamic-input.vue +1 -0
  6. data/app/frontend/editor/components/header-nav/index.vue +4 -1
  7. data/app/frontend/editor/components/kit/collection-item-input.vue +5 -2
  8. data/app/frontend/editor/components/kit/icon.vue +1 -2
  9. data/app/frontend/editor/components/kit/simple-select.vue +14 -2
  10. data/app/frontend/editor/layouts/default.vue +15 -0
  11. data/app/frontend/editor/misc/utils.js +31 -0
  12. data/app/frontend/editor/router/index.js +24 -2
  13. data/app/frontend/editor/router/routes/base.js +6 -0
  14. data/app/frontend/editor/router/routes/page.js +1 -1
  15. data/app/frontend/editor/router/routes/style.js +1 -1
  16. data/app/frontend/editor/services/page.js +1 -1
  17. data/app/frontend/editor/store/actions/page.js +22 -8
  18. data/app/frontend/editor/store/actions/site.js +2 -2
  19. data/app/frontend/editor/store/index.js +1 -0
  20. data/app/frontend/editor/views/page-preview.vue +0 -17
  21. data/app/frontend/live-preview-client/rails.js +3 -0
  22. data/app/frontend/live-preview-client/run-scripts.js +73 -0
  23. data/app/models/maglev/section/setting.rb +10 -1
  24. data/app/services/concerns/maglev/get_page_sections/transform_collection_item_concern.rb +1 -1
  25. data/app/services/maglev/fetch_collection_items.rb +6 -2
  26. data/app/views/maglev/api/pages/_show.json.jbuilder +24 -0
  27. data/app/views/maglev/api/pages/show.json.jbuilder +1 -22
  28. data/app/views/maglev/editor/show.html.erb +1 -0
  29. data/config/routes.rb +1 -1
  30. data/db/migrate/20210830085101_create_maglev_page_paths.rb +1 -1
  31. data/db/migrate/20210906102712_add_canonical_to_pages.rb +1 -1
  32. data/db/migrate/20211008064437_add_locales_to_sites.rb +1 -1
  33. data/db/migrate/20211013210954_translate_section_content.rb +1 -1
  34. data/db/migrate/20211101205001_add_lock_version_to_maglev_pages.rb +1 -1
  35. data/db/migrate/20211116161121_better_page_path_canonical_indices.rb +1 -1
  36. data/db/migrate/20211124101005_fix_page_path_indices.rb +1 -1
  37. data/db/migrate/20211203224112_add_open_graph_tags_to_pages.rb +1 -1
  38. data/db/migrate/20220612092235_add_style_to_sites.rb +1 -1
  39. data/lib/generators/maglev/section_generator.rb +2 -1
  40. data/lib/generators/maglev/templates/section/app/theme/sections/%category%/%file_name%.yml.tt +8 -0
  41. data/lib/maglev/version.rb +1 -1
  42. metadata +4 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1592154952ea460b1a251f422ff5c2a195671024b1dc7cff35244eb0f22121bc
4
- data.tar.gz: '09969af10aa25986f2df6c5ce062f57889489befcc9bbd3d6c122a4fc7d70aba'
3
+ metadata.gz: d6ebce98e16aca2b48cc0029e9c9168602b29f8bcd320261904c634b4d97132d
4
+ data.tar.gz: 463986303bb445fdc237f1089186055284ff29cb62773a878a7f4ae1d90ab7a6
5
5
  SHA512:
6
- metadata.gz: f3937431f0c1da7bfed8238baf1f1179145b128537ebe82184a99ed659aeaf01b523c836c926cb87800e61cfd21ae119b35eecdde85ace9cb3d10f6d8ddf43d7
7
- data.tar.gz: a44cf3da138c61c43cdabd1be26c0253242cc3bfeb817bb59d65952ef716c55527138fcba2b76525d19523f8ff4fa8a6cebe095e9fe50154386415aa6b22fd03
6
+ metadata.gz: c6d853c3d35076069a0ba22053b8828a8e837eabaf3c715e4ac9955ac2f38663cd6cad9d86b55f759b310633e564b19c8373d2fd06df8c3b6a05b2bb8eeaf026
7
+ data.tar.gz: f3e2b09342501025517ef5a11ce16f8e2f213fd1b99fb760312edf99b321220cf2e65ea73c3feabf12c6706f27b5dede2482adb0686d160537558334daa76cd7
@@ -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,19 @@ 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
+
84
+ memo.push(segment)
85
+ end
86
+ end
87
+
75
88
  def maglev_page_sections
76
89
  fetch_maglev_page_sections
77
90
  end
@@ -15,6 +15,8 @@ module Maglev
15
15
  private
16
16
 
17
17
  def fetch_maglev_site_scoped_sections
18
+ return if within_maglev_engine?
19
+
18
20
  fetch_maglev_site
19
21
  fetch_maglev_theme
20
22
  fetch_maglev_dummy_page
@@ -24,5 +26,9 @@ module Maglev
24
26
  def fetch_maglev_dummy_page
25
27
  @fetch_maglev_page = ::Maglev::Page.new(title: 'DummyPage', sections: fetch_maglev_site.sections)
26
28
  end
29
+
30
+ def within_maglev_engine?
31
+ controller_path.starts_with?('maglev/')
32
+ end
27
33
  end
28
34
  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
  }
@@ -80,6 +80,7 @@
80
80
  :label="setting.label"
81
81
  :name="setting.id"
82
82
  v-model="inputValue"
83
+ :collection-id="options.collectionId"
83
84
  v-if="setting.type == 'collection_item'"
84
85
  />
85
86
  </div>
@@ -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
  },
@@ -10,7 +10,7 @@
10
10
  :searchEnabled="true"
11
11
  :searchPlaceholder="$t(`collectionItemInput.select.searchPlaceholder`)"
12
12
  :emptyLabel="$t(`collectionItemInput.select.emptyLabel`)"
13
- :fetchList="(q) => services.collectionItem.findAll('products', { q })"
13
+ :fetchList="(q) => services.collectionItem.findAll(collectionId, { q })"
14
14
  :clearEnabled="true"
15
15
  buttonClass="h-10"
16
16
  v-model="selectedCollectionItem"
@@ -42,18 +42,21 @@
42
42
  </template>
43
43
 
44
44
  <script>
45
+ import { camelizeKeys } from '@/misc/utils'
46
+
45
47
  export default {
46
48
  name: 'CollectionItemInput',
47
49
  props: {
48
50
  label: { type: String, default: 'Label' },
49
51
  name: { type: String, default: 'image' },
50
52
  value: { default: () => null },
53
+ collectionId: { type: String },
51
54
  isFocused: { type: Boolean, default: false },
52
55
  },
53
56
  computed: {
54
57
  selectedCollectionItem: {
55
58
  get() {
56
- return this.value
59
+ return camelizeKeys(this.value)
57
60
  },
58
61
  set(collectionItem) {
59
62
  this.$emit('input', collectionItem ? { ...collectionItem } : null)
@@ -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>
@@ -8,11 +8,28 @@ export const isBlank = (object) => {
8
8
  }
9
9
 
10
10
  export const camelize = (str) => {
11
+ if (!str.includes('_')) return str
11
12
  return str
12
13
  .toLowerCase()
13
14
  .replace(/[^a-zA-Z0-9]+(.)/g, (m, chr) => chr.toUpperCase())
14
15
  }
15
16
 
17
+ export const camelizeKeys = (obj) => {
18
+ if (Array.isArray(obj)) {
19
+ return obj.map(v => camelizeKeys(v));
20
+ } else if (obj != null && obj.constructor === Object) {
21
+ return Object.keys(obj).reduce(
22
+ (result, key) => ({
23
+ ...result,
24
+ [camelize(key)]: camelizeKeys(obj[key]),
25
+ }),
26
+ {},
27
+ );
28
+ }
29
+ return obj;
30
+ }
31
+
32
+
16
33
  export const numberToHumanSize = (size, i18n) => {
17
34
  if (isBlank(size)) return null
18
35
 
@@ -125,3 +142,17 @@ export const deepMerge = (target, source) => {
125
142
 
126
143
  return result
127
144
  }
145
+
146
+ export const hasChanged = (newObject, oldObject, property) => {
147
+ return (
148
+ !!oldObject[property] &&
149
+ !!newObject[property] &&
150
+ oldObject[property] !== newObject[property]
151
+ )
152
+ }
153
+
154
+ export const hasAnyChanged = (newObject, oldObject, ...properties) => {
155
+ return properties.some((property) =>
156
+ hasChanged(newObject, oldObject, property),
157
+ )
158
+ }
@@ -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
+ }
@@ -21,6 +21,7 @@ class Maglev::Section::Setting
21
21
  when :image then build_default_image_content(default)
22
22
  when :link then build_default_link_content(default)
23
23
  when :checkbox then build_default_checkbox_content(default)
24
+ when :collection_item then build_default_collection_item_content(default)
24
25
  else
25
26
  default || label
26
27
  end
@@ -31,13 +32,21 @@ class Maglev::Section::Setting
31
32
  end
32
33
 
33
34
  def build_default_link_content(default)
34
- default.is_a?(String) ? { link_type: 'url', href: default } : { link_type: 'url', href: '#' }.merge(default)
35
+ if default.is_a?(String)
36
+ { text: 'Link', link_type: 'url', href: default }
37
+ else
38
+ { text: 'Link', link_type: 'url', href: '#' }.merge(default)
39
+ end
35
40
  end
36
41
 
37
42
  def build_default_checkbox_content(default)
38
43
  default.presence || false
39
44
  end
40
45
 
46
+ def build_default_collection_item_content(default)
47
+ { id: default }
48
+ end
49
+
41
50
  ## class methods ##
42
51
  def self.build(hash)
43
52
  attributes = hash.slice('id', 'label', 'type', 'default')
@@ -3,7 +3,7 @@
3
3
  # rubocop:disable Style/ClassAndModuleChildren
4
4
  module Maglev::GetPageSections::TransformCollectionItemConcern
5
5
  def transform_collection_item_content_setting(content, setting)
6
- item_id = content.dig('value', 'id')
6
+ item_id = content&.dig('value', 'id')
7
7
  return if item_id.blank?
8
8
 
9
9
  item = fetch_collection_items.call(
@@ -33,7 +33,7 @@ module Maglev
33
33
 
34
34
  def fetch_item
35
35
  build_item(
36
- fetch_original_items.find_by(id: id)
36
+ id == 'any' ? fetch_original_items.first : fetch_original_items.find_by(id: id)
37
37
  )
38
38
  end
39
39
 
@@ -78,7 +78,11 @@ module Maglev
78
78
  end
79
79
 
80
80
  def collection
81
- config.collections[collection_id.to_sym]
81
+ config.collections[collection_id.to_sym].tap do |collection|
82
+ next if collection
83
+
84
+ raise "[Maglev] unregistered '#{collection_id}' collection in the Maglev configuration."
85
+ end
82
86
  end
83
87
 
84
88
  def build_item(original_item)
@@ -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 },
@@ -1,4 +1,4 @@
1
- class CreateMaglevPagePaths < ActiveRecord::Migration[6.1]
1
+ class CreateMaglevPagePaths < ActiveRecord::Migration[6.0]
2
2
  def change
3
3
  create_table :maglev_page_paths do |t|
4
4
  t.references :maglev_page
@@ -1,4 +1,4 @@
1
- class AddCanonicalToPages < ActiveRecord::Migration[6.1]
1
+ class AddCanonicalToPages < ActiveRecord::Migration[6.0]
2
2
  def change
3
3
  add_column :maglev_page_paths, :canonical, :boolean, null: true, default: true
4
4
  add_index :maglev_page_paths, %i[canonical maglev_page_id locale], unique: true, name: 'canonical_uniqueness'
@@ -1,4 +1,4 @@
1
- class AddLocalesToSites < ActiveRecord::Migration[6.1]
1
+ class AddLocalesToSites < ActiveRecord::Migration[6.0]
2
2
  def change
3
3
  add_column :maglev_sites, :locales, :jsonb, default: []
4
4
  end
@@ -1,4 +1,4 @@
1
- class TranslateSectionContent < ActiveRecord::Migration[6.1]
1
+ class TranslateSectionContent < ActiveRecord::Migration[6.0]
2
2
  def change
3
3
  remove_column :maglev_sites, :sections, :jsonb, default: []
4
4
  add_column :maglev_sites, :sections_translations, :jsonb, default: {}
@@ -1,4 +1,4 @@
1
- class AddLockVersionToMaglevPages < ActiveRecord::Migration[6.1]
1
+ class AddLockVersionToMaglevPages < ActiveRecord::Migration[6.0]
2
2
  def change
3
3
  add_column :maglev_sites, :lock_version, :integer
4
4
  add_column :maglev_pages, :lock_version, :integer
@@ -1,4 +1,4 @@
1
- class BetterPagePathCanonicalIndices < ActiveRecord::Migration[6.1]
1
+ class BetterPagePathCanonicalIndices < ActiveRecord::Migration[6.0]
2
2
  def change
3
3
  remove_index :maglev_page_paths, [:value, :locale], unique: true
4
4
  remove_index :maglev_page_paths, %i[canonical maglev_page_id locale], unique: true, name: 'canonical_uniqueness'
@@ -1,4 +1,4 @@
1
- class FixPagePathIndices < ActiveRecord::Migration[6.1]
1
+ class FixPagePathIndices < ActiveRecord::Migration[6.0]
2
2
  def change
3
3
  remove_index :maglev_page_paths, %i[canonical maglev_page_id locale value], unique: true, name: 'canonical_uniqueness'
4
4
  add_index :maglev_page_paths, %i[canonical maglev_page_id locale], name: 'scoped_canonical_speed'
@@ -1,4 +1,4 @@
1
- class AddOpenGraphTagsToPages < ActiveRecord::Migration[6.1]
1
+ class AddOpenGraphTagsToPages < ActiveRecord::Migration[6.0]
2
2
  def change
3
3
  change_table :maglev_pages do |t|
4
4
  t.jsonb :og_title_translations, default: {}
@@ -1,4 +1,4 @@
1
- class AddStyleToSites < ActiveRecord::Migration[6.1]
1
+ class AddStyleToSites < ActiveRecord::Migration[6.0]
2
2
  def change
3
3
  change_table :maglev_sites do |t|
4
4
  t.jsonb :style, default: []
@@ -109,10 +109,11 @@ 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'
116
+ when 'collection_item' then 'any'
116
117
  end
117
118
  end
118
119
  # rubocop:enable Metrics/CyclomaticComplexity
@@ -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
@@ -38,6 +40,8 @@ settings:
38
40
  value: option_2
39
41
  <%- when 'color' -%>
40
42
  presets: ['#E5E7EB', '#FECACA', '#FDE68A', '#A7F3D0', '#BFDBFE']
43
+ <%- when 'collection_item' -%>
44
+ collection_id: products # check your config/initializers/maglev.rb to register your collection
41
45
  <%- end -%>
42
46
  <% if setting.value? -%>default: <%= setting.default %><% end -%>
43
47
 
@@ -59,12 +63,16 @@ blocks: <%- if blocks.blank? -%>[]
59
63
  # html: true
60
64
  # line_break: true
61
65
  # nb_rows: 5
66
+ <%- when 'link' -%>
67
+ with_text: true
62
68
  <%- when 'select', 'radio' -%>
63
69
  options:
64
70
  - label: Option 1
65
71
  value: option_1
66
72
  - label: Option 2
67
73
  value: option_2
74
+ <%- when 'collection_item' -%>
75
+ collection_id: products # check your config/initializers/maglev.rb to register your collection
68
76
  <%- end -%>
69
77
  <%- if setting.value? -%>
70
78
  default: <%= setting.default %>
@@ -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.7'
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.7
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-12-11 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