313 lines
9.7 KiB
PHP
313 lines
9.7 KiB
PHP
<?php
|
||
/**
|
||
* Plugin Name: Maple Local Fonts
|
||
* Plugin URI: https://mapleopentech.org/plugins/maple-local-fonts
|
||
* Description: Import Google Fonts to local storage and register them with WordPress Font Library for GDPR-compliant, privacy-friendly typography.
|
||
* Version: 1.0.0
|
||
* Requires at least: 6.5
|
||
* Requires PHP: 7.4
|
||
* Author: Maple Open Technologies
|
||
* Author URI: https://mapleopentech.org
|
||
* License: GPL-2.0-or-later
|
||
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
|
||
* Text Domain: maple-local-fonts
|
||
* Domain Path: /languages
|
||
*/
|
||
|
||
if (!defined('ABSPATH')) {
|
||
exit;
|
||
}
|
||
|
||
// Plugin constants
|
||
define('MLF_VERSION', '1.0.0');
|
||
define('MLF_PLUGIN_DIR', plugin_dir_path(__FILE__));
|
||
define('MLF_PLUGIN_URL', plugin_dir_url(__FILE__));
|
||
define('MLF_PLUGIN_BASENAME', plugin_basename(__FILE__));
|
||
|
||
// Limits
|
||
define('MLF_MAX_FONTS_PER_REQUEST', 10);
|
||
define('MLF_MAX_WEIGHTS_PER_FONT', 9);
|
||
define('MLF_REQUEST_TIMEOUT', 30);
|
||
define('MLF_MAX_CSS_SIZE', 512 * 1024); // 512KB max CSS response
|
||
define('MLF_MAX_FONT_FILE_SIZE', 5 * 1024 * 1024); // 5MB max font file
|
||
define('MLF_MAX_FONT_FACES', 20); // Max font faces per import (9 weights × 2 styles + buffer)
|
||
|
||
/**
|
||
* Check WordPress version on activation.
|
||
*/
|
||
function mlf_activate() {
|
||
if (version_compare(get_bloginfo('version'), '6.5', '<')) {
|
||
deactivate_plugins(plugin_basename(__FILE__));
|
||
wp_die(
|
||
esc_html__('Maple Local Fonts requires WordPress 6.5 or higher for Font Library support.', 'maple-local-fonts'),
|
||
esc_html__('Plugin Activation Error', 'maple-local-fonts'),
|
||
['back_link' => true]
|
||
);
|
||
}
|
||
|
||
// Ensure fonts directory exists
|
||
$font_dir = wp_get_font_dir();
|
||
if (!file_exists($font_dir['path'])) {
|
||
wp_mkdir_p($font_dir['path']);
|
||
}
|
||
}
|
||
register_activation_hook(__FILE__, 'mlf_activate');
|
||
|
||
/**
|
||
* Check WordPress version on admin init (in case WP was downgraded).
|
||
*/
|
||
function mlf_check_version() {
|
||
if (version_compare(get_bloginfo('version'), '6.5', '<')) {
|
||
deactivate_plugins(plugin_basename(__FILE__));
|
||
add_action('admin_notices', 'mlf_version_notice');
|
||
}
|
||
}
|
||
add_action('admin_init', 'mlf_check_version');
|
||
|
||
/**
|
||
* Display version notice.
|
||
*/
|
||
function mlf_version_notice() {
|
||
echo '<div class="error"><p>';
|
||
esc_html_e('Maple Local Fonts has been deactivated. It requires WordPress 6.5 or higher.', 'maple-local-fonts');
|
||
echo '</p></div>';
|
||
}
|
||
|
||
/**
|
||
* Declare WooCommerce HPOS compatibility.
|
||
*/
|
||
function mlf_declare_hpos_compatibility() {
|
||
if (class_exists(\Automattic\WooCommerce\Utilities\FeaturesUtil::class)) {
|
||
\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility(
|
||
'custom_order_tables',
|
||
__FILE__,
|
||
true
|
||
);
|
||
}
|
||
}
|
||
add_action('before_woocommerce_init', 'mlf_declare_hpos_compatibility');
|
||
|
||
/**
|
||
* Load plugin text domain.
|
||
*/
|
||
function mlf_load_textdomain() {
|
||
load_plugin_textdomain('maple-local-fonts', false, dirname(MLF_PLUGIN_BASENAME) . '/languages');
|
||
}
|
||
add_action('plugins_loaded', 'mlf_load_textdomain');
|
||
|
||
/**
|
||
* Autoload plugin classes.
|
||
*/
|
||
function mlf_autoload($class) {
|
||
$prefix = 'MLF_';
|
||
if (strpos($class, $prefix) !== 0) {
|
||
return;
|
||
}
|
||
|
||
$class_name = str_replace($prefix, '', $class);
|
||
$class_name = str_replace('_', '-', strtolower($class_name));
|
||
$file = MLF_PLUGIN_DIR . 'includes/class-mlf-' . $class_name . '.php';
|
||
|
||
if (file_exists($file)) {
|
||
require_once $file;
|
||
}
|
||
}
|
||
spl_autoload_register('mlf_autoload');
|
||
|
||
/**
|
||
* Initialize the plugin.
|
||
*/
|
||
function mlf_init() {
|
||
// Only load admin functionality
|
||
if (is_admin()) {
|
||
new MLF_Admin_Page();
|
||
new MLF_Ajax_Handler();
|
||
new MLF_Font_Search();
|
||
}
|
||
}
|
||
add_action('plugins_loaded', 'mlf_init', 20);
|
||
|
||
/**
|
||
* Register REST API routes.
|
||
*/
|
||
function mlf_register_rest_routes() {
|
||
$controller = new MLF_Rest_Controller();
|
||
$controller->register_routes();
|
||
}
|
||
add_action('rest_api_init', 'mlf_register_rest_routes');
|
||
|
||
/**
|
||
* Get the required capability for managing fonts.
|
||
*
|
||
* @return string The capability required to manage fonts.
|
||
*/
|
||
function mlf_get_capability() {
|
||
/**
|
||
* Filter the capability required to manage local fonts.
|
||
*
|
||
* @since 1.0.0
|
||
* @param string $capability Default capability is 'edit_theme_options'.
|
||
*/
|
||
return apply_filters('mlf_manage_fonts_capability', 'edit_theme_options');
|
||
}
|
||
|
||
/**
|
||
* Add imported fonts to the theme.json typography settings.
|
||
*
|
||
* This makes fonts appear in the Site Editor typography dropdown.
|
||
*
|
||
* @param WP_Theme_JSON_Data $theme_json The theme.json data.
|
||
* @return WP_Theme_JSON_Data Modified theme.json data.
|
||
*/
|
||
function mlf_add_fonts_to_theme_json($theme_json) {
|
||
$registry = new MLF_Font_Registry();
|
||
$fonts = $registry->get_imported_fonts();
|
||
|
||
if (empty($fonts)) {
|
||
return $theme_json;
|
||
}
|
||
|
||
$font_families = [];
|
||
|
||
foreach ($fonts as $font) {
|
||
$font_faces = [];
|
||
|
||
foreach ($font['variants'] as $variant) {
|
||
$weight = $variant['weight'];
|
||
$style = $variant['style'];
|
||
|
||
// Build filename based on our naming convention
|
||
$font_slug = sanitize_title($font['name']);
|
||
if (strpos($weight, ' ') !== false) {
|
||
// Variable font
|
||
$filename = sprintf('%s_%s_variable.woff2', $font_slug, $style);
|
||
} else {
|
||
$filename = sprintf('%s_%s_%s.woff2', $font_slug, $style, $weight);
|
||
}
|
||
|
||
$font_dir = wp_get_font_dir();
|
||
$font_url = trailingslashit($font_dir['url']) . $filename;
|
||
|
||
$font_faces[] = [
|
||
'fontFamily' => $font['name'],
|
||
'fontWeight' => $weight,
|
||
'fontStyle' => $style,
|
||
'src' => [$font_url],
|
||
];
|
||
}
|
||
|
||
$font_families[] = [
|
||
'name' => $font['name'],
|
||
'slug' => $font['slug'],
|
||
'fontFamily' => "'{$font['name']}', sans-serif",
|
||
'fontFace' => $font_faces,
|
||
];
|
||
}
|
||
|
||
$new_data = [
|
||
'version' => 2,
|
||
'settings' => [
|
||
'typography' => [
|
||
'fontFamilies' => $font_families,
|
||
],
|
||
],
|
||
];
|
||
|
||
return $theme_json->update_with($new_data);
|
||
}
|
||
add_filter('wp_theme_json_data_user', 'mlf_add_fonts_to_theme_json');
|
||
|
||
/**
|
||
* Register admin menu.
|
||
*/
|
||
function mlf_register_menu() {
|
||
add_submenu_page(
|
||
'options-general.php',
|
||
__('Maple Fonts', 'maple-local-fonts'),
|
||
__('Maple Fonts', 'maple-local-fonts'),
|
||
mlf_get_capability(),
|
||
'maple-local-fonts',
|
||
'mlf_render_admin_page'
|
||
);
|
||
}
|
||
add_action('admin_menu', 'mlf_register_menu');
|
||
|
||
/**
|
||
* Add settings link to plugin action links.
|
||
*
|
||
* @param array $links Existing plugin action links.
|
||
* @return array Modified plugin action links.
|
||
*/
|
||
function mlf_plugin_action_links($links) {
|
||
$settings_link = sprintf(
|
||
'<a href="%s">%s</a>',
|
||
esc_url(admin_url('options-general.php?page=maple-local-fonts')),
|
||
esc_html__('Settings', 'maple-local-fonts')
|
||
);
|
||
array_unshift($links, $settings_link);
|
||
return $links;
|
||
}
|
||
add_filter('plugin_action_links_' . MLF_PLUGIN_BASENAME, 'mlf_plugin_action_links');
|
||
|
||
/**
|
||
* Render admin page (delegates to MLF_Admin_Page).
|
||
*/
|
||
function mlf_render_admin_page() {
|
||
$admin_page = new MLF_Admin_Page();
|
||
$admin_page->render();
|
||
}
|
||
|
||
/**
|
||
* Enqueue admin assets.
|
||
*
|
||
* @param string $hook The current admin page hook.
|
||
*/
|
||
function mlf_enqueue_admin_assets($hook) {
|
||
if ($hook !== 'settings_page_maple-local-fonts') {
|
||
return;
|
||
}
|
||
|
||
// Use filemtime for cache-busting during development
|
||
$css_version = MLF_VERSION . '.' . filemtime(MLF_PLUGIN_DIR . 'assets/admin.css');
|
||
$js_version = MLF_VERSION . '.' . filemtime(MLF_PLUGIN_DIR . 'assets/admin.js');
|
||
|
||
wp_enqueue_style(
|
||
'mlf-admin',
|
||
MLF_PLUGIN_URL . 'assets/admin.css',
|
||
[],
|
||
$css_version
|
||
);
|
||
|
||
wp_enqueue_script(
|
||
'mlf-admin',
|
||
MLF_PLUGIN_URL . 'assets/admin.js',
|
||
['jquery'],
|
||
$js_version,
|
||
true
|
||
);
|
||
|
||
wp_localize_script('mlf-admin', 'mapleLocalFontsData', [
|
||
'ajaxUrl' => admin_url('admin-ajax.php'),
|
||
'downloadNonce' => wp_create_nonce('mlf_download_font'),
|
||
'deleteNonce' => wp_create_nonce('mlf_delete_font'),
|
||
'searchNonce' => wp_create_nonce('mlf_search_fonts'),
|
||
'checkUpdatesNonce' => wp_create_nonce('mlf_check_updates'),
|
||
'updateFontNonce' => wp_create_nonce('mlf_update_font'),
|
||
'strings' => [
|
||
'downloading' => __('Downloading...', 'maple-local-fonts'),
|
||
'deleting' => __('Deleting...', 'maple-local-fonts'),
|
||
'updating' => __('Updating...', 'maple-local-fonts'),
|
||
'checking' => __('Checking...', 'maple-local-fonts'),
|
||
'confirmDelete' => __('Are you sure you want to delete this font?', 'maple-local-fonts'),
|
||
'error' => __('An error occurred. Please try again.', 'maple-local-fonts'),
|
||
'searching' => __('Searching...', 'maple-local-fonts'),
|
||
'noResults' => __('No fonts found. Try a different search term.', 'maple-local-fonts'),
|
||
'selectFont' => __('Please select a font first.', 'maple-local-fonts'),
|
||
'previewText' => __('Maple Fonts Preview', 'maple-local-fonts'),
|
||
'minChars' => __('Please enter at least 2 characters.', 'maple-local-fonts'),
|
||
'noUpdates' => __('All fonts are up to date.', 'maple-local-fonts'),
|
||
'updatesFound' => __('Updates available for %d font(s).', 'maple-local-fonts'),
|
||
],
|
||
]);
|
||
}
|
||
add_action('admin_enqueue_scripts', 'mlf_enqueue_admin_assets');
|