Initial commit: WordPress wp-content (themes, plugins, languages)
- Theme: momentry (custom theme with REST API routes) - Plugins: code-snippets (contains all API proxies) - Languages: zh_TW translations - Excludes: cache, backups, uploads, logs
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
import React from 'react'
|
||||
import { Spinner } from '@wordpress/components'
|
||||
import { isRTL } from '@wordpress/i18n'
|
||||
import { useSnippetForm } from '../../hooks/useSnippetForm'
|
||||
import { isNetworkAdmin } from '../../utils/screen'
|
||||
import { isCondition } from '../../utils/snippets/snippets'
|
||||
import { ConditionModalButton } from '../ConditionModal/ConditionModalButton'
|
||||
import { SnippetLocationInput } from '../SnippetForm/fields/SnippetLocationInput'
|
||||
import { Notices } from '../SnippetForm/page/Notices'
|
||||
import { ShortcodeInfo } from './actions/ShortcodeInfo'
|
||||
import { MultisiteSharingSettings } from './controls/MultisiteSharingSettings'
|
||||
import { ExportButtons } from './actions/ExportButtons'
|
||||
import { SubmitButtons } from './actions/SubmitButtons'
|
||||
import { ActivationSwitch } from './controls/ActivationSwitch'
|
||||
import { DeleteButton } from './actions/DeleteButton'
|
||||
import { PriorityInput } from './controls/PriorityInput'
|
||||
import { RTLControl } from './controls/RTLControl'
|
||||
import type { Dispatch, SetStateAction } from 'react'
|
||||
|
||||
export interface EditorSidebarProps {
|
||||
setIsUpgradeDialogOpen: Dispatch<SetStateAction<boolean>>
|
||||
}
|
||||
|
||||
export const EditorSidebar: React.FC<EditorSidebarProps> = ({ setIsUpgradeDialogOpen }) => {
|
||||
const { snippet, isWorking } = useSnippetForm()
|
||||
|
||||
return (
|
||||
<div className="snippet-editor-sidebar">
|
||||
<div className="box">
|
||||
{snippet.id && !isCondition(snippet) ? <ActivationSwitch /> : null}
|
||||
|
||||
{isNetworkAdmin() ? <MultisiteSharingSettings /> : null}
|
||||
|
||||
{isRTL() ? <RTLControl /> : null}
|
||||
|
||||
<ConditionModalButton setIsDialogOpen={setIsUpgradeDialogOpen} />
|
||||
<SnippetLocationInput />
|
||||
<ShortcodeInfo />
|
||||
<PriorityInput />
|
||||
|
||||
{snippet.id
|
||||
? <div className="row-actions visible inline-form-field">
|
||||
<ExportButtons />
|
||||
<DeleteButton />
|
||||
</div> : null}
|
||||
</div>
|
||||
|
||||
<p className="submit">
|
||||
<SubmitButtons />
|
||||
{isWorking ? <Spinner /> : ''}
|
||||
</p>
|
||||
|
||||
<Notices />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import { addQueryArgs } from '@wordpress/url'
|
||||
import React, { useState } from 'react'
|
||||
import { __ } from '@wordpress/i18n'
|
||||
import { useRestAPI } from '../../../hooks/useRestAPI'
|
||||
import { Button } from '../../common/Button'
|
||||
import { ConfirmDialog } from '../../common/ConfirmDialog'
|
||||
import { useSnippetForm } from '../../../hooks/useSnippetForm'
|
||||
|
||||
export const DeleteButton: React.FC = () => {
|
||||
const { snippetsAPI } = useRestAPI()
|
||||
const { snippet, setIsWorking, isWorking, handleRequestError } = useSnippetForm()
|
||||
const [isDialogOpen, setIsDialogOpen] = useState(false)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
id="delete-snippet"
|
||||
className="delete-button"
|
||||
disabled={isWorking}
|
||||
onClick={() => {
|
||||
setIsDialogOpen(true)
|
||||
}}
|
||||
>
|
||||
{__('Delete', 'code-snippets')}
|
||||
</Button>
|
||||
|
||||
<ConfirmDialog
|
||||
open={isDialogOpen}
|
||||
title={__('Delete?', 'code-snippets')}
|
||||
confirmLabel={__('Delete', 'code-snippets')}
|
||||
confirmButtonClassName="is-destructive"
|
||||
onCancel={() => setIsDialogOpen(false)}
|
||||
onConfirm={() => {
|
||||
setIsDialogOpen(false)
|
||||
setIsWorking(true)
|
||||
|
||||
snippetsAPI.delete(snippet)
|
||||
.then(() => {
|
||||
setIsWorking(false)
|
||||
window.location.replace(addQueryArgs(window.CODE_SNIPPETS?.urls.manage, { result: 'deleted' }))
|
||||
})
|
||||
.catch((error: unknown) => handleRequestError(error, __('Could not delete snippet.', 'code-snippets')))
|
||||
}}
|
||||
>
|
||||
<p style={{ marginBlockStart: 0 }}>
|
||||
{__('You are about to delete this snippet.', 'code-snippets')}{' '}
|
||||
{__('Are you sure?', 'code-snippets')}
|
||||
</p>
|
||||
</ConfirmDialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import React from 'react'
|
||||
import { __ } from '@wordpress/i18n'
|
||||
import { useRestAPI } from '../../../hooks/useRestAPI'
|
||||
import { Button } from '../../common/Button'
|
||||
import { downloadSnippetExportFile } from '../../../utils/files'
|
||||
import { useSnippetForm } from '../../../hooks/useSnippetForm'
|
||||
import type { Snippet } from '../../../types/Snippet'
|
||||
import type { SnippetsExport } from '../../../types/schema/SnippetsExport'
|
||||
|
||||
interface ExportButtonProps {
|
||||
name: string
|
||||
label: string
|
||||
makeRequest: (snippet: Snippet) => Promise<SnippetsExport | string>
|
||||
}
|
||||
|
||||
const ExportButton: React.FC<ExportButtonProps> = ({ name, label, makeRequest }) => {
|
||||
const { snippet, isWorking, setIsWorking, handleRequestError } = useSnippetForm()
|
||||
|
||||
const handleClick = () => {
|
||||
setIsWorking(true)
|
||||
|
||||
makeRequest(snippet)
|
||||
.then(response => downloadSnippetExportFile(response, snippet))
|
||||
// translators: %s: error message.
|
||||
.catch((error: unknown) => handleRequestError(error, __('Could not download export file.', 'code-snippets')))
|
||||
.finally(() => setIsWorking(false))
|
||||
}
|
||||
|
||||
return (
|
||||
<Button name={name} onClick={handleClick} disabled={isWorking}>
|
||||
{label}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
export const ExportButtons: React.FC = () => {
|
||||
const { snippetsAPI } = useRestAPI()
|
||||
|
||||
return (
|
||||
<div className="snippet-export-buttons">
|
||||
<ExportButton
|
||||
name="export_snippet"
|
||||
label={__('Export', 'code-snippets')}
|
||||
makeRequest={snippetsAPI.export}
|
||||
/>
|
||||
|
||||
{window.CODE_SNIPPETS_EDIT?.enableDownloads
|
||||
? <ExportButton
|
||||
name="export_snippet_code"
|
||||
label={__('Export Code', 'code-snippets')}
|
||||
makeRequest={snippetsAPI.exportCode}
|
||||
/>
|
||||
: null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
import React, { useState } from 'react'
|
||||
import { CheckboxControl, ExternalLink, Modal } from '@wordpress/components'
|
||||
import { __ } from '@wordpress/i18n'
|
||||
import { useSnippetForm } from '../../../hooks/useSnippetForm'
|
||||
import { Button } from '../../common/Button'
|
||||
import { CopyToClipboardButton } from '../../common/CopyToClipboardButton'
|
||||
import type { Dispatch, SetStateAction } from 'react'
|
||||
|
||||
type ShortcodeAtts = Record<string, unknown>
|
||||
|
||||
const buildShortcodeTag = (tag: string, atts: ShortcodeAtts): string =>
|
||||
`[${[
|
||||
tag,
|
||||
...Object.entries(atts)
|
||||
.filter(([, value]) => Boolean(value))
|
||||
.map(([att, value]) =>
|
||||
'boolean' === typeof value ? att : `${att}=${JSON.stringify(value)}`)
|
||||
].filter(Boolean).join(' ')}]`
|
||||
|
||||
const SHORTCODE_TAG = 'code_snippet'
|
||||
|
||||
interface ShortcodeOptions {
|
||||
php: boolean
|
||||
format: boolean
|
||||
shortcodes: boolean
|
||||
}
|
||||
|
||||
interface CheckboxListProps<T extends string> {
|
||||
options: T[]
|
||||
checked: Record<T, boolean>
|
||||
disabled: boolean
|
||||
setChecked: Dispatch<SetStateAction<Record<T, boolean>>>
|
||||
optionLabels: Partial<Record<T, string>>
|
||||
optionDescriptions: Partial<Record<T, string>>
|
||||
}
|
||||
|
||||
const CheckboxList = <T extends string>({
|
||||
options,
|
||||
checked,
|
||||
disabled,
|
||||
setChecked,
|
||||
optionLabels,
|
||||
optionDescriptions
|
||||
}: CheckboxListProps<T>) =>
|
||||
<ul>
|
||||
{options.map(option =>
|
||||
<li key={option}>
|
||||
<CheckboxControl
|
||||
label={optionLabels[option]}
|
||||
help={optionDescriptions[option]}
|
||||
checked={checked[option]}
|
||||
disabled={disabled}
|
||||
onChange={value =>
|
||||
setChecked(previous => ({ ...previous, [option]: value }))}
|
||||
/>
|
||||
</li>)}
|
||||
</ul>
|
||||
|
||||
const ShortcodeDescription = () =>
|
||||
<p className="description">
|
||||
{__('Copy the below shortcode to insert this snippet into a post, page, or other content.', 'code-snippets')}{'\n'}
|
||||
{__('You can also use the Classic Editor button, Block editor (Pro) or Elementor widget (Pro).', 'code-snippets')}{'\n'}
|
||||
|
||||
<ExternalLink
|
||||
href={__('https://codesnippets.pro/doc/inserting-content-snippets/', 'code-snippets')}
|
||||
>
|
||||
{__('Learn more', 'code-snippets')}
|
||||
</ExternalLink>
|
||||
</p>
|
||||
|
||||
const OPTION_LABELS: Record<keyof ShortcodeOptions, string> = {
|
||||
php: __('Evaluate PHP code', 'code-snippets'),
|
||||
format: __('Add paragraphs and formatting', 'code-snippets'),
|
||||
shortcodes: __('Evaluate additional shortcode tags', 'code-snippets')
|
||||
}
|
||||
|
||||
const OPTION_DESCRIPTIONS: Record<keyof ShortcodeOptions, string> = {
|
||||
php: __('Run code within <?php ?> tags.', 'code-snippets'),
|
||||
format: __('Wrap output in paragraphs and apply formatting.', 'code-snippets'),
|
||||
shortcodes: __('Replace [shortcodes] embedded within the snippet.', 'code-snippets')
|
||||
}
|
||||
|
||||
const ModalContent = () => {
|
||||
const { snippet, isReadOnly } = useSnippetForm()
|
||||
|
||||
const [options, setOptions] = useState<ShortcodeOptions>(() => ({
|
||||
php: snippet.code.includes('<?'),
|
||||
format: true,
|
||||
shortcodes: false
|
||||
}))
|
||||
|
||||
const shortcodeAtts: ShortcodeAtts = {
|
||||
id: snippet.id,
|
||||
network: snippet.network,
|
||||
...options,
|
||||
name: snippet.name
|
||||
}
|
||||
|
||||
const shortcodeTag = buildShortcodeTag(SHORTCODE_TAG, shortcodeAtts)
|
||||
|
||||
return (
|
||||
<>
|
||||
<ShortcodeDescription />
|
||||
|
||||
<p className="shortcode-tag-wrapper">
|
||||
<code className="shortcode-tag">{shortcodeTag}</code>
|
||||
<CopyToClipboardButton primary text={shortcodeTag} />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<h4>{__('Shortcode Options', 'code-snippets')}</h4>
|
||||
|
||||
<CheckboxList
|
||||
options={['php', 'format', 'shortcodes']}
|
||||
checked={options}
|
||||
disabled={isReadOnly}
|
||||
setChecked={setOptions}
|
||||
optionLabels={OPTION_LABELS}
|
||||
optionDescriptions={OPTION_DESCRIPTIONS}
|
||||
/>
|
||||
</p>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const ShortcodeInfo: React.FC = () => {
|
||||
const { snippet, isReadOnly } = useSnippetForm()
|
||||
const [isModalOpen, setIsModalOpen] = useState(false)
|
||||
|
||||
return 'content' === snippet.scope && snippet.id
|
||||
? <div className="inline-form-field">
|
||||
<h4>{__('Shortcode', 'code-snippets')}</h4>
|
||||
<Button onClick={() => setIsModalOpen(true)} disabled={isReadOnly}>
|
||||
{__('See options', 'code-snippets')}
|
||||
</Button>
|
||||
|
||||
{isModalOpen
|
||||
? <Modal
|
||||
size="medium"
|
||||
className="code-snippets-modal"
|
||||
title={__('Embed Snippet with Shortcode', 'code-snippets')}
|
||||
onRequestClose={() => setIsModalOpen(false)}
|
||||
>
|
||||
<div className="modal-content">
|
||||
<ModalContent />
|
||||
</div>
|
||||
|
||||
<div className="modal-footer">
|
||||
<Button link large onClick={() => setIsModalOpen(false)}>
|
||||
{__('Close Popup', 'code-snippets')}
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
: null}
|
||||
</div>
|
||||
: null
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import React from 'react'
|
||||
import { __ } from '@wordpress/i18n'
|
||||
import { SubmitSnippetAction } from '../../../hooks/useSubmitSnippet'
|
||||
import { isCondition } from '../../../utils/snippets/snippets'
|
||||
import { isNetworkAdmin } from '../../../utils/screen'
|
||||
import { useSnippetForm } from '../../../hooks/useSnippetForm'
|
||||
import { SubmitButton } from '../../common/SubmitButton'
|
||||
import type { SubmitButtonProps } from '../../common/SubmitButton'
|
||||
|
||||
const SaveButton = (props: SubmitButtonProps) => {
|
||||
const { snippet } = useSnippetForm()
|
||||
|
||||
return (
|
||||
<SubmitButton
|
||||
large
|
||||
name={SubmitSnippetAction.SAVE}
|
||||
text={isCondition(snippet)
|
||||
? __('Save Condition', 'code-snippets')
|
||||
: __('Save Snippet', 'code-snippets')}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
interface ActivateOrDeactivateButtonProps {
|
||||
primaryActivate: boolean
|
||||
}
|
||||
|
||||
const ActivateOrDeactivateButton: React.FC<ActivateOrDeactivateButtonProps> = ({ primaryActivate }) => {
|
||||
const { snippet, isWorking } = useSnippetForm()
|
||||
|
||||
switch (true) {
|
||||
case isCondition(snippet) || snippet.shared_network && isNetworkAdmin():
|
||||
return null
|
||||
|
||||
case 'single-use' === snippet.scope:
|
||||
return (
|
||||
<SubmitButton
|
||||
large
|
||||
name={SubmitSnippetAction.SAVE_AND_EXECUTE}
|
||||
disabled={isWorking}
|
||||
text={__('Save and Execute Once', 'code-snippets')}
|
||||
/>
|
||||
)
|
||||
|
||||
case snippet.active:
|
||||
return (
|
||||
<SubmitButton
|
||||
name={SubmitSnippetAction.SAVE_AND_DEACTIVATE}
|
||||
disabled={isWorking}
|
||||
large
|
||||
text={__('Save and Deactivate', 'code-snippets')}
|
||||
/>
|
||||
)
|
||||
|
||||
default:
|
||||
case !snippet.active:
|
||||
return (
|
||||
<SubmitButton
|
||||
name={SubmitSnippetAction.SAVE_AND_ACTIVATE}
|
||||
primary={primaryActivate}
|
||||
disabled={isWorking}
|
||||
large
|
||||
text={__('Save and Activate', 'code-snippets')}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export const SubmitButtons: React.FC = () => {
|
||||
const { snippet } = useSnippetForm()
|
||||
|
||||
const activateByDefault =
|
||||
!!window.CODE_SNIPPETS_EDIT?.activateByDefault &&
|
||||
!snippet.active && 'single-use' !== snippet.scope &&
|
||||
(!snippet.shared_network || !isNetworkAdmin())
|
||||
|
||||
return <>
|
||||
{activateByDefault && <SaveButton primary={!activateByDefault} />}
|
||||
<ActivateOrDeactivateButton primaryActivate={activateByDefault} />
|
||||
{!activateByDefault && <SaveButton primary />}
|
||||
</>
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import React from 'react'
|
||||
import { __ } from '@wordpress/i18n'
|
||||
import { useSnippetForm } from '../../../hooks/useSnippetForm'
|
||||
import { SubmitSnippetAction, useSubmitSnippet } from '../../../hooks/useSubmitSnippet'
|
||||
import { handleUnknownError } from '../../../utils/errors'
|
||||
|
||||
export const ActivationSwitch = () => {
|
||||
const { snippet, isWorking } = useSnippetForm()
|
||||
const { submitSnippet } = useSubmitSnippet()
|
||||
|
||||
return (
|
||||
<div className="inline-form-field activation-switch-container">
|
||||
<h4>{__('Status')}</h4>
|
||||
|
||||
<label>
|
||||
{snippet.active
|
||||
? __('Active', 'code-snippets')
|
||||
: __('Inactive', 'code-snippets')}
|
||||
|
||||
<input
|
||||
id="activation-switch"
|
||||
type="checkbox"
|
||||
checked={snippet.active}
|
||||
disabled={isWorking || !!snippet.shared_network}
|
||||
className="switch"
|
||||
onChange={() => {
|
||||
submitSnippet(snippet.active
|
||||
? SubmitSnippetAction.SAVE_AND_DEACTIVATE
|
||||
: SubmitSnippetAction.SAVE_AND_ACTIVATE)
|
||||
.then(() => undefined)
|
||||
.catch(handleUnknownError)
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import React from 'react'
|
||||
import { __ } from '@wordpress/i18n'
|
||||
import { useSnippetForm } from '../../../hooks/useSnippetForm'
|
||||
import { Tooltip } from '../../common/Tooltip'
|
||||
|
||||
export const MultisiteSharingSettings: React.FC = () => {
|
||||
const { snippet, setSnippet, isReadOnly } = useSnippetForm()
|
||||
|
||||
return (
|
||||
<div className="inline-form-field activation-switch-container">
|
||||
<h4>
|
||||
{__('Share with Subsites', 'code-snippets')}
|
||||
</h4>
|
||||
|
||||
<Tooltip inline start>
|
||||
{__('Instead of running on every site, allow this snippet to be activated on individual sites on the network.', 'code-snippets')}
|
||||
</Tooltip>
|
||||
|
||||
<label>
|
||||
{snippet.shared_network
|
||||
? __('Enabled', 'code-snippets')
|
||||
: __('Disabled', 'code-snippets')}
|
||||
|
||||
<input
|
||||
id="snippet_sharing"
|
||||
name="snippet_sharing"
|
||||
type="checkbox"
|
||||
className="switch"
|
||||
checked={!!snippet.shared_network}
|
||||
disabled={isReadOnly}
|
||||
onChange={event =>
|
||||
setSnippet(previous => ({
|
||||
...previous,
|
||||
active: false,
|
||||
shared_network: event.target.checked
|
||||
}))}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import React from 'react'
|
||||
import { __ } from '@wordpress/i18n'
|
||||
import { useSnippetForm } from '../../../hooks/useSnippetForm'
|
||||
import { Tooltip } from '../../common/Tooltip'
|
||||
|
||||
export const PriorityInput = () => {
|
||||
const { snippet, isReadOnly, setSnippet } = useSnippetForm()
|
||||
|
||||
return (
|
||||
<div className="snippet-priority inline-form-field">
|
||||
<h4>
|
||||
<label htmlFor="snippet-priority">
|
||||
{__('Priority', 'code-snippets')}
|
||||
</label>
|
||||
</h4>
|
||||
|
||||
<Tooltip block end>
|
||||
{__('Snippets with a lower priority number will run before those with a higher number.', 'code-snippets')}
|
||||
</Tooltip>
|
||||
|
||||
<input
|
||||
type="number"
|
||||
id="snippet-priority"
|
||||
name="snippet_priority"
|
||||
value={snippet.priority}
|
||||
disabled={isReadOnly}
|
||||
onChange={event => setSnippet(previous => ({
|
||||
...previous,
|
||||
priority: parseInt(event.target.value, 10)
|
||||
}))}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import React from 'react'
|
||||
import { __ } from '@wordpress/i18n'
|
||||
import { useSnippetForm } from '../../../hooks/useSnippetForm'
|
||||
|
||||
export const RTLControl: React.FC = () => {
|
||||
const { codeEditorInstance } = useSnippetForm()
|
||||
|
||||
return (
|
||||
<div className="inline-form-field">
|
||||
<h4>
|
||||
<label htmlFor="snippet-code-direction">
|
||||
{__('Code Direction', 'code-snippets')}
|
||||
</label>
|
||||
</h4>
|
||||
|
||||
<select id="snippet-code-direction" onChange={event =>
|
||||
codeEditorInstance?.codemirror.setOption('direction', 'rtl' === event.target.value ? 'rtl' : 'ltr')
|
||||
}>
|
||||
<option value="ltr">{__('LTR', 'code-snippets')}</option>
|
||||
<option value="rtl">{__('RTL', 'code-snippets')}</option>
|
||||
</select>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './EditorSidebar'
|
||||
Reference in New Issue
Block a user