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:
OpenCode
2026-05-29 19:07:56 +08:00
commit 09ef1f000f
6521 changed files with 867163 additions and 0 deletions

View File

@@ -0,0 +1,170 @@
/**
* Based on work licensed under the BSD 3-Clause license.
*
* Copyright (c) 2017, glayzzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import { Engine } from 'php-parser'
import CodeMirror from 'codemirror'
import type { Block, Location, Node } from 'php-parser'
export interface Annotation {
message: string
severity: string
from: CodeMirror.Position
to: CodeMirror.Position
}
export interface Identifier extends Node {
name: string
}
export interface Declaration extends Node {
name: Identifier | string
}
export class Linter {
private readonly code: string
private readonly function_names: Set<string>
private readonly class_names: Set<string>
public readonly annotations: Annotation[]
/**
* Constructor.
* @param code
*/
constructor(code: string) {
this.code = code
this.annotations = []
this.function_names = new Set()
this.class_names = new Set()
}
/**
* Lint the provided code.
*/
lint() {
const parser = new Engine({
parser: {
suppressErrors: true,
version: 800
},
ast: {
withPositions: true
}
})
try {
const program = parser.parseEval(this.code)
if (0 < program.errors.length) {
for (const error of program.errors) {
this.annotate(error.message, error.loc)
}
}
this.visit(program)
} catch (error) {
console.error(error)
}
}
/**
* Visit nodes recursively.
* @param node
*/
visit(node: Node) {
if (node.kind) {
this.validate(node)
}
if ('children' in node) {
const block = <Block> node
for (const child of block.children) {
this.visit(child)
}
}
}
/**
* Check whether a given identifier has already been defined, creating an annotation if so.
* @param identifier
* @param registry
* @param label
*/
checkDuplicateIdentifier(identifier: Identifier, registry: Set<string>, label: string) {
if (registry.has(identifier.name)) {
this.annotate(`Cannot redeclare ${label} ${identifier.name}()`, identifier.loc)
} else {
registry.add(identifier.name)
}
}
/**
* Perform additional validations on nodes.
* @param node
*/
validate(node: Node) {
const decl = <Declaration> node
const ident = <Identifier> decl.name
if (!('name' in decl && 'name' in ident) || 'identifier' !== ident.kind) {
return
}
if ('function' === node.kind) {
this.checkDuplicateIdentifier(ident, this.function_names, 'function')
} else if ('class' === node.kind) {
this.checkDuplicateIdentifier(ident, this.class_names, 'class')
}
}
/**
* Create a lint annotation.
* @param message
* @param location
* @param severity
*/
annotate(message: string, location: Location | null, severity = 'error') {
const [start, end] = location
? location.end.offset < location.start.offset ? [location.end, location.start] : [location.start, location.end]
: [{ line: 0, column: 0 }, { line: 0, column: 0 }]
this.annotations.push({
message,
severity,
from: CodeMirror.Pos(start.line - 1, start.column),
to: CodeMirror.Pos(end.line - 1, end.column)
})
}
}

View File

@@ -0,0 +1,3 @@
export const handleUnknownError = (error: unknown) => {
console.error(error)
}

View File

@@ -0,0 +1,43 @@
import { getSnippetType } from './snippets/snippets'
import type { SnippetsExport } from '../types/schema/SnippetsExport'
import type { Snippet } from '../types/Snippet'
const SECOND_IN_MS = 1000
const TIMEOUT_SECONDS = 40
const JSON_INDENT_SPACES = 2
const MIME_INFO = <const> {
php: ['php', 'text/php'],
html: ['php', 'text/php'],
css: ['css', 'text/css'],
js: ['js', 'text/javascript'],
cond: ['json', 'application/json'],
json: ['json', 'application/json']
}
export const downloadAsFile = (content: BlobPart, filename: string, type: string) => {
const link = document.createElement('a')
link.download = filename
link.href = URL.createObjectURL(new Blob([content], { type }))
setTimeout(() => URL.revokeObjectURL(link.href), TIMEOUT_SECONDS * SECOND_IN_MS)
setTimeout(() => link.click(), 0)
}
export const downloadSnippetExportFile = (
content: SnippetsExport | string,
{ id, name, scope }: Snippet,
type?: keyof typeof MIME_INFO
) => {
const sanitizedName = name.toLowerCase().replace(/[^\w-]+/g, '-').trim()
const title = '' === sanitizedName ? `snippet-${id}` : sanitizedName
if ('string' === typeof content) {
const [ext, mimeType] = MIME_INFO[type ?? getSnippetType({ scope })]
const filename = `${title}.code-snippets.${ext}`
downloadAsFile(content, filename, mimeType)
} else {
const filename = `${title}.code-snippets.json`
downloadAsFile(JSON.stringify(content, undefined, JSON_INDENT_SPACES), filename, 'application/json')
}
}

View File

@@ -0,0 +1,21 @@
import { createContext, useContext } from 'react'
import type { Context } from 'react'
export const createContextHook = <T>(name: string): [
Context<T | undefined>,
() => T
] => {
const contextValue = createContext<T | undefined>(undefined)
const useContextHook = (): T => {
const value = useContext(contextValue)
if (value === undefined) {
throw Error(`use${name} can only be used within a ${name} context provider.`)
}
return value
}
return [contextValue, useContextHook]
}

View File

@@ -0,0 +1,12 @@
import { trimTrailingChar } from './text'
import type { AxiosRequestConfig } from 'axios'
export const REST_BASE = trimTrailingChar(window.CODE_SNIPPETS?.restAPI.base ?? '', '/')
export const REST_SNIPPETS_BASE = trimTrailingChar(window.CODE_SNIPPETS?.restAPI.snippets ?? '', '/')
export const REST_API_AXIOS_CONFIG: AxiosRequestConfig = {
headers: {
'X-WP-Nonce': window.CODE_SNIPPETS?.restAPI.nonce,
'Access-Control': window.CODE_SNIPPETS?.restAPI.localToken
}
}

View File

@@ -0,0 +1,8 @@
export const isNetworkAdmin = (): boolean =>
window.pagenow.endsWith('-network')
export const isMacOS = (): boolean =>
null !== /mac/i.exec(window.navigator.userAgent)
export const isLicensed = (): boolean =>
!!window.CODE_SNIPPETS?.isLicensed

View File

@@ -0,0 +1,92 @@
import { addQueryArgs } from '@wordpress/url'
import { REST_SNIPPETS_BASE } from '../restAPI'
import { createSnippetObject } from './snippets'
import type { RestAPI } from '../../hooks/useRestAPI'
import type { SnippetSchema, WritableSnippetSchema } from '../../types/schema/SnippetSchema'
import type { Snippet } from '../../types/Snippet'
import type { SnippetsExport } from '../../types/schema/SnippetsExport'
export interface SnippetsAPI {
fetchAll: (network?: boolean | null) => Promise<Snippet[]>
fetch: (snippetId: number, network?: boolean | null) => Promise<Snippet>
create: (snippet: Snippet) => Promise<Snippet>
update: (snippet: Pick<Snippet, 'id' | 'network'> & Partial<Snippet>) => Promise<Snippet>
delete: (snippet: Pick<Snippet, 'id' | 'network'>) => Promise<void>
activate: (snippet: Pick<Snippet, 'id' | 'network'>) => Promise<Snippet>
deactivate: (snippet: Pick<Snippet, 'id' | 'network'>) => Promise<Snippet>
export: (snippet: Pick<Snippet, 'id' | 'network'>) => Promise<SnippetsExport>
exportCode: (snippet: Pick<Snippet, 'id' | 'network'>) => Promise<string>
attach: (snippet: Pick<Snippet, 'id' | 'network' | 'conditionId'>) => Promise<void>
detach: (snippet: Pick<Snippet, 'id' | 'network'>) => Promise<void>
}
const buildURL = ({ id, network }: Pick<Snippet, 'id' | 'network'>, action?: string) =>
addQueryArgs(
[REST_SNIPPETS_BASE, id, action].filter(Boolean).join('/'),
{ network: network ? true : undefined }
)
const mapToSchema = ({
name,
desc,
code,
tags,
scope,
priority,
active,
network,
shared_network,
conditionId
}: Partial<Snippet>): WritableSnippetSchema => ({
name,
desc,
code,
tags,
scope,
priority,
active,
network,
shared_network,
condition_id: conditionId
})
export const buildSnippetsAPI = ({ get, post, del, put }: RestAPI): SnippetsAPI => ({
fetchAll: network =>
get<SnippetSchema[]>(addQueryArgs(REST_SNIPPETS_BASE, { network }))
.then(response => response.map(createSnippetObject)),
fetch: (snippetId, network) =>
get<SnippetSchema>(addQueryArgs(`${REST_SNIPPETS_BASE}/${snippetId}`, { network }))
.then(createSnippetObject),
create: snippet =>
post<SnippetSchema>(REST_SNIPPETS_BASE, mapToSchema(snippet))
.then(createSnippetObject),
update: snippet =>
post<SnippetSchema>(snippet.id ? buildURL(snippet) : REST_SNIPPETS_BASE, mapToSchema(snippet))
.then(createSnippetObject),
delete: snippet =>
del(buildURL(snippet)),
activate: snippet =>
post<SnippetSchema>(buildURL(snippet, 'activate'))
.then(createSnippetObject),
deactivate: snippet =>
post<SnippetSchema>(buildURL(snippet, 'deactivate'))
.then(createSnippetObject),
export: snippet =>
get<SnippetsExport>(buildURL(snippet, 'export')),
exportCode: snippet =>
get<string>(buildURL(snippet, 'export-code')),
attach: snippet =>
put(buildURL(snippet, 'attach'), { condition_id: snippet.conditionId }),
detach: snippet =>
put(buildURL(snippet, 'detach'))
})

View File

@@ -0,0 +1,51 @@
import { SNIPPET_TYPE_SCOPES } from '../../types/Snippet'
import { isNetworkAdmin } from '../screen'
import type { Snippet, SnippetScope } from '../../types/Snippet'
const defaults: Omit<Snippet, 'tags'> = {
id: 0,
name: '',
code: '',
desc: '',
scope: 'global',
modified: '',
active: false,
network: isNetworkAdmin(),
shared_network: null,
priority: 10,
conditionId: 0
}
const isAbsInt = (value: unknown): value is number =>
'number' === typeof value && 0 < value
const parseStringArray = (value: unknown): string[] | undefined =>
Array.isArray(value) ? value.filter(entry => 'string' === typeof entry) : undefined
export const isValidScope = (scope: unknown): scope is SnippetScope =>
'string' === typeof scope && Object.values(SNIPPET_TYPE_SCOPES).some(typeScopes =>
typeScopes.some(typeScope => typeScope === scope))
export const parseSnippetObject = (fields: unknown): Snippet => {
const result: { -readonly [F in keyof Snippet]: Snippet[F] } = { ...defaults, tags: [] }
if ('object' !== typeof fields || null === fields) {
return result
}
return {
...result,
...'id' in fields && isAbsInt(fields.id) && { id: fields.id },
...'name' in fields && 'string' === typeof fields.name && { name: fields.name },
...'desc' in fields && 'string' === typeof fields.desc && { desc: fields.desc },
...'code' in fields && 'string' === typeof fields.code && { code: fields.code },
...'tags' in fields && { tags: parseStringArray(fields.tags) ?? result.tags },
...'scope' in fields && isValidScope(fields.scope) && { scope: fields.scope },
...'modified' in fields && 'string' === typeof fields.modified && { modified: fields.modified },
...'active' in fields && 'boolean' === typeof fields.active && { active: fields.active },
...'network' in fields && 'boolean' === typeof fields.network && { network: fields.network },
...'shared_network' in fields && 'boolean' === typeof fields.shared_network && { shared_network: fields.shared_network },
...'priority' in fields && 'number' === typeof fields.priority && { priority: fields.priority },
...'condition_id' in fields && isAbsInt(fields.condition_id) && { conditionId: fields.condition_id }
}
}

View File

@@ -0,0 +1,55 @@
import { __ } from '@wordpress/i18n'
import { parseSnippetObject } from './objects'
import type { Snippet, SnippetType } from '../../types/Snippet'
const PRO_TYPES = new Set<SnippetType>(['css', 'js', 'cond'])
export const createSnippetObject = (fields: unknown): Snippet =>
parseSnippetObject(fields)
export const getSnippetType = ({ scope }: Pick<Snippet, 'scope'>): SnippetType => {
switch (true) {
case scope.endsWith('-css'):
return 'css'
case scope.endsWith('-js'):
return 'js'
case scope.endsWith('content'):
return 'html'
case 'condition' === scope:
return 'cond'
default:
return 'php'
}
}
export const validateSnippet = (snippet: Snippet): undefined | string => {
const missingTitle = '' === snippet.name.trim()
const missingCode = '' === snippet.code.trim()
switch (true) {
case missingCode && missingTitle:
return __('This snippet has no code or title.', 'code-snippets')
case missingCode:
return __('This snippet has no snippet code.', 'code-snippets')
case missingTitle:
return __('This snippet has no title.', 'code-snippets')
default:
return undefined
}
}
export const isCondition = (snippet: Pick<Snippet, 'scope'>): boolean =>
'condition' === snippet.scope
export const isProSnippet = (snippet: Pick<Snippet, 'scope'>): boolean =>
PRO_TYPES.has(getSnippetType(snippet))
export const isProType = (type: SnippetType): boolean =>
PRO_TYPES.has(type)

View File

@@ -0,0 +1,21 @@
export const toCamelCase = (text: string): string =>
text.replace(/-(?<letter>[a-z])/g, (_, letter: string) => letter.toUpperCase())
export const trimLeadingChar = (text: string, character: string): string =>
character === text.charAt(0) ? text.slice(1) : text
export const trimTrailingChar = (text: string, character: string): string =>
character === text.charAt(text.length - 1) ? text.slice(0, -1) : text
export const truncateWords = (text: string, wordCount: number): string => {
const words = text.trim().split(/\s+/)
return words.length > wordCount
? `${words.slice(0, wordCount).join(' ')}`
: text
}
export const stripTags = (text: string): string =>
text
.replace(/<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi, '')
.replace(/<\/?[a-z][a-z0-9]*\b[^>]*>/gi, '')