initial commit
This commit is contained in:
parent
e468202f95
commit
423b9a25fb
24 changed files with 6670 additions and 0 deletions
560
native/wordpress/maple-icons-wp/WORDPRESS_COMPATIBILITY.md
Normal file
560
native/wordpress/maple-icons-wp/WORDPRESS_COMPATIBILITY.md
Normal file
|
|
@ -0,0 +1,560 @@
|
|||
# WORDPRESS_COMPATIBILITY.md — WordPress & Plugin Compatibility
|
||||
|
||||
## Overview
|
||||
|
||||
This document covers compatibility requirements for WordPress core systems and popular plugins. Reference this when building the font registry class and integration points.
|
||||
|
||||
---
|
||||
|
||||
## WordPress Version Requirements
|
||||
|
||||
**Minimum: WordPress 6.5**
|
||||
|
||||
WordPress 6.5 introduced the Font Library API which this plugin depends on. Earlier versions will not work.
|
||||
|
||||
```php
|
||||
// Check on activation
|
||||
register_activation_hook(__FILE__, function() {
|
||||
if (version_compare(get_bloginfo('version'), '6.5', '<')) {
|
||||
deactivate_plugins(plugin_basename(__FILE__));
|
||||
wp_die(
|
||||
'Maple Local Fonts requires WordPress 6.5 or higher for Font Library support.',
|
||||
'Plugin Activation Error',
|
||||
['back_link' => true]
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Also check on admin init (in case WP was downgraded)
|
||||
add_action('admin_init', function() {
|
||||
if (version_compare(get_bloginfo('version'), '6.5', '<')) {
|
||||
deactivate_plugins(plugin_basename(__FILE__));
|
||||
add_action('admin_notices', function() {
|
||||
echo '<div class="error"><p>Maple Local Fonts has been deactivated. It requires WordPress 6.5 or higher.</p></div>';
|
||||
});
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## WordPress Font Library API
|
||||
|
||||
### How It Works
|
||||
|
||||
WordPress 6.5+ stores fonts using custom post types:
|
||||
|
||||
| Post Type | Purpose |
|
||||
|-----------|---------|
|
||||
| `wp_font_family` | Font family (e.g., "Open Sans") |
|
||||
| `wp_font_face` | Individual weight/style variant (child of family) |
|
||||
|
||||
Fonts are stored in `wp-content/fonts/` by default.
|
||||
|
||||
### Getting the Fonts Directory
|
||||
|
||||
```php
|
||||
// ALWAYS use this function, never hardcode paths
|
||||
$font_dir = wp_get_font_dir();
|
||||
|
||||
// Returns:
|
||||
[
|
||||
'path' => '/var/www/html/wp-content/fonts',
|
||||
'url' => 'https://example.com/wp-content/fonts',
|
||||
'subdir' => '',
|
||||
'basedir' => '/var/www/html/wp-content/fonts',
|
||||
'baseurl' => 'https://example.com/wp-content/fonts',
|
||||
]
|
||||
```
|
||||
|
||||
### Registering a Font Family
|
||||
|
||||
```php
|
||||
/**
|
||||
* Register a font family with WordPress Font Library.
|
||||
*
|
||||
* @param string $font_name Display name (e.g., "Open Sans")
|
||||
* @param string $font_slug Slug (e.g., "open-sans")
|
||||
* @param array $files Array of downloaded file data
|
||||
* @return int|WP_Error Font family post ID or error
|
||||
*/
|
||||
function mlf_register_font_family($font_name, $font_slug, $files) {
|
||||
// Check if font already exists
|
||||
$existing = get_posts([
|
||||
'post_type' => 'wp_font_family',
|
||||
'name' => $font_slug,
|
||||
'posts_per_page' => 1,
|
||||
'post_status' => 'publish',
|
||||
]);
|
||||
|
||||
if (!empty($existing)) {
|
||||
return new WP_Error('font_exists', 'Font family already installed');
|
||||
}
|
||||
|
||||
// Build font family settings
|
||||
$font_family_settings = [
|
||||
'name' => $font_name,
|
||||
'slug' => $font_slug,
|
||||
'fontFamily' => sprintf('"%s", sans-serif', $font_name),
|
||||
'fontFace' => [],
|
||||
];
|
||||
|
||||
// Add each font face
|
||||
foreach ($files as $file) {
|
||||
$font_dir = wp_get_font_dir();
|
||||
$relative_path = str_replace($font_dir['path'], '', $file['path']);
|
||||
|
||||
$font_family_settings['fontFace'][] = [
|
||||
'fontFamily' => $font_name,
|
||||
'fontWeight' => $file['weight'],
|
||||
'fontStyle' => $file['style'],
|
||||
'src' => 'file:.' . $font_dir['basedir'] . $relative_path,
|
||||
];
|
||||
}
|
||||
|
||||
// Create font family post
|
||||
$family_id = wp_insert_post([
|
||||
'post_type' => 'wp_font_family',
|
||||
'post_title' => $font_name,
|
||||
'post_name' => $font_slug,
|
||||
'post_status' => 'publish',
|
||||
'post_content' => wp_json_encode($font_family_settings),
|
||||
]);
|
||||
|
||||
if (is_wp_error($family_id)) {
|
||||
return $family_id;
|
||||
}
|
||||
|
||||
// Mark as imported by our plugin (for identification)
|
||||
update_post_meta($family_id, '_mlf_imported', '1');
|
||||
update_post_meta($family_id, '_mlf_import_date', current_time('mysql'));
|
||||
|
||||
// Create font face posts (children)
|
||||
foreach ($files as $file) {
|
||||
$font_dir = wp_get_font_dir();
|
||||
|
||||
$face_settings = [
|
||||
'fontFamily' => $font_name,
|
||||
'fontWeight' => $file['weight'],
|
||||
'fontStyle' => $file['style'],
|
||||
'src' => 'file:.' . $font_dir['baseurl'] . '/' . basename($file['path']),
|
||||
];
|
||||
|
||||
wp_insert_post([
|
||||
'post_type' => 'wp_font_face',
|
||||
'post_parent' => $family_id,
|
||||
'post_status' => 'publish',
|
||||
'post_content' => wp_json_encode($face_settings),
|
||||
]);
|
||||
}
|
||||
|
||||
// Clear font caches
|
||||
delete_transient('wp_font_library_fonts');
|
||||
|
||||
return $family_id;
|
||||
}
|
||||
```
|
||||
|
||||
### Deleting a Font Family
|
||||
|
||||
```php
|
||||
/**
|
||||
* Delete a font family and its files.
|
||||
*
|
||||
* @param int $family_id Font family post ID
|
||||
* @return bool|WP_Error True on success, error on failure
|
||||
*/
|
||||
function mlf_delete_font_family($family_id) {
|
||||
$family = get_post($family_id);
|
||||
|
||||
if (!$family || $family->post_type !== 'wp_font_family') {
|
||||
return new WP_Error('not_found', 'Font family not found');
|
||||
}
|
||||
|
||||
// Verify it's one we imported
|
||||
if (get_post_meta($family_id, '_mlf_imported', true) !== '1') {
|
||||
return new WP_Error('not_ours', 'Cannot delete fonts not imported by this plugin');
|
||||
}
|
||||
|
||||
// Get font faces
|
||||
$faces = get_children([
|
||||
'post_parent' => $family_id,
|
||||
'post_type' => 'wp_font_face',
|
||||
]);
|
||||
|
||||
$font_dir = wp_get_font_dir();
|
||||
|
||||
// Delete font face files and posts
|
||||
foreach ($faces as $face) {
|
||||
$settings = json_decode($face->post_content, true);
|
||||
|
||||
if (isset($settings['src'])) {
|
||||
// Convert file:. URL to path
|
||||
$src = $settings['src'];
|
||||
$src = str_replace('file:.', '', $src);
|
||||
|
||||
// Handle both URL and path formats
|
||||
if (strpos($src, $font_dir['baseurl']) !== false) {
|
||||
$file_path = str_replace($font_dir['baseurl'], $font_dir['path'], $src);
|
||||
} else {
|
||||
$file_path = $font_dir['path'] . '/' . basename($src);
|
||||
}
|
||||
|
||||
// Validate path before deletion
|
||||
if (mlf_validate_font_path($file_path) && file_exists($file_path)) {
|
||||
wp_delete_file($file_path);
|
||||
}
|
||||
}
|
||||
|
||||
wp_delete_post($face->ID, true);
|
||||
}
|
||||
|
||||
// Delete family post
|
||||
wp_delete_post($family_id, true);
|
||||
|
||||
// Clear caches
|
||||
delete_transient('wp_font_library_fonts');
|
||||
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
### Listing Installed Fonts
|
||||
|
||||
```php
|
||||
/**
|
||||
* Get all fonts imported by this plugin.
|
||||
*
|
||||
* @return array Array of font data
|
||||
*/
|
||||
function mlf_get_imported_fonts() {
|
||||
$fonts = get_posts([
|
||||
'post_type' => 'wp_font_family',
|
||||
'posts_per_page' => 100,
|
||||
'post_status' => 'publish',
|
||||
'meta_key' => '_mlf_imported',
|
||||
'meta_value' => '1',
|
||||
]);
|
||||
|
||||
$result = [];
|
||||
|
||||
foreach ($fonts as $font) {
|
||||
$settings = json_decode($font->post_content, true);
|
||||
|
||||
// Get variants
|
||||
$faces = get_children([
|
||||
'post_parent' => $font->ID,
|
||||
'post_type' => 'wp_font_face',
|
||||
]);
|
||||
|
||||
$variants = [];
|
||||
foreach ($faces as $face) {
|
||||
$face_settings = json_decode($face->post_content, true);
|
||||
$variants[] = [
|
||||
'weight' => $face_settings['fontWeight'] ?? '400',
|
||||
'style' => $face_settings['fontStyle'] ?? 'normal',
|
||||
];
|
||||
}
|
||||
|
||||
$result[] = [
|
||||
'id' => $font->ID,
|
||||
'name' => $settings['name'] ?? $font->post_title,
|
||||
'slug' => $settings['slug'] ?? $font->post_name,
|
||||
'variants' => $variants,
|
||||
'import_date' => get_post_meta($font->ID, '_mlf_import_date', true),
|
||||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Gutenberg FSE Integration
|
||||
|
||||
### How Fonts Appear in the Editor
|
||||
|
||||
Once registered via the Font Library API, fonts automatically appear in:
|
||||
|
||||
1. **Global Styles** → Typography → Font dropdown
|
||||
2. **Block settings** → Typography → Font dropdown (when per-block typography is enabled)
|
||||
|
||||
No additional integration code is needed — WordPress handles this automatically.
|
||||
|
||||
### Theme.json Compatibility
|
||||
|
||||
**DO NOT:**
|
||||
- Directly modify theme.json
|
||||
- Filter `wp_theme_json_data_theme` to inject fonts (let Font Library handle it)
|
||||
- Override global styles CSS directly
|
||||
|
||||
**DO:**
|
||||
- Use the Font Library API (post types)
|
||||
- Let WordPress generate CSS custom properties
|
||||
- Trust the system
|
||||
|
||||
### CSS Custom Properties
|
||||
|
||||
When a font is applied in Global Styles, WordPress generates:
|
||||
|
||||
```css
|
||||
body {
|
||||
--wp--preset--font-family--open-sans: "Open Sans", sans-serif;
|
||||
}
|
||||
```
|
||||
|
||||
And applies it:
|
||||
|
||||
```css
|
||||
body {
|
||||
font-family: var(--wp--preset--font-family--open-sans);
|
||||
}
|
||||
```
|
||||
|
||||
Our plugin doesn't need to touch this — it's automatic.
|
||||
|
||||
---
|
||||
|
||||
## WooCommerce Compatibility
|
||||
|
||||
### HPOS (High-Performance Order Storage)
|
||||
|
||||
WooCommerce's HPOS moves order data from post meta to custom tables. We must declare compatibility.
|
||||
|
||||
```php
|
||||
// Declare HPOS compatibility
|
||||
add_action('before_woocommerce_init', function() {
|
||||
if (class_exists(\Automattic\WooCommerce\Utilities\FeaturesUtil::class)) {
|
||||
\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility(
|
||||
'custom_order_tables',
|
||||
__FILE__,
|
||||
true
|
||||
);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Why We're Compatible
|
||||
|
||||
Our plugin:
|
||||
- Does NOT interact with orders at all
|
||||
- Does NOT query wp_posts for order data
|
||||
- Does NOT use wp_postmeta for order data
|
||||
- Only uses wp_font_family and wp_font_face post types
|
||||
|
||||
We're inherently compatible because we don't touch WooCommerce data.
|
||||
|
||||
### Frontend Considerations
|
||||
|
||||
**DO NOT:**
|
||||
- Override `.woocommerce` class styles
|
||||
- Override `.wc-block-*` styles
|
||||
- Target cart/checkout elements specifically
|
||||
|
||||
**DO:**
|
||||
- Let WooCommerce elements inherit from body/heading fonts
|
||||
- Let global styles cascade naturally
|
||||
|
||||
WooCommerce product titles, descriptions, and other text will naturally inherit the fonts set via Global Styles. No special handling needed.
|
||||
|
||||
---
|
||||
|
||||
## Wordfence Compatibility
|
||||
|
||||
### Potential Concerns
|
||||
|
||||
1. **Outbound requests** to Google Fonts during import
|
||||
2. **AJAX endpoints** for admin actions
|
||||
3. **File operations** in wp-content
|
||||
|
||||
### Why We're Compatible
|
||||
|
||||
**Outbound Requests:**
|
||||
- Only occur during admin import (user-initiated action)
|
||||
- Target well-known domains (fonts.googleapis.com, fonts.gstatic.com)
|
||||
- Use standard `wp_remote_get()` which Wordfence allows
|
||||
- No runtime external requests on frontend
|
||||
|
||||
**AJAX Endpoints:**
|
||||
- Use standard `admin-ajax.php` (not custom endpoints)
|
||||
- Include proper nonces
|
||||
- Follow WordPress patterns that Wordfence expects
|
||||
|
||||
**File Operations:**
|
||||
- Write only to `wp-content/fonts/` (WordPress default directory)
|
||||
- Use WordPress Filesystem API
|
||||
- Don't create executable files
|
||||
|
||||
### Testing with Wordfence
|
||||
|
||||
Test these scenarios with Wordfence active:
|
||||
|
||||
- [ ] Learning Mode: Import should succeed
|
||||
- [ ] Enabled Mode: Import should succeed
|
||||
- [ ] Rate Limiting: Admin AJAX not blocked
|
||||
- [ ] Firewall: No false positives on font download
|
||||
|
||||
---
|
||||
|
||||
## LearnDash Compatibility
|
||||
|
||||
### Overview
|
||||
|
||||
LearnDash is a WordPress LMS that uses:
|
||||
- Custom post types (courses, lessons, topics, quizzes)
|
||||
- Custom templates
|
||||
- Focus Mode (distraction-free learning)
|
||||
|
||||
### Why We're Compatible
|
||||
|
||||
Our plugin:
|
||||
- Doesn't touch LearnDash post types
|
||||
- Doesn't modify LearnDash templates
|
||||
- Doesn't inject CSS on frontend
|
||||
- Lets Global Styles cascade to LearnDash content
|
||||
|
||||
LearnDash course content, lesson text, and quiz questions will inherit the fonts set in Global Styles automatically.
|
||||
|
||||
### Focus Mode Consideration
|
||||
|
||||
LearnDash Focus Mode uses its own template. Fonts set via Global Styles will apply because:
|
||||
- Focus Mode still loads theme.json styles
|
||||
- CSS custom properties cascade to all content
|
||||
- No special handling needed
|
||||
|
||||
**DO NOT:**
|
||||
- Target `.learndash-*` classes specifically
|
||||
- Override Focus Mode styles
|
||||
- Inject custom CSS for LearnDash
|
||||
|
||||
---
|
||||
|
||||
## WPForms Compatibility
|
||||
|
||||
### Overview
|
||||
|
||||
WPForms renders forms via shortcodes and blocks. Form styling is handled by WPForms.
|
||||
|
||||
### Why We're Compatible
|
||||
|
||||
- Form labels and text inherit from body font
|
||||
- We don't override `.wpforms-*` classes
|
||||
- No JavaScript conflicts (we have no frontend JS)
|
||||
|
||||
### Consideration
|
||||
|
||||
If a user wants form text in a different font, they should use WPForms' built-in styling options or custom CSS — not expect our plugin to handle it.
|
||||
|
||||
---
|
||||
|
||||
## General Best Practices
|
||||
|
||||
### What We Hook Into
|
||||
|
||||
```php
|
||||
// Admin menu
|
||||
add_action('admin_menu', [$this, 'register_menu']);
|
||||
|
||||
// Admin assets (only on our page)
|
||||
add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_assets']);
|
||||
|
||||
// AJAX handlers
|
||||
add_action('wp_ajax_mlf_download_font', [$this, 'handle_download']);
|
||||
add_action('wp_ajax_mlf_delete_font', [$this, 'handle_delete']);
|
||||
|
||||
// WooCommerce HPOS compatibility
|
||||
add_action('before_woocommerce_init', [$this, 'declare_hpos_compatibility']);
|
||||
```
|
||||
|
||||
### What We DON'T Hook Into
|
||||
|
||||
```php
|
||||
// NO frontend hooks
|
||||
// add_action('wp_enqueue_scripts', ...); // DON'T DO THIS
|
||||
// add_action('wp_head', ...); // DON'T DO THIS
|
||||
// add_action('wp_footer', ...); // DON'T DO THIS
|
||||
|
||||
// NO theme modification hooks
|
||||
// add_filter('wp_theme_json_data_theme', ...); // Let Font Library handle it
|
||||
|
||||
// NO WooCommerce hooks
|
||||
// add_action('woocommerce_*', ...); // DON'T DO THIS
|
||||
|
||||
// NO content filters
|
||||
// add_filter('the_content', ...); // DON'T DO THIS
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Conflict Debugging
|
||||
|
||||
If a user reports a conflict, check:
|
||||
|
||||
### 1. Plugin Load Order
|
||||
|
||||
Our plugin should load with default priority. Check if another plugin is:
|
||||
- Modifying the Font Library
|
||||
- Overriding font CSS
|
||||
- Filtering theme.json
|
||||
|
||||
### 2. CSS Specificity
|
||||
|
||||
If fonts aren't applying:
|
||||
- Check browser DevTools for CSS cascade
|
||||
- Look for more specific selectors overriding global styles
|
||||
- Check for `!important` declarations
|
||||
|
||||
### 3. Cache Issues
|
||||
|
||||
Font changes not appearing:
|
||||
- Clear browser cache
|
||||
- Clear any caching plugins (WP Rocket, W3TC, etc.)
|
||||
- Clear CDN cache if applicable
|
||||
- WordPress transients: `delete_transient('wp_font_library_fonts')`
|
||||
|
||||
### 4. JavaScript Errors
|
||||
|
||||
If admin page isn't working:
|
||||
- Check browser console for JS errors
|
||||
- Look for conflicts with other admin scripts
|
||||
- Verify jQuery isn't being dequeued
|
||||
|
||||
---
|
||||
|
||||
## Compatibility Checklist
|
||||
|
||||
Before releasing:
|
||||
|
||||
### WordPress Core
|
||||
- [ ] Works on WordPress 6.5
|
||||
- [ ] Works on WordPress 6.6+
|
||||
- [ ] Font Library API integration works
|
||||
- [ ] Fonts appear in Global Styles
|
||||
- [ ] Fonts apply correctly on frontend
|
||||
|
||||
### WooCommerce
|
||||
- [ ] HPOS compatibility declared
|
||||
- [ ] No errors in WooCommerce status page
|
||||
- [ ] Product pages render correctly with custom fonts
|
||||
- [ ] Cart/Checkout not affected
|
||||
|
||||
### Wordfence
|
||||
- [ ] Import works with firewall enabled
|
||||
- [ ] No blocked requests
|
||||
- [ ] No false positive security alerts
|
||||
|
||||
### LearnDash
|
||||
- [ ] Course content inherits fonts
|
||||
- [ ] Focus Mode renders correctly
|
||||
- [ ] No JavaScript conflicts
|
||||
|
||||
### WPForms
|
||||
- [ ] Forms render correctly
|
||||
- [ ] No styling conflicts
|
||||
|
||||
### Other
|
||||
- [ ] No PHP errors in debug.log
|
||||
- [ ] No JavaScript errors in console
|
||||
- [ ] Admin page loads correctly
|
||||
- [ ] No memory issues during import
|
||||
Loading…
Add table
Add a link
Reference in a new issue