alchemy_cms 5.3.1 → 5.3.4

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.

Potentially problematic release.


This version of alchemy_cms might be problematic. Click here for more details.

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