Compare commits
No commits in common. "dd8fa5f82e2c966498fc08f3c2ed4fc8c2eff9c8" and "bd3a4379bd79c45308cccac13260f3f6329c910a" have entirely different histories.
dd8fa5f82e
...
bd3a4379bd
22 changed files with 4878 additions and 6286 deletions
|
|
@ -1,119 +0,0 @@
|
|||
name: Auto Label
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize]
|
||||
|
||||
jobs:
|
||||
auto-label:
|
||||
if: github.repository != 'WenPai-org/ci-workflows'
|
||||
runs-on: linux-arm64
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 自动打标签
|
||||
env:
|
||||
FORGEJO_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
GITHUB_REPOSITORY: ${{ github.repository }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
run: |
|
||||
API_BASE="${GITHUB_SERVER_URL:-https://feicode.com}/api/v1"
|
||||
OWNER="${GITHUB_REPOSITORY%/*}"
|
||||
REPO="${GITHUB_REPOSITORY#*/}"
|
||||
LABELS=""
|
||||
|
||||
# 获取 PR 变更文件
|
||||
FILES=$(curl -s -H "Authorization: token $FORGEJO_TOKEN" \
|
||||
"$API_BASE/repos/$OWNER/$REPO/pulls/$PR_NUMBER/files" | \
|
||||
python3 -c "import json,sys; [print(f['filename']) for f in json.load(sys.stdin)]" 2>/dev/null)
|
||||
|
||||
# 根据文件类型打标签
|
||||
echo "$FILES" | grep -qE '\.php$' && LABELS="$LABELS,php"
|
||||
echo "$FILES" | grep -qE '\.(js|ts|tsx|jsx)$' && LABELS="$LABELS,frontend"
|
||||
echo "$FILES" | grep -qE '\.(css|scss|less)$' && LABELS="$LABELS,style"
|
||||
echo "$FILES" | grep -qE '\.go$' && LABELS="$LABELS,go"
|
||||
echo "$FILES" | grep -qE '\.(yml|yaml)$' && LABELS="$LABELS,ci/cd"
|
||||
echo "$FILES" | grep -qE '(composer\.|package\.json|go\.mod)' && LABELS="$LABELS,dependencies"
|
||||
echo "$FILES" | grep -qE '\.(md|txt|rst)$' && LABELS="$LABELS,documentation"
|
||||
echo "$FILES" | grep -qE '(Dockerfile|docker-compose)' && LABELS="$LABELS,docker"
|
||||
|
||||
# 根据 PR 大小打标签
|
||||
ADDITIONS=$(curl -s -H "Authorization: token $FORGEJO_TOKEN" \
|
||||
"$API_BASE/repos/$OWNER/$REPO/pulls/$PR_NUMBER" | \
|
||||
python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('additions',0)+d.get('deletions',0))" 2>/dev/null)
|
||||
|
||||
if [ "$ADDITIONS" -lt 10 ] 2>/dev/null; then
|
||||
LABELS="$LABELS,size/S"
|
||||
elif [ "$ADDITIONS" -lt 100 ] 2>/dev/null; then
|
||||
LABELS="$LABELS,size/M"
|
||||
elif [ "$ADDITIONS" -lt 500 ] 2>/dev/null; then
|
||||
LABELS="$LABELS,size/L"
|
||||
else
|
||||
LABELS="$LABELS,size/XL"
|
||||
fi
|
||||
|
||||
# 去掉开头逗号
|
||||
LABELS="${LABELS#,}"
|
||||
|
||||
if [ -z "$LABELS" ]; then
|
||||
echo "无需添加标签"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "添加标签: $LABELS"
|
||||
|
||||
# 确保标签存在,不存在则创建
|
||||
IFS=',' read -ra LABEL_ARRAY <<< "$LABELS"
|
||||
LABEL_IDS="["
|
||||
for LABEL in "${LABEL_ARRAY[@]}"; do
|
||||
# 查找标签
|
||||
LABEL_ID=$(curl -s -H "Authorization: token $FORGEJO_TOKEN" \
|
||||
"$API_BASE/repos/$OWNER/$REPO/labels?limit=50" | \
|
||||
python3 -c "
|
||||
import json,sys
|
||||
labels=json.load(sys.stdin)
|
||||
for l in labels:
|
||||
if l['name']=='$LABEL':
|
||||
print(l['id'])
|
||||
break
|
||||
" 2>/dev/null)
|
||||
|
||||
# 标签不存在则创建
|
||||
if [ -z "$LABEL_ID" ]; then
|
||||
# 根据标签类型选颜色
|
||||
case "$LABEL" in
|
||||
php) COLOR="#4F5D95" ;;
|
||||
frontend) COLOR="#f1e05a" ;;
|
||||
go) COLOR="#00ADD8" ;;
|
||||
ci/cd) COLOR="#0075ca" ;;
|
||||
dependencies) COLOR="#0366d6" ;;
|
||||
documentation) COLOR="#0075ca" ;;
|
||||
docker) COLOR="#0db7ed" ;;
|
||||
style) COLOR="#e34c26" ;;
|
||||
size/S) COLOR="#009900" ;;
|
||||
size/M) COLOR="#FFCC00" ;;
|
||||
size/L) COLOR="#FF6600" ;;
|
||||
size/XL) COLOR="#CC0000" ;;
|
||||
*) COLOR="#ededed" ;;
|
||||
esac
|
||||
LABEL_ID=$(curl -s -X POST -H "Authorization: token $FORGEJO_TOKEN" \
|
||||
"$API_BASE/repos/$OWNER/$REPO/labels" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"name\":\"$LABEL\",\"color\":\"$COLOR\"}" | \
|
||||
python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null)
|
||||
fi
|
||||
|
||||
[ -n "$LABEL_ID" ] && LABEL_IDS="$LABEL_IDS$LABEL_ID,"
|
||||
done
|
||||
LABEL_IDS="${LABEL_IDS%,}]"
|
||||
|
||||
# 添加标签到 PR
|
||||
curl -s -X POST -H "Authorization: token $FORGEJO_TOKEN" \
|
||||
"$API_BASE/repos/$OWNER/$REPO/issues/$PR_NUMBER/labels" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"labels\":$LABEL_IDS}" > /dev/null
|
||||
|
||||
echo "标签添加完成"
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
name: gitleaks 密钥泄露扫描
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ['*']
|
||||
pull_request:
|
||||
branches: ['*']
|
||||
|
||||
jobs:
|
||||
gitleaks:
|
||||
runs-on: linux-arm64
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Run gitleaks
|
||||
run: |
|
||||
if [ "$GITHUB_EVENT_NAME" = "push" ]; then
|
||||
gitleaks detect --source=. --log-opts="$GITHUB_SHA~1..$GITHUB_SHA" --verbose --exit-code 1 || {
|
||||
echo "::error::gitleaks 发现了潜在的密钥泄露!请检查上方输出并移除敏感信息。"
|
||||
exit 1
|
||||
}
|
||||
else
|
||||
gitleaks detect --source=. --verbose --exit-code 1 || {
|
||||
echo "::error::gitleaks 发现了潜在的密钥泄露!请检查上方输出并移除敏感信息。"
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
echo "gitleaks 扫描通过,未发现密钥泄露。"
|
||||
|
|
@ -127,7 +127,6 @@ jobs:
|
|||
--exclude="*.md" \
|
||||
--exclude="LICENSE" \
|
||||
--exclude="Makefile" \
|
||||
--exclude="lib" \
|
||||
./ "$BUILD_DIR/"
|
||||
|
||||
(
|
||||
|
|
@ -361,39 +360,3 @@ jobs:
|
|||
fi
|
||||
|
||||
echo "Update API verification passed"
|
||||
|
||||
- name: Notify AI CI assistant on failure (optional)
|
||||
if: ${{ failure() && secrets.AI_WEBHOOK_ENDPOINT != '' }}
|
||||
continue-on-error: true
|
||||
shell: bash
|
||||
env:
|
||||
AI_WEBHOOK_ENDPOINT: ${{ secrets.AI_WEBHOOK_ENDPOINT }}
|
||||
AI_WEBHOOK_TOKEN: ${{ secrets.AI_WEBHOOK_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
OWNER="${GITHUB_REPOSITORY%%/*}"
|
||||
REPO="${GITHUB_REPOSITORY##*/}"
|
||||
BRANCH="${GITHUB_REF#refs/heads/}"
|
||||
RUN_URL="${GITHUB_SERVER_URL%/}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
|
||||
FAILED_STEPS_JSON="$(printf '%s' '${{ toJson(steps) }}' | jq -c '[to_entries[] | select(.value.outcome=="failure" or .value.conclusion=="failure") | .key]')"
|
||||
|
||||
PAYLOAD="$(jq -cn \
|
||||
--arg event "ci_failure_report" \
|
||||
--arg owner "$OWNER" \
|
||||
--arg repo "$REPO" \
|
||||
--arg sha "${GITHUB_SHA}" \
|
||||
--arg branch "$BRANCH" \
|
||||
--arg workflow "${GITHUB_WORKFLOW}" \
|
||||
--arg run_url "$RUN_URL" \
|
||||
--arg log_excerpt "release workflow failed; inspect run_url for full logs" \
|
||||
--argjson failed_steps "$FAILED_STEPS_JSON" \
|
||||
'{event:$event, owner:$owner, repo:$repo, sha:$sha, branch:$branch, workflow:$workflow, run_url:$run_url, failed_steps:$failed_steps, log_excerpt:$log_excerpt}')"
|
||||
|
||||
CURL_HEADERS=(-H "Content-Type: application/json")
|
||||
if [[ -n "${AI_WEBHOOK_TOKEN:-}" ]]; then
|
||||
CURL_HEADERS+=( -H "Authorization: Bearer ${AI_WEBHOOK_TOKEN}" )
|
||||
fi
|
||||
|
||||
curl -sS -X POST "${CURL_HEADERS[@]}" --data "$PAYLOAD" "$AI_WEBHOOK_ENDPOINT" > /dev/null
|
||||
echo "AI CI assistant notified"
|
||||
|
|
|
|||
|
|
@ -1,128 +0,0 @@
|
|||
name: 安全扫描
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ['main', 'master']
|
||||
paths:
|
||||
- 'Dockerfile*'
|
||||
- 'docker-compose*.yml'
|
||||
- 'composer.json'
|
||||
- 'composer.lock'
|
||||
- 'package.json'
|
||||
- 'package-lock.json'
|
||||
- 'yarn.lock'
|
||||
- 'pnpm-lock.yaml'
|
||||
- 'go.mod'
|
||||
- 'go.sum'
|
||||
pull_request:
|
||||
branches: ['main', 'master']
|
||||
schedule:
|
||||
- cron: '0 3 * * 1' # 每周一凌晨 3 点
|
||||
|
||||
jobs:
|
||||
security-scan:
|
||||
runs-on: linux-arm64
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Hadolint Dockerfile 检查
|
||||
run: |
|
||||
# 检查是否存在 Dockerfile
|
||||
DOCKERFILES=$(find . -name 'Dockerfile*' -not -path './.git/*' 2>/dev/null)
|
||||
if [ -z "$DOCKERFILES" ]; then
|
||||
echo "未找到 Dockerfile,跳过"
|
||||
exit 0
|
||||
fi
|
||||
# 如果未安装 hadolint 则跳过
|
||||
if ! command -v hadolint &> /dev/null; then
|
||||
echo "hadolint 未安装,跳过 Dockerfile 检查"
|
||||
exit 0
|
||||
fi
|
||||
FAILED=0
|
||||
for df in $DOCKERFILES; do
|
||||
echo "--- 检查 $df ---"
|
||||
hadolint "$df" || FAILED=1
|
||||
done
|
||||
if [ $FAILED -eq 1 ]; then
|
||||
echo "::warning::Dockerfile 存在规范问题,请检查上方输出"
|
||||
fi
|
||||
|
||||
- name: PHP Composer 安全审计
|
||||
run: |
|
||||
if [ ! -f composer.lock ]; then
|
||||
echo "未找到 composer.lock,跳过"
|
||||
exit 0
|
||||
fi
|
||||
composer audit --format=table || {
|
||||
echo "::error::Composer 依赖存在已知安全漏洞"
|
||||
exit 1
|
||||
}
|
||||
echo "Composer 安全审计通过"
|
||||
|
||||
- name: npm 安全审计
|
||||
run: |
|
||||
if [ ! -f package-lock.json ] && [ ! -f yarn.lock ] && [ ! -f pnpm-lock.yaml ]; then
|
||||
echo "未找到 JS 锁文件,跳过"
|
||||
exit 0
|
||||
fi
|
||||
# npm audit 只报告 high 和 critical
|
||||
if [ -f package-lock.json ]; then
|
||||
npm audit --audit-level=high || {
|
||||
echo "::error::npm 依赖存在高危安全漏洞"
|
||||
exit 1
|
||||
}
|
||||
elif [ -f pnpm-lock.yaml ]; then
|
||||
pnpm audit --audit-level=high || {
|
||||
echo "::error::pnpm 依赖存在高危安全漏洞"
|
||||
exit 1
|
||||
}
|
||||
elif [ -f yarn.lock ]; then
|
||||
yarn npm audit --severity high || {
|
||||
echo "::error::yarn 依赖存在高危安全漏洞"
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
echo "JS 依赖安全审计通过"
|
||||
|
||||
- name: Go 依赖漏洞检查
|
||||
run: |
|
||||
if [ ! -f go.mod ]; then
|
||||
echo "未找到 go.mod,跳过"
|
||||
exit 0
|
||||
fi
|
||||
# govulncheck 检查已知漏洞
|
||||
if ! command -v govulncheck &> /dev/null; then
|
||||
go install golang.org/x/vuln/cmd/govulncheck@latest
|
||||
export PATH="$(go env GOPATH)/bin:$PATH"
|
||||
fi
|
||||
govulncheck ./... || {
|
||||
echo "::error::Go 依赖存在已知安全漏洞"
|
||||
exit 1
|
||||
}
|
||||
echo "Go 依赖安全检查通过"
|
||||
|
||||
- name: Trivy 容器镜像扫描
|
||||
run: |
|
||||
DOCKERFILES=$(find . -name 'Dockerfile' -not -path './.git/*' 2>/dev/null)
|
||||
if [ -z "$DOCKERFILES" ]; then
|
||||
echo "未找到 Dockerfile,跳过镜像扫描"
|
||||
exit 0
|
||||
fi
|
||||
if ! command -v trivy &> /dev/null; then
|
||||
echo "trivy 未安装,跳过容器镜像扫描"
|
||||
exit 0
|
||||
fi
|
||||
# 构建并扫描镜像
|
||||
IMAGE_NAME="ci-security-scan:$$"
|
||||
podman build -t "$IMAGE_NAME" -f Dockerfile . || {
|
||||
echo "::warning::容器构建失败,跳过镜像扫描"
|
||||
exit 0
|
||||
}
|
||||
trivy image --severity HIGH,CRITICAL --exit-code 1 "$IMAGE_NAME" || {
|
||||
echo "::error::容器镜像存在高危漏洞"
|
||||
podman rmi "$IMAGE_NAME" 2>/dev/null
|
||||
exit 1
|
||||
}
|
||||
podman rmi "$IMAGE_NAME" 2>/dev/null
|
||||
echo "容器镜像安全扫描通过"
|
||||
|
|
@ -1,119 +0,0 @@
|
|||
name: Stale Issue/PR 清理
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 5 * * 1' # 每周一凌晨 5 点
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
stale-cleanup:
|
||||
if: github.repository != 'WenPai-org/ci-workflows'
|
||||
runs-on: linux-arm64
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: 清理过期 Issue 和 PR
|
||||
env:
|
||||
FORGEJO_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
GITHUB_REPOSITORY: ${{ github.repository }}
|
||||
run: |
|
||||
API_BASE="${GITHUB_SERVER_URL:-https://feicode.com}/api/v1"
|
||||
OWNER="${GITHUB_REPOSITORY%/*}"
|
||||
REPO="${GITHUB_REPOSITORY#*/}"
|
||||
NOW=$(date +%s)
|
||||
|
||||
# 受保护的标签,带这些标签的不处理
|
||||
PROTECTED_LABELS="pinned|help-wanted|bug|security|wontfix"
|
||||
|
||||
# Issue: 60 天无活动标记 stale,再过 14 天关闭
|
||||
STALE_DAYS=60
|
||||
CLOSE_DAYS=74
|
||||
# PR: 30 天无活动标记 stale,再过 14 天关闭
|
||||
PR_STALE_DAYS=30
|
||||
PR_CLOSE_DAYS=44
|
||||
|
||||
process_items() {
|
||||
local TYPE=$1 # issues 或 pulls
|
||||
local STALE=$2
|
||||
local CLOSE=$3
|
||||
|
||||
PAGE=1
|
||||
while true; do
|
||||
if [ "$TYPE" = "issues" ]; then
|
||||
ITEMS=$(curl -s -H "Authorization: token $FORGEJO_TOKEN" \
|
||||
"$API_BASE/repos/$OWNER/$REPO/issues?state=open&type=issues&page=$PAGE&limit=50")
|
||||
else
|
||||
ITEMS=$(curl -s -H "Authorization: token $FORGEJO_TOKEN" \
|
||||
"$API_BASE/repos/$OWNER/$REPO/issues?state=open&type=pulls&page=$PAGE&limit=50")
|
||||
fi
|
||||
|
||||
COUNT=$(echo "$ITEMS" | python3 -c "import json,sys; print(len(json.load(sys.stdin)))" 2>/dev/null)
|
||||
[ "$COUNT" = "0" ] || [ -z "$COUNT" ] && break
|
||||
|
||||
echo "$ITEMS" | python3 -c "
|
||||
import json, sys, time
|
||||
|
||||
items = json.load(sys.stdin)
|
||||
now = $NOW
|
||||
stale_seconds = $STALE * 86400
|
||||
close_seconds = $CLOSE * 86400
|
||||
protected = set('$PROTECTED_LABELS'.split('|'))
|
||||
|
||||
for item in items:
|
||||
number = item['number']
|
||||
title = item['title']
|
||||
labels = [l['name'] for l in item.get('labels', [])]
|
||||
|
||||
# 跳过受保护标签
|
||||
if protected & set(labels):
|
||||
continue
|
||||
|
||||
updated = item['updated_at']
|
||||
# 解析 ISO 时间
|
||||
from datetime import datetime, timezone
|
||||
dt = datetime.fromisoformat(updated.replace('Z', '+00:00'))
|
||||
updated_ts = int(dt.timestamp())
|
||||
age = now - updated_ts
|
||||
|
||||
if age > close_seconds:
|
||||
print(f'CLOSE|{number}|{title}')
|
||||
elif age > stale_seconds and 'stale' not in labels:
|
||||
print(f'STALE|{number}|{title}')
|
||||
" | while IFS='|' read -r ACTION NUMBER TITLE; do
|
||||
if [ "$ACTION" = "STALE" ]; then
|
||||
echo "标记 #$NUMBER 为 stale: $TITLE"
|
||||
# 添加 stale 标签
|
||||
curl -s -X POST -H "Authorization: token $FORGEJO_TOKEN" \
|
||||
"$API_BASE/repos/$OWNER/$REPO/issues/$NUMBER/labels" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"labels":["stale"]}' > /dev/null
|
||||
# 添加评论
|
||||
curl -s -X POST -H "Authorization: token $FORGEJO_TOKEN" \
|
||||
"$API_BASE/repos/$OWNER/$REPO/issues/$NUMBER/comments" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"body\":\"此 ${TYPE%s} 已超过 $STALE 天无活动,已标记为 stale。如果 14 天内无新活动将自动关闭。\"}" > /dev/null
|
||||
elif [ "$ACTION" = "CLOSE" ]; then
|
||||
echo "关闭 #$NUMBER: $TITLE"
|
||||
curl -s -X PATCH -H "Authorization: token $FORGEJO_TOKEN" \
|
||||
"$API_BASE/repos/$OWNER/$REPO/issues/$NUMBER" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"state":"closed"}' > /dev/null
|
||||
curl -s -X POST -H "Authorization: token $FORGEJO_TOKEN" \
|
||||
"$API_BASE/repos/$OWNER/$REPO/issues/$NUMBER/comments" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"body\":\"此 ${TYPE%s} 因长期无活动已自动关闭。如需继续讨论请重新打开。\"}" > /dev/null
|
||||
fi
|
||||
done
|
||||
|
||||
PAGE=$((PAGE + 1))
|
||||
done
|
||||
}
|
||||
|
||||
echo "=== 处理 Issues (${STALE_DAYS}天stale / ${CLOSE_DAYS}天关闭) ==="
|
||||
process_items "issues" $STALE_DAYS $CLOSE_DAYS
|
||||
|
||||
echo "=== 处理 PRs (${PR_STALE_DAYS}天stale / ${PR_CLOSE_DAYS}天关闭) ==="
|
||||
process_items "pulls" $PR_STALE_DAYS $PR_CLOSE_DAYS
|
||||
|
||||
echo "清理完成"
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
name: Trivy 依赖漏洞扫描
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ['main', 'master']
|
||||
paths:
|
||||
- 'composer.lock'
|
||||
- 'package-lock.json'
|
||||
- 'yarn.lock'
|
||||
- 'pnpm-lock.yaml'
|
||||
pull_request:
|
||||
branches: ['main', 'master']
|
||||
# 每周一早上 8 点定时扫描
|
||||
schedule:
|
||||
- cron: '0 0 * * 1'
|
||||
|
||||
jobs:
|
||||
trivy-scan:
|
||||
runs-on: docker
|
||||
container:
|
||||
image: aquasec/trivy:latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: https://code.forgejo.org/actions/checkout@v4
|
||||
|
||||
- name: Run Trivy filesystem scan
|
||||
run: |
|
||||
trivy filesystem . --scanners vuln --severity HIGH,CRITICAL --exit-code 1 --format table --ignorefile .trivyignore 2>&1 || {
|
||||
echo
|
||||
echo ::warning::Trivy 发现了高危/严重漏洞,请检查上方输出。
|
||||
echo 如需忽略特定 CVE,请在仓库根目录创建 .trivyignore 文件。
|
||||
exit 1
|
||||
}
|
||||
echo ✅ Trivy 扫描通过,未发现高危漏洞。
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
name: WordPress 插件 CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ['main', 'master']
|
||||
pull_request:
|
||||
branches: ['main', 'master']
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
if: github.repository != 'WenPai-org/ci-workflows'
|
||||
runs-on: linux-arm64
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: PHP Parallel Lint
|
||||
run: |
|
||||
parallel-lint --exclude vendor --exclude node_modules .
|
||||
|
||||
- name: PHPCS 代码规范检查
|
||||
run: |
|
||||
# 如果仓库有自定义 phpcs 配置则使用,否则用默认 WordPress 标准
|
||||
if [ -f phpcs.xml ] || [ -f phpcs.xml.dist ] || [ -f .phpcs.xml ] || [ -f .phpcs.xml.dist ]; then
|
||||
phpcs .
|
||||
else
|
||||
phpcs --standard=WordPress-Extra \
|
||||
--extensions=php \
|
||||
--ignore=vendor/*,node_modules/*,tests/*,lib/* \
|
||||
--report=full \
|
||||
-s .
|
||||
fi
|
||||
|
||||
- name: Gitleaks 密钥泄露扫描
|
||||
run: |
|
||||
if [ "$GITHUB_EVENT_NAME" = "push" ]; then
|
||||
gitleaks detect --source=. --log-opts="$GITHUB_SHA~1..$GITHUB_SHA" --verbose --exit-code 1
|
||||
else
|
||||
gitleaks detect --source=. --verbose --exit-code 1
|
||||
fi
|
||||
echo "gitleaks 扫描通过"
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
name: WordPress 插件自动发布
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
- '[0-9]+.[0-9]+*'
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: docker
|
||||
container:
|
||||
image: php:8.2-cli-alpine
|
||||
steps:
|
||||
- name: Install system dependencies
|
||||
run: |
|
||||
apk add --no-cache git curl zip unzip rsync npm composer python3
|
||||
|
||||
- name: Checkout
|
||||
uses: https://code.forgejo.org/actions/checkout@v4
|
||||
|
||||
- name: Install WP-CLI
|
||||
run: |
|
||||
curl -sO https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
|
||||
chmod +x wp-cli.phar
|
||||
mv wp-cli.phar /usr/local/bin/wp
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
if [ -f composer.json ]; then
|
||||
composer install --no-dev --optimize-autoloader --no-interaction
|
||||
fi
|
||||
if [ -f package.json ]; then
|
||||
npm ci --production 2>/dev/null || true
|
||||
fi
|
||||
|
||||
- name: Run PHPCS (if configured)
|
||||
run: |
|
||||
if [ -f phpcs.xml ] || [ -f phpcs.xml.dist ] || [ -f .phpcs.xml ] || [ -f .phpcs.xml.dist ]; then
|
||||
composer lint 2>/dev/null || vendor/bin/phpcs . || {
|
||||
echo "::error::PHPCS 检查失败,请修复代码规范问题后重新打 tag。"
|
||||
exit 1
|
||||
}
|
||||
else
|
||||
echo "未找到 PHPCS 配置,跳过代码规范检查。"
|
||||
fi
|
||||
|
||||
- name: Generate i18n files
|
||||
run: |
|
||||
SLUG=$(basename $GITHUB_REPOSITORY)
|
||||
if [ -d languages ] || [ -d lang ]; then
|
||||
LANG_DIR=$([ -d languages ] && echo languages || echo lang)
|
||||
wp i18n make-pot . "$LANG_DIR/$SLUG.pot" --allow-root 2>/dev/null || true
|
||||
wp i18n make-json "$LANG_DIR/" --no-purge --allow-root 2>/dev/null || true
|
||||
fi
|
||||
|
||||
- name: Build release ZIP
|
||||
run: |
|
||||
PLUGIN_SLUG=$(basename $GITHUB_REPOSITORY)
|
||||
TAG_NAME=${GITHUB_REF#refs/tags/}
|
||||
mkdir -p /tmp/release
|
||||
rsync -a --exclude='.git' --exclude='.forgejo' --exclude='.github' \
|
||||
--exclude='node_modules' --exclude='.phpcs*' --exclude='phpstan*' \
|
||||
--exclude='tests' --exclude='.editorconfig' --exclude='.gitignore' \
|
||||
--exclude='phpunit*' --exclude='Gruntfile*' --exclude='webpack*' \
|
||||
--exclude='package.json' --exclude='package-lock.json' \
|
||||
--exclude='composer.lock' --exclude='.env*' \
|
||||
. /tmp/release/$PLUGIN_SLUG/
|
||||
cd /tmp/release
|
||||
zip -r /tmp/$PLUGIN_SLUG-$TAG_NAME.zip $PLUGIN_SLUG/
|
||||
echo "ZIP_PATH=/tmp/$PLUGIN_SLUG-$TAG_NAME.zip" >> $GITHUB_ENV
|
||||
echo "PLUGIN_SLUG=$PLUGIN_SLUG" >> $GITHUB_ENV
|
||||
echo "TAG_NAME=$TAG_NAME" >> $GITHUB_ENV
|
||||
|
||||
- name: Create Forgejo Release
|
||||
env:
|
||||
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
run: |
|
||||
RELEASE_RESPONSE=$(curl -s -X POST \
|
||||
-H "Authorization: token $RELEASE_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
"$GITHUB_SERVER_URL/api/v1/repos/$GITHUB_REPOSITORY/releases" \
|
||||
-d "{
|
||||
\"tag_name\": \"$TAG_NAME\",
|
||||
\"name\": \"$PLUGIN_SLUG $TAG_NAME\",
|
||||
\"body\": \"自动发布 $TAG_NAME\",
|
||||
\"draft\": false,
|
||||
\"prerelease\": false
|
||||
}")
|
||||
RELEASE_ID=$(echo $RELEASE_RESPONSE | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null)
|
||||
if [ -z "$RELEASE_ID" ]; then
|
||||
echo "::warning::Release 创建失败或已存在。"
|
||||
exit 0
|
||||
fi
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $RELEASE_TOKEN" \
|
||||
-F "attachment=@$ZIP_PATH" \
|
||||
"$GITHUB_SERVER_URL/api/v1/repos/$GITHUB_REPOSITORY/releases/$RELEASE_ID/assets?name=$PLUGIN_SLUG-$TAG_NAME.zip"
|
||||
echo "Release $TAG_NAME 发布成功!"
|
||||
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
vendor/
|
||||
node_modules/
|
||||
.env
|
||||
*.log
|
||||
.DS_Store
|
||||
|
|
@ -91,13 +91,13 @@ class WenPai_Updater {
|
|||
// Update URI: https://updates.wenpai.net 触发此 filter
|
||||
add_filter(
|
||||
'update_plugins_updates.wenpai.net',
|
||||
array( $this, 'check_update' ),
|
||||
[ $this, 'check_update' ],
|
||||
10,
|
||||
4
|
||||
);
|
||||
|
||||
// 插件详情弹窗
|
||||
add_filter( 'plugins_api', array( $this, 'plugin_info' ), 20, 3 );
|
||||
add_filter( 'plugins_api', [ $this, 'plugin_info' ], 20, 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -117,16 +117,13 @@ class WenPai_Updater {
|
|||
return $update;
|
||||
}
|
||||
|
||||
$response = $this->api_request(
|
||||
'update-check',
|
||||
array(
|
||||
'plugins' => array(
|
||||
$this->plugin_file => array(
|
||||
$response = $this->api_request( 'update-check', [
|
||||
'plugins' => [
|
||||
$this->plugin_file => [
|
||||
'Version' => $this->version,
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
],
|
||||
],
|
||||
] );
|
||||
|
||||
if ( is_wp_error( $response ) || empty( $response['plugins'][ $this->plugin_file ] ) ) {
|
||||
return $update;
|
||||
|
|
@ -134,7 +131,7 @@ class WenPai_Updater {
|
|||
|
||||
$data = $response['plugins'][ $this->plugin_file ];
|
||||
|
||||
return (object) array(
|
||||
return (object) [
|
||||
'id' => $data['id'] ?? '',
|
||||
'slug' => $data['slug'] ?? $this->slug,
|
||||
'plugin' => $this->plugin_file,
|
||||
|
|
@ -142,12 +139,12 @@ class WenPai_Updater {
|
|||
'new_version' => $data['version'] ?? '',
|
||||
'url' => $data['url'] ?? '',
|
||||
'package' => $data['package'] ?? '',
|
||||
'icons' => $data['icons'] ?? array(),
|
||||
'banners' => $data['banners'] ?? array(),
|
||||
'icons' => $data['icons'] ?? [],
|
||||
'banners' => $data['banners'] ?? [],
|
||||
'requires' => $data['requires'] ?? '',
|
||||
'tested' => $data['tested'] ?? '',
|
||||
'requires_php' => $data['requires_php'] ?? '',
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -186,9 +183,9 @@ class WenPai_Updater {
|
|||
$info->tested = $response['tested'] ?? '';
|
||||
$info->requires_php = $response['requires_php'] ?? '';
|
||||
$info->last_updated = $response['last_updated'] ?? '';
|
||||
$info->icons = $response['icons'] ?? array();
|
||||
$info->banners = $response['banners'] ?? array();
|
||||
$info->sections = $response['sections'] ?? array();
|
||||
$info->icons = $response['icons'] ?? [];
|
||||
$info->banners = $response['banners'] ?? [];
|
||||
$info->sections = $response['sections'] ?? [];
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
|
@ -203,12 +200,12 @@ class WenPai_Updater {
|
|||
private function api_request( string $endpoint, ?array $body = null ) {
|
||||
$url = self::API_URL . '/' . ltrim( $endpoint, '/' );
|
||||
|
||||
$args = array(
|
||||
$args = [
|
||||
'timeout' => 10,
|
||||
'headers' => array(
|
||||
'headers' => [
|
||||
'Accept' => 'application/json',
|
||||
),
|
||||
);
|
||||
],
|
||||
];
|
||||
|
||||
if ( null !== $body ) {
|
||||
$args['headers']['Content-Type'] = 'application/json';
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
|
|
@ -17,63 +17,60 @@ class WPSlug_Converter {
|
|||
$this->settings = new WPSlug_Settings();
|
||||
}
|
||||
|
||||
public function convert( $text, $options = array() ) {
|
||||
if ( empty( $text ) ) {
|
||||
public function convert($text, $options = array()) {
|
||||
if (empty($text)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$mode = isset( $options['conversion_mode'] ) ? $options['conversion_mode'] : 'pinyin';
|
||||
$start_time = microtime( true );
|
||||
$mode = isset($options['conversion_mode']) ? $options['conversion_mode'] : 'pinyin';
|
||||
$start_time = microtime(true);
|
||||
|
||||
try {
|
||||
switch ( $mode ) {
|
||||
switch ($mode) {
|
||||
case 'pinyin':
|
||||
$result = $this->convertPinyin( $text, $options );
|
||||
$result = $this->convertPinyin($text, $options);
|
||||
break;
|
||||
case 'semantic_pinyin':
|
||||
$result = $this->convertSemanticPinyin( $text, $options );
|
||||
$result = $this->convertSemanticPinyin($text, $options);
|
||||
break;
|
||||
case 'transliteration':
|
||||
$result = $this->convertTransliteration( $text, $options );
|
||||
$result = $this->convertTransliteration($text, $options);
|
||||
break;
|
||||
case 'translation':
|
||||
$result = $this->convertTranslation( $text, $options );
|
||||
$result = $this->convertTranslation($text, $options);
|
||||
break;
|
||||
default:
|
||||
$result = $this->convertPinyin( $text, $options );
|
||||
$result = $this->convertPinyin($text, $options);
|
||||
}
|
||||
|
||||
$execution_time = microtime( true ) - $start_time;
|
||||
$this->settings->updateConversionStats( $mode, true, $execution_time );
|
||||
$execution_time = microtime(true) - $start_time;
|
||||
$this->settings->updateConversionStats($mode, true, $execution_time);
|
||||
|
||||
return $result;
|
||||
|
||||
} catch ( Exception $e ) {
|
||||
$execution_time = microtime( true ) - $start_time;
|
||||
$this->settings->updateConversionStats( $mode, false, $execution_time );
|
||||
$this->settings->logError(
|
||||
'Conversion error in mode ' . $mode . ': ' . $e->getMessage(),
|
||||
array(
|
||||
} catch (Exception $e) {
|
||||
$execution_time = microtime(true) - $start_time;
|
||||
$this->settings->updateConversionStats($mode, false, $execution_time);
|
||||
$this->settings->logError('Conversion error in mode ' . $mode . ': ' . $e->getMessage(), array(
|
||||
'text' => $text,
|
||||
'mode' => $mode,
|
||||
'options' => $options,
|
||||
)
|
||||
);
|
||||
'options' => $options
|
||||
));
|
||||
|
||||
return $this->cleanBasicSlug( $text, $options );
|
||||
return $this->cleanBasicSlug($text, $options);
|
||||
}
|
||||
}
|
||||
|
||||
private function convertPinyin( $text, $options ) {
|
||||
private function convertPinyin($text, $options) {
|
||||
try {
|
||||
if ( $this->detectLanguage( $text ) === 'zh' ) {
|
||||
return $this->pinyin->convertToPinyin( $text, $options );
|
||||
if ($this->detectLanguage($text) === 'zh') {
|
||||
return $this->pinyin->convertToPinyin($text, $options);
|
||||
} else {
|
||||
return $this->cleanBasicSlug( $text, $options );
|
||||
return $this->cleanBasicSlug($text, $options);
|
||||
}
|
||||
} catch ( Exception $e ) {
|
||||
$this->settings->logError( 'Pinyin conversion error: ' . $e->getMessage() );
|
||||
return $this->cleanBasicSlug( $text, $options );
|
||||
} catch (Exception $e) {
|
||||
$this->settings->logError('Pinyin conversion error: ' . $e->getMessage());
|
||||
return $this->cleanBasicSlug($text, $options);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -83,171 +80,168 @@ class WPSlug_Converter {
|
|||
* 与普通拼音不同,这个方法会按词语分隔而非按字分隔
|
||||
* 例如 "你好世界" → "nihao-shijie" 而非 "ni-hao-shi-jie"
|
||||
*/
|
||||
private function convertSemanticPinyin( $text, $options ) {
|
||||
$debug_mode = isset( $options['debug_mode'] ) && $options['debug_mode'];
|
||||
private function convertSemanticPinyin($text, $options) {
|
||||
$debug_mode = isset($options['debug_mode']) && $options['debug_mode'];
|
||||
|
||||
try {
|
||||
// 1. 检查是否是中文
|
||||
if ( $this->detectLanguage( $text ) !== 'zh' ) {
|
||||
return $this->cleanBasicSlug( $text, $options );
|
||||
if ($this->detectLanguage($text) !== 'zh') {
|
||||
return $this->cleanBasicSlug($text, $options);
|
||||
}
|
||||
|
||||
// 2. 检查 WPMind 是否可用
|
||||
if ( ! function_exists( 'wpmind_pinyin' ) || ! function_exists( 'wpmind_is_available' ) || ! wpmind_is_available() ) {
|
||||
if ( $debug_mode ) {
|
||||
error_log( '[WPSlug] WPMind not available for semantic pinyin, falling back to regular pinyin' );
|
||||
if (!function_exists('wpmind_pinyin') || !function_exists('wpmind_is_available') || !wpmind_is_available()) {
|
||||
if ($debug_mode) {
|
||||
error_log('[WPSlug] WPMind not available for semantic pinyin, falling back to regular pinyin');
|
||||
}
|
||||
return $this->convertPinyin( $text, $options );
|
||||
return $this->convertPinyin($text, $options);
|
||||
}
|
||||
|
||||
// 3. 文本长度限制(避免超时)
|
||||
if ( mb_strlen( $text ) > 200 ) {
|
||||
if ( $debug_mode ) {
|
||||
error_log( '[WPSlug] Text too long for semantic pinyin, using regular pinyin' );
|
||||
if (mb_strlen($text) > 200) {
|
||||
if ($debug_mode) {
|
||||
error_log('[WPSlug] Text too long for semantic pinyin, using regular pinyin');
|
||||
}
|
||||
return $this->convertPinyin( $text, $options );
|
||||
return $this->convertPinyin($text, $options);
|
||||
}
|
||||
|
||||
// 4. 调用 WPMind AI 进行语义化拼音转换
|
||||
$result = wpmind_pinyin(
|
||||
$text,
|
||||
array(
|
||||
$result = wpmind_pinyin($text, [
|
||||
'context' => 'wpslug_semantic_pinyin',
|
||||
'cache_ttl' => 604800, // 7天
|
||||
)
|
||||
);
|
||||
]);
|
||||
|
||||
// 5. 处理结果
|
||||
if ( is_wp_error( $result ) ) {
|
||||
if ( $debug_mode ) {
|
||||
error_log( '[WPSlug] WPMind pinyin error: ' . $result->get_error_message() );
|
||||
if (is_wp_error($result)) {
|
||||
if ($debug_mode) {
|
||||
error_log('[WPSlug] WPMind pinyin error: ' . $result->get_error_message());
|
||||
}
|
||||
return $this->convertPinyin( $text, $options );
|
||||
return $this->convertPinyin($text, $options);
|
||||
}
|
||||
|
||||
// 6. 清理结果
|
||||
$slug = $this->cleanSemanticPinyinResult( $result, $options );
|
||||
$slug = $this->cleanSemanticPinyinResult($result, $options);
|
||||
|
||||
if ( empty( $slug ) ) {
|
||||
if ( $debug_mode ) {
|
||||
error_log( '[WPSlug] WPMind returned empty pinyin, using regular pinyin' );
|
||||
if (empty($slug)) {
|
||||
if ($debug_mode) {
|
||||
error_log('[WPSlug] WPMind returned empty pinyin, using regular pinyin');
|
||||
}
|
||||
return $this->convertPinyin( $text, $options );
|
||||
return $this->convertPinyin($text, $options);
|
||||
}
|
||||
|
||||
if ( $debug_mode ) {
|
||||
error_log( '[WPSlug] Semantic pinyin: "' . $text . '" → "' . $slug . '"' );
|
||||
if ($debug_mode) {
|
||||
error_log('[WPSlug] Semantic pinyin: "' . $text . '" → "' . $slug . '"');
|
||||
}
|
||||
|
||||
return $slug;
|
||||
|
||||
} catch ( Exception $e ) {
|
||||
$this->settings->logError( 'Semantic pinyin error: ' . $e->getMessage() );
|
||||
return $this->convertPinyin( $text, $options );
|
||||
} catch (Exception $e) {
|
||||
$this->settings->logError('Semantic pinyin error: ' . $e->getMessage());
|
||||
return $this->convertPinyin($text, $options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理语义化拼音结果
|
||||
*/
|
||||
private function cleanSemanticPinyinResult( $text, $options ) {
|
||||
private function cleanSemanticPinyinResult($text, $options) {
|
||||
// 移除可能的引号和空格
|
||||
$text = trim( $text, " \t\n\r\0\x0B\"'" );
|
||||
$text = trim($text, " \t\n\r\0\x0B\"'");
|
||||
|
||||
// 确保只有小写字母、数字和连字符
|
||||
$text = preg_replace( '/[^a-zA-Z0-9\-]/', '-', $text );
|
||||
$text = strtolower( $text );
|
||||
$text = preg_replace( '/-+/', '-', $text );
|
||||
$text = trim( $text, '-' );
|
||||
$text = preg_replace('/[^a-zA-Z0-9\-]/', '-', $text);
|
||||
$text = strtolower($text);
|
||||
$text = preg_replace('/-+/', '-', $text);
|
||||
$text = trim($text, '-');
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
private function convertTransliteration( $text, $options ) {
|
||||
private function convertTransliteration($text, $options) {
|
||||
try {
|
||||
$detected_lang = $this->detectLanguage( $text );
|
||||
$detected_lang = $this->detectLanguage($text);
|
||||
|
||||
if ( in_array( $detected_lang, array( 'ru', 'ar', 'el', 'he' ) ) ) {
|
||||
return $this->transliterator->transliterate( $text, $options );
|
||||
if (in_array($detected_lang, array('ru', 'ar', 'el', 'he'))) {
|
||||
return $this->transliterator->transliterate($text, $options);
|
||||
} else {
|
||||
return $this->cleanBasicSlug( $text, $options );
|
||||
return $this->cleanBasicSlug($text, $options);
|
||||
}
|
||||
} catch ( Exception $e ) {
|
||||
$this->settings->logError( 'Transliteration error: ' . $e->getMessage() );
|
||||
return $this->cleanBasicSlug( $text, $options );
|
||||
} catch (Exception $e) {
|
||||
$this->settings->logError('Transliteration error: ' . $e->getMessage());
|
||||
return $this->cleanBasicSlug($text, $options);
|
||||
}
|
||||
}
|
||||
|
||||
private function convertTranslation( $text, $options ) {
|
||||
private function convertTranslation($text, $options) {
|
||||
try {
|
||||
$service = isset( $options['translation_service'] ) ? $options['translation_service'] : 'none';
|
||||
$service = isset($options['translation_service']) ? $options['translation_service'] : 'none';
|
||||
|
||||
if ( $service === 'none' ) {
|
||||
return $this->convertPinyin( $text, $options );
|
||||
if ($service === 'none') {
|
||||
return $this->convertPinyin($text, $options);
|
||||
}
|
||||
|
||||
$detected_lang = $this->detectLanguage( $text );
|
||||
$target_lang = isset( $options['translation_target_lang'] ) ? $options['translation_target_lang'] : 'en';
|
||||
$detected_lang = $this->detectLanguage($text);
|
||||
$target_lang = isset($options['translation_target_lang']) ? $options['translation_target_lang'] : 'en';
|
||||
|
||||
if ( $detected_lang === $target_lang ) {
|
||||
return $this->cleanBasicSlug( $text, $options );
|
||||
if ($detected_lang === $target_lang) {
|
||||
return $this->cleanBasicSlug($text, $options);
|
||||
}
|
||||
|
||||
return $this->translator->translate( $text, $options );
|
||||
} catch ( Exception $e ) {
|
||||
$this->settings->logError( 'Translation error: ' . $e->getMessage() );
|
||||
return $this->convertPinyin( $text, $options );
|
||||
return $this->translator->translate($text, $options);
|
||||
} catch (Exception $e) {
|
||||
$this->settings->logError('Translation error: ' . $e->getMessage());
|
||||
return $this->convertPinyin($text, $options);
|
||||
}
|
||||
}
|
||||
|
||||
private function cleanBasicSlug( $text, $options ) {
|
||||
$text = strip_tags( $text );
|
||||
$text = html_entity_decode( $text, ENT_QUOTES | ENT_HTML5, 'UTF-8' );
|
||||
$text = preg_replace( '/&.+?;/', '', $text );
|
||||
$text = preg_replace( '/[^a-zA-Z0-9\s\-_\p{L}\p{N}]/u', '', $text );
|
||||
$text = preg_replace( '/\s+/', '-', $text );
|
||||
$text = preg_replace( '/\-+/', '-', $text );
|
||||
$text = trim( $text, '-_' );
|
||||
private function cleanBasicSlug($text, $options) {
|
||||
$text = strip_tags($text);
|
||||
$text = html_entity_decode($text, ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
$text = preg_replace('/&.+?;/', '', $text);
|
||||
$text = preg_replace('/[^a-zA-Z0-9\s\-_\p{L}\p{N}]/u', '', $text);
|
||||
$text = preg_replace('/\s+/', '-', $text);
|
||||
$text = preg_replace('/\-+/', '-', $text);
|
||||
$text = trim($text, '-_');
|
||||
|
||||
if ( isset( $options['force_lowercase'] ) && $options['force_lowercase'] ) {
|
||||
$text = strtolower( $text );
|
||||
if (isset($options['force_lowercase']) && $options['force_lowercase']) {
|
||||
$text = strtolower($text);
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
public function detectLanguage( $text ) {
|
||||
if ( preg_match( '/[\x{4e00}-\x{9fff}]/u', $text ) ) {
|
||||
public function detectLanguage($text) {
|
||||
if (preg_match('/[\x{4e00}-\x{9fff}]/u', $text)) {
|
||||
return 'zh';
|
||||
}
|
||||
if ( preg_match( '/[\x{0400}-\x{04ff}]/u', $text ) ) {
|
||||
if (preg_match('/[\x{0400}-\x{04ff}]/u', $text)) {
|
||||
return 'ru';
|
||||
}
|
||||
if ( preg_match( '/[\x{0590}-\x{05ff}]/u', $text ) ) {
|
||||
if (preg_match('/[\x{0590}-\x{05ff}]/u', $text)) {
|
||||
return 'he';
|
||||
}
|
||||
if ( preg_match( '/[\x{0600}-\x{06ff}]/u', $text ) ) {
|
||||
if (preg_match('/[\x{0600}-\x{06ff}]/u', $text)) {
|
||||
return 'ar';
|
||||
}
|
||||
if ( preg_match( '/[\x{3040}-\x{309f}]/u', $text ) ) {
|
||||
if (preg_match('/[\x{3040}-\x{309f}]/u', $text)) {
|
||||
return 'ja';
|
||||
}
|
||||
if ( preg_match( '/[\x{30a0}-\x{30ff}]/u', $text ) ) {
|
||||
if (preg_match('/[\x{30a0}-\x{30ff}]/u', $text)) {
|
||||
return 'ja';
|
||||
}
|
||||
if ( preg_match( '/[\x{ac00}-\x{d7af}]/u', $text ) ) {
|
||||
if (preg_match('/[\x{ac00}-\x{d7af}]/u', $text)) {
|
||||
return 'ko';
|
||||
}
|
||||
if ( preg_match( '/[\x{0370}-\x{03ff}]/u', $text ) ) {
|
||||
if (preg_match('/[\x{0370}-\x{03ff}]/u', $text)) {
|
||||
return 'el';
|
||||
}
|
||||
if ( preg_match( '/[\x{0100}-\x{017f}]/u', $text ) ) {
|
||||
if (preg_match('/[\x{0100}-\x{017f}]/u', $text)) {
|
||||
return 'latin-ext';
|
||||
}
|
||||
if ( preg_match( '/[\x{0080}-\x{00ff}]/u', $text ) ) {
|
||||
if (preg_match('/[\x{0080}-\x{00ff}]/u', $text)) {
|
||||
return 'latin-ext';
|
||||
}
|
||||
if ( preg_match( '/[a-zA-Z]/', $text ) ) {
|
||||
if (preg_match('/[a-zA-Z]/', $text)) {
|
||||
return 'en';
|
||||
}
|
||||
|
||||
|
|
@ -255,36 +249,36 @@ class WPSlug_Converter {
|
|||
}
|
||||
|
||||
public function getSupportedModes() {
|
||||
return array( 'pinyin', 'semantic_pinyin', 'transliteration', 'translation' );
|
||||
return array('pinyin', 'semantic_pinyin', 'transliteration', 'translation');
|
||||
}
|
||||
|
||||
public function isModeSupported( $mode ) {
|
||||
return in_array( $mode, $this->getSupportedModes() );
|
||||
public function isModeSupported($mode) {
|
||||
return in_array($mode, $this->getSupportedModes());
|
||||
}
|
||||
|
||||
public function batchConvert( $items, $options = array() ) {
|
||||
public function batchConvert($items, $options = array()) {
|
||||
$results = array();
|
||||
|
||||
if ( ! is_array( $items ) ) {
|
||||
if (!is_array($items)) {
|
||||
return $results;
|
||||
}
|
||||
|
||||
foreach ( $items as $item ) {
|
||||
foreach ($items as $item) {
|
||||
try {
|
||||
$converted = $this->convert( $item, $options );
|
||||
$converted = $this->convert($item, $options);
|
||||
$results[] = array(
|
||||
'original' => $item,
|
||||
'converted' => $converted,
|
||||
'mode' => isset( $options['conversion_mode'] ) ? $options['conversion_mode'] : 'pinyin',
|
||||
'detected_language' => $this->detectLanguage( $item ),
|
||||
'mode' => isset($options['conversion_mode']) ? $options['conversion_mode'] : 'pinyin',
|
||||
'detected_language' => $this->detectLanguage($item)
|
||||
);
|
||||
} catch ( Exception $e ) {
|
||||
$this->settings->logError( 'Batch convert error: ' . $e->getMessage() );
|
||||
} catch (Exception $e) {
|
||||
$this->settings->logError('Batch convert error: ' . $e->getMessage());
|
||||
$results[] = array(
|
||||
'original' => $item,
|
||||
'converted' => sanitize_title( $item ),
|
||||
'converted' => sanitize_title($item),
|
||||
'mode' => 'fallback',
|
||||
'detected_language' => 'unknown',
|
||||
'detected_language' => 'unknown'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -292,13 +286,13 @@ class WPSlug_Converter {
|
|||
return $results;
|
||||
}
|
||||
|
||||
public function testConversion( $text, $options = array() ) {
|
||||
$start_time = microtime( true );
|
||||
public function testConversion($text, $options = array()) {
|
||||
$start_time = microtime(true);
|
||||
|
||||
try {
|
||||
$detected_lang = $this->detectLanguage( $text );
|
||||
$converted = $this->convert( $text, $options );
|
||||
$execution_time = microtime( true ) - $start_time;
|
||||
$detected_lang = $this->detectLanguage($text);
|
||||
$converted = $this->convert($text, $options);
|
||||
$execution_time = microtime(true) - $start_time;
|
||||
|
||||
return array(
|
||||
'success' => true,
|
||||
|
|
@ -306,80 +300,80 @@ class WPSlug_Converter {
|
|||
'converted' => $converted,
|
||||
'detected_language' => $detected_lang,
|
||||
'execution_time' => $execution_time,
|
||||
'mode' => isset( $options['conversion_mode'] ) ? $options['conversion_mode'] : 'pinyin',
|
||||
'mode' => isset($options['conversion_mode']) ? $options['conversion_mode'] : 'pinyin'
|
||||
);
|
||||
|
||||
} catch ( Exception $e ) {
|
||||
$execution_time = microtime( true ) - $start_time;
|
||||
} catch (Exception $e) {
|
||||
$execution_time = microtime(true) - $start_time;
|
||||
|
||||
return array(
|
||||
'success' => false,
|
||||
'original' => $text,
|
||||
'converted' => sanitize_title( $text ),
|
||||
'converted' => sanitize_title($text),
|
||||
'detected_language' => 'unknown',
|
||||
'execution_time' => $execution_time,
|
||||
'mode' => 'fallback',
|
||||
'error' => $e->getMessage(),
|
||||
'error' => $e->getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function getLanguageInfo( $text ) {
|
||||
$detected_lang = $this->detectLanguage( $text );
|
||||
$char_count = mb_strlen( $text, 'UTF-8' );
|
||||
$word_count = str_word_count( $text, 0, 'àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ' );
|
||||
public function getLanguageInfo($text) {
|
||||
$detected_lang = $this->detectLanguage($text);
|
||||
$char_count = mb_strlen($text, 'UTF-8');
|
||||
$word_count = str_word_count($text, 0, 'àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ');
|
||||
|
||||
$info = array(
|
||||
'detected_language' => $detected_lang,
|
||||
'character_count' => $char_count,
|
||||
'word_count' => $word_count,
|
||||
'has_chinese' => preg_match( '/[\x{4e00}-\x{9fff}]/u', $text ) ? true : false,
|
||||
'has_cyrillic' => preg_match( '/[\x{0400}-\x{04ff}]/u', $text ) ? true : false,
|
||||
'has_arabic' => preg_match( '/[\x{0600}-\x{06ff}]/u', $text ) ? true : false,
|
||||
'has_greek' => preg_match( '/[\x{0370}-\x{03ff}]/u', $text ) ? true : false,
|
||||
'has_latin' => preg_match( '/[a-zA-Z]/', $text ) ? true : false,
|
||||
'has_numbers' => preg_match( '/\d/', $text ) ? true : false,
|
||||
'has_special_chars' => preg_match( '/[^\w\s\p{L}\p{N}]/u', $text ) ? true : false,
|
||||
'has_chinese' => preg_match('/[\x{4e00}-\x{9fff}]/u', $text) ? true : false,
|
||||
'has_cyrillic' => preg_match('/[\x{0400}-\x{04ff}]/u', $text) ? true : false,
|
||||
'has_arabic' => preg_match('/[\x{0600}-\x{06ff}]/u', $text) ? true : false,
|
||||
'has_greek' => preg_match('/[\x{0370}-\x{03ff}]/u', $text) ? true : false,
|
||||
'has_latin' => preg_match('/[a-zA-Z]/', $text) ? true : false,
|
||||
'has_numbers' => preg_match('/\d/', $text) ? true : false,
|
||||
'has_special_chars' => preg_match('/[^\w\s\p{L}\p{N}]/u', $text) ? true : false
|
||||
);
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
public function recommendMode( $text ) {
|
||||
$lang_info = $this->getLanguageInfo( $text );
|
||||
public function recommendMode($text) {
|
||||
$lang_info = $this->getLanguageInfo($text);
|
||||
$detected_lang = $lang_info['detected_language'];
|
||||
|
||||
if ( $detected_lang === 'zh' ) {
|
||||
if ($detected_lang === 'zh') {
|
||||
return 'pinyin';
|
||||
} elseif ( in_array( $detected_lang, array( 'ru', 'ar', 'el', 'he' ) ) ) {
|
||||
} elseif (in_array($detected_lang, array('ru', 'ar', 'el', 'he'))) {
|
||||
return 'transliteration';
|
||||
} elseif ( $detected_lang !== 'en' && $detected_lang !== 'unknown' ) {
|
||||
} elseif ($detected_lang !== 'en' && $detected_lang !== 'unknown') {
|
||||
return 'translation';
|
||||
} else {
|
||||
return 'pinyin';
|
||||
}
|
||||
}
|
||||
|
||||
public function validateInput( $text, $options = array() ) {
|
||||
$max_length = isset( $options['max_length'] ) ? intval( $options['max_length'] ) : 1000;
|
||||
public function validateInput($text, $options = array()) {
|
||||
$max_length = isset($options['max_length']) ? intval($options['max_length']) : 1000;
|
||||
|
||||
if ( empty( $text ) ) {
|
||||
if (empty($text)) {
|
||||
return array(
|
||||
'valid' => false,
|
||||
'error' => 'Input text is empty',
|
||||
'error' => 'Input text is empty'
|
||||
);
|
||||
}
|
||||
|
||||
if ( $max_length > 0 && mb_strlen( $text, 'UTF-8' ) > $max_length ) {
|
||||
if ($max_length > 0 && mb_strlen($text, 'UTF-8') > $max_length) {
|
||||
return array(
|
||||
'valid' => false,
|
||||
'error' => 'Input text exceeds maximum length of ' . $max_length . ' characters',
|
||||
'error' => 'Input text exceeds maximum length of ' . $max_length . ' characters'
|
||||
);
|
||||
}
|
||||
|
||||
return array(
|
||||
'valid' => true,
|
||||
'message' => 'Input is valid',
|
||||
'message' => 'Input is valid'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
|
|
@ -15,7 +15,7 @@ class WPSlug_Core {
|
|||
$this->converter = new WPSlug_Converter();
|
||||
$this->optimizer = new WPSlug_Optimizer();
|
||||
|
||||
if ( is_admin() ) {
|
||||
if (is_admin()) {
|
||||
$this->admin = new WPSlug_Admin();
|
||||
}
|
||||
|
||||
|
|
@ -24,171 +24,172 @@ class WPSlug_Core {
|
|||
}
|
||||
|
||||
private function initHooks() {
|
||||
add_filter( 'sanitize_title', array( $this, 'processSanitizeTitle' ), 9, 3 );
|
||||
add_filter( 'wp_insert_post_data', array( $this, 'processPostData' ), 10, 2 );
|
||||
add_filter( 'wp_insert_term_data', array( $this, 'processTermData' ), 10, 3 );
|
||||
add_filter( 'wp_update_term_data', array( $this, 'processTermDataUpdate' ), 10, 4 );
|
||||
add_filter( 'sanitize_file_name', array( $this, 'processFileName' ), 10, 2 );
|
||||
add_filter( 'wp_unique_post_slug', array( $this, 'processUniquePostSlug' ), 10, 6 );
|
||||
add_filter( 'pre_category_nicename', array( $this, 'preCategoryNicename' ), 10, 2 );
|
||||
add_filter('sanitize_title', array($this, 'processSanitizeTitle'), 9, 3);
|
||||
add_filter('wp_insert_post_data', array($this, 'processPostData'), 10, 2);
|
||||
add_filter('wp_insert_term_data', array($this, 'processTermData'), 10, 3);
|
||||
add_filter('wp_update_term_data', array($this, 'processTermDataUpdate'), 10, 4);
|
||||
add_filter('sanitize_file_name', array($this, 'processFileName'), 10, 2);
|
||||
add_filter('wp_unique_post_slug', array($this, 'processUniquePostSlug'), 10, 6);
|
||||
add_filter('pre_category_nicename', array($this, 'preCategoryNicename'), 10, 2);
|
||||
|
||||
add_action( 'transition_post_status', array( $this, 'handlePostStatusTransition' ), 10, 3 );
|
||||
add_action('transition_post_status', array($this, 'handlePostStatusTransition'), 10, 3);
|
||||
|
||||
if ( is_admin() ) {
|
||||
add_filter( 'manage_posts_columns', array( $this, 'addSlugColumn' ) );
|
||||
add_action( 'manage_posts_custom_column', array( $this, 'displaySlugColumn' ), 10, 2 );
|
||||
add_filter( 'manage_pages_columns', array( $this, 'addSlugColumn' ) );
|
||||
add_action( 'manage_pages_custom_column', array( $this, 'displaySlugColumn' ), 10, 2 );
|
||||
if (is_admin()) {
|
||||
add_filter('manage_posts_columns', array($this, 'addSlugColumn'));
|
||||
add_action('manage_posts_custom_column', array($this, 'displaySlugColumn'), 10, 2);
|
||||
add_filter('manage_pages_columns', array($this, 'addSlugColumn'));
|
||||
add_action('manage_pages_custom_column', array($this, 'displaySlugColumn'), 10, 2);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Disabled: migrated to WPSlug_Updater (Update URI)
|
||||
*/
|
||||
private function init_update_checker() {
|
||||
private function init_update_checker()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
public function processSanitizeTitle( $title, $raw_title = '', $context = 'display' ) {
|
||||
if ( $context !== 'save' || empty( $title ) ) {
|
||||
public function processSanitizeTitle($title, $raw_title = '', $context = 'display') {
|
||||
if ($context !== 'save' || empty($title)) {
|
||||
return $title;
|
||||
}
|
||||
|
||||
try {
|
||||
$options = $this->settings->getOptions();
|
||||
|
||||
if ( ! $options['enable_conversion'] ) {
|
||||
if (!$options['enable_conversion']) {
|
||||
return $title;
|
||||
}
|
||||
|
||||
$processed_title = $this->converter->convert( $title, $options );
|
||||
$processed_title = $this->optimizer->optimize( $processed_title, $options );
|
||||
$processed_title = $this->converter->convert($title, $options);
|
||||
$processed_title = $this->optimizer->optimize($processed_title, $options);
|
||||
|
||||
return $processed_title;
|
||||
} catch ( Exception $e ) {
|
||||
$this->settings->logError( 'processSanitizeTitle error: ' . $e->getMessage() );
|
||||
} catch (Exception $e) {
|
||||
$this->settings->logError('processSanitizeTitle error: ' . $e->getMessage());
|
||||
return $title;
|
||||
}
|
||||
}
|
||||
|
||||
public function processPostData( $data, $postarr ) {
|
||||
if ( empty( $data['post_title'] ) ) {
|
||||
public function processPostData($data, $postarr) {
|
||||
if (empty($data['post_title'])) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
try {
|
||||
$options = $this->settings->getOptions();
|
||||
|
||||
if ( ! $options['enable_conversion'] || ! $options['auto_convert'] ) {
|
||||
if (!$options['enable_conversion'] || !$options['auto_convert']) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$post_type = $data['post_type'];
|
||||
if ( ! $this->settings->isPostTypeEnabled( $post_type ) ) {
|
||||
if (!$this->settings->isPostTypeEnabled($post_type)) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
if ( isset( $postarr['wpslug_disable_conversion'] ) && $postarr['wpslug_disable_conversion'] ) {
|
||||
if (isset($postarr['wpslug_disable_conversion']) && $postarr['wpslug_disable_conversion']) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
if ( ! empty( $data['post_name'] ) && ! $this->shouldUpdateSlug( $postarr ) ) {
|
||||
if (!empty($data['post_name']) && !$this->shouldUpdateSlug($postarr)) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$slug = $this->converter->convert( $data['post_title'], $options );
|
||||
$slug = $this->optimizer->optimize( $slug, $options );
|
||||
$slug = $this->converter->convert($data['post_title'], $options);
|
||||
$slug = $this->optimizer->optimize($slug, $options);
|
||||
|
||||
if ( ! empty( $slug ) && $slug !== $data['post_name'] ) {
|
||||
if (!empty($slug) && $slug !== $data['post_name']) {
|
||||
$data['post_name'] = $slug;
|
||||
}
|
||||
|
||||
return $data;
|
||||
} catch ( Exception $e ) {
|
||||
$this->settings->logError( 'processPostData error: ' . $e->getMessage() );
|
||||
} catch (Exception $e) {
|
||||
$this->settings->logError('processPostData error: ' . $e->getMessage());
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
public function processTermData( $data, $taxonomy, $args ) {
|
||||
public function processTermData($data, $taxonomy, $args) {
|
||||
try {
|
||||
$options = $this->settings->getOptions();
|
||||
|
||||
if ( ! $options['enable_conversion'] || ! $options['auto_convert'] ) {
|
||||
if (!$options['enable_conversion'] || !$options['auto_convert']) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
if ( ! $this->settings->isTaxonomyEnabled( $taxonomy ) ) {
|
||||
if (!$this->settings->isTaxonomyEnabled($taxonomy)) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
if ( ! empty( $data['name'] ) && empty( $args['slug'] ) ) {
|
||||
$slug = $this->converter->convert( $data['name'], $options );
|
||||
$slug = $this->optimizer->optimize( $slug, $options );
|
||||
if (!empty($data['name']) && empty($args['slug'])) {
|
||||
$slug = $this->converter->convert($data['name'], $options);
|
||||
$slug = $this->optimizer->optimize($slug, $options);
|
||||
|
||||
if ( ! empty( $slug ) ) {
|
||||
if (!empty($slug)) {
|
||||
$data['slug'] = $slug;
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
} catch ( Exception $e ) {
|
||||
$this->settings->logError( 'processTermData error: ' . $e->getMessage() );
|
||||
} catch (Exception $e) {
|
||||
$this->settings->logError('processTermData error: ' . $e->getMessage());
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
public function processTermDataUpdate( $data, $term_id, $taxonomy, $args ) {
|
||||
public function processTermDataUpdate($data, $term_id, $taxonomy, $args) {
|
||||
try {
|
||||
$options = $this->settings->getOptions();
|
||||
|
||||
if ( ! $options['enable_conversion'] || ! $options['auto_convert'] ) {
|
||||
if (!$options['enable_conversion'] || !$options['auto_convert']) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
if ( ! $this->settings->isTaxonomyEnabled( $taxonomy ) ) {
|
||||
if (!$this->settings->isTaxonomyEnabled($taxonomy)) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
if ( ! empty( $data['name'] ) && empty( $args['slug'] ) ) {
|
||||
$slug = $this->converter->convert( $data['name'], $options );
|
||||
$slug = $this->optimizer->optimize( $slug, $options );
|
||||
if (!empty($data['name']) && empty($args['slug'])) {
|
||||
$slug = $this->converter->convert($data['name'], $options);
|
||||
$slug = $this->optimizer->optimize($slug, $options);
|
||||
|
||||
if ( ! empty( $slug ) ) {
|
||||
$data['slug'] = wp_unique_term_slug( $slug, (object) $args );
|
||||
if (!empty($slug)) {
|
||||
$data['slug'] = wp_unique_term_slug($slug, (object) $args);
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
} catch ( Exception $e ) {
|
||||
$this->settings->logError( 'processTermDataUpdate error: ' . $e->getMessage() );
|
||||
} catch (Exception $e) {
|
||||
$this->settings->logError('processTermDataUpdate error: ' . $e->getMessage());
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
public function processFileName( $filename, $filename_raw = '' ) {
|
||||
public function processFileName($filename, $filename_raw = '') {
|
||||
try {
|
||||
$options = $this->settings->getOptions();
|
||||
|
||||
if ( ! $options['enable_conversion'] || $options['disable_file_convert'] ) {
|
||||
if (!$options['enable_conversion'] || $options['disable_file_convert']) {
|
||||
return $filename;
|
||||
}
|
||||
|
||||
$media_mode = $this->settings->getOption( 'media_conversion_mode', 'normal' );
|
||||
$media_prefix = $this->settings->getOption( 'media_file_prefix', '' );
|
||||
$media_mode = $this->settings->getOption('media_conversion_mode', 'normal');
|
||||
$media_prefix = $this->settings->getOption('media_file_prefix', '');
|
||||
|
||||
$parts = explode( '.', $filename );
|
||||
$parts = explode('.', $filename);
|
||||
$extension = '';
|
||||
|
||||
if ( count( $parts ) > 1 ) {
|
||||
$extension = array_pop( $parts );
|
||||
$name = implode( '.', $parts );
|
||||
if (count($parts) > 1) {
|
||||
$extension = array_pop($parts);
|
||||
$name = implode('.', $parts);
|
||||
} else {
|
||||
$name = $filename;
|
||||
}
|
||||
|
||||
switch ( $media_mode ) {
|
||||
switch ($media_mode) {
|
||||
case 'md5':
|
||||
$converted_name = md5( $name . time() );
|
||||
$converted_name = md5($name . time());
|
||||
break;
|
||||
|
||||
case 'none':
|
||||
|
|
@ -197,173 +198,171 @@ class WPSlug_Core {
|
|||
|
||||
case 'normal':
|
||||
default:
|
||||
if ( $this->needsConversion( $name ) ) {
|
||||
$converted_name = $this->converter->convert( $name, $options );
|
||||
$converted_name = $this->optimizer->optimize( $converted_name, $options );
|
||||
if ($this->needsConversion($name)) {
|
||||
$converted_name = $this->converter->convert($name, $options);
|
||||
$converted_name = $this->optimizer->optimize($converted_name, $options);
|
||||
} else {
|
||||
$converted_name = $name;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if ( ! empty( $media_prefix ) ) {
|
||||
if (!empty($media_prefix)) {
|
||||
$converted_name = $media_prefix . $converted_name;
|
||||
}
|
||||
|
||||
return $extension ? $converted_name . '.' . $extension : $converted_name;
|
||||
} catch ( Exception $e ) {
|
||||
$this->settings->logError( 'processFileName error: ' . $e->getMessage() );
|
||||
} catch (Exception $e) {
|
||||
$this->settings->logError('processFileName error: ' . $e->getMessage());
|
||||
return $filename;
|
||||
}
|
||||
}
|
||||
|
||||
public function processUniquePostSlug( $slug, $post_ID, $post_status, $post_type, $post_parent, $original_slug ) {
|
||||
public function processUniquePostSlug($slug, $post_ID, $post_status, $post_type, $post_parent, $original_slug) {
|
||||
try {
|
||||
$options = $this->settings->getOptions();
|
||||
|
||||
if ( ! $options['enable_conversion'] || ! $options['auto_convert'] ) {
|
||||
if (!$options['enable_conversion'] || !$options['auto_convert']) {
|
||||
return $slug;
|
||||
}
|
||||
|
||||
if ( $post_type === 'attachment' ) {
|
||||
if ($post_type === 'attachment') {
|
||||
return $slug;
|
||||
}
|
||||
|
||||
if ( ! $this->settings->isPostTypeEnabled( $post_type ) ) {
|
||||
if (!$this->settings->isPostTypeEnabled($post_type)) {
|
||||
return $slug;
|
||||
}
|
||||
|
||||
$old_status = get_post_field( 'post_status', $post_ID, 'edit' );
|
||||
$old_status = get_post_field('post_status', $post_ID, 'edit');
|
||||
|
||||
if ( $old_status !== 'publish' && $post_status === 'publish' ) {
|
||||
$converted_slug = $this->converter->convert( $slug, $options );
|
||||
$converted_slug = $this->optimizer->optimize( $converted_slug, $options );
|
||||
if ( ! empty( $converted_slug ) ) {
|
||||
if ($old_status !== 'publish' && $post_status === 'publish') {
|
||||
$converted_slug = $this->converter->convert($slug, $options);
|
||||
$converted_slug = $this->optimizer->optimize($converted_slug, $options);
|
||||
if (!empty($converted_slug)) {
|
||||
return $converted_slug;
|
||||
}
|
||||
}
|
||||
|
||||
return $slug;
|
||||
} catch ( Exception $e ) {
|
||||
$this->settings->logError( 'processUniquePostSlug error: ' . $e->getMessage() );
|
||||
} catch (Exception $e) {
|
||||
$this->settings->logError('processUniquePostSlug error: ' . $e->getMessage());
|
||||
return $slug;
|
||||
}
|
||||
}
|
||||
|
||||
public function preCategoryNicename( $slug, $name = '' ) {
|
||||
public function preCategoryNicename($slug, $name = '') {
|
||||
try {
|
||||
$options = $this->settings->getOptions();
|
||||
|
||||
if ( ! $options['enable_conversion'] || ! $options['auto_convert'] ) {
|
||||
if (!$options['enable_conversion'] || !$options['auto_convert']) {
|
||||
return $slug;
|
||||
}
|
||||
|
||||
if ( $slug ) {
|
||||
if ($slug) {
|
||||
return $slug;
|
||||
}
|
||||
|
||||
$tag_name = isset( $_POST['tag-name'] ) ? sanitize_text_field( $_POST['tag-name'] ) : $name;
|
||||
$tag_name = isset($_POST['tag-name']) ? sanitize_text_field($_POST['tag-name']) : $name;
|
||||
|
||||
if ( $tag_name ) {
|
||||
$converted_slug = $this->converter->convert( $tag_name, $options );
|
||||
$converted_slug = $this->optimizer->optimize( $converted_slug, $options );
|
||||
return sanitize_title( $converted_slug );
|
||||
if ($tag_name) {
|
||||
$converted_slug = $this->converter->convert($tag_name, $options);
|
||||
$converted_slug = $this->optimizer->optimize($converted_slug, $options);
|
||||
return sanitize_title($converted_slug);
|
||||
}
|
||||
|
||||
return $slug;
|
||||
} catch ( Exception $e ) {
|
||||
$this->settings->logError( 'preCategoryNicename error: ' . $e->getMessage() );
|
||||
} catch (Exception $e) {
|
||||
$this->settings->logError('preCategoryNicename error: ' . $e->getMessage());
|
||||
return $slug;
|
||||
}
|
||||
}
|
||||
|
||||
public function handlePostStatusTransition( $new_status, $old_status, $post ) {
|
||||
if ( $new_status !== 'publish' || $old_status === 'publish' ) {
|
||||
public function handlePostStatusTransition($new_status, $old_status, $post) {
|
||||
if ($new_status !== 'publish' || $old_status === 'publish') {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$options = $this->settings->getOptions();
|
||||
|
||||
if ( ! $options['enable_conversion'] || ! $options['auto_convert'] ) {
|
||||
if (!$options['enable_conversion'] || !$options['auto_convert']) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! $this->settings->isPostTypeEnabled( $post->post_type ) ) {
|
||||
if (!$this->settings->isPostTypeEnabled($post->post_type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$new_slug = $this->converter->convert( $post->post_title, $options );
|
||||
$new_slug = $this->optimizer->optimize( $new_slug, $options );
|
||||
$new_slug = $this->converter->convert($post->post_title, $options);
|
||||
$new_slug = $this->optimizer->optimize($new_slug, $options);
|
||||
|
||||
if ( ! empty( $new_slug ) && $new_slug !== $post->post_name ) {
|
||||
$unique_slug = $this->optimizer->generateUniqueSlug( $new_slug, $post->ID, $post->post_type );
|
||||
if (!empty($new_slug) && $new_slug !== $post->post_name) {
|
||||
$unique_slug = $this->optimizer->generateUniqueSlug($new_slug, $post->ID, $post->post_type);
|
||||
|
||||
wp_update_post(
|
||||
array(
|
||||
wp_update_post(array(
|
||||
'ID' => $post->ID,
|
||||
'post_name' => $unique_slug,
|
||||
)
|
||||
);
|
||||
'post_name' => $unique_slug
|
||||
));
|
||||
}
|
||||
} catch ( Exception $e ) {
|
||||
$this->settings->logError( 'handlePostStatusTransition error: ' . $e->getMessage() );
|
||||
} catch (Exception $e) {
|
||||
$this->settings->logError('handlePostStatusTransition error: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function addSlugColumn( $columns ) {
|
||||
public function addSlugColumn($columns) {
|
||||
try {
|
||||
$options = $this->settings->getOptions();
|
||||
|
||||
if ( isset( $options['show_slug_column'] ) && $options['show_slug_column'] ) {
|
||||
$columns['wpslug_slug'] = __( 'Slug', 'wpslug' );
|
||||
if (isset($options['show_slug_column']) && $options['show_slug_column']) {
|
||||
$columns['wpslug_slug'] = __('Slug', 'wpslug');
|
||||
}
|
||||
|
||||
return $columns;
|
||||
} catch ( Exception $e ) {
|
||||
} catch (Exception $e) {
|
||||
return $columns;
|
||||
}
|
||||
}
|
||||
|
||||
public function displaySlugColumn( $column_name, $post_id ) {
|
||||
if ( $column_name !== 'wpslug_slug' ) {
|
||||
public function displaySlugColumn($column_name, $post_id) {
|
||||
if ($column_name !== 'wpslug_slug') {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$post = get_post( $post_id );
|
||||
if ( ! $post || $post->post_status === 'trash' ) {
|
||||
echo '<span class="wpslug-na">' . esc_html__( 'N/A', 'wpslug' ) . '</span>';
|
||||
$post = get_post($post_id);
|
||||
if (!$post || $post->post_status === 'trash') {
|
||||
echo '<span class="wpslug-na">' . esc_html__('N/A', 'wpslug') . '</span>';
|
||||
return;
|
||||
}
|
||||
|
||||
$permalink = get_permalink( $post_id );
|
||||
$home_url = trailingslashit( home_url() );
|
||||
$permalink = get_permalink($post_id);
|
||||
$home_url = trailingslashit(home_url());
|
||||
|
||||
if ( $permalink && strpos( $permalink, $home_url ) === 0 ) {
|
||||
$slug = str_replace( $home_url, '/', $permalink );
|
||||
if ($permalink && strpos($permalink, $home_url) === 0) {
|
||||
$slug = str_replace($home_url, '/', $permalink);
|
||||
} else {
|
||||
$slug = '/' . $post->post_name;
|
||||
}
|
||||
|
||||
echo '<code class="wpslug-slug">' . esc_html( $slug ) . '</code>';
|
||||
} catch ( Exception $e ) {
|
||||
echo '<span class="wpslug-error">' . esc_html__( 'Error', 'wpslug' ) . '</span>';
|
||||
echo '<code class="wpslug-slug">' . esc_html($slug) . '</code>';
|
||||
} catch (Exception $e) {
|
||||
echo '<span class="wpslug-error">' . esc_html__('Error', 'wpslug') . '</span>';
|
||||
}
|
||||
}
|
||||
|
||||
private function needsConversion( $text ) {
|
||||
return preg_match( '/[^\x00-\x7F]/', $text );
|
||||
private function needsConversion($text) {
|
||||
return preg_match('/[^\x00-\x7F]/', $text);
|
||||
}
|
||||
|
||||
private function shouldUpdateSlug( $postarr ) {
|
||||
if ( isset( $postarr['post_status'] ) && $postarr['post_status'] === 'auto-draft' ) {
|
||||
private function shouldUpdateSlug($postarr) {
|
||||
if (isset($postarr['post_status']) && $postarr['post_status'] === 'auto-draft') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( isset( $postarr['ID'] ) && $postarr['ID'] > 0 ) {
|
||||
$existing_post = get_post( $postarr['ID'] );
|
||||
if ( $existing_post && $existing_post->post_status === 'auto-draft' ) {
|
||||
if (isset($postarr['ID']) && $postarr['ID'] > 0) {
|
||||
$existing_post = get_post($postarr['ID']);
|
||||
if ($existing_post && $existing_post->post_status === 'auto-draft') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -375,8 +374,8 @@ class WPSlug_Core {
|
|||
try {
|
||||
$this->settings->createDefaultOptions();
|
||||
flush_rewrite_rules();
|
||||
} catch ( Exception $e ) {
|
||||
$this->settings->logError( 'activate error: ' . $e->getMessage() );
|
||||
} catch (Exception $e) {
|
||||
$this->settings->logError('activate error: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,207 +1,199 @@
|
|||
<?php
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class WPSlug_Optimizer {
|
||||
|
||||
public function __construct() {
|
||||
|
||||
}
|
||||
|
||||
public function optimize( $text, $options = array() ) {
|
||||
if ( empty( $text ) ) {
|
||||
public function optimize($text, $options = array()) {
|
||||
if (empty($text)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$force_lowercase = isset( $options['force_lowercase'] ) ? $options['force_lowercase'] : true;
|
||||
$max_length = isset( $options['max_length'] ) ? (int) $options['max_length'] : 50;
|
||||
$conversion_mode = isset( $options['conversion_mode'] ) ? $options['conversion_mode'] : 'pinyin';
|
||||
$pinyin_format = isset( $options['pinyin_format'] ) ? $options['pinyin_format'] : 'full';
|
||||
$force_lowercase = isset($options['force_lowercase']) ? $options['force_lowercase'] : true;
|
||||
$max_length = isset($options['max_length']) ? (int)$options['max_length'] : 50;
|
||||
$conversion_mode = isset($options['conversion_mode']) ? $options['conversion_mode'] : 'pinyin';
|
||||
$pinyin_format = isset($options['pinyin_format']) ? $options['pinyin_format'] : 'full';
|
||||
|
||||
$is_first_letter_mode = ( $conversion_mode === 'pinyin' && $pinyin_format === 'first' );
|
||||
$should_apply_seo = isset( $options['enable_seo_optimization'] ) ? $options['enable_seo_optimization'] : true;
|
||||
$is_first_letter_mode = ($conversion_mode === 'pinyin' && $pinyin_format === 'first');
|
||||
$should_apply_seo = isset($options['enable_seo_optimization']) ? $options['enable_seo_optimization'] : true;
|
||||
|
||||
if ( $should_apply_seo && ! $is_first_letter_mode ) {
|
||||
$text = $this->applySEOOptimizations( $text, $options );
|
||||
if ($should_apply_seo && !$is_first_letter_mode) {
|
||||
$text = $this->applySEOOptimizations($text, $options);
|
||||
}
|
||||
|
||||
if ( $max_length > 0 && strlen( $text ) > $max_length ) {
|
||||
$text = $this->truncateSlug( $text, $max_length );
|
||||
if ($max_length > 0 && strlen($text) > $max_length) {
|
||||
$text = $this->truncateSlug($text, $max_length);
|
||||
}
|
||||
|
||||
$text = $this->finalCleanup( $text );
|
||||
$text = $this->finalCleanup($text);
|
||||
|
||||
if ( $force_lowercase ) {
|
||||
$text = strtolower( $text );
|
||||
if ($force_lowercase) {
|
||||
$text = strtolower($text);
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
private function applySEOOptimizations( $text, $options ) {
|
||||
if ( isset( $options['smart_punctuation'] ) && $options['smart_punctuation'] ) {
|
||||
$text = $this->handleSmartPunctuation( $text );
|
||||
private function applySEOOptimizations($text, $options) {
|
||||
if (isset($options['smart_punctuation']) && $options['smart_punctuation']) {
|
||||
$text = $this->handleSmartPunctuation($text);
|
||||
}
|
||||
|
||||
if ( isset( $options['mixed_content_optimization'] ) && $options['mixed_content_optimization'] ) {
|
||||
$text = $this->optimizeMixedContent( $text );
|
||||
if (isset($options['mixed_content_optimization']) && $options['mixed_content_optimization']) {
|
||||
$text = $this->optimizeMixedContent($text);
|
||||
}
|
||||
|
||||
if ( isset( $options['remove_stop_words'] ) && $options['remove_stop_words'] ) {
|
||||
$text = $this->removeStopWords( $text, $options );
|
||||
if (isset($options['remove_stop_words']) && $options['remove_stop_words']) {
|
||||
$text = $this->removeStopWords($text, $options);
|
||||
}
|
||||
|
||||
if ( isset( $options['seo_max_words'] ) && $options['seo_max_words'] > 0 ) {
|
||||
$text = $this->limitWords( $text, $options['seo_max_words'] );
|
||||
if (isset($options['seo_max_words']) && $options['seo_max_words'] > 0) {
|
||||
$text = $this->limitWords($text, $options['seo_max_words']);
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
private function handleSmartPunctuation( $text ) {
|
||||
$text = str_replace( array( ':', ';', ',' ), '-', $text );
|
||||
$text = str_replace( array( '.', '?', '!', '(', ')', '[', ']', '<', '>' ), '', $text );
|
||||
$text = str_replace( array( '|', '/', '\\' ), '-', $text );
|
||||
$text = str_replace( '&', 'and', $text );
|
||||
$text = str_replace( '+', 'plus', $text );
|
||||
$text = str_replace( '=', 'equal', $text );
|
||||
$text = str_replace( '%', 'percent', $text );
|
||||
$text = str_replace( '#', 'hash', $text );
|
||||
$text = str_replace( '@', 'at', $text );
|
||||
$text = str_replace( '$', 'dollar', $text );
|
||||
private function handleSmartPunctuation($text) {
|
||||
$text = str_replace(array(':', ';', ','), '-', $text);
|
||||
$text = str_replace(array('.', '?', '!', '(', ')', '[', ']', '<', '>'), '', $text);
|
||||
$text = str_replace(array('|', '/', '\\'), '-', $text);
|
||||
$text = str_replace('&', 'and', $text);
|
||||
$text = str_replace('+', 'plus', $text);
|
||||
$text = str_replace('=', 'equal', $text);
|
||||
$text = str_replace('%', 'percent', $text);
|
||||
$text = str_replace('#', 'hash', $text);
|
||||
$text = str_replace('@', 'at', $text);
|
||||
$text = str_replace('$', 'dollar', $text);
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
private function optimizeMixedContent( $text ) {
|
||||
$text = preg_replace( '/(\d+)(\p{Han})/u', '$1-$2', $text );
|
||||
$text = preg_replace( '/(\p{Han})(\d+)/u', '$1-$2', $text );
|
||||
private function optimizeMixedContent($text) {
|
||||
$text = preg_replace('/(\d+)(\p{Han})/u', '$1-$2', $text);
|
||||
$text = preg_replace('/(\p{Han})(\d+)/u', '$1-$2', $text);
|
||||
|
||||
$text = preg_replace( '/([a-zA-Z])(\p{Han})/u', '$1-$2', $text );
|
||||
$text = preg_replace( '/(\p{Han})([a-zA-Z])/u', '$1-$2', $text );
|
||||
$text = preg_replace('/([a-zA-Z])(\p{Han})/u', '$1-$2', $text);
|
||||
$text = preg_replace('/(\p{Han})([a-zA-Z])/u', '$1-$2', $text);
|
||||
|
||||
$text = preg_replace( '/([a-zA-Z])(\d+)/u', '$1$2', $text );
|
||||
$text = preg_replace( '/(\d+)([a-zA-Z])/u', '$1$2', $text );
|
||||
$text = preg_replace('/([a-zA-Z])(\d+)/u', '$1$2', $text);
|
||||
$text = preg_replace('/(\d+)([a-zA-Z])/u', '$1$2', $text);
|
||||
|
||||
$text = preg_replace( '/\s*-\s*/', '-', $text );
|
||||
$text = preg_replace('/\s*-\s*/', '-', $text);
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
private function removeStopWords( $text, $options ) {
|
||||
if ( ! isset( $options['stop_words_list'] ) ) {
|
||||
private function removeStopWords($text, $options) {
|
||||
if (!isset($options['stop_words_list'])) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
$stop_words_string = $options['stop_words_list'];
|
||||
if ( empty( $stop_words_string ) ) {
|
||||
if (empty($stop_words_string)) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
$stop_words = explode( ',', $stop_words_string );
|
||||
$stop_words = array_map( 'trim', $stop_words );
|
||||
$stop_words = array_filter( $stop_words );
|
||||
$stop_words = explode(',', $stop_words_string);
|
||||
$stop_words = array_map('trim', $stop_words);
|
||||
$stop_words = array_filter($stop_words);
|
||||
|
||||
if ( empty( $stop_words ) ) {
|
||||
if (empty($stop_words)) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
$words = preg_split( '/[-\s_]+/', $text );
|
||||
$words = preg_split('/[-\s_]+/', $text);
|
||||
$filtered_words = array();
|
||||
|
||||
foreach ( $words as $word ) {
|
||||
$word = trim( $word );
|
||||
if ( empty( $word ) ) {
|
||||
foreach ($words as $word) {
|
||||
$word = trim($word);
|
||||
if (empty($word)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! in_array( strtolower( $word ), array_map( 'strtolower', $stop_words ) ) ) {
|
||||
if (!in_array(strtolower($word), array_map('strtolower', $stop_words))) {
|
||||
$filtered_words[] = $word;
|
||||
}
|
||||
}
|
||||
|
||||
return implode( '-', $filtered_words );
|
||||
return implode('-', $filtered_words);
|
||||
}
|
||||
|
||||
private function limitWords( $text, $max_words ) {
|
||||
if ( $max_words <= 0 ) {
|
||||
private function limitWords($text, $max_words) {
|
||||
if ($max_words <= 0) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
$words = preg_split( '/[-\s_]+/', $text );
|
||||
$words = array_filter(
|
||||
$words,
|
||||
function ( $word ) {
|
||||
return ! empty( trim( $word ) );
|
||||
}
|
||||
);
|
||||
$words = preg_split('/[-\s_]+/', $text);
|
||||
$words = array_filter($words, function($word) {
|
||||
return !empty(trim($word));
|
||||
});
|
||||
|
||||
if ( count( $words ) <= $max_words ) {
|
||||
if (count($words) <= $max_words) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
$limited_words = array_slice( $words, 0, $max_words );
|
||||
return implode( '-', $limited_words );
|
||||
$limited_words = array_slice($words, 0, $max_words);
|
||||
return implode('-', $limited_words);
|
||||
}
|
||||
|
||||
private function finalCleanup( $slug ) {
|
||||
$slug = preg_replace( '/[^a-zA-Z0-9\-_\p{Han}]/u', '', $slug );
|
||||
$slug = preg_replace( '/\-+/', '-', $slug );
|
||||
$slug = preg_replace( '/_+/', '_', $slug );
|
||||
$slug = trim( $slug, '-_' );
|
||||
private function finalCleanup($slug) {
|
||||
$slug = preg_replace('/[^a-zA-Z0-9\-_\p{Han}]/u', '', $slug);
|
||||
$slug = preg_replace('/\-+/', '-', $slug);
|
||||
$slug = preg_replace('/_+/', '_', $slug);
|
||||
$slug = trim($slug, '-_');
|
||||
|
||||
return $slug;
|
||||
}
|
||||
|
||||
public function truncateSlug( $slug, $max_length ) {
|
||||
if ( strlen( $slug ) <= $max_length ) {
|
||||
public function truncateSlug($slug, $max_length) {
|
||||
if (strlen($slug) <= $max_length) {
|
||||
return $slug;
|
||||
}
|
||||
|
||||
$parts = explode( '-', $slug );
|
||||
$parts = explode('-', $slug);
|
||||
$result = '';
|
||||
|
||||
foreach ( $parts as $part ) {
|
||||
if ( strlen( $result . '-' . $part ) <= $max_length ) {
|
||||
$result .= ( $result ? '-' : '' ) . $part;
|
||||
foreach ($parts as $part) {
|
||||
if (strlen($result . '-' . $part) <= $max_length) {
|
||||
$result .= ($result ? '-' : '') . $part;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $result ?: substr( $slug, 0, $max_length );
|
||||
return $result ?: substr($slug, 0, $max_length);
|
||||
}
|
||||
|
||||
public function generateUniqueSlug( $slug, $post_id = 0, $post_type = 'post' ) {
|
||||
public function generateUniqueSlug($slug, $post_id = 0, $post_type = 'post') {
|
||||
global $wpdb;
|
||||
|
||||
$original_slug = $slug;
|
||||
$suffix = 1;
|
||||
|
||||
while ( true ) {
|
||||
$query = $wpdb->prepare(
|
||||
"
|
||||
while (true) {
|
||||
$query = $wpdb->prepare("
|
||||
SELECT ID FROM {$wpdb->posts}
|
||||
WHERE post_name = %s
|
||||
AND post_type = %s
|
||||
AND ID != %d
|
||||
LIMIT 1
|
||||
",
|
||||
$slug,
|
||||
$post_type,
|
||||
$post_id
|
||||
);
|
||||
", $slug, $post_type, $post_id);
|
||||
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $query is prepared above
|
||||
if ( ! $wpdb->get_var( $query ) ) {
|
||||
if (!$wpdb->get_var($query)) {
|
||||
break;
|
||||
}
|
||||
|
||||
$slug = $original_slug . '-' . $suffix;
|
||||
++$suffix;
|
||||
$suffix++;
|
||||
|
||||
if ( $suffix > 100 ) {
|
||||
if ($suffix > 100) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -209,102 +201,102 @@ class WPSlug_Optimizer {
|
|||
return $slug;
|
||||
}
|
||||
|
||||
public function validateSlug( $slug ) {
|
||||
if ( empty( $slug ) ) {
|
||||
public function validateSlug($slug) {
|
||||
if (empty($slug)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( strlen( $slug ) > 200 ) {
|
||||
if (strlen($slug) > 200) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( preg_match( '/[^a-zA-Z0-9\-_\p{Han}]/u', $slug ) ) {
|
||||
if (preg_match('/[^a-zA-Z0-9\-_\p{Han}]/u', $slug)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function detectLanguage( $text ) {
|
||||
if ( preg_match( '/[\x{4e00}-\x{9fff}]/u', $text ) ) {
|
||||
public function detectLanguage($text) {
|
||||
if (preg_match('/[\x{4e00}-\x{9fff}]/u', $text)) {
|
||||
return 'zh';
|
||||
}
|
||||
|
||||
return 'en';
|
||||
}
|
||||
|
||||
public function getSEOScore( $original_text, $optimized_slug ) {
|
||||
public function getSEOScore($original_text, $optimized_slug) {
|
||||
$score = 0;
|
||||
$max_score = 100;
|
||||
|
||||
if ( strlen( $optimized_slug ) > 0 ) {
|
||||
if (strlen($optimized_slug) > 0) {
|
||||
$score += 20;
|
||||
}
|
||||
|
||||
if ( strlen( $optimized_slug ) <= 60 ) {
|
||||
if (strlen($optimized_slug) <= 60) {
|
||||
$score += 15;
|
||||
}
|
||||
|
||||
if ( preg_match( '/^[a-zA-Z0-9\-_\p{Han}]+$/u', $optimized_slug ) ) {
|
||||
if (preg_match('/^[a-zA-Z0-9\-_\p{Han}]+$/u', $optimized_slug)) {
|
||||
$score += 15;
|
||||
}
|
||||
|
||||
if ( substr_count( $optimized_slug, '-' ) <= 5 ) {
|
||||
if (substr_count($optimized_slug, '-') <= 5) {
|
||||
$score += 10;
|
||||
}
|
||||
|
||||
if ( ! preg_match( '/^-|-$/', $optimized_slug ) ) {
|
||||
if (!preg_match('/^-|-$/', $optimized_slug)) {
|
||||
$score += 10;
|
||||
}
|
||||
|
||||
$word_count = count( explode( '-', $optimized_slug ) );
|
||||
if ( $word_count >= 2 && $word_count <= 5 ) {
|
||||
$word_count = count(explode('-', $optimized_slug));
|
||||
if ($word_count >= 2 && $word_count <= 5) {
|
||||
$score += 15;
|
||||
}
|
||||
|
||||
if ( strtolower( $optimized_slug ) === $optimized_slug ) {
|
||||
if (strtolower($optimized_slug) === $optimized_slug) {
|
||||
$score += 5;
|
||||
}
|
||||
|
||||
if ( ! preg_match( '/\d{4,}/', $optimized_slug ) ) {
|
||||
if (!preg_match('/\d{4,}/', $optimized_slug)) {
|
||||
$score += 5;
|
||||
}
|
||||
|
||||
if ( strlen( $optimized_slug ) >= 10 ) {
|
||||
if (strlen($optimized_slug) >= 10) {
|
||||
$score += 5;
|
||||
}
|
||||
|
||||
return min( $score, $max_score );
|
||||
return min($score, $max_score);
|
||||
}
|
||||
|
||||
public function suggestAlternatives( $text, $options = array() ) {
|
||||
public function suggestAlternatives($text, $options = array()) {
|
||||
$suggestions = array();
|
||||
|
||||
$base_slug = $this->optimize( $text, $options );
|
||||
$base_slug = $this->optimize($text, $options);
|
||||
$suggestions[] = $base_slug;
|
||||
|
||||
$short_options = $options;
|
||||
$short_options['seo_max_words'] = 3;
|
||||
$short_slug = $this->optimize( $text, $short_options );
|
||||
if ( $short_slug !== $base_slug ) {
|
||||
$short_slug = $this->optimize($text, $short_options);
|
||||
if ($short_slug !== $base_slug) {
|
||||
$suggestions[] = $short_slug;
|
||||
}
|
||||
|
||||
$no_stop_words_options = $options;
|
||||
$no_stop_words_options['remove_stop_words'] = true;
|
||||
$no_stop_words_slug = $this->optimize( $text, $no_stop_words_options );
|
||||
if ( $no_stop_words_slug !== $base_slug ) {
|
||||
$no_stop_words_slug = $this->optimize($text, $no_stop_words_options);
|
||||
if ($no_stop_words_slug !== $base_slug) {
|
||||
$suggestions[] = $no_stop_words_slug;
|
||||
}
|
||||
|
||||
$minimal_options = $options;
|
||||
$minimal_options['seo_max_words'] = 2;
|
||||
$minimal_options['remove_stop_words'] = true;
|
||||
$minimal_slug = $this->optimize( $text, $minimal_options );
|
||||
if ( $minimal_slug !== $base_slug && ! in_array( $minimal_slug, $suggestions ) ) {
|
||||
$minimal_slug = $this->optimize($text, $minimal_options);
|
||||
if ($minimal_slug !== $base_slug && !in_array($minimal_slug, $suggestions)) {
|
||||
$suggestions[] = $minimal_slug;
|
||||
}
|
||||
|
||||
return array_unique( $suggestions );
|
||||
return array_unique($suggestions);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
|
|
@ -9,7 +9,7 @@ class WPSlug_Pinyin {
|
|||
private static $instance = null;
|
||||
|
||||
public static function getInstance() {
|
||||
if ( self::$instance === null ) {
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
|
|
@ -19,79 +19,81 @@ class WPSlug_Pinyin {
|
|||
$this->initPinyinDict();
|
||||
}
|
||||
|
||||
public function convertToPinyin( $text, $options = array() ) {
|
||||
if ( empty( $text ) ) {
|
||||
public function convertToPinyin($text, $options = array()) {
|
||||
if (empty($text)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$separator = isset( $options['pinyin_separator'] ) ? $options['pinyin_separator'] : '-';
|
||||
$format = isset( $options['pinyin_format'] ) ? $options['pinyin_format'] : 'full';
|
||||
$max_length = isset( $options['max_length'] ) ? (int) $options['max_length'] : 100;
|
||||
$preserve_english = isset( $options['preserve_english'] ) ? $options['preserve_english'] : true;
|
||||
$preserve_numbers = isset( $options['preserve_numbers'] ) ? $options['preserve_numbers'] : true;
|
||||
$separator = isset($options['pinyin_separator']) ? $options['pinyin_separator'] : '-';
|
||||
$format = isset($options['pinyin_format']) ? $options['pinyin_format'] : 'full';
|
||||
$max_length = isset($options['max_length']) ? (int)$options['max_length'] : 100;
|
||||
$preserve_english = isset($options['preserve_english']) ? $options['preserve_english'] : true;
|
||||
$preserve_numbers = isset($options['preserve_numbers']) ? $options['preserve_numbers'] : true;
|
||||
|
||||
if ( $max_length > 0 && mb_strlen( $text, 'UTF-8' ) > $max_length ) {
|
||||
$text = mb_substr( $text, 0, $max_length, 'UTF-8' );
|
||||
if ($max_length > 0 && mb_strlen($text, 'UTF-8') > $max_length) {
|
||||
$text = mb_substr($text, 0, $max_length, 'UTF-8');
|
||||
}
|
||||
|
||||
$result = '';
|
||||
$text_array = $this->mbStrSplit( $text );
|
||||
$text_array = $this->mbStrSplit($text);
|
||||
|
||||
foreach ( $text_array as $char ) {
|
||||
if ( $this->isChinese( $char ) ) {
|
||||
$pinyin = $this->getPinyinForChar( $char );
|
||||
if ( ! empty( $pinyin ) ) {
|
||||
if ( $format === 'first' ) {
|
||||
$first_letter = mb_substr( $pinyin, 0, 1, 'UTF-8' );
|
||||
$result .= $separator . strtolower( $first_letter ) . $separator;
|
||||
foreach ($text_array as $char) {
|
||||
if ($this->isChinese($char)) {
|
||||
$pinyin = $this->getPinyinForChar($char);
|
||||
if (!empty($pinyin)) {
|
||||
if ($format === 'first') {
|
||||
$first_letter = mb_substr($pinyin, 0, 1, 'UTF-8');
|
||||
$result .= $separator . strtolower($first_letter) . $separator;
|
||||
} else {
|
||||
$result .= $separator . strtolower( $pinyin ) . $separator;
|
||||
$result .= $separator . strtolower($pinyin) . $separator;
|
||||
}
|
||||
}
|
||||
} elseif ( $preserve_english && preg_match( '/[a-zA-Z]/', $char ) ) {
|
||||
} else {
|
||||
if ($preserve_english && preg_match('/[a-zA-Z]/', $char)) {
|
||||
$result .= $char;
|
||||
} elseif ( $preserve_numbers && preg_match( '/[0-9]/', $char ) ) {
|
||||
} elseif ($preserve_numbers && preg_match('/[0-9]/', $char)) {
|
||||
$result .= $char;
|
||||
} elseif ( preg_match( '/\s/', $char ) ) {
|
||||
} elseif (preg_match('/\s/', $char)) {
|
||||
$result .= $separator;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $separator ) ) {
|
||||
$result = trim( preg_replace( '/' . preg_quote( $separator, '/' ) . '+/', $separator, $result ), $separator );
|
||||
if (!empty($separator)) {
|
||||
$result = trim(preg_replace('/' . preg_quote($separator, '/') . '+/', $separator, $result), $separator);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function isChinese( $char ) {
|
||||
return preg_match( '/[\x{4e00}-\x{9fff}]/u', $char );
|
||||
public function isChinese($char) {
|
||||
return preg_match('/[\x{4e00}-\x{9fff}]/u', $char);
|
||||
}
|
||||
|
||||
public function detectLanguage( $text ) {
|
||||
if ( preg_match( '/[\x{4e00}-\x{9fff}]/u', $text ) ) {
|
||||
public function detectLanguage($text) {
|
||||
if (preg_match('/[\x{4e00}-\x{9fff}]/u', $text)) {
|
||||
return 'zh';
|
||||
}
|
||||
return 'en';
|
||||
}
|
||||
|
||||
private function getPinyinForChar( $char ) {
|
||||
if ( isset( $this->pinyin_dict[ $char ] ) ) {
|
||||
return $this->pinyin_dict[ $char ];
|
||||
private function getPinyinForChar($char) {
|
||||
if (isset($this->pinyin_dict[$char])) {
|
||||
return $this->pinyin_dict[$char];
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
private function mbStrSplit( $string, $length = 1 ) {
|
||||
if ( function_exists( 'mb_str_split' ) ) {
|
||||
return mb_str_split( $string, $length, 'UTF-8' );
|
||||
private function mbStrSplit($string, $length = 1) {
|
||||
if (function_exists('mb_str_split')) {
|
||||
return mb_str_split($string, $length, 'UTF-8');
|
||||
}
|
||||
|
||||
$result = array();
|
||||
$string_length = mb_strlen( $string, 'UTF-8' );
|
||||
$string_length = mb_strlen($string, 'UTF-8');
|
||||
|
||||
for ( $i = 0; $i < $string_length; $i += $length ) {
|
||||
$result[] = mb_substr( $string, $i, $length, 'UTF-8' );
|
||||
for ($i = 0; $i < $string_length; $i += $length) {
|
||||
$result[] = mb_substr($string, $i, $length, 'UTF-8');
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
|
@ -100,17 +102,17 @@ class WPSlug_Pinyin {
|
|||
private function initPinyinDict() {
|
||||
$dictionary_file = WPSLUG_PLUGIN_DIR . 'includes/dictionary.php';
|
||||
|
||||
if ( file_exists( $dictionary_file ) ) {
|
||||
if (file_exists($dictionary_file)) {
|
||||
$dictionary = include $dictionary_file;
|
||||
|
||||
if ( is_array( $dictionary ) ) {
|
||||
foreach ( $dictionary as $pinyin => $chars ) {
|
||||
$pinyin_lower = strtolower( $pinyin );
|
||||
$char_array = $this->mbStrSplit( $chars );
|
||||
if (is_array($dictionary)) {
|
||||
foreach ($dictionary as $pinyin => $chars) {
|
||||
$pinyin_lower = strtolower($pinyin);
|
||||
$char_array = $this->mbStrSplit($chars);
|
||||
|
||||
foreach ( $char_array as $char ) {
|
||||
if ( ! empty( $char ) ) {
|
||||
$this->pinyin_dict[ $char ] = $pinyin_lower;
|
||||
foreach ($char_array as $char) {
|
||||
if (!empty($char)) {
|
||||
$this->pinyin_dict[$char] = $pinyin_lower;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -122,190 +124,43 @@ class WPSlug_Pinyin {
|
|||
|
||||
private function initFallbackDict() {
|
||||
$this->pinyin_dict = array(
|
||||
'一' => 'yi',
|
||||
'二' => 'er',
|
||||
'三' => 'san',
|
||||
'四' => 'si',
|
||||
'五' => 'wu',
|
||||
'六' => 'liu',
|
||||
'七' => 'qi',
|
||||
'八' => 'ba',
|
||||
'九' => 'jiu',
|
||||
'十' => 'shi',
|
||||
'的' => 'de',
|
||||
'了' => 'le',
|
||||
'是' => 'shi',
|
||||
'我' => 'wo',
|
||||
'你' => 'ni',
|
||||
'他' => 'ta',
|
||||
'她' => 'ta',
|
||||
'它' => 'ta',
|
||||
'好' => 'hao',
|
||||
'很' => 'hen',
|
||||
'都' => 'dou',
|
||||
'会' => 'hui',
|
||||
'个' => 'ge',
|
||||
'这' => 'zhe',
|
||||
'那' => 'na',
|
||||
'中' => 'zhong',
|
||||
'国' => 'guo',
|
||||
'人' => 'ren',
|
||||
'有' => 'you',
|
||||
'来' => 'lai',
|
||||
'可' => 'ke',
|
||||
'以' => 'yi',
|
||||
'上' => 'shang',
|
||||
'下' => 'xia',
|
||||
'大' => 'da',
|
||||
'小' => 'xiao',
|
||||
'多' => 'duo',
|
||||
'少' => 'shao',
|
||||
'什' => 'shen',
|
||||
'么' => 'me',
|
||||
'时' => 'shi',
|
||||
'间' => 'jian',
|
||||
'地' => 'di',
|
||||
'方' => 'fang',
|
||||
'年' => 'nian',
|
||||
'月' => 'yue',
|
||||
'日' => 'ri',
|
||||
'天' => 'tian',
|
||||
'水' => 'shui',
|
||||
'火' => 'huo',
|
||||
'木' => 'mu',
|
||||
'金' => 'jin',
|
||||
'土' => 'tu',
|
||||
'山' => 'shan',
|
||||
'海' => 'hai',
|
||||
'河' => 'he',
|
||||
'学' => 'xue',
|
||||
'校' => 'xiao',
|
||||
'老' => 'lao',
|
||||
'师' => 'shi',
|
||||
'生' => 'sheng',
|
||||
'活' => 'huo',
|
||||
'工' => 'gong',
|
||||
'作' => 'zuo',
|
||||
'家' => 'jia',
|
||||
'庭' => 'ting',
|
||||
'朋' => 'peng',
|
||||
'友' => 'you',
|
||||
'爱' => 'ai',
|
||||
'情' => 'qing',
|
||||
'心' => 'xin',
|
||||
'想' => 'xiang',
|
||||
'知' => 'zhi',
|
||||
'道' => 'dao',
|
||||
'看' => 'kan',
|
||||
'见' => 'jian',
|
||||
'听' => 'ting',
|
||||
'说' => 'shuo',
|
||||
'话' => 'hua',
|
||||
'言' => 'yan',
|
||||
'文' => 'wen',
|
||||
'字' => 'zi',
|
||||
'书' => 'shu',
|
||||
'读' => 'du',
|
||||
'写' => 'xie',
|
||||
'画' => 'hua',
|
||||
'吃' => 'chi',
|
||||
'喝' => 'he',
|
||||
'睡' => 'shui',
|
||||
'觉' => 'jue',
|
||||
'走' => 'zou',
|
||||
'跑' => 'pao',
|
||||
'飞' => 'fei',
|
||||
'坐' => 'zuo',
|
||||
'站' => 'zhan',
|
||||
'躺' => 'tang',
|
||||
'笑' => 'xiao',
|
||||
'哭' => 'ku',
|
||||
'高' => 'gao',
|
||||
'兴' => 'xing',
|
||||
'快' => 'kuai',
|
||||
'乐' => 'le',
|
||||
'难' => 'nan',
|
||||
'过' => 'guo',
|
||||
'新' => 'xin',
|
||||
'旧' => 'jiu',
|
||||
'长' => 'chang',
|
||||
'短' => 'duan',
|
||||
'宽' => 'kuan',
|
||||
'窄' => 'zhai',
|
||||
'厚' => 'hou',
|
||||
'薄' => 'bao',
|
||||
'深' => 'shen',
|
||||
'浅' => 'qian',
|
||||
'远' => 'yuan',
|
||||
'近' => 'jin',
|
||||
'美' => 'mei',
|
||||
'丽' => 'li',
|
||||
'漂' => 'piao',
|
||||
'亮' => 'liang',
|
||||
'帅' => 'shuai',
|
||||
'聪' => 'cong',
|
||||
'明' => 'ming',
|
||||
'笨' => 'ben',
|
||||
'懒' => 'lan',
|
||||
'勤' => 'qin',
|
||||
'忙' => 'mang',
|
||||
'闲' => 'xian',
|
||||
'累' => 'lei',
|
||||
'轻' => 'qing',
|
||||
'重' => 'zhong',
|
||||
'松' => 'song',
|
||||
'紧' => 'jin',
|
||||
'开' => 'kai',
|
||||
'关' => 'guan',
|
||||
'门' => 'men',
|
||||
'窗' => 'chuang',
|
||||
'户' => 'hu',
|
||||
'房' => 'fang',
|
||||
'子' => 'zi',
|
||||
'屋' => 'wu',
|
||||
'楼' => 'lou',
|
||||
'层' => 'ceng',
|
||||
'街' => 'jie',
|
||||
'路' => 'lu',
|
||||
'桥' => 'qiao',
|
||||
'车' => 'che',
|
||||
'船' => 'chuan',
|
||||
'机' => 'ji',
|
||||
'电' => 'dian',
|
||||
'话' => 'hua',
|
||||
'视' => 'shi',
|
||||
'脑' => 'nao',
|
||||
'手' => 'shou',
|
||||
'网' => 'wang',
|
||||
'络' => 'luo',
|
||||
'游' => 'you',
|
||||
'戏' => 'xi',
|
||||
'音' => 'yin',
|
||||
'影' => 'ying',
|
||||
'唱' => 'chang',
|
||||
'歌' => 'ge',
|
||||
'跳' => 'tiao',
|
||||
'舞' => 'wu',
|
||||
'购' => 'gou',
|
||||
'物' => 'wu',
|
||||
'买' => 'mai',
|
||||
'卖' => 'mai',
|
||||
'钱' => 'qian',
|
||||
'价' => 'jia',
|
||||
'格' => 'ge',
|
||||
'便' => 'bian',
|
||||
'宜' => 'yi',
|
||||
'贵' => 'gui',
|
||||
'医' => 'yi',
|
||||
'院' => 'yuan',
|
||||
'病' => 'bing',
|
||||
'痛' => 'tong',
|
||||
'健' => 'jian',
|
||||
'康' => 'kang',
|
||||
'运' => 'yun',
|
||||
'动' => 'dong',
|
||||
'锻' => 'duan',
|
||||
'炼' => 'lian',
|
||||
'一' => 'yi', '二' => 'er', '三' => 'san', '四' => 'si', '五' => 'wu',
|
||||
'六' => 'liu', '七' => 'qi', '八' => 'ba', '九' => 'jiu', '十' => 'shi',
|
||||
'的' => 'de', '了' => 'le', '是' => 'shi', '我' => 'wo', '你' => 'ni',
|
||||
'他' => 'ta', '她' => 'ta', '它' => 'ta', '好' => 'hao', '很' => 'hen',
|
||||
'都' => 'dou', '会' => 'hui', '个' => 'ge', '这' => 'zhe', '那' => 'na',
|
||||
'中' => 'zhong', '国' => 'guo', '人' => 'ren', '有' => 'you', '来' => 'lai',
|
||||
'可' => 'ke', '以' => 'yi', '上' => 'shang', '下' => 'xia', '大' => 'da',
|
||||
'小' => 'xiao', '多' => 'duo', '少' => 'shao', '什' => 'shen', '么' => 'me',
|
||||
'时' => 'shi', '间' => 'jian', '地' => 'di', '方' => 'fang', '年' => 'nian',
|
||||
'月' => 'yue', '日' => 'ri', '天' => 'tian', '水' => 'shui', '火' => 'huo',
|
||||
'木' => 'mu', '金' => 'jin', '土' => 'tu', '山' => 'shan', '海' => 'hai',
|
||||
'河' => 'he', '学' => 'xue', '校' => 'xiao', '老' => 'lao', '师' => 'shi',
|
||||
'生' => 'sheng', '活' => 'huo', '工' => 'gong', '作' => 'zuo', '家' => 'jia',
|
||||
'庭' => 'ting', '朋' => 'peng', '友' => 'you', '爱' => 'ai', '情' => 'qing',
|
||||
'心' => 'xin', '想' => 'xiang', '知' => 'zhi', '道' => 'dao', '看' => 'kan',
|
||||
'见' => 'jian', '听' => 'ting', '说' => 'shuo', '话' => 'hua', '言' => 'yan',
|
||||
'文' => 'wen', '字' => 'zi', '书' => 'shu', '读' => 'du', '写' => 'xie',
|
||||
'画' => 'hua', '吃' => 'chi', '喝' => 'he', '睡' => 'shui', '觉' => 'jue',
|
||||
'走' => 'zou', '跑' => 'pao', '飞' => 'fei', '坐' => 'zuo', '站' => 'zhan',
|
||||
'躺' => 'tang', '笑' => 'xiao', '哭' => 'ku', '高' => 'gao', '兴' => 'xing',
|
||||
'快' => 'kuai', '乐' => 'le', '难' => 'nan', '过' => 'guo', '新' => 'xin',
|
||||
'旧' => 'jiu', '长' => 'chang', '短' => 'duan', '宽' => 'kuan', '窄' => 'zhai',
|
||||
'厚' => 'hou', '薄' => 'bao', '深' => 'shen', '浅' => 'qian', '远' => 'yuan',
|
||||
'近' => 'jin', '美' => 'mei', '丽' => 'li', '漂' => 'piao', '亮' => 'liang',
|
||||
'帅' => 'shuai', '聪' => 'cong', '明' => 'ming', '笨' => 'ben', '懒' => 'lan',
|
||||
'勤' => 'qin', '忙' => 'mang', '闲' => 'xian', '累' => 'lei', '轻' => 'qing',
|
||||
'重' => 'zhong', '松' => 'song', '紧' => 'jin', '开' => 'kai', '关' => 'guan',
|
||||
'门' => 'men', '窗' => 'chuang', '户' => 'hu', '房' => 'fang', '子' => 'zi',
|
||||
'屋' => 'wu', '楼' => 'lou', '层' => 'ceng', '街' => 'jie', '路' => 'lu',
|
||||
'桥' => 'qiao', '车' => 'che', '船' => 'chuan', '机' => 'ji', '电' => 'dian',
|
||||
'话' => 'hua', '视' => 'shi', '脑' => 'nao', '手' => 'shou', '网' => 'wang',
|
||||
'络' => 'luo', '游' => 'you', '戏' => 'xi', '音' => 'yin', '影' => 'ying',
|
||||
'唱' => 'chang', '歌' => 'ge', '跳' => 'tiao', '舞' => 'wu', '购' => 'gou',
|
||||
'物' => 'wu', '买' => 'mai', '卖' => 'mai', '钱' => 'qian', '价' => 'jia',
|
||||
'格' => 'ge', '便' => 'bian', '宜' => 'yi', '贵' => 'gui', '医' => 'yi',
|
||||
'院' => 'yuan', '病' => 'bing', '痛' => 'tong', '健' => 'jian', '康' => 'kang',
|
||||
'运' => 'yun', '动' => 'dong', '锻' => 'duan', '炼' => 'lian'
|
||||
);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
|
|
@ -20,49 +20,49 @@ class WPSlug_Translator {
|
|||
$this->converter = null;
|
||||
}
|
||||
|
||||
public function translate( $text, $options = array() ) {
|
||||
if ( empty( $text ) ) {
|
||||
public function translate($text, $options = array()) {
|
||||
if (empty($text)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// 防止循环调用:如果正在翻译中,直接回退到拼音
|
||||
if ( self::$is_translating ) {
|
||||
return $this->fallbackTranslate( $text, $options );
|
||||
if (self::$is_translating) {
|
||||
return $this->fallbackTranslate($text, $options);
|
||||
}
|
||||
|
||||
$service = isset( $options['translation_service'] ) ? $options['translation_service'] : 'none';
|
||||
$service = isset($options['translation_service']) ? $options['translation_service'] : 'none';
|
||||
|
||||
switch ( $service ) {
|
||||
switch ($service) {
|
||||
case 'wpmind':
|
||||
// WPMind 服务需要循环保护
|
||||
self::$is_translating = true;
|
||||
try {
|
||||
$result = $this->translateWPMind( $text, $options );
|
||||
$result = $this->translateWPMind($text, $options);
|
||||
} finally {
|
||||
self::$is_translating = false;
|
||||
}
|
||||
return $result;
|
||||
case 'google':
|
||||
return $this->translateGoogle( $text, $options );
|
||||
return $this->translateGoogle($text, $options);
|
||||
case 'baidu':
|
||||
return $this->translateBaidu( $text, $options );
|
||||
return $this->translateBaidu($text, $options);
|
||||
case 'none':
|
||||
default:
|
||||
return $this->fallbackTranslate( $text, $options );
|
||||
return $this->fallbackTranslate($text, $options);
|
||||
}
|
||||
}
|
||||
|
||||
private function translateGoogle( $text, $options ) {
|
||||
$api_key = isset( $options['google_api_key'] ) ? trim( $options['google_api_key'] ) : '';
|
||||
$source_lang = isset( $options['translation_source_lang'] ) ? $options['translation_source_lang'] : 'auto';
|
||||
$target_lang = isset( $options['translation_target_lang'] ) ? $options['translation_target_lang'] : 'en';
|
||||
private function translateGoogle($text, $options) {
|
||||
$api_key = isset($options['google_api_key']) ? trim($options['google_api_key']) : '';
|
||||
$source_lang = isset($options['translation_source_lang']) ? $options['translation_source_lang'] : 'auto';
|
||||
$target_lang = isset($options['translation_target_lang']) ? $options['translation_target_lang'] : 'en';
|
||||
|
||||
if ( empty( $api_key ) ) {
|
||||
return $this->fallbackTranslate( $text, $options );
|
||||
if (empty($api_key)) {
|
||||
return $this->fallbackTranslate($text, $options);
|
||||
}
|
||||
|
||||
if ( strlen( $text ) > 5000 ) {
|
||||
return $this->fallbackTranslate( $text, $options );
|
||||
if (strlen($text) > 5000) {
|
||||
return $this->fallbackTranslate($text, $options);
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
@ -72,69 +72,66 @@ class WPSlug_Translator {
|
|||
'q' => $text,
|
||||
'source' => $source_lang,
|
||||
'target' => $target_lang,
|
||||
'format' => 'text',
|
||||
'format' => 'text'
|
||||
);
|
||||
|
||||
$response = wp_remote_post(
|
||||
$url,
|
||||
array(
|
||||
$response = wp_remote_post($url, array(
|
||||
'timeout' => 15,
|
||||
'body' => $params,
|
||||
'headers' => array(
|
||||
'User-Agent' => 'WPSlug/' . WPSLUG_VERSION,
|
||||
),
|
||||
'User-Agent' => 'WPSlug/' . WPSLUG_VERSION
|
||||
)
|
||||
);
|
||||
));
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
if ( isset( $options['debug_mode'] ) && $options['debug_mode'] ) {
|
||||
error_log( 'WPSlug Google Translate Error: ' . $response->get_error_message() );
|
||||
if (is_wp_error($response)) {
|
||||
if (isset($options['debug_mode']) && $options['debug_mode']) {
|
||||
error_log('WPSlug Google Translate Error: ' . $response->get_error_message());
|
||||
}
|
||||
return $this->fallbackTranslate( $text, $options );
|
||||
return $this->fallbackTranslate($text, $options);
|
||||
}
|
||||
|
||||
$response_code = wp_remote_retrieve_response_code( $response );
|
||||
if ( $response_code !== 200 ) {
|
||||
if ( isset( $options['debug_mode'] ) && $options['debug_mode'] ) {
|
||||
error_log( 'WPSlug Google Translate HTTP Error: ' . $response_code );
|
||||
$response_code = wp_remote_retrieve_response_code($response);
|
||||
if ($response_code !== 200) {
|
||||
if (isset($options['debug_mode']) && $options['debug_mode']) {
|
||||
error_log('WPSlug Google Translate HTTP Error: ' . $response_code);
|
||||
}
|
||||
return $this->fallbackTranslate( $text, $options );
|
||||
return $this->fallbackTranslate($text, $options);
|
||||
}
|
||||
|
||||
$body = wp_remote_retrieve_body( $response );
|
||||
$data = json_decode( $body, true );
|
||||
$body = wp_remote_retrieve_body($response);
|
||||
$data = json_decode($body, true);
|
||||
|
||||
if ( isset( $data['data']['translations'][0]['translatedText'] ) ) {
|
||||
if (isset($data['data']['translations'][0]['translatedText'])) {
|
||||
$translated = $data['data']['translations'][0]['translatedText'];
|
||||
return $this->cleanTranslatedText( $translated );
|
||||
return $this->cleanTranslatedText($translated);
|
||||
}
|
||||
|
||||
return $this->fallbackTranslate( $text, $options );
|
||||
} catch ( Exception $e ) {
|
||||
if ( isset( $options['debug_mode'] ) && $options['debug_mode'] ) {
|
||||
error_log( 'WPSlug translateGoogle error: ' . $e->getMessage() );
|
||||
return $this->fallbackTranslate($text, $options);
|
||||
} catch (Exception $e) {
|
||||
if (isset($options['debug_mode']) && $options['debug_mode']) {
|
||||
error_log('WPSlug translateGoogle error: ' . $e->getMessage());
|
||||
}
|
||||
return $this->fallbackTranslate( $text, $options );
|
||||
return $this->fallbackTranslate($text, $options);
|
||||
}
|
||||
}
|
||||
|
||||
private function translateBaidu( $text, $options ) {
|
||||
$app_id = isset( $options['baidu_app_id'] ) ? trim( $options['baidu_app_id'] ) : '';
|
||||
$secret_key = isset( $options['baidu_secret_key'] ) ? trim( $options['baidu_secret_key'] ) : '';
|
||||
$source_lang = isset( $options['translation_source_lang'] ) ? $options['translation_source_lang'] : 'auto';
|
||||
$target_lang = isset( $options['translation_target_lang'] ) ? $options['translation_target_lang'] : 'en';
|
||||
private function translateBaidu($text, $options) {
|
||||
$app_id = isset($options['baidu_app_id']) ? trim($options['baidu_app_id']) : '';
|
||||
$secret_key = isset($options['baidu_secret_key']) ? trim($options['baidu_secret_key']) : '';
|
||||
$source_lang = isset($options['translation_source_lang']) ? $options['translation_source_lang'] : 'auto';
|
||||
$target_lang = isset($options['translation_target_lang']) ? $options['translation_target_lang'] : 'en';
|
||||
|
||||
if ( empty( $app_id ) || empty( $secret_key ) ) {
|
||||
return $this->fallbackTranslate( $text, $options );
|
||||
if (empty($app_id) || empty($secret_key)) {
|
||||
return $this->fallbackTranslate($text, $options);
|
||||
}
|
||||
|
||||
if ( strlen( $text ) > 6000 ) {
|
||||
return $this->fallbackTranslate( $text, $options );
|
||||
if (strlen($text) > 6000) {
|
||||
return $this->fallbackTranslate($text, $options);
|
||||
}
|
||||
|
||||
try {
|
||||
$salt = wp_rand( 10000, 99999 );
|
||||
$sign = md5( $app_id . $text . $salt . $secret_key );
|
||||
$salt = wp_rand(10000, 99999);
|
||||
$sign = md5($app_id . $text . $salt . $secret_key);
|
||||
|
||||
$url = 'https://fanyi-api.baidu.com/api/trans/vip/translate';
|
||||
$params = array(
|
||||
|
|
@ -143,55 +140,52 @@ class WPSlug_Translator {
|
|||
'to' => $target_lang,
|
||||
'appid' => $app_id,
|
||||
'salt' => $salt,
|
||||
'sign' => $sign,
|
||||
'sign' => $sign
|
||||
);
|
||||
|
||||
$response = wp_remote_post(
|
||||
$url,
|
||||
array(
|
||||
$response = wp_remote_post($url, array(
|
||||
'timeout' => 15,
|
||||
'body' => $params,
|
||||
'headers' => array(
|
||||
'User-Agent' => 'WPSlug/' . WPSLUG_VERSION,
|
||||
),
|
||||
'User-Agent' => 'WPSlug/' . WPSLUG_VERSION
|
||||
)
|
||||
);
|
||||
));
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
if ( isset( $options['debug_mode'] ) && $options['debug_mode'] ) {
|
||||
error_log( 'WPSlug Baidu Translate Error: ' . $response->get_error_message() );
|
||||
if (is_wp_error($response)) {
|
||||
if (isset($options['debug_mode']) && $options['debug_mode']) {
|
||||
error_log('WPSlug Baidu Translate Error: ' . $response->get_error_message());
|
||||
}
|
||||
return $this->fallbackTranslate( $text, $options );
|
||||
return $this->fallbackTranslate($text, $options);
|
||||
}
|
||||
|
||||
$response_code = wp_remote_retrieve_response_code( $response );
|
||||
if ( $response_code !== 200 ) {
|
||||
if ( isset( $options['debug_mode'] ) && $options['debug_mode'] ) {
|
||||
error_log( 'WPSlug Baidu Translate HTTP Error: ' . $response_code );
|
||||
$response_code = wp_remote_retrieve_response_code($response);
|
||||
if ($response_code !== 200) {
|
||||
if (isset($options['debug_mode']) && $options['debug_mode']) {
|
||||
error_log('WPSlug Baidu Translate HTTP Error: ' . $response_code);
|
||||
}
|
||||
return $this->fallbackTranslate( $text, $options );
|
||||
return $this->fallbackTranslate($text, $options);
|
||||
}
|
||||
|
||||
$body = wp_remote_retrieve_body( $response );
|
||||
$data = json_decode( $body, true );
|
||||
$body = wp_remote_retrieve_body($response);
|
||||
$data = json_decode($body, true);
|
||||
|
||||
if ( isset( $data['trans_result'][0]['dst'] ) ) {
|
||||
if (isset($data['trans_result'][0]['dst'])) {
|
||||
$translated = $data['trans_result'][0]['dst'];
|
||||
return $this->cleanTranslatedText( $translated );
|
||||
return $this->cleanTranslatedText($translated);
|
||||
}
|
||||
|
||||
if ( isset( $data['error_code'] ) ) {
|
||||
if ( isset( $options['debug_mode'] ) && $options['debug_mode'] ) {
|
||||
error_log( 'WPSlug Baidu Translate API Error: ' . $data['error_code'] );
|
||||
if (isset($data['error_code'])) {
|
||||
if (isset($options['debug_mode']) && $options['debug_mode']) {
|
||||
error_log('WPSlug Baidu Translate API Error: ' . $data['error_code']);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->fallbackTranslate( $text, $options );
|
||||
} catch ( Exception $e ) {
|
||||
if ( isset( $options['debug_mode'] ) && $options['debug_mode'] ) {
|
||||
error_log( 'WPSlug translateBaidu error: ' . $e->getMessage() );
|
||||
return $this->fallbackTranslate($text, $options);
|
||||
} catch (Exception $e) {
|
||||
if (isset($options['debug_mode']) && $options['debug_mode']) {
|
||||
error_log('WPSlug translateBaidu error: ' . $e->getMessage());
|
||||
}
|
||||
return $this->fallbackTranslate( $text, $options );
|
||||
return $this->fallbackTranslate($text, $options);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -202,140 +196,135 @@ class WPSlug_Translator {
|
|||
* @param array $options 选项
|
||||
* @return string 翻译后的文本(slug 格式)
|
||||
*/
|
||||
private function translateWPMind( $text, $options ) {
|
||||
$debug_mode = isset( $options['debug_mode'] ) && $options['debug_mode'];
|
||||
private function translateWPMind($text, $options) {
|
||||
$debug_mode = isset($options['debug_mode']) && $options['debug_mode'];
|
||||
|
||||
// 获取语言设置(提前获取以便用于缓存键)
|
||||
$source_lang = isset( $options['translation_source_lang'] ) ? $options['translation_source_lang'] : 'zh';
|
||||
$target_lang = isset( $options['translation_target_lang'] ) ? $options['translation_target_lang'] : 'en';
|
||||
$source_lang = isset($options['translation_source_lang']) ? $options['translation_source_lang'] : 'zh';
|
||||
$target_lang = isset($options['translation_target_lang']) ? $options['translation_target_lang'] : 'en';
|
||||
|
||||
// 1. 先检查本地缓存(缓存键包含语言设置)
|
||||
$cache_key = 'wpslug_wpmind_' . md5( $text . '_' . $source_lang . '_' . $target_lang );
|
||||
$cached = get_transient( $cache_key );
|
||||
if ( $cached !== false ) {
|
||||
if ( $debug_mode ) {
|
||||
error_log( '[WPSlug] WPMind cache hit for: ' . $text . ' (' . $source_lang . ' -> ' . $target_lang . ')' );
|
||||
$cache_key = 'wpslug_wpmind_' . md5($text . '_' . $source_lang . '_' . $target_lang);
|
||||
$cached = get_transient($cache_key);
|
||||
if ($cached !== false) {
|
||||
if ($debug_mode) {
|
||||
error_log('[WPSlug] WPMind cache hit for: ' . $text . ' (' . $source_lang . ' -> ' . $target_lang . ')');
|
||||
}
|
||||
return $cached;
|
||||
}
|
||||
|
||||
// 2. 检查 WPMind 是否可用
|
||||
if ( ! function_exists( 'wpmind_is_available' ) || ! wpmind_is_available() ) {
|
||||
if ( $debug_mode ) {
|
||||
error_log( '[WPSlug] WPMind not available, falling back to pinyin' );
|
||||
if (!function_exists('wpmind_is_available') || !wpmind_is_available()) {
|
||||
if ($debug_mode) {
|
||||
error_log('[WPSlug] WPMind not available, falling back to pinyin');
|
||||
}
|
||||
return $this->fallbackTranslate( $text, $options );
|
||||
return $this->fallbackTranslate($text, $options);
|
||||
}
|
||||
|
||||
// 3. 文本长度限制(避免超时)
|
||||
if ( mb_strlen( $text ) > 200 ) {
|
||||
if ( $debug_mode ) {
|
||||
error_log( '[WPSlug] Text too long (' . mb_strlen( $text ) . ' chars), using pinyin' );
|
||||
if (mb_strlen($text) > 200) {
|
||||
if ($debug_mode) {
|
||||
error_log('[WPSlug] Text too long (' . mb_strlen($text) . ' chars), using pinyin');
|
||||
}
|
||||
return $this->fallbackTranslate( $text, $options );
|
||||
return $this->fallbackTranslate($text, $options);
|
||||
}
|
||||
|
||||
// 4. 中文字符数限制
|
||||
$chinese_count = preg_match_all( '/[\x{4e00}-\x{9fff}]/u', $text );
|
||||
if ( $chinese_count > 50 ) {
|
||||
if ( $debug_mode ) {
|
||||
error_log( '[WPSlug] Too many Chinese characters (' . $chinese_count . '), using pinyin' );
|
||||
$chinese_count = preg_match_all('/[\x{4e00}-\x{9fff}]/u', $text);
|
||||
if ($chinese_count > 50) {
|
||||
if ($debug_mode) {
|
||||
error_log('[WPSlug] Too many Chinese characters (' . $chinese_count . '), using pinyin');
|
||||
}
|
||||
return $this->fallbackTranslate( $text, $options );
|
||||
return $this->fallbackTranslate($text, $options);
|
||||
}
|
||||
|
||||
// 5. 调用 WPMind API(语言设置已在前面获取)
|
||||
$start_time = microtime( true );
|
||||
$start_time = microtime(true);
|
||||
|
||||
$result = wpmind_translate(
|
||||
$text,
|
||||
$source_lang,
|
||||
$target_lang,
|
||||
array(
|
||||
$result = wpmind_translate($text, $source_lang, $target_lang, [
|
||||
'context' => 'wpslug_translation',
|
||||
'format' => 'slug',
|
||||
'cache_ttl' => 86400, // WPMind 内部缓存 1 天
|
||||
'max_tokens' => 100,
|
||||
'temperature' => 0.3,
|
||||
)
|
||||
);
|
||||
]);
|
||||
|
||||
$elapsed_time = round( ( microtime( true ) - $start_time ) * 1000 );
|
||||
$elapsed_time = round((microtime(true) - $start_time) * 1000);
|
||||
|
||||
// 7. 处理结果
|
||||
if ( is_wp_error( $result ) ) {
|
||||
if ( $debug_mode ) {
|
||||
error_log( '[WPSlug] WPMind error: ' . $result->get_error_message() . ' (took ' . $elapsed_time . 'ms)' );
|
||||
if (is_wp_error($result)) {
|
||||
if ($debug_mode) {
|
||||
error_log('[WPSlug] WPMind error: ' . $result->get_error_message() . ' (took ' . $elapsed_time . 'ms)');
|
||||
}
|
||||
return $this->fallbackTranslate( $text, $options );
|
||||
return $this->fallbackTranslate($text, $options);
|
||||
}
|
||||
|
||||
$slug = $this->cleanTranslatedText( $result );
|
||||
$slug = $this->cleanTranslatedText($result);
|
||||
|
||||
// 8. 验证结果有效性
|
||||
if ( empty( $slug ) ) {
|
||||
if ( $debug_mode ) {
|
||||
error_log( '[WPSlug] WPMind returned empty result, using pinyin' );
|
||||
if (empty($slug)) {
|
||||
if ($debug_mode) {
|
||||
error_log('[WPSlug] WPMind returned empty result, using pinyin');
|
||||
}
|
||||
return $this->fallbackTranslate( $text, $options );
|
||||
return $this->fallbackTranslate($text, $options);
|
||||
}
|
||||
|
||||
// 9. 缓存结果(7 天)
|
||||
set_transient( $cache_key, $slug, 7 * DAY_IN_SECONDS );
|
||||
set_transient($cache_key, $slug, 7 * DAY_IN_SECONDS);
|
||||
|
||||
if ( $debug_mode ) {
|
||||
error_log( '[WPSlug] WPMind translated "' . $text . '" to "' . $slug . '" in ' . $elapsed_time . 'ms' );
|
||||
if ($debug_mode) {
|
||||
error_log('[WPSlug] WPMind translated "' . $text . '" to "' . $slug . '" in ' . $elapsed_time . 'ms');
|
||||
}
|
||||
|
||||
return $slug;
|
||||
}
|
||||
|
||||
private function fallbackTranslate( $text, $options ) {
|
||||
if ( $this->converter === null ) {
|
||||
private function fallbackTranslate($text, $options) {
|
||||
if ($this->converter === null) {
|
||||
$this->converter = new WPSlug_Converter();
|
||||
}
|
||||
|
||||
$fallback_options = $options;
|
||||
$fallback_options['conversion_mode'] = 'pinyin';
|
||||
|
||||
return $this->converter->convert( $text, $fallback_options );
|
||||
return $this->converter->convert($text, $fallback_options);
|
||||
}
|
||||
|
||||
private function cleanTranslatedText( $text ) {
|
||||
$text = strip_tags( $text );
|
||||
$text = html_entity_decode( $text, ENT_QUOTES | ENT_HTML5, 'UTF-8' );
|
||||
$text = preg_replace( '/&.+?;/', '', $text );
|
||||
$text = preg_replace( '/[^a-zA-Z0-9\s\-_]/', '', $text );
|
||||
$text = preg_replace( '/\s+/', '-', $text );
|
||||
$text = preg_replace( '/\-+/', '-', $text );
|
||||
$text = trim( $text, '-_' );
|
||||
private function cleanTranslatedText($text) {
|
||||
$text = strip_tags($text);
|
||||
$text = html_entity_decode($text, ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
$text = preg_replace('/&.+?;/', '', $text);
|
||||
$text = preg_replace('/[^a-zA-Z0-9\s\-_]/', '', $text);
|
||||
$text = preg_replace('/\s+/', '-', $text);
|
||||
$text = preg_replace('/\-+/', '-', $text);
|
||||
$text = trim($text, '-_');
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
public function batchTranslate( $items, $options = array() ) {
|
||||
public function batchTranslate($items, $options = array()) {
|
||||
$results = array();
|
||||
|
||||
if ( ! is_array( $items ) ) {
|
||||
if (!is_array($items)) {
|
||||
return $results;
|
||||
}
|
||||
|
||||
foreach ( $items as $item ) {
|
||||
foreach ($items as $item) {
|
||||
try {
|
||||
$translated = $this->translate( $item, $options );
|
||||
$translated = $this->translate($item, $options);
|
||||
$results[] = array(
|
||||
'original' => $item,
|
||||
'translated' => $translated,
|
||||
'service' => isset( $options['translation_service'] ) ? $options['translation_service'] : 'none',
|
||||
'service' => isset($options['translation_service']) ? $options['translation_service'] : 'none'
|
||||
);
|
||||
} catch ( Exception $e ) {
|
||||
if ( isset( $options['debug_mode'] ) && $options['debug_mode'] ) {
|
||||
error_log( 'WPSlug batchTranslate error: ' . $e->getMessage() );
|
||||
} catch (Exception $e) {
|
||||
if (isset($options['debug_mode']) && $options['debug_mode']) {
|
||||
error_log('WPSlug batchTranslate error: ' . $e->getMessage());
|
||||
}
|
||||
$results[] = array(
|
||||
'original' => $item,
|
||||
'translated' => sanitize_title( $item ),
|
||||
'service' => 'fallback',
|
||||
'translated' => sanitize_title($item),
|
||||
'service' => 'fallback'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -344,45 +333,45 @@ class WPSlug_Translator {
|
|||
}
|
||||
|
||||
public function getSupportedServices() {
|
||||
return array( 'none', 'google', 'baidu' );
|
||||
return array('none', 'google', 'baidu');
|
||||
}
|
||||
|
||||
public function isServiceSupported( $service ) {
|
||||
return in_array( $service, $this->getSupportedServices() );
|
||||
public function isServiceSupported($service) {
|
||||
return in_array($service, $this->getSupportedServices());
|
||||
}
|
||||
|
||||
public function isServiceConfigured( $service, $options ) {
|
||||
switch ( $service ) {
|
||||
public function isServiceConfigured($service, $options) {
|
||||
switch ($service) {
|
||||
case 'google':
|
||||
return ! empty( $options['google_api_key'] );
|
||||
return !empty($options['google_api_key']);
|
||||
case 'baidu':
|
||||
return ! empty( $options['baidu_app_id'] ) && ! empty( $options['baidu_secret_key'] );
|
||||
return !empty($options['baidu_app_id']) && !empty($options['baidu_secret_key']);
|
||||
case 'none':
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public function detectLanguage( $text ) {
|
||||
if ( preg_match( '/[\x{4e00}-\x{9fff}]/u', $text ) ) {
|
||||
public function detectLanguage($text) {
|
||||
if (preg_match('/[\x{4e00}-\x{9fff}]/u', $text)) {
|
||||
return 'zh';
|
||||
}
|
||||
if ( preg_match( '/[\x{0400}-\x{04ff}]/u', $text ) ) {
|
||||
if (preg_match('/[\x{0400}-\x{04ff}]/u', $text)) {
|
||||
return 'ru';
|
||||
}
|
||||
if ( preg_match( '/[\x{0590}-\x{05ff}]/u', $text ) ) {
|
||||
if (preg_match('/[\x{0590}-\x{05ff}]/u', $text)) {
|
||||
return 'he';
|
||||
}
|
||||
if ( preg_match( '/[\x{0600}-\x{06ff}]/u', $text ) ) {
|
||||
if (preg_match('/[\x{0600}-\x{06ff}]/u', $text)) {
|
||||
return 'ar';
|
||||
}
|
||||
if ( preg_match( '/[\x{3040}-\x{309f}]/u', $text ) ) {
|
||||
if (preg_match('/[\x{3040}-\x{309f}]/u', $text)) {
|
||||
return 'ja';
|
||||
}
|
||||
if ( preg_match( '/[\x{30a0}-\x{30ff}]/u', $text ) ) {
|
||||
if (preg_match('/[\x{30a0}-\x{30ff}]/u', $text)) {
|
||||
return 'ja';
|
||||
}
|
||||
if ( preg_match( '/[\x{ac00}-\x{d7af}]/u', $text ) ) {
|
||||
if (preg_match('/[\x{ac00}-\x{d7af}]/u', $text)) {
|
||||
return 'ko';
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
|
|
@ -11,401 +11,187 @@ class WPSlug_Transliterator {
|
|||
$this->initCharMaps();
|
||||
}
|
||||
|
||||
public function transliterate( $text, $options = array() ) {
|
||||
if ( empty( $text ) ) {
|
||||
public function transliterate($text, $options = array()) {
|
||||
if (empty($text)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$method = isset( $options['transliteration_method'] ) ? $options['transliteration_method'] : 'basic';
|
||||
$method = isset($options['transliteration_method']) ? $options['transliteration_method'] : 'basic';
|
||||
|
||||
switch ( $method ) {
|
||||
switch ($method) {
|
||||
case 'iconv':
|
||||
return $this->transliterateIconv( $text, $options );
|
||||
return $this->transliterateIconv($text, $options);
|
||||
case 'intl':
|
||||
return $this->transliterateIntl( $text, $options );
|
||||
return $this->transliterateIntl($text, $options);
|
||||
case 'basic':
|
||||
default:
|
||||
return $this->transliterateBasic( $text, $options );
|
||||
return $this->transliterateBasic($text, $options);
|
||||
}
|
||||
}
|
||||
|
||||
private function transliterateBasic( $text, $options ) {
|
||||
$text = $this->applyCharMaps( $text );
|
||||
$text = $this->cleanSlug( $text );
|
||||
private function transliterateBasic($text, $options) {
|
||||
$text = $this->applyCharMaps($text);
|
||||
$text = $this->cleanSlug($text);
|
||||
|
||||
if ( isset( $options['force_lowercase'] ) && $options['force_lowercase'] ) {
|
||||
$text = strtolower( $text );
|
||||
if (isset($options['force_lowercase']) && $options['force_lowercase']) {
|
||||
$text = strtolower($text);
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
private function transliterateIconv( $text, $options ) {
|
||||
if ( ! function_exists( 'iconv' ) ) {
|
||||
return $this->transliterateBasic( $text, $options );
|
||||
private function transliterateIconv($text, $options) {
|
||||
if (!function_exists('iconv')) {
|
||||
return $this->transliterateBasic($text, $options);
|
||||
}
|
||||
|
||||
try {
|
||||
$text = iconv( 'UTF-8', 'ASCII//TRANSLIT//IGNORE', $text );
|
||||
$text = $this->cleanSlug( $text );
|
||||
$text = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $text);
|
||||
$text = $this->cleanSlug($text);
|
||||
|
||||
if ( isset( $options['force_lowercase'] ) && $options['force_lowercase'] ) {
|
||||
$text = strtolower( $text );
|
||||
if (isset($options['force_lowercase']) && $options['force_lowercase']) {
|
||||
$text = strtolower($text);
|
||||
}
|
||||
|
||||
return $text;
|
||||
} catch ( Exception $e ) {
|
||||
return $this->transliterateBasic( $text, $options );
|
||||
} catch (Exception $e) {
|
||||
return $this->transliterateBasic($text, $options);
|
||||
}
|
||||
}
|
||||
|
||||
private function transliterateIntl( $text, $options ) {
|
||||
if ( ! class_exists( 'Transliterator' ) ) {
|
||||
return $this->transliterateIconv( $text, $options );
|
||||
private function transliterateIntl($text, $options) {
|
||||
if (!class_exists('Transliterator')) {
|
||||
return $this->transliterateIconv($text, $options);
|
||||
}
|
||||
|
||||
try {
|
||||
$transliterator = Transliterator::create( 'Any-Latin; Latin-ASCII' );
|
||||
if ( $transliterator ) {
|
||||
$text = $transliterator->transliterate( $text );
|
||||
$text = $this->cleanSlug( $text );
|
||||
$transliterator = Transliterator::create('Any-Latin; Latin-ASCII');
|
||||
if ($transliterator) {
|
||||
$text = $transliterator->transliterate($text);
|
||||
$text = $this->cleanSlug($text);
|
||||
|
||||
if ( isset( $options['force_lowercase'] ) && $options['force_lowercase'] ) {
|
||||
$text = strtolower( $text );
|
||||
if (isset($options['force_lowercase']) && $options['force_lowercase']) {
|
||||
$text = strtolower($text);
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
} catch ( Exception $e ) {
|
||||
if ( isset( $options['debug_mode'] ) && $options['debug_mode'] ) {
|
||||
error_log( 'WPSlug transliterateIntl error: ' . $e->getMessage() );
|
||||
} catch (Exception $e) {
|
||||
if (isset($options['debug_mode']) && $options['debug_mode']) {
|
||||
error_log('WPSlug transliterateIntl error: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return $this->transliterateIconv( $text, $options );
|
||||
return $this->transliterateIconv($text, $options);
|
||||
}
|
||||
|
||||
private function applyCharMaps( $text ) {
|
||||
foreach ( $this->char_maps as $from => $to ) {
|
||||
$text = str_replace( $from, $to, $text );
|
||||
private function applyCharMaps($text) {
|
||||
foreach ($this->char_maps as $from => $to) {
|
||||
$text = str_replace($from, $to, $text);
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
||||
private function cleanSlug( $text ) {
|
||||
$text = strip_tags( $text );
|
||||
$text = html_entity_decode( $text, ENT_QUOTES | ENT_HTML5, 'UTF-8' );
|
||||
$text = preg_replace( '/&.+?;/', '', $text );
|
||||
$text = preg_replace( '/[^a-zA-Z0-9\s\-_]/', '', $text );
|
||||
$text = preg_replace( '/\s+/', '-', $text );
|
||||
$text = preg_replace( '/\-+/', '-', $text );
|
||||
$text = trim( $text, '-_' );
|
||||
private function cleanSlug($text) {
|
||||
$text = strip_tags($text);
|
||||
$text = html_entity_decode($text, ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
$text = preg_replace('/&.+?;/', '', $text);
|
||||
$text = preg_replace('/[^a-zA-Z0-9\s\-_]/', '', $text);
|
||||
$text = preg_replace('/\s+/', '-', $text);
|
||||
$text = preg_replace('/\-+/', '-', $text);
|
||||
$text = trim($text, '-_');
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
private function initCharMaps() {
|
||||
$this->char_maps = array(
|
||||
'А' => 'A',
|
||||
'а' => 'a',
|
||||
'Б' => 'B',
|
||||
'б' => 'b',
|
||||
'В' => 'V',
|
||||
'в' => 'v',
|
||||
'Г' => 'G',
|
||||
'г' => 'g',
|
||||
'Д' => 'D',
|
||||
'д' => 'd',
|
||||
'Е' => 'E',
|
||||
'е' => 'e',
|
||||
'Ё' => 'Yo',
|
||||
'ё' => 'yo',
|
||||
'Ж' => 'Zh',
|
||||
'ж' => 'zh',
|
||||
'З' => 'Z',
|
||||
'з' => 'z',
|
||||
'И' => 'I',
|
||||
'и' => 'i',
|
||||
'Й' => 'J',
|
||||
'й' => 'j',
|
||||
'К' => 'K',
|
||||
'к' => 'k',
|
||||
'Л' => 'L',
|
||||
'л' => 'l',
|
||||
'М' => 'M',
|
||||
'м' => 'm',
|
||||
'Н' => 'N',
|
||||
'н' => 'n',
|
||||
'О' => 'O',
|
||||
'о' => 'o',
|
||||
'П' => 'P',
|
||||
'п' => 'p',
|
||||
'Р' => 'R',
|
||||
'р' => 'r',
|
||||
'С' => 'S',
|
||||
'с' => 's',
|
||||
'Т' => 'T',
|
||||
'т' => 't',
|
||||
'У' => 'U',
|
||||
'у' => 'u',
|
||||
'Ф' => 'F',
|
||||
'ф' => 'f',
|
||||
'Х' => 'H',
|
||||
'х' => 'h',
|
||||
'Ц' => 'C',
|
||||
'ц' => 'c',
|
||||
'Ч' => 'Ch',
|
||||
'ч' => 'ch',
|
||||
'Ш' => 'Sh',
|
||||
'ш' => 'sh',
|
||||
'Щ' => 'Shh',
|
||||
'щ' => 'shh',
|
||||
'Ъ' => '',
|
||||
'ъ' => '',
|
||||
'Ы' => 'Y',
|
||||
'ы' => 'y',
|
||||
'Ь' => '',
|
||||
'ь' => '',
|
||||
'Э' => 'E',
|
||||
'э' => 'e',
|
||||
'Ю' => 'Yu',
|
||||
'ю' => 'yu',
|
||||
'Я' => 'Ya',
|
||||
'я' => 'ya',
|
||||
'А' => 'A', 'а' => 'a', 'Б' => 'B', 'б' => 'b', 'В' => 'V', 'в' => 'v',
|
||||
'Г' => 'G', 'г' => 'g', 'Д' => 'D', 'д' => 'd', 'Е' => 'E', 'е' => 'e',
|
||||
'Ё' => 'Yo', 'ё' => 'yo', 'Ж' => 'Zh', 'ж' => 'zh', 'З' => 'Z', 'з' => 'z',
|
||||
'И' => 'I', 'и' => 'i', 'Й' => 'J', 'й' => 'j', 'К' => 'K', 'к' => 'k',
|
||||
'Л' => 'L', 'л' => 'l', 'М' => 'M', 'м' => 'm', 'Н' => 'N', 'н' => 'n',
|
||||
'О' => 'O', 'о' => 'o', 'П' => 'P', 'п' => 'p', 'Р' => 'R', 'р' => 'r',
|
||||
'С' => 'S', 'с' => 's', 'Т' => 'T', 'т' => 't', 'У' => 'U', 'у' => 'u',
|
||||
'Ф' => 'F', 'ф' => 'f', 'Х' => 'H', 'х' => 'h', 'Ц' => 'C', 'ц' => 'c',
|
||||
'Ч' => 'Ch', 'ч' => 'ch', 'Ш' => 'Sh', 'ш' => 'sh', 'Щ' => 'Shh', 'щ' => 'shh',
|
||||
'Ъ' => '', 'ъ' => '', 'Ы' => 'Y', 'ы' => 'y', 'Ь' => '', 'ь' => '',
|
||||
'Э' => 'E', 'э' => 'e', 'Ю' => 'Yu', 'ю' => 'yu', 'Я' => 'Ya', 'я' => 'ya',
|
||||
|
||||
'ä' => 'ae',
|
||||
'ö' => 'oe',
|
||||
'ü' => 'ue',
|
||||
'ß' => 'ss',
|
||||
'Ä' => 'Ae',
|
||||
'Ö' => 'Oe',
|
||||
'Ü' => 'Ue',
|
||||
'ä' => 'ae', 'ö' => 'oe', 'ü' => 'ue', 'ß' => 'ss',
|
||||
'Ä' => 'Ae', 'Ö' => 'Oe', 'Ü' => 'Ue',
|
||||
|
||||
'à' => 'a',
|
||||
'á' => 'a',
|
||||
'â' => 'a',
|
||||
'ã' => 'a',
|
||||
'ä' => 'a',
|
||||
'å' => 'a',
|
||||
'À' => 'A',
|
||||
'Á' => 'A',
|
||||
'Â' => 'A',
|
||||
'Ã' => 'A',
|
||||
'Ä' => 'A',
|
||||
'Å' => 'A',
|
||||
'è' => 'e',
|
||||
'é' => 'e',
|
||||
'ê' => 'e',
|
||||
'ë' => 'e',
|
||||
'È' => 'E',
|
||||
'É' => 'E',
|
||||
'Ê' => 'E',
|
||||
'Ë' => 'E',
|
||||
'ì' => 'i',
|
||||
'í' => 'i',
|
||||
'î' => 'i',
|
||||
'ï' => 'i',
|
||||
'Ì' => 'I',
|
||||
'Í' => 'I',
|
||||
'Î' => 'I',
|
||||
'Ï' => 'I',
|
||||
'ò' => 'o',
|
||||
'ó' => 'o',
|
||||
'ô' => 'o',
|
||||
'õ' => 'o',
|
||||
'ö' => 'o',
|
||||
'ø' => 'o',
|
||||
'Ò' => 'O',
|
||||
'Ó' => 'O',
|
||||
'Ô' => 'O',
|
||||
'Õ' => 'O',
|
||||
'Ö' => 'O',
|
||||
'Ø' => 'O',
|
||||
'ù' => 'u',
|
||||
'ú' => 'u',
|
||||
'û' => 'u',
|
||||
'ü' => 'u',
|
||||
'Ù' => 'U',
|
||||
'Ú' => 'U',
|
||||
'Û' => 'U',
|
||||
'Ü' => 'U',
|
||||
'ý' => 'y',
|
||||
'ÿ' => 'y',
|
||||
'Ý' => 'Y',
|
||||
'Ÿ' => 'Y',
|
||||
'ñ' => 'n',
|
||||
'Ñ' => 'N',
|
||||
'ç' => 'c',
|
||||
'Ç' => 'C',
|
||||
'æ' => 'ae',
|
||||
'Æ' => 'AE',
|
||||
'œ' => 'oe',
|
||||
'Œ' => 'OE',
|
||||
'à' => 'a', 'á' => 'a', 'â' => 'a', 'ã' => 'a', 'ä' => 'a', 'å' => 'a',
|
||||
'À' => 'A', 'Á' => 'A', 'Â' => 'A', 'Ã' => 'A', 'Ä' => 'A', 'Å' => 'A',
|
||||
'è' => 'e', 'é' => 'e', 'ê' => 'e', 'ë' => 'e',
|
||||
'È' => 'E', 'É' => 'E', 'Ê' => 'E', 'Ë' => 'E',
|
||||
'ì' => 'i', 'í' => 'i', 'î' => 'i', 'ï' => 'i',
|
||||
'Ì' => 'I', 'Í' => 'I', 'Î' => 'I', 'Ï' => 'I',
|
||||
'ò' => 'o', 'ó' => 'o', 'ô' => 'o', 'õ' => 'o', 'ö' => 'o', 'ø' => 'o',
|
||||
'Ò' => 'O', 'Ó' => 'O', 'Ô' => 'O', 'Õ' => 'O', 'Ö' => 'O', 'Ø' => 'O',
|
||||
'ù' => 'u', 'ú' => 'u', 'û' => 'u', 'ü' => 'u',
|
||||
'Ù' => 'U', 'Ú' => 'U', 'Û' => 'U', 'Ü' => 'U',
|
||||
'ý' => 'y', 'ÿ' => 'y', 'Ý' => 'Y', 'Ÿ' => 'Y',
|
||||
'ñ' => 'n', 'Ñ' => 'N',
|
||||
'ç' => 'c', 'Ç' => 'C',
|
||||
'æ' => 'ae', 'Æ' => 'AE',
|
||||
'œ' => 'oe', 'Œ' => 'OE',
|
||||
|
||||
'ā' => 'a',
|
||||
'ē' => 'e',
|
||||
'ī' => 'i',
|
||||
'ō' => 'o',
|
||||
'ū' => 'u',
|
||||
'Ā' => 'A',
|
||||
'Ē' => 'E',
|
||||
'Ī' => 'I',
|
||||
'Ō' => 'O',
|
||||
'Ū' => 'U',
|
||||
'ā' => 'a', 'ē' => 'e', 'ī' => 'i', 'ō' => 'o', 'ū' => 'u',
|
||||
'Ā' => 'A', 'Ē' => 'E', 'Ī' => 'I', 'Ō' => 'O', 'Ū' => 'U',
|
||||
|
||||
'ą' => 'a',
|
||||
'ć' => 'c',
|
||||
'ę' => 'e',
|
||||
'ł' => 'l',
|
||||
'ń' => 'n',
|
||||
'ó' => 'o',
|
||||
'ś' => 's',
|
||||
'ź' => 'z',
|
||||
'ż' => 'z',
|
||||
'Ą' => 'A',
|
||||
'Ć' => 'C',
|
||||
'Ę' => 'E',
|
||||
'Ł' => 'L',
|
||||
'Ń' => 'N',
|
||||
'Ó' => 'O',
|
||||
'Ś' => 'S',
|
||||
'Ź' => 'Z',
|
||||
'Ż' => 'Z',
|
||||
'ą' => 'a', 'ć' => 'c', 'ę' => 'e', 'ł' => 'l', 'ń' => 'n',
|
||||
'ó' => 'o', 'ś' => 's', 'ź' => 'z', 'ż' => 'z',
|
||||
'Ą' => 'A', 'Ć' => 'C', 'Ę' => 'E', 'Ł' => 'L', 'Ń' => 'N',
|
||||
'Ó' => 'O', 'Ś' => 'S', 'Ź' => 'Z', 'Ż' => 'Z',
|
||||
|
||||
'ă' => 'a',
|
||||
'î' => 'i',
|
||||
'ș' => 's',
|
||||
'ț' => 't',
|
||||
'Ă' => 'A',
|
||||
'Î' => 'I',
|
||||
'Ș' => 'S',
|
||||
'Ț' => 'T',
|
||||
'ă' => 'a', 'î' => 'i', 'ș' => 's', 'ț' => 't',
|
||||
'Ă' => 'A', 'Î' => 'I', 'Ș' => 'S', 'Ț' => 'T',
|
||||
|
||||
'α' => 'a',
|
||||
'β' => 'b',
|
||||
'γ' => 'g',
|
||||
'δ' => 'd',
|
||||
'ε' => 'e',
|
||||
'ζ' => 'z',
|
||||
'η' => 'i',
|
||||
'θ' => 'th',
|
||||
'ι' => 'i',
|
||||
'κ' => 'k',
|
||||
'λ' => 'l',
|
||||
'μ' => 'm',
|
||||
'ν' => 'n',
|
||||
'ξ' => 'x',
|
||||
'ο' => 'o',
|
||||
'π' => 'p',
|
||||
'ρ' => 'r',
|
||||
'σ' => 's',
|
||||
'τ' => 't',
|
||||
'υ' => 'y',
|
||||
'φ' => 'f',
|
||||
'χ' => 'ch',
|
||||
'ψ' => 'ps',
|
||||
'ω' => 'o',
|
||||
'Α' => 'A',
|
||||
'Β' => 'B',
|
||||
'Γ' => 'G',
|
||||
'Δ' => 'D',
|
||||
'Ε' => 'E',
|
||||
'Ζ' => 'Z',
|
||||
'Η' => 'I',
|
||||
'Θ' => 'TH',
|
||||
'Ι' => 'I',
|
||||
'Κ' => 'K',
|
||||
'Λ' => 'L',
|
||||
'Μ' => 'M',
|
||||
'Ν' => 'N',
|
||||
'Ξ' => 'X',
|
||||
'Ο' => 'O',
|
||||
'Π' => 'P',
|
||||
'Ρ' => 'R',
|
||||
'Σ' => 'S',
|
||||
'Τ' => 'T',
|
||||
'Υ' => 'Y',
|
||||
'Φ' => 'F',
|
||||
'Χ' => 'CH',
|
||||
'Ψ' => 'PS',
|
||||
'Ω' => 'O',
|
||||
'α' => 'a', 'β' => 'b', 'γ' => 'g', 'δ' => 'd', 'ε' => 'e',
|
||||
'ζ' => 'z', 'η' => 'i', 'θ' => 'th', 'ι' => 'i', 'κ' => 'k',
|
||||
'λ' => 'l', 'μ' => 'm', 'ν' => 'n', 'ξ' => 'x', 'ο' => 'o',
|
||||
'π' => 'p', 'ρ' => 'r', 'σ' => 's', 'τ' => 't', 'υ' => 'y',
|
||||
'φ' => 'f', 'χ' => 'ch', 'ψ' => 'ps', 'ω' => 'o',
|
||||
'Α' => 'A', 'Β' => 'B', 'Γ' => 'G', 'Δ' => 'D', 'Ε' => 'E',
|
||||
'Ζ' => 'Z', 'Η' => 'I', 'Θ' => 'TH', 'Ι' => 'I', 'Κ' => 'K',
|
||||
'Λ' => 'L', 'Μ' => 'M', 'Ν' => 'N', 'Ξ' => 'X', 'Ο' => 'O',
|
||||
'Π' => 'P', 'Ρ' => 'R', 'Σ' => 'S', 'Τ' => 'T', 'Υ' => 'Y',
|
||||
'Φ' => 'F', 'Χ' => 'CH', 'Ψ' => 'PS', 'Ω' => 'O',
|
||||
|
||||
'ã' => 'a',
|
||||
'õ' => 'o',
|
||||
'ç' => 'c',
|
||||
'Ã' => 'A',
|
||||
'Õ' => 'O',
|
||||
'Ç' => 'C',
|
||||
'ã' => 'a', 'õ' => 'o', 'ç' => 'c',
|
||||
'Ã' => 'A', 'Õ' => 'O', 'Ç' => 'C',
|
||||
|
||||
'ğ' => 'g',
|
||||
'ı' => 'i',
|
||||
'ş' => 's',
|
||||
'ü' => 'u',
|
||||
'ö' => 'o',
|
||||
'ç' => 'c',
|
||||
'Ğ' => 'G',
|
||||
'İ' => 'I',
|
||||
'Ş' => 'S',
|
||||
'Ü' => 'U',
|
||||
'Ö' => 'O',
|
||||
'Ç' => 'C',
|
||||
'ğ' => 'g', 'ı' => 'i', 'ş' => 's', 'ü' => 'u', 'ö' => 'o', 'ç' => 'c',
|
||||
'Ğ' => 'G', 'İ' => 'I', 'Ş' => 'S', 'Ü' => 'U', 'Ö' => 'O', 'Ç' => 'C',
|
||||
|
||||
'ک' => 'k',
|
||||
'گ' => 'g',
|
||||
'چ' => 'ch',
|
||||
'پ' => 'p',
|
||||
'ژ' => 'zh',
|
||||
'ی' => 'y',
|
||||
'ء' => 'a',
|
||||
'ؤ' => 'w',
|
||||
'ئ' => 'y',
|
||||
'ة' => 'h',
|
||||
'ا' => 'a',
|
||||
'ب' => 'b',
|
||||
'ت' => 't',
|
||||
'ث' => 'th',
|
||||
'ج' => 'j',
|
||||
'ح' => 'h',
|
||||
'خ' => 'kh',
|
||||
'د' => 'd',
|
||||
'ذ' => 'dh',
|
||||
'ر' => 'r',
|
||||
'ز' => 'z',
|
||||
'س' => 's',
|
||||
'ش' => 'sh',
|
||||
'ص' => 's',
|
||||
'ض' => 'd',
|
||||
'ط' => 't',
|
||||
'ظ' => 'dh',
|
||||
'ع' => 'a',
|
||||
'غ' => 'gh',
|
||||
'ف' => 'f',
|
||||
'ق' => 'q',
|
||||
'ل' => 'l',
|
||||
'م' => 'm',
|
||||
'ن' => 'n',
|
||||
'ه' => 'h',
|
||||
'و' => 'w',
|
||||
'ي' => 'y',
|
||||
'ک' => 'k', 'گ' => 'g', 'چ' => 'ch', 'پ' => 'p', 'ژ' => 'zh',
|
||||
'ی' => 'y', 'ء' => 'a', 'ؤ' => 'w', 'ئ' => 'y', 'ة' => 'h',
|
||||
'ا' => 'a', 'ب' => 'b', 'ت' => 't', 'ث' => 'th', 'ج' => 'j',
|
||||
'ح' => 'h', 'خ' => 'kh', 'د' => 'd', 'ذ' => 'dh', 'ر' => 'r',
|
||||
'ز' => 'z', 'س' => 's', 'ش' => 'sh', 'ص' => 's', 'ض' => 'd',
|
||||
'ط' => 't', 'ظ' => 'dh', 'ع' => 'a', 'غ' => 'gh', 'ف' => 'f',
|
||||
'ق' => 'q', 'ل' => 'l', 'م' => 'm', 'ن' => 'n', 'ه' => 'h',
|
||||
'و' => 'w', 'ي' => 'y'
|
||||
);
|
||||
}
|
||||
|
||||
public function getSupportedMethods() {
|
||||
return array( 'basic', 'iconv', 'intl' );
|
||||
return array('basic', 'iconv', 'intl');
|
||||
}
|
||||
|
||||
public function isMethodSupported( $method ) {
|
||||
return in_array( $method, $this->getSupportedMethods() );
|
||||
public function isMethodSupported($method) {
|
||||
return in_array($method, $this->getSupportedMethods());
|
||||
}
|
||||
|
||||
public function getAvailableMethods() {
|
||||
$methods = array( 'basic' );
|
||||
$methods = array('basic');
|
||||
|
||||
if ( function_exists( 'iconv' ) ) {
|
||||
if (function_exists('iconv')) {
|
||||
$methods[] = 'iconv';
|
||||
}
|
||||
|
||||
if ( class_exists( 'Transliterator' ) ) {
|
||||
if (class_exists('Transliterator')) {
|
||||
$methods[] = 'intl';
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,233 +1,203 @@
|
|||
<?php
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class WPSlug_Validator {
|
||||
|
||||
public static function validateBoolean( $value ) {
|
||||
if ( is_bool( $value ) ) {
|
||||
public static function validateBoolean($value) {
|
||||
if (is_bool($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if ( is_string( $value ) ) {
|
||||
return ( $value === '1' || strtolower( $value ) === 'true' );
|
||||
if (is_string($value)) {
|
||||
return ($value === '1' || strtolower($value) === 'true');
|
||||
}
|
||||
|
||||
if ( is_numeric( $value ) ) {
|
||||
return (int) $value === 1;
|
||||
if (is_numeric($value)) {
|
||||
return (int)$value === 1;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function validateInteger( $value, $min = null, $max = null ) {
|
||||
$int_value = intval( $value );
|
||||
public static function validateInteger($value, $min = null, $max = null) {
|
||||
$int_value = intval($value);
|
||||
|
||||
if ( $min !== null && $int_value < $min ) {
|
||||
if ($min !== null && $int_value < $min) {
|
||||
return $min;
|
||||
}
|
||||
|
||||
if ( $max !== null && $int_value > $max ) {
|
||||
if ($max !== null && $int_value > $max) {
|
||||
return $max;
|
||||
}
|
||||
|
||||
return $int_value;
|
||||
}
|
||||
|
||||
public static function validateString( $value, $max_length = 200 ) {
|
||||
$string_value = sanitize_text_field( $value );
|
||||
public static function validateString($value, $max_length = 200) {
|
||||
$string_value = sanitize_text_field($value);
|
||||
|
||||
if ( strlen( $string_value ) > $max_length ) {
|
||||
return substr( $string_value, 0, $max_length );
|
||||
if (strlen($string_value) > $max_length) {
|
||||
return substr($string_value, 0, $max_length);
|
||||
}
|
||||
|
||||
return $string_value;
|
||||
}
|
||||
|
||||
public static function validateTextarea( $value ) {
|
||||
return sanitize_textarea_field( $value );
|
||||
public static function validateTextarea($value) {
|
||||
return sanitize_textarea_field($value);
|
||||
}
|
||||
|
||||
public static function validateArray( $value, $default = array() ) {
|
||||
if ( ! is_array( $value ) ) {
|
||||
public static function validateArray($value, $default = array()) {
|
||||
if (!is_array($value)) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
$sanitized = array_map( 'sanitize_text_field', $value );
|
||||
$filtered = array_filter( $sanitized );
|
||||
$sanitized = array_map('sanitize_text_field', $value);
|
||||
$filtered = array_filter($sanitized);
|
||||
|
||||
return array_slice( $filtered, 0, 50 );
|
||||
return array_slice($filtered, 0, 50);
|
||||
}
|
||||
|
||||
public static function validateSelect( $value, $valid_options, $default ) {
|
||||
return in_array( $value, $valid_options, true ) ? $value : $default;
|
||||
public static function validateSelect($value, $valid_options, $default) {
|
||||
return in_array($value, $valid_options, true) ? $value : $default;
|
||||
}
|
||||
|
||||
public static function validateApiKey( $value ) {
|
||||
return self::validateString( $value, 200 );
|
||||
public static function validateApiKey($value) {
|
||||
return self::validateString($value, 200);
|
||||
}
|
||||
|
||||
public static function validateSlug( $slug ) {
|
||||
if ( empty( $slug ) ) {
|
||||
public static function validateSlug($slug) {
|
||||
if (empty($slug)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( strlen( $slug ) > 200 ) {
|
||||
if (strlen($slug) > 200) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( preg_match( '/[^a-zA-Z0-9\-_\p{Han}]/u', $slug ) ) {
|
||||
if (preg_match('/[^a-zA-Z0-9\-_\p{Han}]/u', $slug)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function validateLanguageCode( $code ) {
|
||||
public static function validateLanguageCode($code) {
|
||||
$valid_codes = array(
|
||||
'auto',
|
||||
'zh',
|
||||
'zh-TW',
|
||||
'en',
|
||||
'es',
|
||||
'fr',
|
||||
'de',
|
||||
'ja',
|
||||
'ko',
|
||||
'ru',
|
||||
'ar',
|
||||
'it',
|
||||
'pt',
|
||||
'nl',
|
||||
'pl',
|
||||
'tr',
|
||||
'sv',
|
||||
'da',
|
||||
'no',
|
||||
'fi',
|
||||
'cs',
|
||||
'hu',
|
||||
'ro',
|
||||
'bg',
|
||||
'hr',
|
||||
'sk',
|
||||
'sl',
|
||||
'et',
|
||||
'lv',
|
||||
'lt',
|
||||
'mt',
|
||||
'el',
|
||||
'cy',
|
||||
'auto', 'zh', 'zh-TW', 'en', 'es', 'fr', 'de', 'ja', 'ko', 'ru',
|
||||
'ar', 'it', 'pt', 'nl', 'pl', 'tr', 'sv', 'da', 'no', 'fi', 'cs',
|
||||
'hu', 'ro', 'bg', 'hr', 'sk', 'sl', 'et', 'lv', 'lt', 'mt', 'el', 'cy'
|
||||
);
|
||||
|
||||
return in_array( $code, $valid_codes, true ) ? $code : 'auto';
|
||||
return in_array($code, $valid_codes, true) ? $code : 'auto';
|
||||
}
|
||||
|
||||
public static function validatePostTypes( $post_types ) {
|
||||
if ( ! is_array( $post_types ) ) {
|
||||
return array( 'post', 'page' );
|
||||
public static function validatePostTypes($post_types) {
|
||||
if (!is_array($post_types)) {
|
||||
return array('post', 'page');
|
||||
}
|
||||
|
||||
$all_post_types = get_post_types( array( 'public' => true ) );
|
||||
$all_post_types = get_post_types(array('public' => true));
|
||||
$validated = array();
|
||||
|
||||
foreach ( $post_types as $post_type ) {
|
||||
if ( in_array( $post_type, $all_post_types ) ) {
|
||||
foreach ($post_types as $post_type) {
|
||||
if (in_array($post_type, $all_post_types)) {
|
||||
$validated[] = $post_type;
|
||||
}
|
||||
}
|
||||
|
||||
return empty( $validated ) ? array( 'post', 'page' ) : $validated;
|
||||
return empty($validated) ? array('post', 'page') : $validated;
|
||||
}
|
||||
|
||||
public static function validateTaxonomies( $taxonomies ) {
|
||||
if ( ! is_array( $taxonomies ) ) {
|
||||
return array( 'category', 'post_tag' );
|
||||
public static function validateTaxonomies($taxonomies) {
|
||||
if (!is_array($taxonomies)) {
|
||||
return array('category', 'post_tag');
|
||||
}
|
||||
|
||||
$all_taxonomies = get_taxonomies( array( 'public' => true ) );
|
||||
$all_taxonomies = get_taxonomies(array('public' => true));
|
||||
$validated = array();
|
||||
|
||||
foreach ( $taxonomies as $taxonomy ) {
|
||||
if ( in_array( $taxonomy, $all_taxonomies ) ) {
|
||||
foreach ($taxonomies as $taxonomy) {
|
||||
if (in_array($taxonomy, $all_taxonomies)) {
|
||||
$validated[] = $taxonomy;
|
||||
}
|
||||
}
|
||||
|
||||
return empty( $validated ) ? array( 'category', 'post_tag' ) : $validated;
|
||||
return empty($validated) ? array('category', 'post_tag') : $validated;
|
||||
}
|
||||
|
||||
public static function validateConversionMode( $mode ) {
|
||||
$valid_modes = array( 'pinyin', 'semantic_pinyin', 'transliteration', 'translation' );
|
||||
return self::validateSelect( $mode, $valid_modes, 'pinyin' );
|
||||
public static function validateConversionMode($mode) {
|
||||
$valid_modes = array('pinyin', 'semantic_pinyin', 'transliteration', 'translation');
|
||||
return self::validateSelect($mode, $valid_modes, 'pinyin');
|
||||
}
|
||||
|
||||
public static function validateTranslationService( $service ) {
|
||||
public static function validateTranslationService($service) {
|
||||
// 注意:wpmind 服务需要在此列表中才能保存
|
||||
$valid_services = array( 'none', 'google', 'baidu', 'wpmind' );
|
||||
return self::validateSelect( $service, $valid_services, 'none' );
|
||||
$valid_services = array('none', 'google', 'baidu', 'wpmind');
|
||||
return self::validateSelect($service, $valid_services, 'none');
|
||||
}
|
||||
|
||||
public static function validateTransliterationMethod( $method ) {
|
||||
$valid_methods = array( 'basic', 'iconv', 'intl' );
|
||||
return self::validateSelect( $method, $valid_methods, 'basic' );
|
||||
public static function validateTransliterationMethod($method) {
|
||||
$valid_methods = array('basic', 'iconv', 'intl');
|
||||
return self::validateSelect($method, $valid_methods, 'basic');
|
||||
}
|
||||
|
||||
public static function hasRequiredApiCredentials( $options ) {
|
||||
public static function hasRequiredApiCredentials($options) {
|
||||
$errors = array();
|
||||
|
||||
if ( $options['conversion_mode'] === 'translation' ) {
|
||||
if ($options['conversion_mode'] === 'translation') {
|
||||
$service = $options['translation_service'];
|
||||
|
||||
if ( $service === 'google' && empty( $options['google_api_key'] ) ) {
|
||||
$errors[] = __( 'Google API key is required for Google Translate service.', 'wpslug' );
|
||||
if ($service === 'google' && empty($options['google_api_key'])) {
|
||||
$errors[] = __('Google API key is required for Google Translate service.', 'wpslug');
|
||||
}
|
||||
|
||||
if ( $service === 'baidu' && ( empty( $options['baidu_app_id'] ) || empty( $options['baidu_secret_key'] ) ) ) {
|
||||
$errors[] = __( 'Baidu App ID and Secret Key are required for Baidu Translate service.', 'wpslug' );
|
||||
if ($service === 'baidu' && (empty($options['baidu_app_id']) || empty($options['baidu_secret_key']))) {
|
||||
$errors[] = __('Baidu App ID and Secret Key are required for Baidu Translate service.', 'wpslug');
|
||||
}
|
||||
|
||||
// WPMind 需要已配置
|
||||
if ( $service === 'wpmind' ) {
|
||||
if ( ! function_exists( 'wpmind_is_available' ) ) {
|
||||
$errors[] = __( 'WPMind plugin is not installed.', 'wpslug' );
|
||||
} elseif ( ! wpmind_is_available() ) {
|
||||
$errors[] = __( 'WPMind is not configured. Please configure it in WPMind settings.', 'wpslug' );
|
||||
if ($service === 'wpmind') {
|
||||
if (!function_exists('wpmind_is_available')) {
|
||||
$errors[] = __('WPMind plugin is not installed.', 'wpslug');
|
||||
} elseif (!wpmind_is_available()) {
|
||||
$errors[] = __('WPMind is not configured. Please configure it in WPMind settings.', 'wpslug');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return empty( $errors ) ? true : $errors;
|
||||
return empty($errors) ? true : $errors;
|
||||
}
|
||||
|
||||
public static function validateSystemRequirements() {
|
||||
$requirements = array(
|
||||
'php_version' => version_compare( PHP_VERSION, '7.0', '>=' ),
|
||||
'wordpress_version' => version_compare( get_bloginfo( 'version' ), '5.0', '>=' ),
|
||||
'mbstring_extension' => extension_loaded( 'mbstring' ),
|
||||
'json_extension' => extension_loaded( 'json' ),
|
||||
'php_version' => version_compare(PHP_VERSION, '7.0', '>='),
|
||||
'wordpress_version' => version_compare(get_bloginfo('version'), '5.0', '>='),
|
||||
'mbstring_extension' => extension_loaded('mbstring'),
|
||||
'json_extension' => extension_loaded('json')
|
||||
);
|
||||
|
||||
$errors = array();
|
||||
|
||||
if ( ! $requirements['php_version'] ) {
|
||||
$errors[] = __( 'PHP 7.0 or higher is required.', 'wpslug' );
|
||||
if (!$requirements['php_version']) {
|
||||
$errors[] = __('PHP 7.0 or higher is required.', 'wpslug');
|
||||
}
|
||||
|
||||
if ( ! $requirements['wordpress_version'] ) {
|
||||
$errors[] = __( 'WordPress 5.0 or higher is required.', 'wpslug' );
|
||||
if (!$requirements['wordpress_version']) {
|
||||
$errors[] = __('WordPress 5.0 or higher is required.', 'wpslug');
|
||||
}
|
||||
|
||||
if ( ! $requirements['mbstring_extension'] ) {
|
||||
$errors[] = __( 'PHP mbstring extension is required.', 'wpslug' );
|
||||
if (!$requirements['mbstring_extension']) {
|
||||
$errors[] = __('PHP mbstring extension is required.', 'wpslug');
|
||||
}
|
||||
|
||||
if ( ! $requirements['json_extension'] ) {
|
||||
$errors[] = __( 'PHP JSON extension is required.', 'wpslug' );
|
||||
if (!$requirements['json_extension']) {
|
||||
$errors[] = __('PHP JSON extension is required.', 'wpslug');
|
||||
}
|
||||
|
||||
return empty( $errors ) ? true : $errors;
|
||||
return empty($errors) ? true : $errors;
|
||||
}
|
||||
}
|
||||
39
phpcs.xml
39
phpcs.xml
|
|
@ -1,39 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<ruleset name="WPSlug">
|
||||
<description>WPSlug 项目代码规范</description>
|
||||
|
||||
<file>.</file>
|
||||
<arg name="extensions" value="php"/>
|
||||
<arg name="warning-severity" value="0"/>
|
||||
<exclude-pattern>vendor/*</exclude-pattern>
|
||||
<exclude-pattern>node_modules/*</exclude-pattern>
|
||||
<exclude-pattern>tests/*</exclude-pattern>
|
||||
<exclude-pattern>lib/*</exclude-pattern>
|
||||
<exclude-pattern>languages/*</exclude-pattern>
|
||||
|
||||
<rule ref="WordPress-Extra">
|
||||
<!-- 项目使用 camelCase 方法名 -->
|
||||
<exclude name="WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid"/>
|
||||
<!-- 不强制 Yoda 条件 -->
|
||||
<exclude name="WordPress.PHP.YodaConditions.NotYoda"/>
|
||||
<!-- 允许 error_log -->
|
||||
<exclude name="WordPress.PHP.DevelopmentFunctions.error_log_error_log"/>
|
||||
<!-- 允许短三元运算符 -->
|
||||
<exclude name="Universal.Operators.DisallowShortTernary.Found"/>
|
||||
<!-- _e() 是 WordPress 标准翻译函数 -->
|
||||
<exclude name="WordPress.Security.EscapeOutput.UnsafePrintingFunction"/>
|
||||
<!-- 拼音字典允许重复键 -->
|
||||
<exclude name="Universal.Arrays.DuplicateArrayKey.Found"/>
|
||||
<!-- Nonce 验证由其他机制处理 -->
|
||||
<exclude name="WordPress.Security.NonceVerification.Recommended"/>
|
||||
<exclude name="WordPress.Security.NonceVerification.Missing"/>
|
||||
<!-- 主文件结构是 WordPress 插件标准格式 -->
|
||||
<exclude name="WordPress.Files.FileName.InvalidClassFileName"/>
|
||||
<exclude name="Universal.Files.SeparateFunctionsFromOO.Mixed"/>
|
||||
</rule>
|
||||
|
||||
<!-- 输出转义降为 warning,不阻断 CI -->
|
||||
<rule ref="WordPress.Security.EscapeOutput.OutputNotEscaped">
|
||||
<type>warning</type>
|
||||
</rule>
|
||||
</ruleset>
|
||||
192
wpslug.php
192
wpslug.php
|
|
@ -15,38 +15,41 @@ Requires at least: 6.0
|
|||
Requires PHP: 7.4
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
if (!defined("ABSPATH")) {
|
||||
exit();
|
||||
}
|
||||
|
||||
define( 'WPSLUG_VERSION', '1.1.0' );
|
||||
define( 'WPSLUG_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
|
||||
define( 'WPSLUG_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
|
||||
define( 'WPSLUG_PLUGIN_BASENAME', plugin_basename( __FILE__ ) );
|
||||
|
||||
class WPSlug {
|
||||
define("WPSLUG_VERSION", "1.1.0");
|
||||
define("WPSLUG_PLUGIN_DIR", plugin_dir_path(__FILE__));
|
||||
define("WPSLUG_PLUGIN_URL", plugin_dir_url(__FILE__));
|
||||
define("WPSLUG_PLUGIN_BASENAME", plugin_basename(__FILE__));
|
||||
|
||||
class WPSlug
|
||||
{
|
||||
private static $instance = null;
|
||||
private $core = null;
|
||||
|
||||
public static function getInstance() {
|
||||
if ( self::$instance === null ) {
|
||||
public static function getInstance()
|
||||
{
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
private function __construct() {
|
||||
add_action( 'plugins_loaded', array( $this, 'loadPlugin' ) );
|
||||
register_activation_hook( __FILE__, array( $this, 'activate' ) );
|
||||
register_deactivation_hook( __FILE__, array( $this, 'deactivate' ) );
|
||||
register_uninstall_hook( __FILE__, array( 'WPSlug', 'uninstall' ) );
|
||||
private function __construct()
|
||||
{
|
||||
add_action("plugins_loaded", [$this, "loadPlugin"]);
|
||||
register_activation_hook(__FILE__, [$this, "activate"]);
|
||||
register_deactivation_hook(__FILE__, [$this, "deactivate"]);
|
||||
register_uninstall_hook(__FILE__, ["WPSlug", "uninstall"]);
|
||||
|
||||
add_action( 'init', array( $this, 'initLanguages' ) );
|
||||
add_action('init', [$this, 'initLanguages']);
|
||||
}
|
||||
|
||||
public function loadPlugin() {
|
||||
if ( ! $this->checkRequirements() ) {
|
||||
public function loadPlugin()
|
||||
{
|
||||
if (!$this->checkRequirements()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -56,78 +59,77 @@ class WPSlug {
|
|||
new WenPai_Updater( WPSLUG_PLUGIN_BASENAME, WPSLUG_VERSION );
|
||||
}
|
||||
|
||||
private function checkRequirements() {
|
||||
if ( version_compare( PHP_VERSION, '7.0', '<' ) ) {
|
||||
add_action(
|
||||
'admin_notices',
|
||||
function () {
|
||||
private function checkRequirements()
|
||||
{
|
||||
if (version_compare(PHP_VERSION, "7.0", "<")) {
|
||||
add_action('admin_notices', function() {
|
||||
echo '<div class="notice notice-error"><p>';
|
||||
echo esc_html__( 'WP Slug requires PHP 7.0 or higher. Please upgrade your PHP version.', 'wpslug' );
|
||||
echo esc_html__('WP Slug requires PHP 7.0 or higher. Please upgrade your PHP version.', 'wpslug');
|
||||
echo '</p></div>';
|
||||
}
|
||||
);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( version_compare( get_bloginfo( 'version' ), '5.0', '<' ) ) {
|
||||
add_action(
|
||||
'admin_notices',
|
||||
function () {
|
||||
if (version_compare(get_bloginfo("version"), "5.0", "<")) {
|
||||
add_action('admin_notices', function() {
|
||||
echo '<div class="notice notice-error"><p>';
|
||||
echo esc_html__( 'WP Slug requires WordPress 5.0 or higher. Please upgrade your WordPress version.', 'wpslug' );
|
||||
echo esc_html__('WP Slug requires WordPress 5.0 or higher. Please upgrade your WordPress version.', 'wpslug');
|
||||
echo '</p></div>';
|
||||
}
|
||||
);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function loadDependencies() {
|
||||
require_once WPSLUG_PLUGIN_DIR . 'includes/class-wpslug-validator.php';
|
||||
require_once WPSLUG_PLUGIN_DIR . 'includes/class-wpslug-settings.php';
|
||||
require_once WPSLUG_PLUGIN_DIR . 'includes/class-wpslug-pinyin.php';
|
||||
require_once WPSLUG_PLUGIN_DIR . 'includes/class-wpslug-optimizer.php';
|
||||
require_once WPSLUG_PLUGIN_DIR . 'includes/class-wpslug-transliterator.php';
|
||||
require_once WPSLUG_PLUGIN_DIR . 'includes/class-wpslug-translator.php';
|
||||
require_once WPSLUG_PLUGIN_DIR . 'includes/class-wpslug-converter.php';
|
||||
require_once WPSLUG_PLUGIN_DIR . 'includes/class-wpslug-core.php';
|
||||
require_once WPSLUG_PLUGIN_DIR . 'includes/class-wenpai-updater.php';
|
||||
private function loadDependencies()
|
||||
{
|
||||
require_once WPSLUG_PLUGIN_DIR . "includes/class-wpslug-validator.php";
|
||||
require_once WPSLUG_PLUGIN_DIR . "includes/class-wpslug-settings.php";
|
||||
require_once WPSLUG_PLUGIN_DIR . "includes/class-wpslug-pinyin.php";
|
||||
require_once WPSLUG_PLUGIN_DIR . "includes/class-wpslug-optimizer.php";
|
||||
require_once WPSLUG_PLUGIN_DIR . "includes/class-wpslug-transliterator.php";
|
||||
require_once WPSLUG_PLUGIN_DIR . "includes/class-wpslug-translator.php";
|
||||
require_once WPSLUG_PLUGIN_DIR . "includes/class-wpslug-converter.php";
|
||||
require_once WPSLUG_PLUGIN_DIR . "includes/class-wpslug-core.php";
|
||||
require_once WPSLUG_PLUGIN_DIR . "includes/class-wenpai-updater.php";
|
||||
|
||||
if ( is_admin() ) {
|
||||
require_once WPSLUG_PLUGIN_DIR . 'includes/class-wpslug-admin.php';
|
||||
if (is_admin()) {
|
||||
require_once WPSLUG_PLUGIN_DIR . "includes/class-wpslug-admin.php";
|
||||
}
|
||||
}
|
||||
|
||||
public function initLanguages() {
|
||||
$locale = apply_filters( 'plugin_locale', get_locale(), 'wpslug' );
|
||||
public function initLanguages()
|
||||
{
|
||||
$locale = apply_filters('plugin_locale', get_locale(), 'wpslug');
|
||||
$mo_file = WPSLUG_PLUGIN_DIR . "languages/wpslug-{$locale}.mo";
|
||||
|
||||
if ( file_exists( $mo_file ) ) {
|
||||
load_textdomain( 'wpslug', $mo_file );
|
||||
if (file_exists($mo_file)) {
|
||||
load_textdomain('wpslug', $mo_file);
|
||||
}
|
||||
}
|
||||
|
||||
public function loadTextdomain() {
|
||||
public function loadTextdomain()
|
||||
{
|
||||
load_plugin_textdomain(
|
||||
'wpslug',
|
||||
"wpslug",
|
||||
false,
|
||||
dirname( plugin_basename( __FILE__ ) ) . '/languages/'
|
||||
dirname(plugin_basename(__FILE__)) . "/languages/"
|
||||
);
|
||||
}
|
||||
|
||||
public function activate() {
|
||||
if ( ! function_exists( 'is_plugin_active' ) ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||
public function activate()
|
||||
{
|
||||
if (!function_exists("is_plugin_active")) {
|
||||
require_once ABSPATH . "wp-admin/includes/plugin.php";
|
||||
}
|
||||
|
||||
if ( ! $this->checkRequirements() ) {
|
||||
deactivate_plugins( plugin_basename( __FILE__ ) );
|
||||
if (!$this->checkRequirements()) {
|
||||
deactivate_plugins(plugin_basename(__FILE__));
|
||||
wp_die(
|
||||
esc_html__( 'WP Slug plugin requirements not met. Please check your PHP and WordPress versions.', 'wpslug' ),
|
||||
esc_html__( 'Plugin Activation Error', 'wpslug' ),
|
||||
array( 'back_link' => true )
|
||||
esc_html__('WP Slug plugin requirements not met. Please check your PHP and WordPress versions.', 'wpslug'),
|
||||
esc_html__('Plugin Activation Error', 'wpslug'),
|
||||
array('back_link' => true)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -140,88 +142,92 @@ class WPSlug {
|
|||
$core = new WPSlug_Core();
|
||||
$core->activate();
|
||||
|
||||
add_option( 'wpslug_activation_redirect', true );
|
||||
add_option('wpslug_activation_redirect', true);
|
||||
|
||||
} catch ( Exception $e ) {
|
||||
error_log( 'WP Slug activation error: ' . $e->getMessage() );
|
||||
} catch (Exception $e) {
|
||||
error_log('WP Slug activation error: ' . $e->getMessage());
|
||||
wp_die(
|
||||
esc_html__( 'An error occurred during plugin activation. Please check your server logs.', 'wpslug' ),
|
||||
esc_html__( 'Plugin Activation Error', 'wpslug' ),
|
||||
array( 'back_link' => true )
|
||||
esc_html__('An error occurred during plugin activation. Please check your server logs.', 'wpslug'),
|
||||
esc_html__('Plugin Activation Error', 'wpslug'),
|
||||
array('back_link' => true)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function deactivate() {
|
||||
public function deactivate()
|
||||
{
|
||||
try {
|
||||
if ( $this->core ) {
|
||||
if ($this->core) {
|
||||
$this->core->deactivate();
|
||||
}
|
||||
|
||||
delete_option( 'wpslug_activation_redirect' );
|
||||
delete_option('wpslug_activation_redirect');
|
||||
|
||||
} catch ( Exception $e ) {
|
||||
error_log( 'WP Slug deactivation error: ' . $e->getMessage() );
|
||||
} catch (Exception $e) {
|
||||
error_log('WP Slug deactivation error: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static function uninstall() {
|
||||
public static function uninstall()
|
||||
{
|
||||
try {
|
||||
if ( class_exists( 'WPSlug_Settings' ) ) {
|
||||
if (class_exists("WPSlug_Settings")) {
|
||||
$settings = new WPSlug_Settings();
|
||||
$settings->uninstall();
|
||||
}
|
||||
|
||||
delete_option( 'wpslug_activation_redirect' );
|
||||
delete_option('wpslug_activation_redirect');
|
||||
|
||||
} catch ( Exception $e ) {
|
||||
error_log( 'WP Slug uninstall error: ' . $e->getMessage() );
|
||||
} catch (Exception $e) {
|
||||
error_log('WP Slug uninstall error: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function getCore() {
|
||||
public function getCore()
|
||||
{
|
||||
return $this->core;
|
||||
}
|
||||
|
||||
public function getSettings() {
|
||||
if ( $this->core ) {
|
||||
public function getSettings()
|
||||
{
|
||||
if ($this->core) {
|
||||
return $this->core->getSettings();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getConverter() {
|
||||
if ( $this->core ) {
|
||||
public function getConverter()
|
||||
{
|
||||
if ($this->core) {
|
||||
return $this->core->getConverter();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getOptimizer() {
|
||||
if ( $this->core ) {
|
||||
public function getOptimizer()
|
||||
{
|
||||
if ($this->core) {
|
||||
return $this->core->getOptimizer();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function wpslug() {
|
||||
function wpslug()
|
||||
{
|
||||
return WPSlug::getInstance();
|
||||
}
|
||||
|
||||
if ( is_admin() ) {
|
||||
add_action(
|
||||
'admin_init',
|
||||
function () {
|
||||
if ( get_option( 'wpslug_activation_redirect', false ) ) {
|
||||
delete_option( 'wpslug_activation_redirect' );
|
||||
if ( ! isset( $_GET['activate-multi'] ) ) {
|
||||
wp_safe_redirect( admin_url( 'options-general.php?page=wpslug' ) );
|
||||
if (is_admin()) {
|
||||
add_action('admin_init', function() {
|
||||
if (get_option('wpslug_activation_redirect', false)) {
|
||||
delete_option('wpslug_activation_redirect');
|
||||
if (!isset($_GET['activate-multi'])) {
|
||||
wp_safe_redirect(admin_url('options-general.php?page=wpslug'));
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
wpslug();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue