maglevcms 1.1.5 → 1.1.7

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