Skip to content
wiki.fftac.org

Dictionariespage - Source Excerpt 01

Back to Dictionariespage

Summary

This source excerpt preserves a bounded section of Antichrist.net/wp-content/plugins/uaix-locale-router/src/Admin/DictionariesPage.php so readers can inspect the evidence without opening the full source file.

**Source path:** Antichrist.net/wp-content/plugins/uaix-locale-router/src/Admin/DictionariesPage.php

<?php

namespace UAIXLocaleRouter\Admin;

use UAIXLocaleRouter\DictionaryRepository;
use UAIXLocaleRouter\DictionaryValidator;
use UAIXLocaleRouter\LocaleRepository;
use UAIXLocaleRouter\LocaleValidator;
use UAIXLocaleRouter\Plugin;
use UAIXLocaleRouter\TranslationCatalog;
use UAIXLocaleRouter\Support\Html;
use UAIXLocaleRouter\Support\Json;


if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

final class DictionariesPage {
	/**
	 * Render the dictionaries page.
	 *
	 * @return void
	 */
	public static function render_page() {
		$manifest       = DictionaryRepository::dictionary_manifest();
		$export_locales = self::export_locale_options();
		?>
		<div class="wrap">
			<h1>Dictionaries</h1>

			<div class="uaixlr-grid">
				<div class="uaixlr-card">
					<h2>Upload Dictionary</h2>
					<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" enctype="multipart/form-data">
						<?php wp_nonce_field( 'uaixlr_upload_dictionary' ); ?>
						<input type="hidden" name="action" value="uaixlr_upload_dictionary" />
						<p>
							<label for="uaixlr_dictionary_file">Dictionary JSON file</label><br />
							<input type="file" id="uaixlr_dictionary_file" name="dictionary_file" accept=".json,application/json" required />
						</p>
						<?php submit_button( 'Upload Dictionary', 'primary', 'submit', false ); ?>
					</form>
				</div>

				<div class="uaixlr-card">
					<h2>Template Dictionary</h2>
					<p>Download the full source-text template for the current site inventory. The file uses English source text as the key and empty values for translation.</p>
					<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
						<?php wp_nonce_field( 'uaixlr_download_template' ); ?>
						<input type="hidden" name="action" value="uaixlr_download_template" />
						<?php submit_button( 'Download Template JSON', 'secondary', 'submit', false ); ?>
					</form>
				</div>

				<div class="uaixlr-card">
					<h2>Locale Dictionary Export</h2>
					<p>Download one complete translator-facing JSON file per locale. English repeats the source text. Other locales use the same English keys with translated values.</p>
					<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
						<?php wp_nonce_field( 'uaixlr_download_dictionary' ); ?>
						<input type="hidden" name="action" value="uaixlr_download_dictionary" />
						<p>
							<label for="uaixlr_export_locale">Locale</label><br />
							<select id="uaixlr_export_locale" name="locale_tag">
								<?php foreach ( $export_locales as $value => $label ) : ?>
									<option value="<?php echo esc_attr( $value ); ?>"><?php echo esc_html( $label ); ?></option>
								<?php endforeach; ?>
							</select>
						</p>
						<?php submit_button( 'Download Locale JSON', 'secondary', 'submit', false ); ?>
					</form>
				</div>
			</div>

			<div class="uaixlr-card uaixlr-card-wide">
				<h2>Dictionary Manifest</h2>
				<?php if ( empty( $manifest ) ) : ?>
					<p>No dictionaries have been uploaded yet.</p>
				<?php else : ?>
					<table class="widefat striped">
						<thead>
							<tr>
								<th>Locale</th>
								<th>Status</th>
								<th>Quality</th>
								<th>Issues</th>
								<th>Uploaded</th>
								<th>Hash</th>
							</tr>
						</thead>
						<tbody>
							<?php foreach ( $manifest as $locale_tag => $entry ) : ?>
								<tr>
									<td><code><?php echo esc_html( $locale_tag ); ?></code></td>
									<td><?php echo wp_kses_post( Html::badge( isset( $entry['lastValidationStatus'] ) ? $entry['lastValidationStatus'] : 'draft', isset( $entry['lastValidationStatus'] ) && 'approved' === $entry['lastValidationStatus'] ? 'success' : ( isset( $entry['lastValidationStatus'] ) && 'rejected' === $entry['lastValidationStatus'] ? 'error' : 'warning' ) ) ); ?></td>
									<td><?php echo wp_kses_post( Html::badge( isset( $entry['qualityStatus'] ) ? $entry['qualityStatus'] : 'draft', 'neutral' ) ); ?></td>
									<td><?php echo esc_html( isset( $entry['issueCount'] ) ? (int) $entry['issueCount'] : 0 ); ?></td>
									<td><?php echo esc_html( isset( $entry['uploadedAt'] ) ? $entry['uploadedAt'] : '-' ); ?></td>
									<td><code><?php echo esc_html( isset( $entry['fileHash'] ) ? substr( $entry['fileHash'], 0, 12 ) : '-' ); ?></code></td>
								</tr>
								<?php if ( ! empty( $entry['lastIssues'] ) && is_array( $entry['lastIssues'] ) ) : ?>
									<tr>
										<td colspan="6"><?php echo wp_kses_post( Html::issues_list( $entry['lastIssues'] ) ); ?></td>
									</tr>
								<?php endif; ?>
							<?php endforeach; ?>
						</tbody>
					</table>
				<?php endif; ?>
			</div>
		</div>
		<?php
	}

	/**
	 * Handle secure dictionary upload.
	 *
	 * @return void
	 */
	public static function handle_upload() {
		self::assert_permissions( 'uaixlr_upload_dictionary' );

		if ( empty( $_FILES['dictionary_file']['tmp_name'] ) || empty( $_FILES['dictionary_file']['name'] ) ) {
			Plugin::set_flash_notice( 'Please choose a dictionary JSON file to upload.', 'error' );
			wp_safe_redirect( AdminPages::page_url( 'uaixlr-dictionaries' ) );
			exit;
		}

		$file_name = sanitize_file_name( wp_unslash( $_FILES['dictionary_file']['name'] ) );
		$tmp_name  = $_FILES['dictionary_file']['tmp_name'];
		$file_size = isset( $_FILES['dictionary_file']['size'] ) ? absint( $_FILES['dictionary_file']['size'] ) : 0;

		if ( ! is_uploaded_file( $tmp_name ) ) {
			Plugin::set_flash_notice( 'The uploaded file could not be verified.', 'error' );
			wp_safe_redirect( AdminPages::page_url( 'uaixlr-dictionaries' ) );
			exit;
		}

		if ( '.json' !== strtolower( substr( $file_name, -5 ) ) ) {
			Plugin::set_flash_notice( 'Only JSON dictionary files are supported.', 'error' );
			wp_safe_redirect( AdminPages::page_url( 'uaixlr-dictionaries' ) );
			exit;
		}

		if ( $file_size > DictionaryValidator::MAX_JSON_BYTES ) {
			Plugin::set_flash_notice( 'The uploaded dictionary is larger than the allowed 1 MB limit.', 'error' );
			wp_safe_redirect( AdminPages::page_url( 'uaixlr-dictionaries' ) );
			exit;
		}