This commit is contained in:
hanyixuanten
2026-08-06 18:36:37 +08:00
parent dd9bef2df9
commit 3a435b38a0
7 changed files with 552 additions and 54 deletions
+139
View File
@@ -0,0 +1,139 @@
name: Deploy to WordPress.org SVN
on:
workflow_dispatch:
inputs:
release_tag:
description: GitHub Release tag to deploy (for example, v0.1.0)
required: true
type: string
permissions:
contents: read
concurrency:
group: wordpress-svn-deploy
cancel-in-progress: false
jobs:
deploy:
name: Deploy release to WordPress.org
runs-on: ubuntu-latest
env:
PLUGIN_SLUG: oi-contest-schedule
SVN_URL: https://plugins.svn.wordpress.org/oi-contest-schedule
SVN_USERNAME: ${{ secrets.SVN_USERNAME }}
SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }}
steps:
- name: Resolve release tag and archive name
id: release
env:
MANUAL_TAG: ${{ inputs.release_tag }}
run: |
set -euo pipefail
release_tag="${MANUAL_TAG}"
if [[ ! "${release_tag}" =~ ^v?([0-9]+\.[0-9]+\.[0-9]+)$ ]]; then
printf 'Release tag must be vX.Y.Z or X.Y.Z, got: %s\n' "${release_tag}" >&2
exit 1
fi
version="${BASH_REMATCH[1]}"
archive="${PLUGIN_SLUG}-${version}.zip"
printf 'tag=%s\nversion=%s\narchive=%s\n' \
"${release_tag}" "${version}" "${archive}" >> "${GITHUB_OUTPUT}"
- name: Install deployment tools
run: sudo apt-get update && sudo apt-get install --yes rsync subversion unzip
- name: Download ZIP from GitHub Release
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ steps.release.outputs.tag }}
ARCHIVE: ${{ steps.release.outputs.archive }}
run: |
set -euo pipefail
mkdir -p release
gh release download "${RELEASE_TAG}" \
--repo "${GITHUB_REPOSITORY}" \
--pattern "${ARCHIVE}" \
--dir release
test -f "release/${ARCHIVE}"
- name: Extract and validate plugin package
env:
ARCHIVE: ${{ steps.release.outputs.archive }}
VERSION: ${{ steps.release.outputs.version }}
run: |
set -euo pipefail
mkdir -p package
unzip -q "release/${ARCHIVE}" -d package
plugin_dir="package/${PLUGIN_SLUG}"
plugin_file="${plugin_dir}/${PLUGIN_SLUG}.php"
test -f "${plugin_file}"
test -f "${plugin_dir}/readme.txt"
package_version="$(sed -nE 's/^ \* Version: *([^ ]+) *$/\1/p' "${plugin_file}")"
stable_tag="$(sed -nE 's/^Stable tag: *([^ ]+) *$/\1/p' "${plugin_dir}/readme.txt")"
if [[ "${package_version}" != "${VERSION}" || "${stable_tag}" != "${VERSION}" ]]; then
printf 'Version mismatch: release=%s, plugin=%s, stable tag=%s\n' \
"${VERSION}" "${package_version}" "${stable_tag}" >&2
exit 1
fi
- name: Check out WordPress.org SVN repository
run: |
set -euo pipefail
svn checkout "${SVN_URL}" wordpress-svn \
--non-interactive \
--no-auth-cache \
--username "${SVN_USERNAME}" \
--password "${SVN_PASSWORD}"
- name: Update trunk and create SVN tag
env:
VERSION: ${{ steps.release.outputs.version }}
run: |
set -euo pipefail
source_dir="${GITHUB_WORKSPACE}/package/${PLUGIN_SLUG}/"
trunk_dir="${GITHUB_WORKSPACE}/wordpress-svn/trunk"
tag_dir="${GITHUB_WORKSPACE}/wordpress-svn/tags/${VERSION}"
if svn info "${SVN_URL}/tags/${VERSION}" \
--non-interactive \
--no-auth-cache \
--username "${SVN_USERNAME}" \
--password "${SVN_PASSWORD}" >/dev/null 2>&1; then
printf 'SVN tag %s already exists; refusing to overwrite it.\n' "${VERSION}" >&2
exit 1
fi
rsync -a --delete --exclude='.svn' "${source_dir}" "${trunk_dir}/"
svn add --force "${trunk_dir}" --parents
svn status "${trunk_dir}" | while IFS= read -r status_line; do
if [[ "${status_line:0:1}" == '!' ]]; then
svn rm --force "${status_line:8}"
fi
done
svn copy "${trunk_dir}" "${tag_dir}"
- name: Commit release to WordPress.org SVN
env:
VERSION: ${{ steps.release.outputs.version }}
working-directory: wordpress-svn
run: |
set -euo pipefail
svn status
svn commit . \
--message "Release ${VERSION}" \
--non-interactive \
--no-auth-cache \
--username "${SVN_USERNAME}" \
--password "${SVN_PASSWORD}"
+63
View File
@@ -0,0 +1,63 @@
name: Package Plugin
on:
push:
tags:
- 'v*'
pull_request:
branches:
- main
workflow_dispatch:
permissions:
actions: write
contents: write
jobs:
package:
name: Package plugin
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Install gettext
run: sudo apt-get update && sudo apt-get install --yes gettext
- name: Build installable plugin archive
id: package
run: |
set -eu
version="$(grep -E '^ \* Version:' oi-contest-schedule.php | sed -E 's/^ \* Version: *//')"
archive_name="oi-contest-schedule-${version}.zip"
if [ "${GITHUB_REF_TYPE}" = "tag" ] && [ "${GITHUB_REF_NAME}" != "v${version}" ]; then
printf 'Tag %s does not match plugin version %s.\n' "${GITHUB_REF_NAME}" "${version}" >&2
exit 1
fi
./build.sh build
printf 'archive=%s\n' "${archive_name}" >> "$GITHUB_OUTPUT"
- name: Upload plugin archive
uses: actions/upload-artifact@v4
with:
name: oi-contest-schedule-plugin
path: ${{ steps.package.outputs.archive }}
if-no-files-found: error
retention-days: 30
- name: Publish plugin ZIP to GitHub Release
if: startsWith( github.ref, 'refs/tags/v' )
uses: softprops/action-gh-release@v2
with:
files: ${{ steps.package.outputs.archive }}
- name: Deploy GitHub Release to WordPress.org SVN
if: startsWith( github.ref, 'refs/tags/v' )
env:
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
GH_TOKEN: ${{ github.token }}
run: |
gh workflow run deploy-wordpress-svn.yml \
--ref "${DEFAULT_BRANCH}" \
--field release_tag="${GITHUB_REF_NAME}"
+45
View File
@@ -0,0 +1,45 @@
# Repository Instructions
## Scope and source layout
- These instructions apply to the whole repository.
- Treat root PHP files, `includes/`, `assets/`, `languages/`, and `readme.txt` as source. Never edit `build/` or release ZIP files directly; `./build.sh build` recreates them.
- Read [README.md](README.md) for installation, shortcode, feed, cache, build, and layout details. Keep [README_zh.md](README_zh.md) synchronized when changing shared documentation.
- The plugin supports WordPress 6.4+ and PHP 7.4+. Do not introduce syntax requiring a newer PHP version.
- Preserve the existing WordPress naming and security conventions: `OICS_` classes/constants, `oics_` hooks/handles/keys/functions, direct-access guards, safe remote requests, sanitization, and context-appropriate escaping.
## Architecture
- `oi-contest-schedule.php` owns plugin metadata, constants, loading, and startup hooks.
- `includes/class-plugin.php` owns WordPress integration, widgets, shortcode registration, and asset enqueueing.
- `includes/class-contest-client.php` owns remote fetching, transient caching, validation, normalization, and sorting.
- `includes/class-schedule-renderer.php` owns escaped server-rendered markup.
- `assets/js/schedule.js` formats Unix timestamps in the visitor's locale/time zone and updates countdowns; `assets/css/schedule.css` owns presentation.
- Keep the transient key synchronized between the contest client and `uninstall.php`.
## Localization and text changes
- Whenever modifying a file that contains text, explicitly check whether the change adds, removes, or changes user-facing/translatable strings and whether `languages/oi-contest-schedule.pot` and `languages/oi-contest-schedule-zh_CN.po` must also change.
- PHP gettext calls use the `oi-contest-schedule` text domain. JavaScript-facing translated strings are localized from PHP.
- Treat POT and PO files as editable localization sources. MO files are generated by the build and must not be committed from the source tree.
- After PO changes, run `./build.sh build`; it validates gettext syntax and format placeholders with `msgfmt`.
## Validation
- Run `./build.sh build` after code, metadata, readme, asset, or translation changes. This is the repository's packaging and automated validation command.
- Use `./build.sh clean` only to remove generated `build/` and ZIP artifacts.
- There is no separate test, lint, Composer, or npm command. Do not invent one; add focused manual checks when behavior cannot be covered by the build.
## Release requests
When the user's message is exactly `release X.Y.Z` or `release vX.Y.Z` (allowing surrounding whitespace), perform the release preparation workflow below. Normalize the requested version to `X.Y.Z` and the tag to `vX.Y.Z`.
1. Confirm the version is valid SemVer and inspect the worktree before editing. Do not overwrite unrelated user changes.
2. Find the previous release tag from Git history, preferring the nearest reachable version tag: `git describe --tags --abbrev=0 --match 'v*'`. If none exists, state that the release notes cover the full repository history.
3. Search tracked source files for the current version and release metadata. Update every applicable version occurrence, including the plugin header and `OICS_VERSION` in `oi-contest-schedule.php`, `Stable tag` plus changelog/upgrade-notice headings in `readme.txt`, and `Project-Id-Version` in both PO and POT files. Review other search results rather than assuming this list is exhaustive. Do not edit generated copies under `build/`.
4. Review `git diff <previous-tag>..HEAD` and the commit log over the same range. Derive release notes only from that evidence; group user-visible changes, fixes, compatibility changes, and developer-facing changes as appropriate. Do not include uncommitted release-preparation edits as previously completed work unless clearly labeled.
5. Add concise release entries to the repository changelog/upgrade notice where appropriate, keeping WordPress.org formatting valid. Check whether English and Chinese documentation or translations need corresponding updates.
6. Run `./build.sh build` and report any validation failures. Confirm the produced ZIP version and the requested tag agree with the plugin header and `Stable tag`.
7. Return release notes in both English and Simplified Chinese. The notes must be Markdown wrapped in a fenced code block, with clear `English` and `简体中文` sections. Include the version and comparison range (`<previous-tag>...vX.Y.Z`, or full history when no tag exists). Do not create a commit, tag, GitHub Release, or push unless the user explicitly requests it.
The release tag must equal `v<plugin-header-version>` because the packaging workflow enforces that relationship. WordPress.org version tags are immutable; never attempt to overwrite an existing release tag.
+78 -14
View File
@@ -1,41 +1,105 @@
# OI Contest Schedule WordPress Plugin
# OI Contest Schedule
This subproject packages the OI contest feed as a WordPress plugin. It follows the repository layout and release pattern used by `wp-translate`: the installable plugin lives in its slug directory, while `build.sh` creates a versioned ZIP archive under `build/`.
[简体中文](README_zh.md)
OI Contest Schedule is a lightweight WordPress plugin that displays upcoming competitive programming contests in the WordPress dashboard or on any post or page.
Contest data is provided by [OI-contest-fetch](https://github.com/hanyixuanten/OI-contest-fetch) and displayed in each visitor's device time zone.
## Features
- Adds an **Upcoming OI Contests** widget to the WordPress dashboard.
- Provides the `[oi_contest_schedule]` shortcode for posts and pages.
- Formats timestamps in the visitor's browser time zone.
- Shows live countdowns and distinguishes currently running contests.
- Caches the public GitHub JSON feed with a five-minute WordPress transient.
- Shows contest platform, title, start and end times, and a live countdown.
- Identifies contests that are currently running.
- Supports responsive and compact layouts.
- Caches the remote contest feed for five minutes with the WordPress Transients API.
- Includes English and Simplified Chinese translations.
## Requirements
- WordPress 6.4 or newer
- PHP 7.4 or newer
- WordPress 6.4 or later
- PHP 7.4 or later
- Outbound HTTPS access to `raw.githubusercontent.com`
## Usage
## Installation
Activate the plugin and add a Shortcode block containing:
### Install a release package
1. Download the latest plugin ZIP from the repository's [Releases](https://github.com/hanyixuanten/oi-contest-schedule/releases) page.
2. In WordPress, go to **Plugins > Add New Plugin > Upload Plugin**.
3. Select the ZIP file, then install and activate the plugin.
### Install from source
1. Copy or clone this repository to `wp-content/plugins/oi-contest-schedule`.
2. Activate **OI Contest Schedule** from the WordPress **Plugins** page.
After activation, the dashboard widget appears automatically on **Dashboard > Home**.
## Shortcode
Add a Shortcode block to a post or page and use:
```text
[oi_contest_schedule]
```
The shortcode accepts `limit` from 1 to 50 and a compact layout flag:
The shortcode accepts the following attributes:
| Attribute | Default | Description |
| --- | --- | --- |
| `limit` | `10` | Number of contests to show. Values are constrained to the range 1-50. |
| `compact` | `false` | Set to `true` to use the compact layout. |
Example:
```text
[oi_contest_schedule limit="20" compact="true"]
```
## Build
## Data And Caching
Run from this directory:
The plugin retrieves contest data from the public JSON feed maintained by [OI-contest-fetch](https://github.com/hanyixuanten/OI-contest-fetch). Only running and upcoming contests with valid links are shown, ordered by start time.
The normalized response is cached for five minutes. Uninstalling the plugin removes this cached transient. Site developers can replace the feed URL with the `oics_contest_data_url` filter:
```php
add_filter(
'oics_contest_data_url',
static function () {
return 'https://example.com/contests.json';
}
);
```
The replacement endpoint must return the same JSON structure as the default feed.
## Development
The plugin has no Composer or npm dependencies. To build an installable archive, ensure `grep`, `sed`, GNU gettext's `msgfmt`, `zip`, and `unzip` are available, then run:
```bash
chmod +x build.sh
./build.sh build
```
The archive is written to `build/oi-contest-schedule-<version>.zip`.
The command validates translations and package contents, then creates `oi-contest-schedule-<version>.zip` in the repository root. To remove local build artifacts, run:
```bash
./build.sh clean
```
## Project Structure
```text
oi-contest-schedule.php Plugin bootstrap and metadata
includes/ Data client, renderer, and plugin integration
assets/ Frontend styles and countdown script
languages/ Translation template and Chinese translation
uninstall.php Cache cleanup on uninstall
build.sh Release package builder
```
## License
This project is licensed under the [GNU General Public License v3.0](LICENSE).
+80 -16
View File
@@ -1,41 +1,105 @@
# OI Contest Schedule WordPress 插件
# OI Contest Schedule
该子项目将 OI 赛程数据封装为 WordPress 插件。目录和发布方式参考 `wp-translate`:可安装插件位于同名 slug 目录中,`build.sh``build/` 下生成带版本号的 ZIP 包。
[English](README.md)
## 功能
OI Contest Schedule 是一款轻量级 WordPress 插件,可在 WordPress 仪表盘、文章或页面中展示即将开始的程序设计竞赛。
- 在 WordPress 仪表盘添加“即将到来的 OI 赛事”小组件
比赛数据由 [OI-contest-fetch](https://github.com/hanyixuanten/OI-contest-fetch) 提供,时间会根据访客设备的时区显示
## 功能特性
- 在 WordPress 仪表盘中添加“即将到来的 OI 赛事”小组件。
- 提供 `[oi_contest_schedule]` 短代码,可插入文章或页面。
- 根据访客浏览器时区显示比赛时间
- 显示实时倒计时,并标记正在进行的比赛。
- 使用 WordPress transient 将 GitHub 上的公开 JSON 数据缓存五分钟
- 展示比赛平台、标题、开始和结束时间,以及实时倒计时
- 自动标记正在进行的比赛。
- 支持响应式布局和紧凑布局
- 使用 WordPress Transients API 缓存远程比赛数据五分钟。
- 内置英文和简体中文翻译。
## 环境要求
- WordPress 6.4 或更版本
- PHP 7.4 或更版本
- WordPress 6.4 或更版本
- PHP 7.4 或更版本
- 服务器能够通过 HTTPS 访问 `raw.githubusercontent.com`
## 使用方式
## 安装
启用插件后,在页面中添加“短代码”区块并输入:
### 安装发行包
1. 从仓库的 [Releases](https://github.com/hanyixuanten/oi-contest-schedule/releases) 页面下载最新的插件 ZIP 包。
2. 在 WordPress 后台进入 **插件 > 安装插件 > 上传插件**
3. 选择 ZIP 文件,然后安装并启用插件。
### 从源码安装
1. 将本仓库复制或克隆到 `wp-content/plugins/oi-contest-schedule`
2. 在 WordPress 后台的 **插件** 页面启用 **OI Contest Schedule**
启用后,赛事小组件会自动显示在 **仪表盘 > 首页** 中。
## 短代码
在文章或页面中添加“短代码”区块,然后输入:
```text
[oi_contest_schedule]
```
`limit` 可设置 1 到 50 条比赛,`compact` 可启用紧凑布局
短代码支持以下属性
| 属性 | 默认值 | 说明 |
| --- | --- | --- |
| `limit` | `10` | 显示的比赛数量,取值范围限制为 1-50。 |
| `compact` | `false` | 设置为 `true` 时使用紧凑布局。 |
示例:
```text
[oi_contest_schedule limit="20" compact="true"]
```
## 构建
## 数据与缓存
在本目录运行:
插件从 [OI-contest-fetch](https://github.com/hanyixuanten/OI-contest-fetch) 维护的公开 JSON 数据源获取比赛信息,仅展示链接有效且正在进行或尚未开始的比赛,并按照开始时间排序。
规范化后的数据会缓存五分钟,卸载插件时会删除该缓存。站点开发者可以通过 `oics_contest_data_url` 过滤器替换数据源地址:
```php
add_filter(
'oics_contest_data_url',
static function () {
return 'https://example.com/contests.json';
}
);
```
替换后的接口需要返回与默认数据源相同的 JSON 结构。
## 开发与构建
本插件不依赖 Composer 或 npm。构建安装包前,请确保系统中已安装 `grep``sed`、GNU gettext 的 `msgfmt``zip``unzip`,然后运行:
```bash
chmod +x build.sh
./build.sh build
```
安装包将生成 `build/oi-contest-schedule-<版本>.zip`
该命令会检查翻译和安装包内容,并在仓库根目录生成 `oi-contest-schedule-<版本>.zip`清理本地构建产物可运行:
```bash
./build.sh clean
```
## 项目结构
```text
oi-contest-schedule.php 插件入口与元数据
includes/ 数据客户端、渲染器和插件集成
assets/ 前端样式与倒计时脚本
languages/ 翻译模板与中文翻译
uninstall.php 卸载时清理缓存
build.sh 发布包构建脚本
```
## 许可证
本项目基于 [GNU General Public License v3.0](LICENSE) 发布。
Executable
+81
View File
@@ -0,0 +1,81 @@
#!/usr/bin/env sh
set -eu
plugin_slug="oi-contest-schedule"
plugin_file="${plugin_slug}.php"
script_dir="$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd)"
build_dir="${script_dir}/build"
plugin_dir="${build_dir}/${plugin_slug}"
clean() {
find "${script_dir}" -maxdepth 1 -type f -name "${plugin_slug}-*.zip" -delete
rm -rf "${build_dir}"
}
build() {
for command in grep sed msgfmt zip unzip; do
if ! command -v "${command}" >/dev/null 2>&1; then
printf 'Required command not found: %s\n' "${command}" >&2
exit 1
fi
done
version="$(grep -E '^ \* Version:' "${script_dir}/${plugin_file}" | sed -E 's/^ \* Version: *//')"
archive_name="${plugin_slug}-${version}.zip"
archive_path="${script_dir}/${archive_name}"
clean
mkdir -p "${plugin_dir}"
cp "${script_dir}/${plugin_file}" \
"${script_dir}/uninstall.php" \
"${script_dir}/readme.txt" \
"${script_dir}/LICENSE" \
"${plugin_dir}/"
cp -R "${script_dir}/includes" "${plugin_dir}/"
cp -R "${script_dir}/assets" "${plugin_dir}/"
mkdir -p "${plugin_dir}/languages"
cp "${script_dir}/languages/${plugin_slug}.pot" \
"${script_dir}/languages/${plugin_slug}-zh_CN.po" \
"${plugin_dir}/languages/"
msgfmt --check --check-format \
--output-file="${plugin_dir}/languages/${plugin_slug}-zh_CN.mo" \
"${script_dir}/languages/${plugin_slug}-zh_CN.po"
(
cd "${build_dir}"
zip -qr "${archive_path}" "${plugin_slug}"
)
unzip -p "${archive_path}" "${plugin_slug}/${plugin_file}" | grep -q '^ \* Plugin Name: OI Contest Schedule$'
unzip -p "${archive_path}" "${plugin_slug}/readme.txt" | grep -q "^Stable tag: ${version}$"
unzip -Z1 "${archive_path}" | grep -q "^${plugin_slug}/languages/${plugin_slug}-zh_CN.mo$"
if unzip -Z1 "${archive_path}" | grep -q '/wp-plugins-.*\.po$'; then
printf 'Unexpected GlotPress submission file found in archive.\n' >&2
exit 1
fi
unzip -Z1 "${archive_path}" | grep -q "^${plugin_slug}/includes/class-contest-client.php$"
unzip -Z1 "${archive_path}" | grep -q "^${plugin_slug}/includes/class-plugin.php$"
unzip -Z1 "${archive_path}" | grep -q "^${plugin_slug}/includes/class-schedule-renderer.php$"
unzip -Z1 "${archive_path}" | grep -q "^${plugin_slug}/assets/css/schedule.css$"
unzip -Z1 "${archive_path}" | grep -q "^${plugin_slug}/assets/js/schedule.js$"
if unzip -Z1 "${archive_path}" | grep -Eq '/README(\.zh-CN)?\.md$'; then
printf 'Unexpected development README found in archive.\n' >&2
exit 1
fi
printf 'Built %s\n' "${archive_path}"
}
case "${1:-build}" in
build)
build
;;
clean)
clean
printf 'Removed local build artifacts.\n'
;;
*)
printf 'Usage: %s [build|clean]\n' "$0" >&2
exit 2
;;
esac
+66 -24
View File
@@ -1,5 +1,6 @@
=== OI Contest Schedule ===
Contributors: hanyixuanten
Tags: contest, schedule, shortcode, dashboard, competitive programming
Requires at least: 6.4
Tested up to: 6.9
Requires PHP: 7.4
@@ -7,51 +8,92 @@ Stable tag: 0.1.0
License: GPL-3.0-only
License URI: https://www.gnu.org/licenses/gpl-3.0.html
Display upcoming OI contests in the WordPress dashboard or on a page.
Display upcoming OI and competitive programming contests in the WordPress dashboard or on any page with a shortcode.
== Description ==
OI Contest Schedule reads the public contest feed generated by OI-contest-fetch and displays active contests with local times and live countdowns.
OI Contest Schedule displays upcoming competitive programming contests with local times and live countdowns.
The plugin adds an Upcoming OI Contests widget to the WordPress dashboard. Use the `[oi_contest_schedule]` shortcode in a post, page, or Shortcode block to show the schedule on the front end.
The plugin adds an **Upcoming OI Contests** widget to the WordPress dashboard. To display the schedule on the front end, add the `[oi_contest_schedule]` shortcode to a post, page, or Shortcode block.
Shortcode options:
Features include:
* `[oi_contest_schedule limit="10"]` controls the number of contests, from 1 to 50.
* `[oi_contest_schedule compact="true"]` uses the compact dashboard-style layout.
* Upcoming and currently running contests ordered by start time.
* Contest platform, title, start time, end time, and live countdown.
* Dates formatted in each visitor's device time zone.
* Responsive and compact layouts.
* A five-minute cache using the WordPress Transients API.
* English and Simplified Chinese translations.
= Shortcode options =
The default shortcode displays up to 10 contests:
`[oi_contest_schedule]`
Use `limit` to display between 1 and 50 contests:
`[oi_contest_schedule limit="20"]`
Set `compact` to `true` to use the compact layout:
`[oi_contest_schedule limit="20" compact="true"]`
== Installation ==
1. Upload the `oi-contest-schedule` directory to `/wp-content/plugins/` or install the generated ZIP archive.
2. Activate OI Contest Schedule through the Plugins screen.
3. Open the WordPress dashboard to see the widget, or insert `[oi_contest_schedule]` into a page.
== External Services ==
This plugin requests the public `contests_all.json` file from GitHub Raw Content when its five-minute WordPress transient cache expires. The request is required to obtain the latest contest schedule. No visitor content, account data, or cookies are sent by the plugin. Standard request metadata, such as the website server IP address and the plugin user-agent string, is visible to GitHub.
* Data source: https://raw.githubusercontent.com/hanyixuanten/OI-contest-fetch/master/contests_all.json
* GitHub Terms of Service: https://docs.github.com/en/site-policy/github-terms/github-terms-of-service
* GitHub Privacy Statement: https://docs.github.com/en/site-policy/privacy-policies/github-general-privacy-statement
1. Upload the `oi-contest-schedule` directory to `/wp-content/plugins/`, or install the plugin ZIP through the WordPress Plugins screen.
2. Activate **OI Contest Schedule** through the Plugins screen.
3. Open the WordPress dashboard to see the contest widget.
4. To show the schedule on the front end, insert `[oi_contest_schedule]` into a post or page.
== Frequently Asked Questions ==
= Which contests are shown? =
The plugin shows contests whose end time is still in the future, including contests currently running. Finished contests are omitted.
The plugin shows contests whose end time is still in the future, including contests that are currently running. Finished contests and entries without a valid URL are omitted.
= Which time zone is used? =
Dates are formatted in the visitor's browser time zone using the browser internationalization API.
Dates are formatted in the visitor's device time zone using the browser's internationalization API.
= Can I use another data URL? =
= How often is the contest data refreshed? =
Developers can replace the URL with the `oics_contest_data_url` filter.
The remote response is cached by WordPress for five minutes. After the cache expires, the next schedule request retrieves fresh data.
= What happens if the data source is unavailable? =
The schedule displays a temporary-unavailable notice instead of contest entries. WordPress pages and the dashboard continue to work normally.
= Can I use another data source? =
Developers can replace the feed URL with the `oics_contest_data_url` filter. The replacement endpoint must return the same JSON structure as the default feed.
== External Services ==
This plugin connects to GitHub Raw Content to retrieve the public `contests_all.json` feed maintained by the OI-contest-fetch project. This connection is required to obtain current contest information and occurs when the five-minute WordPress transient cache has expired.
The plugin does not send visitor content, WordPress account data, or cookies. The request includes a user-agent containing the plugin version and the site's home URL. Standard network metadata, including the server IP address, is visible to GitHub when the request is made.
Service and data source:
* Contest feed: https://raw.githubusercontent.com/hanyixuanten/OI-contest-fetch/master/contests_all.json
* OI-contest-fetch project: https://github.com/hanyixuanten/OI-contest-fetch
* GitHub Terms of Service: https://docs.github.com/en/site-policy/github-terms/github-terms-of-service
* GitHub General Privacy Statement: https://docs.github.com/en/site-policy/privacy-policies/github-general-privacy-statement
== Changelog ==
= 0.1.0 =
* Add the upcoming contest dashboard widget.
* Add the `[oi_contest_schedule]` shortcode with limit and compact options.
* Add local-time formatting, countdowns, platform colors, and transient caching.
* Added the upcoming contest dashboard widget.
* Added the `[oi_contest_schedule]` shortcode with `limit` and `compact` options.
* Added visitor-local time formatting and live countdowns.
* Added platform styling and responsive layouts.
* Added five-minute transient caching for the contest feed.
* Added English and Simplified Chinese translations.
== Upgrade Notice ==
= 0.1.0 =
Initial release.