initial commit
This commit is contained in:
parent
e468202f95
commit
423b9a25fb
24 changed files with 6670 additions and 0 deletions
|
|
@ -0,0 +1,416 @@
|
|||
<?php
|
||||
/**
|
||||
* Icon Registry class - Manages downloaded sets and provides icon search/retrieval.
|
||||
*
|
||||
* @package MapleIcons
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class MI_Icon_Registry
|
||||
*
|
||||
* Manages downloaded icon sets and provides search/retrieval functionality.
|
||||
*/
|
||||
class MI_Icon_Registry {
|
||||
|
||||
/**
|
||||
* Singleton instance.
|
||||
*
|
||||
* @var MI_Icon_Registry|null
|
||||
*/
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* Cached settings.
|
||||
*
|
||||
* @var array|null
|
||||
*/
|
||||
private $settings = null;
|
||||
|
||||
/**
|
||||
* Get singleton instance.
|
||||
*
|
||||
* @return MI_Icon_Registry
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( null === self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
private function __construct() {
|
||||
$this->load_settings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load settings from database.
|
||||
*/
|
||||
private function load_settings() {
|
||||
$this->settings = get_option(
|
||||
'maple_icons_settings',
|
||||
array(
|
||||
'active_set' => '',
|
||||
'downloaded_sets' => array(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save settings to database.
|
||||
*
|
||||
* @return bool True on success.
|
||||
*/
|
||||
private function save_settings() {
|
||||
return update_option( 'maple_icons_settings', $this->settings );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all downloaded sets.
|
||||
*
|
||||
* @return array Array of downloaded set data.
|
||||
*/
|
||||
public function get_downloaded_sets() {
|
||||
return isset( $this->settings['downloaded_sets'] ) ? $this->settings['downloaded_sets'] : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the active set slug.
|
||||
*
|
||||
* @return string|null Active set slug or null if none.
|
||||
*/
|
||||
public function get_active_set() {
|
||||
$active = isset( $this->settings['active_set'] ) ? $this->settings['active_set'] : '';
|
||||
return ! empty( $active ) ? $active : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the active icon set.
|
||||
*
|
||||
* @param string $slug Icon set slug.
|
||||
* @return bool|WP_Error True on success, error on failure.
|
||||
*/
|
||||
public function set_active( $slug ) {
|
||||
// Allow empty string to deactivate.
|
||||
if ( empty( $slug ) ) {
|
||||
$this->settings['active_set'] = '';
|
||||
return $this->save_settings();
|
||||
}
|
||||
|
||||
// Validate slug.
|
||||
if ( ! MI_Icon_Sets::is_valid_slug( $slug ) ) {
|
||||
return new WP_Error( 'invalid_set', __( 'Invalid icon set.', 'maple-icons' ) );
|
||||
}
|
||||
|
||||
// Check if downloaded.
|
||||
if ( ! $this->is_downloaded( $slug ) ) {
|
||||
return new WP_Error( 'not_downloaded', __( 'Icon set is not downloaded.', 'maple-icons' ) );
|
||||
}
|
||||
|
||||
$this->settings['active_set'] = $slug;
|
||||
return $this->save_settings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a set is downloaded.
|
||||
*
|
||||
* @param string $slug Icon set slug.
|
||||
* @return bool True if downloaded.
|
||||
*/
|
||||
public function is_downloaded( $slug ) {
|
||||
$downloaded = $this->get_downloaded_sets();
|
||||
return isset( $downloaded[ $slug ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a set as downloaded.
|
||||
*
|
||||
* @param string $slug Icon set slug.
|
||||
* @param int $icon_count Number of icons downloaded.
|
||||
* @return bool True on success.
|
||||
*/
|
||||
public function mark_downloaded( $slug, $icon_count ) {
|
||||
if ( ! MI_Icon_Sets::is_valid_slug( $slug ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$set_config = MI_Icon_Sets::get( $slug );
|
||||
|
||||
$this->settings['downloaded_sets'][ $slug ] = array(
|
||||
'version' => $set_config['version'],
|
||||
'downloaded_at' => current_time( 'mysql' ),
|
||||
'icon_count' => $icon_count,
|
||||
);
|
||||
|
||||
return $this->save_settings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a set from downloaded list.
|
||||
*
|
||||
* @param string $slug Icon set slug.
|
||||
* @return bool True on success.
|
||||
*/
|
||||
public function unmark_downloaded( $slug ) {
|
||||
if ( isset( $this->settings['downloaded_sets'][ $slug ] ) ) {
|
||||
unset( $this->settings['downloaded_sets'][ $slug ] );
|
||||
}
|
||||
|
||||
// If this was the active set, clear it.
|
||||
if ( $this->settings['active_set'] === $slug ) {
|
||||
$this->settings['active_set'] = '';
|
||||
}
|
||||
|
||||
return $this->save_settings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all icons for a set and style.
|
||||
*
|
||||
* @param string $slug Icon set slug.
|
||||
* @param string $style Style slug.
|
||||
* @return array Array of icon data.
|
||||
*/
|
||||
public function get_icons_for_set( $slug, $style ) {
|
||||
if ( ! MI_Icon_Sets::is_valid_slug( $slug ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
if ( ! MI_Icon_Sets::is_valid_style( $slug, $style ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// Load manifest.
|
||||
$manifest = MI_Icon_Sets::load_manifest( $slug );
|
||||
if ( is_wp_error( $manifest ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$icons = isset( $manifest['icons'] ) ? $manifest['icons'] : array();
|
||||
$result = array();
|
||||
|
||||
foreach ( $icons as $icon ) {
|
||||
$icon_styles = isset( $icon['styles'] ) ? $icon['styles'] : array_keys( MI_Icon_Sets::get( $slug )['styles'] );
|
||||
|
||||
// Only include if this icon has the requested style.
|
||||
if ( in_array( $style, $icon_styles, true ) ) {
|
||||
$result[] = array(
|
||||
'name' => $icon['name'],
|
||||
'tags' => isset( $icon['tags'] ) ? $icon['tags'] : array(),
|
||||
'category' => isset( $icon['category'] ) ? $icon['category'] : '',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search icons in the active set.
|
||||
*
|
||||
* @param string $query Search query.
|
||||
* @param string $style Optional style filter.
|
||||
* @param int $limit Maximum results.
|
||||
* @param int $offset Offset for pagination.
|
||||
* @return array Search results.
|
||||
*/
|
||||
public function search_icons( $query = '', $style = '', $limit = MI_SEARCH_LIMIT, $offset = 0 ) {
|
||||
$active_set = $this->get_active_set();
|
||||
if ( ! $active_set ) {
|
||||
return array(
|
||||
'icons' => array(),
|
||||
'total' => 0,
|
||||
);
|
||||
}
|
||||
|
||||
$set_config = MI_Icon_Sets::get( $active_set );
|
||||
if ( ! $set_config ) {
|
||||
return array(
|
||||
'icons' => array(),
|
||||
'total' => 0,
|
||||
);
|
||||
}
|
||||
|
||||
// Default to first style if not specified.
|
||||
if ( empty( $style ) ) {
|
||||
$style = $set_config['default_style'];
|
||||
}
|
||||
|
||||
// Validate style.
|
||||
if ( ! MI_Icon_Sets::is_valid_style( $active_set, $style ) ) {
|
||||
$style = $set_config['default_style'];
|
||||
}
|
||||
|
||||
// Load manifest.
|
||||
$manifest = MI_Icon_Sets::load_manifest( $active_set );
|
||||
if ( is_wp_error( $manifest ) ) {
|
||||
return array(
|
||||
'icons' => array(),
|
||||
'total' => 0,
|
||||
);
|
||||
}
|
||||
|
||||
$icons = isset( $manifest['icons'] ) ? $manifest['icons'] : array();
|
||||
$query = strtolower( trim( $query ) );
|
||||
$results = array();
|
||||
|
||||
foreach ( $icons as $icon ) {
|
||||
$icon_styles = isset( $icon['styles'] ) ? $icon['styles'] : array_keys( $set_config['styles'] );
|
||||
|
||||
// Skip if this icon doesn't have the requested style.
|
||||
if ( ! in_array( $style, $icon_styles, true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If no query, include all.
|
||||
if ( empty( $query ) ) {
|
||||
$results[] = $icon;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Search in name.
|
||||
if ( strpos( strtolower( $icon['name'] ), $query ) !== false ) {
|
||||
$results[] = $icon;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Search in tags.
|
||||
if ( isset( $icon['tags'] ) && is_array( $icon['tags'] ) ) {
|
||||
foreach ( $icon['tags'] as $tag ) {
|
||||
if ( strpos( strtolower( $tag ), $query ) !== false ) {
|
||||
$results[] = $icon;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$total = count( $results );
|
||||
|
||||
// Apply offset and limit.
|
||||
$results = array_slice( $results, $offset, $limit );
|
||||
|
||||
// Format results.
|
||||
$formatted = array();
|
||||
foreach ( $results as $icon ) {
|
||||
$formatted[] = array(
|
||||
'name' => $icon['name'],
|
||||
'tags' => isset( $icon['tags'] ) ? $icon['tags'] : array(),
|
||||
'category' => isset( $icon['category'] ) ? $icon['category'] : '',
|
||||
'set' => $active_set,
|
||||
'style' => $style,
|
||||
);
|
||||
}
|
||||
|
||||
return array(
|
||||
'icons' => $formatted,
|
||||
'total' => $total,
|
||||
'set' => $active_set,
|
||||
'style' => $style,
|
||||
'styles' => MI_Icon_Sets::get_style_labels( $active_set ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get SVG content for a specific icon.
|
||||
*
|
||||
* @param string $slug Icon set slug.
|
||||
* @param string $style Style slug.
|
||||
* @param string $name Icon name.
|
||||
* @return string|WP_Error SVG content or error.
|
||||
*/
|
||||
public function get_icon_svg( $slug, $style, $name ) {
|
||||
// Validate inputs.
|
||||
if ( ! MI_Icon_Sets::is_valid_slug( $slug ) ) {
|
||||
return new WP_Error( 'invalid_set', __( 'Invalid icon set.', 'maple-icons' ) );
|
||||
}
|
||||
|
||||
if ( ! MI_Icon_Sets::is_valid_style( $slug, $style ) ) {
|
||||
return new WP_Error( 'invalid_style', __( 'Invalid icon style.', 'maple-icons' ) );
|
||||
}
|
||||
|
||||
// Validate icon name.
|
||||
if ( ! preg_match( '/^[a-z0-9\-_]+$/i', $name ) ) {
|
||||
return new WP_Error( 'invalid_name', __( 'Invalid icon name.', 'maple-icons' ) );
|
||||
}
|
||||
|
||||
$local_path = MI_Icon_Sets::get_local_path( $slug, $style, $name );
|
||||
|
||||
// Validate path is within icons directory (prevent path traversal).
|
||||
$real_path = realpath( $local_path );
|
||||
$allowed_base = realpath( MI_ICONS_DIR );
|
||||
|
||||
if ( false === $real_path ) {
|
||||
return new WP_Error( 'not_found', __( 'Icon not found.', 'maple-icons' ) );
|
||||
}
|
||||
|
||||
if ( false === $allowed_base || strpos( $real_path, $allowed_base ) !== 0 ) {
|
||||
return new WP_Error( 'invalid_path', __( 'Invalid icon path.', 'maple-icons' ) );
|
||||
}
|
||||
|
||||
if ( ! file_exists( $real_path ) ) {
|
||||
return new WP_Error( 'not_found', __( 'Icon not found.', 'maple-icons' ) );
|
||||
}
|
||||
|
||||
$svg = file_get_contents( $real_path ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
|
||||
|
||||
if ( false === $svg ) {
|
||||
return new WP_Error( 'read_error', __( 'Could not read icon file.', 'maple-icons' ) );
|
||||
}
|
||||
|
||||
return $svg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a downloaded set.
|
||||
*
|
||||
* @param string $slug Icon set slug.
|
||||
* @return bool|WP_Error True on success, error on failure.
|
||||
*/
|
||||
public function delete_set( $slug ) {
|
||||
if ( ! MI_Icon_Sets::is_valid_slug( $slug ) ) {
|
||||
return new WP_Error( 'invalid_set', __( 'Invalid icon set.', 'maple-icons' ) );
|
||||
}
|
||||
|
||||
if ( ! $this->is_downloaded( $slug ) ) {
|
||||
return new WP_Error( 'not_downloaded', __( 'Icon set is not downloaded.', 'maple-icons' ) );
|
||||
}
|
||||
|
||||
// Delete files.
|
||||
$downloader = new MI_Downloader();
|
||||
$deleted = $downloader->delete_set( $slug );
|
||||
|
||||
if ( ! $deleted ) {
|
||||
return new WP_Error( 'delete_error', __( 'Could not delete icon files.', 'maple-icons' ) );
|
||||
}
|
||||
|
||||
// Update settings.
|
||||
$this->unmark_downloaded( $slug );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get info about a downloaded set.
|
||||
*
|
||||
* @param string $slug Icon set slug.
|
||||
* @return array|null Set info or null if not downloaded.
|
||||
*/
|
||||
public function get_downloaded_info( $slug ) {
|
||||
$downloaded = $this->get_downloaded_sets();
|
||||
return isset( $downloaded[ $slug ] ) ? $downloaded[ $slug ] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh settings from database.
|
||||
*/
|
||||
public function refresh() {
|
||||
$this->load_settings();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue