alchemy_cms 5.3.1 → 5.3.4

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: 0d0f346b9e40e791697acdf21d5e1193c72023f8f2570b777683ba2e781b67c1
4
- data.tar.gz: b0c6a7f6e466bd0b134f9a7fdc76cb88ec1a56d0ca068209fa6b004619ed3a8e
3
+ metadata.gz: 37f7ca98f0e6c0d8729e8ba6ef3fd432449dc5dba0b900ef72f9bd7494e406d5
4
+ data.tar.gz: 0d3871f0f550d07b09eda00cd5a26427d9c80303cc0f008aea496a7a3e9a6579
5
5
  SHA512:
6
- metadata.gz: e10199dcdc4b12d01bfebef27d23b752d1dd95c393263dac664c2d5588677aca80113c8b99ca281aefa130592767cc01304ab635d09273c63f3cc575ab2a1313
7
- data.tar.gz: eaf3f967e1cb5ac4644cddf31ffdfcf3e931107be5508091fe94f61ceb7c096bacc4e39a8203ab8ba1ddf820992abfdae795774f91ac4ff571fc7687f7efd0ba
6
+ metadata.gz: c91fb49f9939ad01bb30e88fe3042898d4cca8126e0d3ebc143f7a2232cf67cbbc176ca796c8b14f517683e975172ed8d7f92359989297cce30274bdb5bc981d
7
+ data.tar.gz: a3e8bcf872db1d3276dffb0e56baa01a125e24f4ede546359854032a45446d5abc44b7559ab8f76fb1da8b692572ed7608f51542bf5a70dafb8a1ecb75b9bce3
data/CHANGELOG.md CHANGED
@@ -1,3 +1,20 @@
1
+ ## 5.3.4 (2022-04-11)
2
+
3
+ - Don't delete locals in render_element so they can be used by all elem… [#2284](https://github.com/AlchemyCMS/alchemy_cms/pull/2284) ([dbwinger](https://github.com/dbwinger))
4
+ - Show site and language name on page select in Link dialog [#2280](https://github.com/AlchemyCMS/alchemy_cms/pull/2280) ([dbwinger](https://github.com/dbwinger))
5
+
6
+ ## 5.3.3 (2022-03-24)
7
+
8
+ - fix admin sitemap feature specs ([tvdeyen](https://github.com/tvdeyen))
9
+ - fix: Add support for ajax.get query params ([tvdeyen](https://github.com/tvdeyen))
10
+ - fix(Sitemap): Use response data ([tvdeyen](https://github.com/tvdeyen))
11
+ - Revert "Ajax: Send method override" ([tvdeyen](https://github.com/tvdeyen))
12
+
13
+ ## 5.3.2 (2022-03-24)
14
+
15
+ - ImageLoader: Add error handling ([tvdeyen](https://github.com/tvdeyen))
16
+ - Fix new Sitemap ([tvdeyen](https://github.com/tvdeyen))
17
+
1
18
  ## 5.3.1 (2022-03-11)
2
19
 
3
20
  - Allow all pages in API again ([tvdeyen](https://github.com/tvdeyen))
@@ -54,9 +54,12 @@ $.extend Alchemy,
54
54
  image.on 'load', ->
55
55
  spinner.stop()
56
56
  image.fadeIn 400
57
- image.on 'error', ->
57
+ image.on 'error', (evt) ->
58
+ message = "Could not load #{this.src}"
58
59
  spinner.stop()
59
- $parent.html('<span class="icon warn"/>')
60
+ console.error(message, evt)
61
+ $parent.html('<span class="icon fas fa-exclamation-triangle" title="' + message + '" />')
62
+ return
60
63
 
61
64
  # Removes the picture from essence picture thumbnail
62
65
  removePicture: (selector) ->
@@ -80,6 +80,8 @@ class window.Alchemy.LinkDialog extends Alchemy.Dialog
80
80
  name: page.name
81
81
  url_path: page.url_path
82
82
  page_id: page.id
83
+ language: page.language
84
+ site: page.site
83
85
  more: meta.page * meta.per_page < meta.total_count
84
86
  initSelection: ($element, callback) =>
85
87
  urlname = $element.val()
@@ -33,6 +33,10 @@ div#image_assign_filter_and_image_sizing {
33
33
  background-color: $thumbnail-background-color;
34
34
  width: 100%;
35
35
  height: 120px;
36
+
37
+ &:hover {
38
+ text-decoration: none;
39
+ }
36
40
  }
37
41
 
38
42
  .picture_thumbnail {
@@ -142,8 +142,8 @@ module Alchemy
142
142
  render element, {
143
143
  element: element,
144
144
  counter: counter,
145
- options: options,
146
- }.merge(options.delete(:locals) || {})
145
+ options: options.except(:locals),
146
+ }.merge(options[:locals] || {})
147
147
  rescue ActionView::MissingTemplate => e
148
148
  warning(%(
149
149
  Element view partial not found for #{element.name}.\n
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Alchemy
4
- VERSION = "5.3.1"
4
+ VERSION = "5.3.4"
5
5
 
6
6
  def self.version
7
7
  VERSION
@@ -1,5 +1,5 @@
1
1
  import Sortable from "sortablejs"
2
- import ajax from "./utils/ajax"
2
+ import { patch } from "./utils/ajax"
3
3
  import { on } from "./utils/events"
4
4
 
5
5
  function displayNodeFolders() {
@@ -23,7 +23,7 @@ function onFinishDragging(evt) {
23
23
  new_position: evt.newIndex
24
24
  }
25
25
 
26
- ajax("PATCH", url, data)
26
+ patch(url, data)
27
27
  .then(() => {
28
28
  const message = Alchemy.t("Successfully moved menu item")
29
29
  Alchemy.growl(message)
@@ -41,7 +41,7 @@ function handleNodeFolders() {
41
41
  const url = Alchemy.routes.toggle_folded_api_node_path(nodeId)
42
42
  const list = menu_item.querySelector(".children")
43
43
 
44
- ajax("PATCH", url)
44
+ patch(url)
45
45
  .then(() => {
46
46
  list.classList.toggle("folded")
47
47
  menu_item.dataset.folded =
@@ -1,4 +1,5 @@
1
1
  import Sortable from "sortablejs"
2
+ import { patch } from "./utils/ajax"
2
3
 
3
4
  function onFinishDragging(evt) {
4
5
  const pageId = evt.item.dataset.pageId
@@ -8,16 +9,9 @@ function onFinishDragging(evt) {
8
9
  new_position: evt.newIndex
9
10
  }
10
11
 
11
- fetch(url, {
12
- method: "PATCH",
13
- headers: {
14
- "Content-Type": "application/json",
15
- Accept: "application/json"
16
- },
17
- body: JSON.stringify(data)
18
- })
12
+ patch(url, data)
19
13
  .then(async (response) => {
20
- const pageData = await response.json()
14
+ const pageData = await response.data
21
15
  const pageEl = document.getElementById(`page_${pageId}`)
22
16
  const urlPathEl = pageEl.querySelector(".sitemap_url")
23
17
 
@@ -1,6 +1,7 @@
1
1
  // The admin sitemap Alchemy class
2
2
  import PageSorter from "./page_sorter"
3
3
  import { on } from "./utils/events"
4
+ import { get, patch } from "./utils/ajax"
4
5
  import { createSortables, displayPageFolders } from "./page_sorter"
5
6
 
6
7
  export default class Sitemap {
@@ -30,9 +31,9 @@ export default class Sitemap {
30
31
  const spinTarget = this.sitemap_wrapper
31
32
  spinTarget.innerHTML = ""
32
33
  spinner.spin(spinTarget)
33
- fetch(`${this.options.url}?id=${pageId}`)
34
+ get(this.options.url, { id: pageId })
34
35
  .then(async (response) => {
35
- this.render(await response.json())
36
+ this.render(await response.data)
36
37
  this.handlePageFolders()
37
38
  spinner.stop()
38
39
  })
@@ -52,14 +53,9 @@ export default class Sitemap {
52
53
  pageFolder.innerHTML = ""
53
54
  spinner.spin(pageFolder)
54
55
 
55
- fetch(Alchemy.routes.fold_admin_page_path(pageId), {
56
- method: "PATCH",
57
- headers: {
58
- Accept: "application/json"
59
- }
60
- })
56
+ patch(Alchemy.routes.fold_admin_page_path(pageId))
61
57
  .then(async (response) => {
62
- this.reRender(pageId, await response.json())
58
+ this.reRender(pageId, await response.data)
63
59
  spinner.stop()
64
60
  })
65
61
  .catch(this.errorHandler)
@@ -1,5 +1,5 @@
1
1
  import xhrMock from "xhr-mock"
2
- import ajax from "../ajax"
2
+ import { get, patch, post } from "../ajax"
3
3
 
4
4
  const token = "s3cr3t"
5
5
 
@@ -8,116 +8,160 @@ beforeEach(() => {
8
8
  xhrMock.setup()
9
9
  })
10
10
 
11
- describe("ajax('get')", () => {
11
+ describe("get", () => {
12
12
  it("sends X-CSRF-TOKEN header", async () => {
13
- xhrMock.get("/users", (req, res) => {
13
+ xhrMock.get("http://localhost/users", (req, res) => {
14
14
  expect(req.header("X-CSRF-TOKEN")).toEqual(token)
15
15
  return res.status(200).body('{"message":"Ok"}')
16
16
  })
17
- await ajax("get", "/users")
17
+ await get("/users")
18
18
  })
19
19
 
20
20
  it("sends Content-Type header", async () => {
21
- xhrMock.get("/users", (req, res) => {
21
+ xhrMock.get("http://localhost/users", (req, res) => {
22
22
  expect(req.header("Content-Type")).toEqual(
23
23
  "application/json; charset=utf-8"
24
24
  )
25
25
  return res.status(200).body('{"message":"Ok"}')
26
26
  })
27
- await ajax("get", "/users")
27
+ await get("/users")
28
28
  })
29
29
 
30
30
  it("sends Accept header", async () => {
31
- xhrMock.get("/users", (req, res) => {
31
+ xhrMock.get("http://localhost/users", (req, res) => {
32
32
  expect(req.header("Accept")).toEqual("application/json")
33
33
  return res.status(200).body('{"message":"Ok"}')
34
34
  })
35
- await ajax("get", "/users")
35
+ await get("/users")
36
36
  })
37
37
 
38
38
  it("returns JSON", async () => {
39
- xhrMock.get("/users", (_req, res) => {
39
+ xhrMock.get("http://localhost/users", (_req, res) => {
40
40
  return res.status(200).body('{"email":"mail@example.com"}')
41
41
  })
42
- await ajax("get", "/users").then((res) => {
42
+ await get("/users").then((res) => {
43
43
  expect(res.data).toEqual({ email: "mail@example.com" })
44
44
  })
45
45
  })
46
46
 
47
47
  it("JSON parse errors get rejected", async () => {
48
- xhrMock.get("/users", (_req, res) => {
48
+ xhrMock.get("http://localhost/users", (_req, res) => {
49
49
  return res.status(200).body('email => "mail@example.com"')
50
50
  })
51
51
  expect.assertions(1)
52
- await ajax("get", "/users").catch((e) => {
52
+ await get("/users").catch((e) => {
53
53
  expect(e.message).toMatch("Unexpected token")
54
54
  })
55
55
  })
56
56
 
57
57
  it("network errors get rejected", async () => {
58
- xhrMock.get("/users", () => {
58
+ xhrMock.get("http://localhost/users", () => {
59
59
  return Promise.reject(new Error())
60
60
  })
61
61
  expect.assertions(1)
62
- await ajax("get", "/users").catch((e) => {
62
+ await get("/users").catch((e) => {
63
63
  expect(e.message).toEqual("An error occurred during the transaction")
64
64
  })
65
65
  })
66
66
 
67
67
  it("server errors get rejected", async () => {
68
- xhrMock.get("/users", (_req, res) => {
68
+ xhrMock.get("http://localhost/users", (_req, res) => {
69
69
  return res.status(401).body('{"error":"Unauthorized"}')
70
70
  })
71
71
  expect.assertions(1)
72
- await ajax("get", "/users").catch((e) => {
72
+ await get("/users").catch((e) => {
73
73
  expect(e.error).toEqual("Unauthorized")
74
74
  })
75
75
  })
76
76
 
77
77
  it("server errors parsing errors get rejected", async () => {
78
- xhrMock.get("/users", (_req, res) => {
78
+ xhrMock.get("http://localhost/users", (_req, res) => {
79
79
  return res.status(401).body("Unauthorized")
80
80
  })
81
81
  expect.assertions(1)
82
- await ajax("get", "/users").catch((e) => {
82
+ await get("/users").catch((e) => {
83
83
  expect(e.message).toMatch("Unexpected token")
84
84
  })
85
85
  })
86
+
87
+ it("params get attached as query string", async () => {
88
+ xhrMock.get("http://localhost/users?name=foo", (_req, res) => {
89
+ return res.status(200).body(`{"name":"foo"}`)
90
+ })
91
+ const { data } = await get("/users", { name: "foo" })
92
+ expect(data.name).toEqual("foo")
93
+ })
94
+ })
95
+
96
+ describe("patch", () => {
97
+ it("sends X-CSRF-TOKEN header", async () => {
98
+ xhrMock.patch("http://localhost/users", (req, res) => {
99
+ expect(req.header("X-CSRF-TOKEN")).toEqual(token)
100
+ return res.status(200).body('{"message":"Ok"}')
101
+ })
102
+ await patch("/users")
103
+ })
104
+
105
+ it("sends Content-Type header", async () => {
106
+ xhrMock.patch("http://localhost/users", (req, res) => {
107
+ expect(req.header("Content-Type")).toEqual(
108
+ "application/json; charset=utf-8"
109
+ )
110
+ return res.status(200).body('{"message":"Ok"}')
111
+ })
112
+ await patch("/users")
113
+ })
114
+
115
+ it("sends Accept header", async () => {
116
+ xhrMock.patch("http://localhost/users", (req, res) => {
117
+ expect(req.header("Accept")).toEqual("application/json")
118
+ return res.status(200).body('{"message":"Ok"}')
119
+ })
120
+ await patch("/users")
121
+ })
122
+
123
+ it("sends JSON data", async () => {
124
+ xhrMock.patch("http://localhost/users", (req, res) => {
125
+ expect(req.body()).toEqual('{"email":"mail@example.com"}')
126
+ return res.status(200).body('{"message":"Ok"}')
127
+ })
128
+ await patch("/users", { email: "mail@example.com" })
129
+ })
86
130
  })
87
131
 
88
- describe("ajax('post')", () => {
132
+ describe("post", () => {
89
133
  it("sends X-CSRF-TOKEN header", async () => {
90
- xhrMock.post("/users", (req, res) => {
134
+ xhrMock.post("http://localhost/users", (req, res) => {
91
135
  expect(req.header("X-CSRF-TOKEN")).toEqual(token)
92
136
  return res.status(200).body('{"message":"Ok"}')
93
137
  })
94
- await ajax("post", "/users")
138
+ await post("/users")
95
139
  })
96
140
 
97
141
  it("sends Content-Type header", async () => {
98
- xhrMock.post("/users", (req, res) => {
142
+ xhrMock.post("http://localhost/users", (req, res) => {
99
143
  expect(req.header("Content-Type")).toEqual(
100
144
  "application/json; charset=utf-8"
101
145
  )
102
146
  return res.status(200).body('{"message":"Ok"}')
103
147
  })
104
- await ajax("post", "/users")
148
+ await post("/users")
105
149
  })
106
150
 
107
151
  it("sends Accept header", async () => {
108
- xhrMock.post("/users", (req, res) => {
152
+ xhrMock.post("http://localhost/users", (req, res) => {
109
153
  expect(req.header("Accept")).toEqual("application/json")
110
154
  return res.status(200).body('{"message":"Ok"}')
111
155
  })
112
- await ajax("post", "/users")
156
+ await post("/users")
113
157
  })
114
158
 
115
159
  it("sends JSON data", async () => {
116
- xhrMock.post("/users", (req, res) => {
160
+ xhrMock.post("http://localhost/users", (req, res) => {
117
161
  expect(req.body()).toEqual('{"email":"mail@example.com"}')
118
162
  return res.status(200).body('{"message":"Ok"}')
119
163
  })
120
- await ajax("post", "/users", { email: "mail@example.com" })
164
+ await post("/users", { email: "mail@example.com" })
121
165
  })
122
166
  })
123
167
 
@@ -29,16 +29,33 @@ function getToken() {
29
29
  return metaTag.attributes.content.textContent
30
30
  }
31
31
 
32
- export default function ajax(method, url, data) {
32
+ export function get(url, params) {
33
+ return ajax("GET", url, params)
34
+ }
35
+
36
+ export function patch(url, data) {
37
+ return ajax("PATCH", url, data)
38
+ }
39
+
40
+ export function post(url, data) {
41
+ return ajax("POST", url, data)
42
+ }
43
+
44
+ export default function ajax(method, path, data) {
33
45
  const xhr = new XMLHttpRequest()
34
46
  const promise = buildPromise(xhr)
47
+ const url = new URL(window.location.origin + path)
48
+
49
+ if (data && method.toLowerCase() === "get") {
50
+ url.search = new URLSearchParams(data).toString()
51
+ }
35
52
 
36
- xhr.open(method, url)
53
+ xhr.open(method, url.toString())
37
54
  xhr.setRequestHeader("Content-type", "application/json; charset=utf-8")
38
55
  xhr.setRequestHeader("Accept", "application/json")
39
56
  xhr.setRequestHeader("X-CSRF-Token", getToken())
40
57
 
41
- if (data) {
58
+ if (data && method.toLowerCase() !== "get") {
42
59
  xhr.send(JSON.stringify(data))
43
60
  } else {
44
61
  xhr.send()
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alchemy_cms/admin",
3
- "version": "5.3.1",
3
+ "version": "5.3.4",
4
4
  "description": "AlchemyCMS",
5
5
  "browser": "package/admin.js",
6
6
  "files": [
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: alchemy_cms
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.3.1
4
+ version: 5.3.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas von Deyen
@@ -13,7 +13,7 @@ authors:
13
13
  autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
- date: 2022-03-11 00:00:00.000000000 Z
16
+ date: 2022-04-11 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: active_model_serializers