PluginProbe
Code Snippets / 4.0.0-beta.2
Code Snippets v4.0.0-beta.2
4.0.0-beta.2 3.10.2 3.10.1 3.10.0 3.10.0-beta.2 3.10.0-beta.1 4.0.0-beta.1 3.9.6 trunk 2.10.0 2.10.1 2.12.0 2.12.1 2.13.0 2.13.1 2.13.2 2.13.3 2.14.0 2.14.1 2.14.2 2.14.3 2.14.4 2.14.5 2.14.6 3.0.0 All 65 releases
← All changes | js/utils/restAPI.ts +52 -5 3.10.24.0.0-beta.2 View file →
@@ -1,5 +1,7 @@
1 1 import { trimTrailingChar } from './text'
2 +import { buildUrl } from './urls'
3 +import type { UrlQueryArgs } from './urls'
2 4 import type { AxiosRequestConfig, InternalAxiosRequestConfig } from 'axios'
3 5
4 6 const normalizeUrl = (url: string | undefined) =>
5 7 trimTrailingChar(url ?? '', '/')
@@ -4,13 +6,20 @@
4 6 const normalizeUrl = (url: string | undefined) =>
5 7 trimTrailingChar(url ?? '', '/')
6 8
7 9 export const REST_BASES = {
10 + base: normalizeUrl(window.CODE_SNIPPETS?.restAPI.base),
8 11 snippets: normalizeUrl(window.CODE_SNIPPETS?.restAPI.snippets),
9 12 recentlyActive: normalizeUrl(window.CODE_SNIPPETS?.restAPI.recentlyActive),
10 - preferences: normalizeUrl(window.CODE_SNIPPETS?.restAPI.preferences),
11 - importPlugins: normalizeUrl(window.CODE_SNIPPETS?.restAPI.importPlugins),
12 - importFiles: normalizeUrl(window.CODE_SNIPPETS?.restAPI.importFiles),
13 + import: {
14 + plugins: normalizeUrl(window.CODE_SNIPPETS?.restAPI.importPlugins),
15 + files: normalizeUrl(window.CODE_SNIPPETS?.restAPI.importFiles),
16 + },
17 + preferences: {
18 + snippetView: normalizeUrl(window.CODE_SNIPPETS?.restAPI.snippetView),
19 + insights: normalizeUrl(window.CODE_SNIPPETS?.restAPI.insightsView),
20 + demosSeen: normalizeUrl(window.CODE_SNIPPETS?.restAPI.demosSeen),
21 + },
13 22 cloud: {
14 23 snippets: normalizeUrl(window.CODE_SNIPPETS?.restAPI.cloud.snippets),
15 24 }
16 25 }
@@ -48,11 +57,18 @@
48 57 * the page was rendered with does not stay valid. A nonce expires with the
49 58 * session, and the snippet editor is a screen people leave open for a long
50 59 * time. Once it lapsed, every save failed with a 403 and the only cure was
51 60 * reloading the page, which loses whatever was being written.
61 + *
62 + * The feedback reporter mounts on screens that do not enqueue the main
63 + * `CODE_SNIPPETS` object, so it carries a nonce of its own to fall back on.
52 64 */
53 -let restNonce = window.CODE_SNIPPETS?.restAPI.nonce
65 +let restNonce = window.CODE_SNIPPETS?.restAPI.nonce ?? window.CODE_SNIPPETS_FEEDBACK?.nonce
66 +let runOnceNonce = window.CODE_SNIPPETS_MANAGE?.runOnceNonce
54 67
68 +/** The Run Once nonce as last refreshed by the Heartbeat, or the one rendered with the page. */
69 +export const getRunOnceNonce = (): string => runOnceNonce ?? ''
70 +
55 71 /**
56 72 * Keep the REST nonce current for as long as the page is open.
57 73 *
58 74 * WordPress already sends a freshly minted nonce with every Heartbeat response,
@@ -65,12 +81,16 @@
65 81 // depending on jQuery being present and typed.
66 82 window.wp.hooks?.addAction(
67 83 'heartbeat.tick',
68 84 'code-snippets/refresh-rest-nonce',
69 - (data: { rest_nonce?: string }) => {
85 + (data: { rest_nonce?: string, code_snippets_run_once_nonce?: string }) => {
70 86 if (data.rest_nonce) {
71 87 restNonce = data.rest_nonce
72 88 }
89 +
90 + if (data.code_snippets_run_once_nonce) {
91 + runOnceNonce = data.code_snippets_run_once_nonce
92 + }
73 93 }
74 94 )
75 95 }
76 96
@@ -91,4 +111,31 @@
91 111 headers: {
92 112 'Access-Control': window.CODE_SNIPPETS?.restAPI.cloud.token
93 113 }
94 114 }
115 +
116 +export interface QueryArg {
117 + url: string
118 + name: string
119 + value: string
120 +}
121 +
122 +/**
123 + * Add a query parameter to a REST URL.
124 + *
125 + * Concatenation is not enough: with plain permalinks a REST URL already carries the route
126 + * in a query string, so a second `?` would bury the parameter inside the route instead of
127 + * adding one.
128 + */
129 +export const addQueryArg = ({ url, name, value }: QueryArg): string => {
130 + const parsed = new URL(url, window.location.origin)
131 + parsed.searchParams.set(name, value)
132 + return parsed.toString()
133 +}
134 +
135 +/**
136 + * A WordPress core REST route (`wp/v2/…`) with its query arguments, built so it
137 + * works whether or not the site has pretty permalinks: without them the REST
138 + * base already carries a query string, so arguments must be appended with `&`.
139 + */
140 +export const buildWpRestUrl = (route: string, args: UrlQueryArgs = {}): string =>
141 + buildUrl(`${REST_BASES.base}/wp/v2/${route}`, args)