| @@ -1,13 +1,64 @@ | ||
| 1 | -import { __ } from '@wordpress/i18n' | |
| 1 | +import { __, sprintf } from '@wordpress/i18n' | |
| 2 | +import { isLicensed, isNetworkAdmin } from '../screen' | |
| 3 | +import { buildUrl } from '../urls' | |
| 2 | 4 | import { parseSnippetObject } from './objects' |
| 3 | -import type { Snippet, SnippetType } from '../../types/Snippet' | |
| 5 | +import type { CloudSnippetSchema } from '../../types/schema/CloudSnippetSchema' | |
| 6 | +import type { SnippetSchema } from '../../types/schema/SnippetSchema' | |
| 7 | +import type { Snippet, SnippetCodeScope, SnippetScope, SnippetType } from '../../types/Snippet' | |
| 4 | 8 | |
| 5 | 9 | const PRO_TYPES = new Set<SnippetType>(['css', 'js', 'cond']) |
| 6 | 10 | |
| 7 | -export const createSnippetObject = (fields: unknown): Snippet => | |
| 11 | +export const SNIPPET_TYPE_LABELS: Record<SnippetType, string> = { | |
| 12 | + php: __('Functions', 'code-snippets'), | |
| 13 | + html: __('Content', 'code-snippets'), | |
| 14 | + css: __('Styles', 'code-snippets'), | |
| 15 | + js: __('Scripts', 'code-snippets'), | |
| 16 | + cond: __('Conditions', 'code-snippets') | |
| 17 | +} | |
| 18 | + | |
| 19 | +export const SNIPPET_SCOPE_ICONS: Record<SnippetCodeScope, string> = { | |
| 20 | + 'global': 'admin-site', | |
| 21 | + 'admin': 'admin-tools', | |
| 22 | + 'front-end': 'admin-appearance', | |
| 23 | + 'single-use': 'clock', | |
| 24 | + 'content': 'shortcode', | |
| 25 | + 'head-content': 'editor-code', | |
| 26 | + 'body-content': 'editor-code', | |
| 27 | + 'footer-content': 'editor-code', | |
| 28 | + 'admin-css': 'dashboard', | |
| 29 | + 'site-css': 'admin-customizer', | |
| 30 | + 'site-head-js': 'media-code', | |
| 31 | + 'site-footer-js': 'media-code' | |
| 32 | +} | |
| 33 | + | |
| 34 | +export const SNIPPET_SCOPE_DESCRIPTIONS: Record<SnippetCodeScope, string> = { | |
| 35 | + 'global': __('Run everywhere', 'code-snippets'), | |
| 36 | + 'admin': __('Only run in administration area', 'code-snippets'), | |
| 37 | + 'front-end': __('Only run on site front-end', 'code-snippets'), | |
| 38 | + 'single-use': __('Only run once', 'code-snippets'), | |
| 39 | + 'content': __('Where inserted in editor', 'code-snippets'), | |
| 40 | + 'head-content': __('In site header (<head> section)', 'code-snippets'), | |
| 41 | + 'body-content': __('In site content (start of <body>)', 'code-snippets'), | |
| 42 | + 'footer-content': __('In site footer (end of <body>)', 'code-snippets'), | |
| 43 | + 'site-css': __('Site front-end', 'code-snippets'), | |
| 44 | + 'admin-css': __('Administration area', 'code-snippets'), | |
| 45 | + 'site-head-js': __('In site header (<head> section)', 'code-snippets'), | |
| 46 | + 'site-footer-js': __('In site footer (end of <body>)', 'code-snippets') | |
| 47 | +} | |
| 48 | + | |
| 49 | +export const createSnippetObject = (fields?: Partial<Snippet> | Partial<SnippetSchema>): Snippet => | |
| 8 | 50 | parseSnippetObject(fields) |
| 9 | 51 | |
| 52 | +export const cloneSnippetObject = (snippet: Snippet): Snippet => | |
| 53 | + createSnippetObject({ | |
| 54 | + ...snippet, | |
| 55 | + id: 0, | |
| 56 | + active: false, | |
| 57 | + // translators: %s: snippet title. | |
| 58 | + name: sprintf(__('%s [CLONE]', 'code-snippets'), snippet.name) | |
| 59 | + }) | |
| 60 | + | |
| 10 | 61 | export const getSnippetType = ({ scope }: Pick<Snippet, 'scope'>): SnippetType => { |
| 11 | 62 | switch (true) { |
| 12 | 63 | case scope.endsWith('-css'): |
| 13 | 64 | return 'css' |
| @@ -25,8 +76,24 @@ | ||
| 25 | 76 | return 'php' |
| 26 | 77 | } |
| 27 | 78 | } |
| 28 | 79 | |
| 80 | +export const getSnippetEditUrl = (snippet?: Pick<Snippet, 'id'>): string | undefined => | |
| 81 | + snippet?.id | |
| 82 | + ? buildUrl(window.CODE_SNIPPETS?.urls.edit, { id: snippet.id }) | |
| 83 | + : window.CODE_SNIPPETS?.urls.addNew | |
| 84 | + | |
| 85 | +export const getSnippetAddNewUrl = (type?: SnippetType): string | undefined => | |
| 86 | + type && (isLicensed() || !isProType(type)) | |
| 87 | + ? buildUrl(window.CODE_SNIPPETS?.urls.addNew, { type }) | |
| 88 | + : window.CODE_SNIPPETS?.urls.addNew | |
| 89 | + | |
| 90 | +export const getSnippetDisplayName = (snippet: Pick<Snippet, 'name' | 'id' | 'scope'>): string => | |
| 91 | + '' === snippet.name.trim() | |
| 92 | + // translators: %s: snippet identifier. | |
| 93 | + ? sprintf(isCondition(snippet) ? __('Condition #%d', 'code-snippets') : __('Snippet #%d', 'code-snippets'), snippet.id) | |
| 94 | + : snippet.name | |
| 95 | + | |
| 29 | 96 | export const validateSnippet = (snippet: Snippet): undefined | string => { |
| 30 | 97 | const missingTitle = '' === snippet.name.trim() |
| 31 | 98 | const missingCode = '' === snippet.code.trim() |
| 32 | 99 | |
| @@ -44,9 +111,9 @@ | ||
| 44 | 111 | return undefined |
| 45 | 112 | } |
| 46 | 113 | } |
| 47 | 114 | |
| 48 | -export const isCondition = (snippet: Pick<Snippet, 'scope'>): boolean => | |
| 115 | +export const isCondition = (snippet: { scope?: SnippetScope }): boolean => | |
| 49 | 116 | 'condition' === snippet.scope |
| 50 | 117 | |
| 51 | 118 | export const isProSnippet = (snippet: Pick<Snippet, 'scope'>): boolean => |
| 52 | 119 | PRO_TYPES.has(getSnippetType(snippet)) |
| @@ -52,4 +119,32 @@ | ||
| 52 | 119 | PRO_TYPES.has(getSnippetType(snippet)) |
| 53 | 120 | |
| 54 | 121 | export const isProType = (type: SnippetType): boolean => |
| 55 | 122 | PRO_TYPES.has(type) |
| 123 | + | |
| 124 | +export const isSnippetActive = ( | |
| 125 | + snippet: Snippet, | |
| 126 | + activeByCondition: Map<Snippet['id'], Snippet[]> | |
| 127 | +): boolean => | |
| 128 | + 'cond' === getSnippetType(snippet) | |
| 129 | + ? 0 < (activeByCondition.get(snippet.id)?.length ?? 0) | |
| 130 | + : snippet.active | |
| 131 | + | |
| 132 | +/** | |
| 133 | + * Whether the snippet belongs to the network and is not shared with subsites, | |
| 134 | + * making it read-only outside the network admin. | |
| 135 | + */ | |
| 136 | +export const isNetworkOnlySnippet = (snippet: Snippet): boolean => | |
| 137 | + !isNetworkAdmin() && snippet.network && !snippet.shared_network | |
| 138 | + | |
| 139 | +/** | |
| 140 | + * Whether the current user is able to modify the snippet, either directly or | |
| 141 | + * through actions such as cloning and trashing. | |
| 142 | + */ | |
| 143 | +export const canModifySnippet = (snippet: Snippet): boolean => | |
| 144 | + !isNetworkOnlySnippet(snippet) && | |
| 145 | + !(snippet.shared_network && !window.CODE_SNIPPETS_MANAGE?.hasNetworkCap) | |
| 146 | + | |
| 147 | +export const isCloudSnippetDownloadable = ( | |
| 148 | + snippet: Pick<CloudSnippetSchema, 'scope' | 'local_id'> | |
| 149 | +): boolean => | |
| 150 | + !snippet.local_id && (!isProSnippet(snippet) || isLicensed()) | |