diff --git a/package-lock.json b/package-lock.json index 14270bc640ff..763499462cfb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8569,6 +8569,7 @@ }, "node_modules/array-buffer-byte-length": { "version": "1.0.0", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -8689,24 +8690,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array.prototype.foreach": { - "version": "1.0.5", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-array-method-boxes-properly": "^1.0.0", - "get-intrinsic": "^1.2.1", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/array.prototype.tosorted": { "version": "1.1.2", "dev": true, @@ -8721,6 +8704,7 @@ }, "node_modules/arraybuffer.prototype.slice": { "version": "1.0.2", + "dev": true, "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.0", @@ -10216,12 +10200,18 @@ } }, "node_modules/call-bind": { - "version": "1.0.5", - "license": "MIT", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -10274,9 +10264,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001587", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001587.tgz", - "integrity": "sha512-HMFNotUmLXn71BQxg8cijvqxnIAofforZOwGsxyXJ0qugTdspUF4sPSJ2vhgprHCB996tIDzEq1ubumPDV8ULA==", + "version": "1.0.30001646", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001646.tgz", + "integrity": "sha512-dRg00gudiBDDTmUhClSdv3hqRfpbOnU28IpI1T6PBTLWa+kOj0681C8uML3PifYfREuBrVjDGhL3adYpBT6spw==", "dev": true, "funding": [ { @@ -12691,15 +12681,19 @@ } }, "node_modules/define-data-property": { - "version": "1.1.1", - "license": "MIT", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/define-lazy-prop": { @@ -13331,6 +13325,7 @@ }, "node_modules/es-abstract": { "version": "1.22.2", + "dev": true, "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.0", @@ -13380,9 +13375,24 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-array-method-boxes-properly": { + "node_modules/es-define-property": { "version": "1.0.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } }, "node_modules/es-get-iterator": { "version": "1.1.3", @@ -13429,8 +13439,20 @@ "dev": true, "license": "MIT" }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-set-tostringtag": { "version": "2.0.1", + "dev": true, "license": "MIT", "dependencies": { "get-intrinsic": "^1.1.3", @@ -13451,6 +13473,7 @@ }, "node_modules/es-to-primitive": { "version": "1.2.1", + "dev": true, "license": "MIT", "dependencies": { "is-callable": "^1.1.4", @@ -14895,6 +14918,7 @@ }, "node_modules/function.prototype.name": { "version": "1.1.6", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -14911,6 +14935,7 @@ }, "node_modules/functions-have-names": { "version": "1.2.3", + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -14981,13 +15006,18 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.1", - "license": "MIT", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -15061,6 +15091,7 @@ }, "node_modules/get-symbol-description": { "version": "1.0.0", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -15338,6 +15369,7 @@ }, "node_modules/globalthis": { "version": "1.0.3", + "dev": true, "license": "MIT", "dependencies": { "define-properties": "^1.1.3" @@ -15533,6 +15565,7 @@ }, "node_modules/has-bigints": { "version": "1.0.2", + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -15546,10 +15579,11 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.0", - "license": "MIT", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dependencies": { - "get-intrinsic": "^1.1.1" + "es-define-property": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -15661,6 +15695,17 @@ "minimalistic-assert": "^1.0.1" } }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/hast-util-embedded": { "version": "1.0.6", "license": "MIT", @@ -16436,6 +16481,7 @@ }, "node_modules/internal-slot": { "version": "1.0.5", + "dev": true, "license": "MIT", "dependencies": { "get-intrinsic": "^1.2.0", @@ -16528,6 +16574,7 @@ }, "node_modules/is-array-buffer": { "version": "3.0.2", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -16558,6 +16605,7 @@ }, "node_modules/is-bigint": { "version": "1.0.4", + "dev": true, "license": "MIT", "dependencies": { "has-bigints": "^1.0.1" @@ -16579,6 +16627,7 @@ }, "node_modules/is-boolean-object": { "version": "1.1.2", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -16652,6 +16701,7 @@ }, "node_modules/is-date-object": { "version": "1.0.5", + "dev": true, "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" @@ -16839,6 +16889,7 @@ }, "node_modules/is-negative-zero": { "version": "2.0.2", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -16860,6 +16911,7 @@ }, "node_modules/is-number-object": { "version": "1.0.7", + "dev": true, "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" @@ -16919,6 +16971,7 @@ }, "node_modules/is-regex": { "version": "1.1.4", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -16941,6 +16994,7 @@ }, "node_modules/is-shared-array-buffer": { "version": "1.0.2", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2" @@ -16969,6 +17023,7 @@ }, "node_modules/is-string": { "version": "1.0.7", + "dev": true, "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" @@ -16982,6 +17037,7 @@ }, "node_modules/is-symbol": { "version": "1.0.4", + "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.2" @@ -17045,6 +17101,7 @@ }, "node_modules/is-weakref": { "version": "1.0.2", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2" @@ -17101,6 +17158,7 @@ }, "node_modules/isarray": { "version": "2.0.5", + "dev": true, "license": "MIT" }, "node_modules/isexe": { @@ -20872,14 +20930,16 @@ "integrity": "sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ==" }, "node_modules/node-polyglot": { - "version": "2.5.0", - "license": "BSD-2-Clause", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/node-polyglot/-/node-polyglot-2.6.0.tgz", + "integrity": "sha512-ZZFkaYzIfGfBvSM6QhA9dM8EEaUJOVewzGSRcXWbJELXDj0lajAtKaENCYxvF5yE+TgHg6NQb0CmgYMsMdcNJQ==", "dependencies": { - "array.prototype.foreach": "^1.0.2", - "has": "^1.0.3", - "object.entries": "^1.1.5", - "string.prototype.trim": "^1.2.6", + "hasown": "^2.0.2", + "object.entries": "^1.1.8", "warning": "^4.0.3" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/node-releases": { @@ -25021,12 +25081,13 @@ } }, "node_modules/object.entries": { - "version": "1.1.7", - "license": "MIT", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", + "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -27943,6 +28004,7 @@ }, "node_modules/regexp.prototype.flags": { "version": "1.5.1", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -28387,6 +28449,7 @@ }, "node_modules/safe-array-concat": { "version": "1.0.1", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -28429,6 +28492,7 @@ }, "node_modules/safe-regex-test": { "version": "1.0.0", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -28679,13 +28743,16 @@ "license": "ISC" }, "node_modules/set-function-length": { - "version": "1.1.1", - "license": "MIT", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dependencies": { - "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -28693,6 +28760,7 @@ }, "node_modules/set-function-name": { "version": "2.0.1", + "dev": true, "license": "MIT", "dependencies": { "define-data-property": "^1.0.1", @@ -29834,6 +29902,7 @@ }, "node_modules/string.prototype.trim": { "version": "1.2.8", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -29849,6 +29918,7 @@ }, "node_modules/string.prototype.trimend": { "version": "1.0.7", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -29861,6 +29931,7 @@ }, "node_modules/string.prototype.trimstart": { "version": "1.0.7", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -31289,6 +31360,7 @@ }, "node_modules/typed-array-buffer": { "version": "1.0.0", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -31301,6 +31373,7 @@ }, "node_modules/typed-array-byte-length": { "version": "1.0.0", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -31317,6 +31390,7 @@ }, "node_modules/typed-array-byte-offset": { "version": "1.0.0", + "dev": true, "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.5", @@ -31334,6 +31408,7 @@ }, "node_modules/typed-array-length": { "version": "1.0.4", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -31387,6 +31462,7 @@ }, "node_modules/unbox-primitive": { "version": "1.0.2", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -32684,6 +32760,7 @@ }, "node_modules/which-boxed-primitive": { "version": "1.0.2", + "dev": true, "license": "MIT", "dependencies": { "is-bigint": "^1.0.1", diff --git a/packages/decap-cms-backend-aws-cognito-github-proxy/src/implementation.tsx b/packages/decap-cms-backend-aws-cognito-github-proxy/src/implementation.tsx index e7bc6de432b8..61969986c751 100644 --- a/packages/decap-cms-backend-aws-cognito-github-proxy/src/implementation.tsx +++ b/packages/decap-cms-backend-aws-cognito-github-proxy/src/implementation.tsx @@ -5,6 +5,7 @@ import AuthenticationPage from './AuthenticationPage'; import type { GitHubUser } from 'decap-cms-backend-github/src/implementation'; import type { Config } from 'decap-cms-lib-util/src'; +import type { Octokit } from '@octokit/rest'; export default class AwsCognitoGitHubProxyBackend extends GitHubBackend { constructor(config: Config, options = {}) { @@ -44,4 +45,8 @@ export default class AwsCognitoGitHubProxyBackend extends GitHubBackend { } return this._currentUserPromise; } + + async getPullRequestAuthor(pullRequest: Octokit.PullsListResponseItem) { + return pullRequest.user?.login; + } } diff --git a/packages/decap-cms-backend-git-gateway/src/implementation.ts b/packages/decap-cms-backend-git-gateway/src/implementation.ts index b529950af282..4f29a3e1d28b 100644 --- a/packages/decap-cms-backend-git-gateway/src/implementation.ts +++ b/packages/decap-cms-backend-git-gateway/src/implementation.ts @@ -362,7 +362,11 @@ export default class GitGateway implements Implementation { if (!(await this.api!.hasWriteAccess())) { throw new Error("You don't have sufficient permissions to access Decap CMS"); } - return { name: userData.name, login: userData.email } as User; + return { + name: userData.name, + login: userData.email, + avatar_url: userData.avatar_url, + } as unknown as User; }); } async restoreUser() { diff --git a/packages/decap-cms-backend-github/src/API.ts b/packages/decap-cms-backend-github/src/API.ts index a044342c5fca..244a3e228509 100644 --- a/packages/decap-cms-backend-github/src/API.ts +++ b/packages/decap-cms-backend-github/src/API.ts @@ -580,7 +580,7 @@ export default class API { } try { - const user = await this.user(); + const user: GitHubUser = await this.request(`/users/${pullRequest.user.login}`); return user.name || user.login; } catch { return; diff --git a/packages/decap-cms-backend-gitlab/src/implementation.ts b/packages/decap-cms-backend-gitlab/src/implementation.ts index 769257f0bea3..5b1a15a9ef42 100644 --- a/packages/decap-cms-backend-gitlab/src/implementation.ts +++ b/packages/decap-cms-backend-gitlab/src/implementation.ts @@ -158,6 +158,7 @@ export default class GitLab implements Implementation { backend: 'gitlab', repo: this.repo, token: this.token, + apiRoot: this.apiRoot, }); if (defaultBranchName) { this.branch = defaultBranchName; diff --git a/packages/decap-cms-core/src/__tests__/backend.spec.js b/packages/decap-cms-core/src/__tests__/backend.spec.js index 9b69519ff1dc..c673c19a01c4 100644 --- a/packages/decap-cms-core/src/__tests__/backend.spec.js +++ b/packages/decap-cms-core/src/__tests__/backend.spec.js @@ -318,6 +318,81 @@ describe('Backend', () => { }); }); + describe('persistEntry', () => { + it('should update the draft with the new entry returned by preSave event', async () => { + const implementation = { + init: jest.fn(() => implementation), + persistEntry: jest.fn(() => implementation), + }; + + const config = { + backend: { + commit_messages: 'commit-messages', + }, + }; + const collection = Map({ + name: 'posts', + }); + const entry = Map({ + data: 'old_data', + }); + const newEntry = Map({ + data: 'new_data', + }); + const entryDraft = Map({ + entry, + }); + const user = { login: 'login', name: 'name' }; + const backend = new Backend(implementation, { config, backendName: 'github' }); + + backend.currentUser = jest.fn().mockResolvedValue(user); + backend.entryToRaw = jest.fn().mockReturnValue('content'); + backend.invokePreSaveEvent = jest.fn().mockReturnValueOnce(newEntry); + + await backend.persistEntry({ config, collection, entryDraft }); + + expect(backend.entryToRaw).toHaveBeenCalledTimes(1); + expect(backend.entryToRaw).toHaveBeenCalledWith(collection, newEntry); + }); + + it('should update the draft with the new data returned by preSave event', async () => { + const implementation = { + init: jest.fn(() => implementation), + persistEntry: jest.fn(() => implementation), + }; + + const config = { + backend: { + commit_messages: 'commit-messages', + }, + }; + const collection = Map({ + name: 'posts', + }); + const entry = Map({ + data: Map({}), + }); + const newData = Map({}); + const newEntry = Map({ + data: newData, + }); + const entryDraft = Map({ + entry, + }); + const user = { login: 'login', name: 'name' }; + const backend = new Backend(implementation, { config, backendName: 'github' }); + + backend.currentUser = jest.fn().mockResolvedValue(user); + backend.entryToRaw = jest.fn().mockReturnValue('content'); + backend.invokePreSaveEvent = jest.fn().mockReturnValueOnce(newData); + + await backend.persistEntry({ config, collection, entryDraft }); + + expect(backend.entryToRaw).toHaveBeenCalledTimes(1); + expect(backend.entryToRaw).toHaveBeenCalledWith(collection, newEntry); + }); + }); + describe('persistMedia', () => { it('should persist media', async () => { const persistMediaResult = {}; diff --git a/packages/decap-cms-core/src/backend.ts b/packages/decap-cms-core/src/backend.ts index 49e668d042ef..bcb8881de045 100644 --- a/packages/decap-cms-core/src/backend.ts +++ b/packages/decap-cms-core/src/backend.ts @@ -1091,8 +1091,14 @@ export class Backend { unpublished = false, status, }: PersistArgs) { - const modifiedData = await this.invokePreSaveEvent(draft.get('entry')); - const entryDraft = (modifiedData && draft.setIn(['entry', 'data'], modifiedData)) || draft; + const updatedEntity = await this.invokePreSaveEvent(draft.get('entry')); + + let entryDraft; + if (updatedEntity.get('data') === undefined) { + entryDraft = (updatedEntity && draft.setIn(['entry', 'data'], updatedEntity)) || draft; + } else { + entryDraft = (updatedEntity && draft.setIn(['entry'], updatedEntity)) || draft; + } const newEntry = entryDraft.getIn(['entry', 'newRecord']) || false; diff --git a/packages/decap-cms-core/src/components/Editor/EditorControlPane/EditorControl.js b/packages/decap-cms-core/src/components/Editor/EditorControlPane/EditorControl.js index d11915139657..217f4447ffe8 100644 --- a/packages/decap-cms-core/src/components/Editor/EditorControlPane/EditorControl.js +++ b/packages/decap-cms-core/src/components/Editor/EditorControlPane/EditorControl.js @@ -81,12 +81,9 @@ const ControlErrorsList = styled.ul` list-style-type: none; font-size: 12px; color: ${colors.errorText}; - margin-bottom: 5px; + margin-bottom: 8px; text-align: right; - text-transform: uppercase; - position: relative; font-weight: 600; - top: 20px; `; export const ControlHint = styled.p` diff --git a/packages/decap-cms-lib-util/src/API.ts b/packages/decap-cms-lib-util/src/API.ts index 277f045d3603..c3483968501a 100644 --- a/packages/decap-cms-lib-util/src/API.ts +++ b/packages/decap-cms-lib-util/src/API.ts @@ -142,6 +142,7 @@ type Backend = 'github' | 'gitlab' | 'bitbucket'; type RequestConfig = Omit & HeaderConfig & { backend: Backend; + apiRoot?: string; params?: ParamObject; }; @@ -182,7 +183,7 @@ async function constructRequestHeaders(headerConfig: HeaderConfig) { const { token, headers } = headerConfig; const baseHeaders: HeaderObj = { 'Content-Type': 'application/json; charset=utf-8', ...headers }; if (token) { - baseHeaders['Authorization'] = `token ${token}`; + baseHeaders['Authorization'] = `Bearer ${token}`; } return Promise.resolve(baseHeaders); } @@ -199,7 +200,7 @@ export async function apiRequest( const { token, backend, ...props } = config; const options = { cache: 'no-cache', ...props }; const headers = await constructRequestHeaders({ headers: options.headers || {}, token }); - const baseUrl = apiRoots[backend]; + const baseUrl = config.apiRoot ?? apiRoots[backend]; const url = constructUrlWithParams(`${baseUrl}${path}`, options.params); let responseStatus = 500; try { @@ -220,9 +221,10 @@ export async function getDefaultBranchName(configs: { backend: Backend; repo: string; token?: string; + apiRoot?: string; }) { let apiPath; - const { token, backend, repo } = configs; + const { token, backend, repo, apiRoot } = configs; switch (backend) { case 'gitlab': { apiPath = `/projects/${encodeURIComponent(repo)}`; @@ -236,7 +238,7 @@ export async function getDefaultBranchName(configs: { apiPath = `/repos/${repo}`; } } - const repoInfo = await apiRequest(apiPath, { token, backend }); + const repoInfo = await apiRequest(apiPath, { token, backend, apiRoot }); let defaultBranchName; if (backend === 'bitbucket') { const { diff --git a/packages/decap-cms-locales/src/th/index.js b/packages/decap-cms-locales/src/th/index.js index 72e0f74085fb..76599fb659d9 100644 --- a/packages/decap-cms-locales/src/th/index.js +++ b/packages/decap-cms-locales/src/th/index.js @@ -3,23 +3,24 @@ const th = { login: 'เข้าสู่ระบบ', loggingIn: 'กำลังเข้าสู่ระบบ...', loginWithNetlifyIdentity: 'เข้าสู่ระบบด้วย Netlify Identity', + loginWithAzure: 'เข้าสู่ระบบด้วย Azure', loginWithBitbucket: 'เข้าสู่ระบบด้วย Bitbucket', loginWithGitHub: 'เข้าสู่ระบบด้วย GitHub', loginWithGitLab: 'เข้าสู่ระบบด้วย GitLab', loginWithGitea: 'เข้าสู่ระบบด้วย Gitea', errors: { - email: 'ตรวจสอบให้แน่ใจว่าได้ใส่อีเมลล์แล้ว', + email: 'ตรวจสอบให้แน่ใจว่าได้ใส่อีเมลแล้ว', password: 'โปรดใส่รหัสผ่านของคุณ', identitySettings: - 'ไม่สามารถเข้าถึงการตั้งค่าส่วนตัว เมื่อใช้ git-gateway backend ตรวจสอบให้แน่ใจว่าได้เปิดใช้งานระบบยืนยันตัวตนและ Git Gateway.', + 'ไม่สามารถเข้าถึงการตั้งค่าส่วนตัว เมื่อใช้ git-gateway backend ตรวจสอบให้แน่ใจว่าได้เปิดใช้งานระบบยืนยันตัวตนและ Git Gateway แล้ว', }, }, app: { header: { content: 'เนื้อหา', - workflow: 'ขั้นตอนการทำงาน', - media: 'มีเดีย', - quickAdd: 'เพิ่มเนื้อหา อย่างเร็ว', + workflow: 'กระแสงาน', + media: 'ไฟล์สื่อ', + quickAdd: 'เพิ่มเนื้อหาด่วน', }, app: { errorHeader: 'เกิดข้อผิดพลาดในการโหลดการตั้งค่า CMS', @@ -45,9 +46,10 @@ const th = { newButton: 'สร้าง %{collectionLabel}', ascending: 'น้อยไปมาก', descending: 'มากไปน้อย', - searchResults: 'ค้นหาผลลัพธ์สำหรับ "%{searchTerm}"', - searchResultsInCollection: 'ค้นหาผลลัพธ์สำหรับ "%{searchTerm}" ใน %{collection}', + searchResults: 'ผลลัพธ์การค้นหา "%{searchTerm}"', + searchResultsInCollection: 'ผลลัพธ์การค้นหา "%{searchTerm}" ในกลุ่ม %{collection}', filterBy: 'กรองตาม', + groupBy: 'จัดกลุ่มตาม', }, entries: { loadingEntries: 'กำลังโหลดเนิ้อหา...', @@ -55,12 +57,16 @@ const th = { longerLoading: 'อาจจะโหลดนานหลายนาที', noEntries: 'ไม่มีเนื้อหา', }, + groups: { + other: 'อื่น ๆ', + negateLabel: 'ไม่ใช่ %{label}', + }, defaultFields: { author: { label: 'ผู้เขียน', }, updatedOn: { - label: 'อัพเดตเมื่อ', + label: 'เวลาที่อัปเดต', }, }, }, @@ -72,7 +78,7 @@ const th = { }, editorControlPane: { widget: { - required: '%{fieldLabel} ต้องระบุ', + required: 'จำเป็นต้องระบุ %{fieldLabel}', regexPattern: '%{fieldLabel} ไม่ตรงกับรูปแบบ: %{pattern}', processing: '%{fieldLabel} กำลังประมวลผล', range: '%{fieldLabel} ต้องอยู่ระหว่าง %{minValue} และ %{maxValue}', @@ -82,11 +88,14 @@ const th = { rangeCountExact: '%{fieldLabel} จะต้องมี %{count} รายการ', rangeMin: '%{fieldLabel} จะต้องมีไม่ต่ำกว่า %{minCount} รายการ', rangeMax: '%{fieldLabel} จะต้องมีไม่มากกว่า %{maxCount} รายการ', - invalidPath: `'%{path}' พาทไม่ถูกต้อง`, + invalidPath: `'%{path}' ไม่ใช่พาทที่ถูกต้อง`, pathExists: `พาท '%{path}' มีอยู่แล้ว`, }, i18n: { - writingInLocale: 'เขียนด้วยภาษา %{locale}', + writingInLocale: 'กำลังเขียนด้วยภาษา %{locale}', + copyFromLocale: 'คัดลอกจากภาษาอื่น', + copyFromLocaleConfirm: + 'คุณต้องการคัดลอกข้อมูลจากภาษา %{locale} หรือไม่?\nเนื้อหาทั้งหมดจะถูกแทนที่', }, }, editor: { @@ -105,7 +114,12 @@ const th = { 'คุณแน่ใจหรือว่าจะต้องการลบเนื้อหาที่ยังไม่ได้เผยแพร่ทั้งหมดนี้ รวมถึงการเปลี่ยนแปลงที่ยังไม่ได้บันทึก?', onDeleteUnpublishedChanges: 'คุณแน่ใจหรือว่าจะต้องการลบเนื้อหาที่ยังไม่ได้เผยแพร่ทั้งหมดนี้?', loadingEntry: 'กำลังโหลดเนื้อหา...', - confirmLoadBackup: 'ข้อมูลสำรองได้ถูกกู้คืนสำหรับเนื้อหานี้ คุณต้องการใช้มันไหม?', + confirmLoadBackup: 'มีการกู้คืนข้อมูลสำรองสำหรับเนื้อหานี้ คุณต้องการใช้มันหรือไม่?', + }, + editorInterface: { + toggleI18n: 'เปิด/ปิด i18n', + togglePreview: 'เปิด/ปิดการแสดงตัวอย่าง', + toggleScrollSync: 'เปิด/ปิดการเลื่อนพร้อมกัน', }, editorToolbar: { publishing: 'กำลังเผยแพร่...', @@ -113,15 +127,19 @@ const th = { published: 'เผยแพร่แล้ว', unpublish: 'ไม่ได้เผยแพร่', duplicate: 'ทำซ้ำ', - unpublishing: 'ไม่ทำการเผยแพร่...', - publishAndCreateNew: 'เผยแพร่ และ สร้างใหม่', - publishAndDuplicate: 'เผยแพร่ และ ทำซ้ำ', + unpublishing: 'กำยังยกเลิกการเผยแพร่...', + publishAndCreateNew: 'เผยแพร่ และสร้างใหม่', + publishAndDuplicate: 'เผยแพร่ และทำซ้ำ', deleteUnpublishedChanges: 'ลบการเปลี่ยแปลงเนื้อหาที่ยังไม่ได้เผยแพร่', deleteUnpublishedEntry: 'ลบเนื้อหาที่ยังไม่ได้เผยแพร่', - deletePublishedEntry: 'ลบเนื้อหาที่เผยแพร่', + deletePublishedEntry: 'ลบเนื้อหาที่เผยแพร่แล้ว', deleteEntry: 'ลบเนื้อหา', saving: 'กำลังบันทึก...', save: 'บันทึก', + statusInfoTooltipDraft: + 'เนื้อหาอยู่ในสถานะร่าง หากเนื้อหาเสร็จสมบูรณ์แล้วและต้องการส่งเพื่อตรวจสอบ ให้ปรับเปลี่ยนสถานะเป็น ‘อยู่ระหว่างการตรวจสอบ’', + statusInfoTooltipInReview: + 'เนื้อหาอยู่ระหว่างการตรวจสอบ ไม่จำเป็นต้องทำอะไรเพิ่มเติม อย่างไรก็ตาม คุณสามารถแก้ไขเนื้อหาเพิ่มเติมได้ระหว่างการตรวจสอบ', deleting: 'กำลังลบ...', updating: 'กำลังอัปเดต...', status: 'สถานะ: %{status}', @@ -138,18 +156,40 @@ const th = { }, editorWidgets: { markdown: { - richText: 'Rich Text', - markdown: 'Markdown', + bold: 'ตัวหนา', + italic: 'ตัวเอียง', + code: 'โคด', + link: 'ลิงก์', + linkPrompt: 'ระบุ URL ของลิงก์', + headings: 'หัวข้อ', + quote: 'ยกคำพูดมา', + bulletedList: 'รายการแบบไม่มีหมายเลข', + numberedList: 'รายการแบบมีหมายเลข', + addComponent: 'เพิ่มองค์ประกอบ', + richText: 'ข้อความฟอร์แมต', + markdown: 'มาร์คดาวน์', }, image: { choose: 'เลือกรูปภาพ', + chooseMultiple: 'เลือกรูปภาพหลายรูป', + chooseUrl: 'แทรกจาก URL', + replaceUrl: 'แทนที่ด้วย URL', + promptUrl: 'ระบุ URL ของรูปภาพ', chooseDifferent: 'เลือกรูปภาพอื่น', + addMore: 'เพิ่มรูปภาพ', remove: 'เอารูปภาพออก', + removeAll: 'เอารูปภาพออกทั้งหมด', }, file: { choose: 'เลือกไฟล์', + chooseUrl: 'แทรกจาก URL', + chooseMultiple: 'เลือกหลายไฟล์', + replaceUrl: 'แทนที่ด้วย URL', + promptUrl: 'ระบุ URL ของไฟล์', chooseDifferent: 'เลือกไฟล์อื่น', + addMore: 'เพิ่มไฟล์', remove: 'เอาไฟล์ออก', + removeAll: 'เอาไฟล์ออกทั้งหมด', }, unknownControl: { noControl: "ไม่มีการควบคุม widget '%{widget}'.", @@ -158,25 +198,34 @@ const th = { noPreview: "ไม่มีตัวอย่างสำหรับ widget '%{widget}'.", }, headingOptions: { - headingOne: 'Heading 1', - headingTwo: 'Heading 2', - headingThree: 'Heading 3', - headingFour: 'Heading 4', - headingFive: 'Heading 5', - headingSix: 'Heading 6', + headingOne: 'หัวข้อ 1', + headingTwo: 'หัวข้อ 2', + headingThree: 'หัวข้อ 3', + headingFour: 'หัวข้อ 4', + headingFive: 'หัวข้อ 5', + headingSix: 'หัวข้อ 6', }, datetime: { now: 'เวลาตอนนี้', clear: 'ล้าง', }, + list: { + add: 'เพิ่ม %{item}', + addType: 'เพิ่ม %{item}', + }, }, }, mediaLibrary: { mediaLibraryCard: { draft: 'ร่าง', + copy: 'คัดลอก', + copyUrl: 'คัดลอก URL', + copyPath: 'คัดลอกพาท', + copyName: 'คัดลอกชื่อ', + copied: 'คัดลอกแล้ว', }, mediaLibrary: { - onDelete: 'คุณแน่ใจหรือว่าจะลบมีเดียที่ถูกเลือก?', + onDelete: 'คุณแน่ใจหรือว่าจะลบไฟล์สื่อที่ถูกเลือก?', fileTooLarge: 'ไฟล์ใหญ่เกินไป\n ค่าที่ตั้งไว้ไม่ยอมรับไฟล์ที่ใหญ่กว่า %{size} kB.', }, mediaLibraryModal: { @@ -186,7 +235,7 @@ const th = { noImagesFound: 'ไม่พบรูปภาพ', private: 'ส่วนตัว ', images: 'รูปภาพ', - mediaAssets: 'ข้อมูลมีเดีย', + mediaAssets: 'ข้อมูลไฟล์สื่อ', search: 'ค้นหา...', uploading: 'กำลังอัปโหลด...', upload: 'อัปโหลด', @@ -222,12 +271,12 @@ const th = { onFailToPersist: 'ล้มเหลวในการยืนยันเนื้อหา: %{details}', onFailToDelete: 'ล้มเหลวในการลบเนื้อหา: %{details}', onFailToUpdateStatus: 'ล้มเหลวในการอัปเดตสถานะ: %{details}', - missingRequiredField: 'คุณไม่ได้ใส่ข้อมูลในช่องที่ต้องการ กรุณาใส่ข้อมูลก่อนบันทึก', + missingRequiredField: 'คุณไม่ได้ใส่ข้อมูลในช่องที่จำเป็น กรุณาใส่ข้อมูลก่อนบันทึก', entrySaved: 'เนื้อหาถูกบันทึก', entryPublished: 'เนื้อหาถูกเผยแพร่', - entryUnpublished: 'เนื้อหาไม่ได้ถูกเผยแพร่', + entryUnpublished: 'เนื้อหาถูกยกเลิกการเผยแพร่', onFailToPublishEntry: 'ล้มเหลวในการเผยแพร่เนื้อหา: %{details}', - onFailToUnpublishEntry: 'ล้มเหลวในการไม่เผยแพร่เนื้อหา: %{details}', + onFailToUnpublishEntry: 'ล้มเหลวในการยกเลิกการเผยแพร่เนื้อหา: %{details}', entryUpdated: 'สถานะเนื้อหาถูกอัปเดต', onDeleteUnpublishedChanges: 'การเปลี่ยนแปลงเนื้อหาไม่ถูกเผยแพร่ได้ถูกลบ', onFailToAuth: '%{details}', @@ -237,10 +286,11 @@ const th = { }, workflow: { workflow: { - loading: 'กำลังโหลดเนื้อหาขั้นตอนการทำงานของบรรณาธิการ', - workflowHeading: 'ขั้นตอนการทำงานของบรรณาธิการ', + loading: 'กำลังโหลดเนื้อหากระแสงานของบรรณาธิการ', + workflowHeading: 'กระแสงานของบรรณาธิการ', newPost: 'สร้างโพสต์ใหม่', - description: '%{smart_count} เนื้อหารอการตรวจสอบ, %{readyCount} พร้อมที่จะเผยแพร่ ่', + description: + 'เนื้อหา %{smart_count} รายการอยู่ระหว่างการตรวจสอบ, %{readyCount} รายการพร้อมที่จะเผยแพร่', dateFormat: 'MMMM D', }, workflowCard: { @@ -255,12 +305,12 @@ const th = { workflowList: { onDeleteEntry: 'คุณแน่ใจหรือว่าจะต้องการลบเนื้อหานี้?', onPublishingNotReadyEntry: - 'เฉพาะรายการที่มีสถานะ "พร้อม" สามารถทำการเผยแพร่ โปรดลากเนื้อหาไปยังช่อง "พร้อม" เพื่อจะทำการเผยแพร่.', + 'สามารถเผยแพร่เฉพาะรายการที่มีสถานะ "พร้อม" โปรดลากเนื้อหาไปยังช่อง "พร้อม" เพื่อให้เผยแพร่ได้', onPublishEntry: 'คุณแน่ใจหรือว่าจะต้องการเผยแพร่เนื้อหานี้?', draftHeader: 'ร่าง', - inReviewHeader: 'อยู่ในการตรวจสอบ', + inReviewHeader: 'อยู่ระหว่างการตรวจสอบ', readyHeader: 'พร้อม', - currentEntries: '%{smart_count} เนื้อหา', + currentEntries: 'เนื้อหา %{smart_count} รายการ', }, }, }; diff --git a/packages/decap-cms-locales/src/uk/index.js b/packages/decap-cms-locales/src/uk/index.js index 3a2cd58b0aff..7e22b7a8a61e 100644 --- a/packages/decap-cms-locales/src/uk/index.js +++ b/packages/decap-cms-locales/src/uk/index.js @@ -1,9 +1,25 @@ const uk = { + auth: { + login: 'Увійти', + loggingIn: 'Вхід...', + loginWithNetlifyIdentity: 'Увійти через Netlify Identity', + loginWithAzure: 'Увійти через Azure', + loginWithBitbucket: 'Увійти через Bitbucket', + loginWithGitHub: 'Увійти через GitHub', + loginWithGitLab: 'Увійти через GitLab', + loginWithGitea: 'Увійти через Gitea', + errors: { + email: 'Введіть email.', + password: 'Введіть пароль.', + identitySettings: + 'Немає доступу до налаштувань. Якщо використовуєте git-gateway, переконайтеся, що включили Identity service та Git Gateway.', + }, + }, app: { header: { content: 'Зміст', workflow: 'Робочий процес', - media: 'Медіа', + media: 'Медіафайли', quickAdd: 'Додати', }, app: { @@ -20,16 +36,26 @@ const uk = { collection: { sidebar: { collections: 'Колекції', - searchAll: 'Пошук', + allCollections: 'Всі колекції', + searchAll: 'Пошук всюди', + searchIn: 'Шукати в', }, collectionTop: { viewAs: 'Змінити вигляд', newButton: 'Створити %{collectionLabel}', + ascending: 'За зростанням', + descending: 'За спаданням', + searchResults: 'Результати по запиту "%{searchTerm}"', + searchResultsInCollection: 'Результати по запиту "%{searchTerm}" в %{collection}', + sortBy: 'Сортувати за', + filterBy: 'Фільтрувати за', + groupBy: 'Групувати за', }, entries: { - loadingEntries: 'Завантаження записів', - cachingEntries: 'Кешування записів', - longerLoading: 'Це може зайняти декілька хвилинок', + loadingEntries: 'Завантаження записів...', + cachingEntries: 'Кешування записів...', + longerLoading: 'Це може зайняти певний час', + noEntries: 'Немає записів', }, }, editor: { @@ -40,12 +66,24 @@ const uk = { }, editorControlPane: { widget: { - required: "%{fieldLabel} є обов'язковим.", + required: "Поле %{fieldLabel} є обов'язковим.", regexPattern: '%{fieldLabel} не задовільняє умові: %{pattern}.', - processing: 'обробляється %{fieldLabel}.', - range: 'значення %{fieldLabel} повинне бути від %{minValue} до %{maxValue}.', - min: 'значення %{fieldLabel} має бути від %{minValue}.', - max: 'значення %{fieldLabel} має бути %{maxValue} та менше.', + processing: 'Обробляється %{fieldLabel}...', + range: 'Значення %{fieldLabel} повинне бути від %{minValue} до %{maxValue}.', + min: 'Значення %{fieldLabel} має бути від %{minValue}.', + max: 'Значення %{fieldLabel} має бути %{maxValue} та менше.', + rangeCount: '%{fieldLabel} повинно містити від %{minCount} до %{maxCount} елементів.', + rangeCountExact: '%{fieldLabel} повинно містити чітко %{count} елементів.', + rangeMin: '%{fieldLabel} повинно містити не менше %{minCount} елементів.', + rangeMax: '%{fieldLabel} повинно містити %{maxCount} або менше елементів.', + invalidPath: `Шлях '%{path}' містить помилки.`, + pathExists: `Шлях '%{path}' вже існує.`, + }, + i18n: { + writingInLocale: 'Мова запису %{locale}', + copyFromLocale: 'Заповнити з іншої мови', + copyFromLocaleConfirm: + 'Дійсно бажаєте заповнити дані з мови %{locale}?\nУсі наявні дані буде перезаписано.', }, }, editor: { @@ -66,11 +104,20 @@ const uk = { loadingEntry: 'Завантаження...', confirmLoadBackup: 'Відновлено резервну копію, бажаєте її використати?', }, + editorInterface: { + toggleI18n: 'Порівняння записів', + togglePreview: 'Попередній перегляд', + toggleScrollSync: 'Синхронна прокрутка', + }, editorToolbar: { publishing: 'Публікація...', publish: 'Опублікувати', published: 'Опубліковано', - publishAndCreateNew: 'Опублікувати і створити нову', + unpublish: 'Скасувати публікацію', + duplicate: 'Продублювати', + unpublishing: 'Скасування публікації...', + publishAndCreateNew: 'Опублікувати та створити нову', + publishAndDuplicate: 'Опублікувати та продублювати', deleteUnpublishedChanges: 'Видалити неопубліковані зміни', deleteUnpublishedEntry: 'Видалити неопубліковану сторінку', deletePublishedEntry: 'Видалити опубліковану сторінку', @@ -80,7 +127,7 @@ const uk = { deleting: 'Видалення...', updating: 'Оновлення...', status: 'Cтан: %{status}', - backCollection: ' Робота над %{collectionLabel} колекцією', + backCollection: 'Колекція %{collectionLabel}', unsavedChanges: 'Незбережені зміни', changesSaved: 'Зміни збережено', draft: 'В роботі', @@ -92,15 +139,41 @@ const uk = { deployButtonLabel: 'Переглянути наживо', }, editorWidgets: { + markdown: { + bold: 'Напівжирний', + italic: 'Курсив', + code: 'Код', + link: 'Посилання', + linkPrompt: 'Введіть URL посилання', + headings: 'Заголовки', + quote: 'Цитата', + bulletedList: 'Маркований список', + numberedList: 'Нумерований список', + addComponent: 'Додати компонент', + richText: 'RichText', + markdown: 'Markdown', + }, image: { - choose: 'Виберіть зображення', - chooseDifferent: 'Виберіть інше зображення', - remove: 'Видалити зображення', + choose: 'Вибрати зображення', + chooseMultiple: 'Вибрати зображення', + chooseUrl: 'Вставити з URL', + replaceUrl: 'Замінити на URL', + promptUrl: 'Введіть URL зображення', + chooseDifferent: 'Обрати інше зображення', + addMore: 'Додати більше зображень', + remove: 'Видалити', + removeAll: 'Видалити всі', }, file: { - choose: 'Виберіть файл', + choose: 'Вибрати файл', + chooseUrl: 'Вставити з URL', + chooseMultiple: 'Вибрати файли', + replaceUrl: 'Замінити на URL', + promptUrl: 'Введіть URL файлу', chooseDifferent: 'Виберіть інший файл', - remove: 'Видалити файл', + addMore: 'Додати більше', + remove: 'Видалити', + removeAll: 'Видалити всі', }, unknownControl: { noControl: "Відсутній модуль для '%{widget}'.", @@ -109,67 +182,92 @@ const uk = { noPreview: "Відсутній перегляд для '%{widget}'.", }, headingOptions: { - headingOne: 'Heading 1', - headingTwo: 'Heading 2', - headingThree: 'Heading 3', - headingFour: 'Heading 4', - headingFive: 'Heading 5', - headingSix: 'Heading 6', + headingOne: 'Заголовок 1', + headingTwo: 'Заголовок 2', + headingThree: 'Заголовок 3', + headingFour: 'Заголовок 4', + headingFive: 'Заголовок 5', + headingSix: 'Заголовок 6', + }, + datetime: { + now: 'Зараз', + clear: 'Скинути', + }, + list: { + add: 'Додати %{item}', + addType: 'Додати %{item}', }, }, }, mediaLibrary: { mediaLibraryCard: { draft: 'В роботі', + copy: 'Копіювати', + copyUrl: 'Копіювати URL', + copyPath: 'Копіювати шлях', + copyName: 'Копіювати ім’я', + copied: 'Скопійовано', }, mediaLibrary: { onDelete: 'Ви дійсно бажаєте видалити обрані матеріали?', + fileTooLarge: + 'Файл занадто великий.\nНалаштування не дозволяють зберігати файли більше %{size} kB.', }, mediaLibraryModal: { loading: 'Завантаження...', noResults: 'Результати відсутні.', noAssetsFound: 'Матеріали відсутні.', noImagesFound: 'Зображення відсутні.', - private: 'Private ', + private: 'Приватні ', images: 'Зображення', - mediaAssets: 'Медіа матеріали', + mediaAssets: 'Медіафайли', search: 'Пошук...', uploading: 'Завантаження...', upload: 'Завантажити', + download: 'Отримати', deleting: 'Видалення...', - deleteSelected: 'Видалити обране', - chooseSelected: 'Додати обране', + deleteSelected: 'Видалити', + chooseSelected: 'Обрати виділені', }, }, ui: { + default: { + goBackToSite: 'Повернутися на сайт', + }, errorBoundary: { title: 'Помилка', - details: 'Відбулась помилка - будь ласка ', - reportIt: 'надішліть нам деталі.', + details: 'Сталася помилка. Будь ласка, ', + reportIt: 'повідомте про неї.', detailsHeading: 'Деталі', + privacyWarning: + 'При відкритті тікету автоматично заповнюється повідомленням про помилку та відлагоджувальною інформацією.\nБудь ласка, перевірте, що дані є вірними та не містять конфіденційної інформації.', recoveredEntry: { heading: 'Відновлено документ', - warning: 'Будь ласка, збережіть це десь перед тим як піти!', + warning: 'Будь ласка, скопіюйте це повідомлення кудись, перед тим як залишити сторінку!', copyButtonLabel: 'Скопіювати в буфер', }, }, settingsDropdown: { - logOut: 'Вихід', + logOut: 'Вийти', }, toast: { - onFailToLoadEntries: 'Помилка завантаження: %{details}', - onFailToLoadDeployPreview: 'Помилка завантаження перегляду: %{details}', - onFailToPersist: 'Помилка перезапису: %{details}', - onFailToDelete: 'Помилка видалення: %{details}', - onFailToUpdateStatus: 'Помилка оновлення статусу: %{details}', + onFailToLoadEntries: 'Не вдалося завантажити запис: %{details}', + onFailToLoadDeployPreview: 'Не вдалося завантажити попередній перегляд: %{details}', + onFailToPersist: 'Не вдалося зберегти запис: %{details}', + onFailToDelete: 'Не вдалося видалити запис: %{details}', + onFailToUpdateStatus: 'Не вдалося оновити статус: %{details}', missingRequiredField: - "Йой, здається пропущено обов'язкове поле. Будь ласка, заповніть перед збереженням.", - entrySaved: 'Збережено', - entryPublished: 'Опубліковано', - onFailToPublishEntry: 'Помилка публікації: %{details}', - entryUpdated: 'Статус оновлено', - onDeleteUnpublishedChanges: 'Видалено неопубліковані зміни', + "На жаль, ви пропустили обов'язкове поле. Будь ласка, заповніть перед збереженням.", + entrySaved: 'Запис збережений', + entryPublished: 'Запис опублікований', + entryUnpublished: 'Публікація запису скасована', + onFailToPublishEntry: 'Не вдалося опублікувати запис: %{details}', + onFailToUnpublishEntry: 'Не вдалося скасувати публікацію запису: %{details}', + entryUpdated: 'Статус запису оновлено', + onDeleteUnpublishedChanges: 'Неопубліковані зміни видалені', onFailToAuth: '%{details}', + onLoggedOut: 'Ви вийшли. Будь ласка, збережіть усі дані та увійдіть знову', + onBackendDown: 'Трапився збій у роботі серверу. Див. %{details}', }, }, workflow: { @@ -177,27 +275,28 @@ const uk = { loading: 'Завантаження редакційних матеріалів', workflowHeading: 'Редакція', newPost: 'Новий запис', - description: '%{smart_count} записів очікують розгляду, %{readyCount} готові до публікації. ', + description: + '%{smart_count} запис очікує розгляду, %{readyCount} до публікації. |||| %{smart_count} записи очікують розгляду, %{readyCount} до публікації. |||| %{smart_count} записів очікують розгляду, %{readyCount} до публікації.', dateFormat: 'MMMM D', }, workflowCard: { - lastChange: '%{date} від %{author}', + lastChange: '%{date}, %{author}', lastChangeNoAuthor: '%{date}', - lastChangeNoDate: 'від %{author}', + lastChangeNoDate: '— %{author}', deleteChanges: 'Видалити зміни', - deleteNewEntry: 'Видалити новий запис', - publishChanges: 'Опублікувати всі зміни', - publishNewEntry: 'Опублікувати новий запис', + deleteNewEntry: 'Видалити запис', + publishChanges: 'Опублікувати зміни', + publishNewEntry: 'Опублікувати запис', }, workflowList: { onDeleteEntry: 'Ви дійсно бажаєте видалити запис?', onPublishingNotReadyEntry: - 'Тільки елементи з статусом "Готово" можуть бути опубліковані. Будь ласка перемістіть картку в колонку "Готово" для публікації.', + 'Лише елементи зі статусом "Готово" можуть бути опубліковані. Перетягніть картку в стовпчик "Готово", щоб дозволити публікацію.', onPublishEntry: 'Дійсно бажаєте опублікувати запис?', draftHeader: 'В роботі', inReviewHeader: 'На розгляді', readyHeader: 'Готово', - currentEntries: '%{smart_count} запис |||| %{smart_count} записів', + currentEntries: '%{smart_count} запис |||| %{smart_count} записи |||| %{smart_count} записів', }, }, }; diff --git a/packages/decap-cms-widget-list/src/ListControl.js b/packages/decap-cms-widget-list/src/ListControl.js index 958975a311c0..3db08db07e73 100644 --- a/packages/decap-cms-widget-list/src/ListControl.js +++ b/packages/decap-cms-widget-list/src/ListControl.js @@ -671,7 +671,7 @@ export default class ListControl extends React.Component { id={key} /> - {errorMessage}aaaasdd + {errorMessage} ); diff --git a/packages/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/events/keyDown.js b/packages/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/events/keyDown.js index 7d75353a7105..0ce618dd1403 100644 --- a/packages/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/events/keyDown.js +++ b/packages/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/events/keyDown.js @@ -1,9 +1,11 @@ import isHotkey from 'is-hotkey'; +import { Editor, Transforms } from 'slate'; import keyDownEnter from './keyDownEnter'; import keyDownBackspace from './keyDownBackspace'; import isCursorInNonDefaultBlock from '../locations/isCursorInNonDefaultBlock'; import toggleBlock from './toggleBlock'; +import isCursorCollapsedAfterSoftBreak from '../locations/isCursorCollapsedAfterSoftBreak'; const HEADING_HOTKEYS = { 'mod+1': 'heading-one', @@ -25,6 +27,13 @@ function keyDown(event, editor) { } } + if (isHotkey('backspace', event) && isCursorCollapsedAfterSoftBreak(editor)) { + const [, path] = Editor.previous(editor); + Transforms.removeNodes(editor, { at: path }); + event.preventDefault(); + return false; + } + if (!isCursorInNonDefaultBlock(editor)) return; if (isHotkey('enter', event)) { diff --git a/packages/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/locations/isCursorCollapsedAfterSoftBreak.js b/packages/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/locations/isCursorCollapsedAfterSoftBreak.js new file mode 100644 index 000000000000..7355b0061c90 --- /dev/null +++ b/packages/decap-cms-widget-markdown/src/MarkdownControl/plugins/blocks/locations/isCursorCollapsedAfterSoftBreak.js @@ -0,0 +1,13 @@ +import { Editor, Range } from 'slate'; + +function isCursorCollapsedAfterSoftBreak(editor) { + const { selection } = editor; + if (!selection) return false; + if (Range.isExpanded(selection)) return false; + + const previous = Editor.previous(editor); + + return previous && previous[0].type == 'break'; +} + +export default isCursorCollapsedAfterSoftBreak; diff --git a/packages/decap-cms-widget-select/src/SelectControl.js b/packages/decap-cms-widget-select/src/SelectControl.js index 816a10f39ec0..90a22aa10675 100644 --- a/packages/decap-cms-widget-select/src/SelectControl.js +++ b/packages/decap-cms-widget-select/src/SelectControl.js @@ -8,7 +8,9 @@ import { reactSelectStyles } from 'decap-cms-ui-default'; import { validations } from 'decap-cms-lib-widgets'; function optionToString(option) { - return option && option.value ? option.value : null; + return option && (typeof option.value === 'number' || typeof option.value === 'string') + ? option.value + : null; } function convertToOption(raw) { @@ -47,9 +49,10 @@ export default class SelectControl extends React.Component { options: ImmutablePropTypes.listOf( PropTypes.oneOfType([ PropTypes.string, + PropTypes.number, ImmutablePropTypes.contains({ label: PropTypes.string.isRequired, - value: PropTypes.string.isRequired, + value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, }), ]), ).isRequired, diff --git a/packages/decap-cms-widget-select/src/__tests__/select.spec.js b/packages/decap-cms-widget-select/src/__tests__/select.spec.js index 778d1d9c4184..448d87bcae08 100644 --- a/packages/decap-cms-widget-select/src/__tests__/select.spec.js +++ b/packages/decap-cms-widget-select/src/__tests__/select.spec.js @@ -12,6 +12,11 @@ const options = [ { value: 'baz', label: 'Baz' }, ]; const stringOptions = ['foo', 'bar', 'baz']; +const numberOptions = [ + { value: 0, label: 'Foo' }, + { value: 1, label: 'Bar' }, + { value: 2, label: 'Baz' }, +]; class SelectController extends React.Component { state = { @@ -134,6 +139,18 @@ describe('Select widget', () => { expect(getByText('baz')).toBeInTheDocument(); }); + it('should call onChange with correct selectedItem when value is number 0', () => { + const field = fromJS({ options: numberOptions }); + const { getByText, input, onChangeSpy } = setup({ field }); + + fireEvent.focus(input); + fireEvent.keyDown(input, { key: 'ArrowDown' }); + fireEvent.click(getByText('Foo')); + + expect(onChangeSpy).toHaveBeenCalledTimes(1); + expect(onChangeSpy).toHaveBeenCalledWith(numberOptions[0].value); + }); + describe('with multiple', () => { it('should call onChange with correct selectedItem', () => { const field = fromJS({ options, multiple: true }); @@ -248,6 +265,22 @@ describe('Select widget', () => { expect(getByText('bar')).toBeInTheDocument(); expect(getByText('baz')).toBeInTheDocument(); }); + + it('should call onChange with correct selectedItem when values are numbers including 0', () => { + const field = fromJS({ options: numberOptions, multiple: true }); + const { getByText, input, onChangeSpy } = setup({ field }); + + fireEvent.keyDown(input, { key: 'ArrowDown' }); + fireEvent.click(getByText('Foo')); + fireEvent.keyDown(input, { key: 'ArrowDown' }); + fireEvent.click(getByText('Baz')); + + expect(onChangeSpy).toHaveBeenCalledTimes(2); + expect(onChangeSpy).toHaveBeenCalledWith(fromJS([numberOptions[0].value])); + expect(onChangeSpy).toHaveBeenCalledWith( + fromJS([numberOptions[0].value, numberOptions[2].value]), + ); + }); }); describe('validation', () => { function validate(setupOpts) {