PluginProbe
ActivityPub / 9.1.0
ActivityPub v9.1.0
9.3.1 9.3.0 9.2.2 9.2.1 9.2.0 9.1.0 9.0.2 9.0.1 9.0.0 8.3.0 8.2.1 8.2.0 8.1.1 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.2.0 1.3.0 2.0.0 2.0.1 2.1.0 2.1.1 All 160 releases
activitypub / includes / cache / class-emoji.php

class-emoji.php in ActivityPub 9.1.0, at includes/cache/class-emoji.php

185 lines 4.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Emoji cache class.
4 *
5 * @package Activitypub
6 */
7
8 namespace Activitypub\Cache;
9
10 /**
11 * Emoji cache class.
12 *
13 * Handles file caching of custom emoji locally.
14 * Emoji are stored in /wp-content/uploads/activitypub/emoji/{domain}/
15 * organized by source domain for easier management.
16 *
17 * This class is responsible ONLY for file operations (download, validate, store, optimize).
18 * Content transformation (replacing shortcodes with img tags) is handled by the main
19 * Activitypub\Emoji class.
20 *
21 * @since 5.6.0
22 */
23 class Emoji extends File {
24 /**
25 * Maximum dimension for emoji in pixels.
26 *
27 * @var int
28 */
29 const MAX_DIMENSION = 128;
30
31 /**
32 * Context identifier for the filter.
33 *
34 * @var string
35 */
36 const CONTEXT = 'emoji';
37
38 /**
39 * Base directory for emoji storage.
40 *
41 * @var string
42 */
43 const BASE_DIR = '/activitypub/emoji/';
44
45 /**
46 * Get the cache type identifier.
47 *
48 * @return string Cache type.
49 */
50 public static function get_type() {
51 return 'emoji';
52 }
53
54 /**
55 * Get the base directory path relative to uploads.
56 *
57 * @return string Base directory path.
58 */
59 public static function get_base_dir() {
60 return self::BASE_DIR;
61 }
62
63 /**
64 * Get the context identifier for the filter.
65 *
66 * @return string Context identifier.
67 */
68 public static function get_context() {
69 return self::CONTEXT;
70 }
71
72 /**
73 * Get the maximum dimension for emoji.
74 *
75 * @return int Maximum width/height in pixels.
76 */
77 public static function get_max_dimension() {
78 return self::MAX_DIMENSION;
79 }
80
81 /**
82 * Initialize the cache handler.
83 */
84 public static function init() {
85 if ( ! self::is_enabled() ) {
86 return;
87 }
88
89 // Hook into the universal remote media URL filter.
90 // This allows third-party CDN plugins to intercept emoji URLs.
91 \add_filter( 'activitypub_remote_media_url', array( self::class, 'maybe_cache' ), 10, 4 );
92 }
93
94 /**
95 * Maybe cache an emoji URL.
96 *
97 * Hooked to the activitypub_remote_media_url filter.
98 * Delegates to import() to preserve the activitypub_pre_import_emoji filter.
99 *
100 * @param string $url The remote URL.
101 * @param string $context The context ('avatar', 'media', 'emoji', etc.).
102 * @param string|null $entity_id The entity identifier (unused for emoji, domain extracted from URL).
103 * @param array $options Optional. Additional options like 'updated' timestamp.
104 *
105 * @return string The local URL if cached successfully, otherwise the original URL.
106 */
107 public static function maybe_cache( $url, $context, $entity_id = null, $options = array() ) {
108 if ( self::CONTEXT !== $context || empty( $url ) ) {
109 return $url;
110 }
111
112 // Delegate to import() which handles the activitypub_pre_import_emoji filter.
113 $cached_url = self::import( $url, $options['updated'] ?? null );
114
115 return $cached_url ?: $url;
116 }
117
118 /**
119 * Import a remote emoji image locally.
120 *
121 * This is a convenience method that wraps the cache functionality
122 * with staleness checking based on the updated timestamp.
123 *
124 * @param string $emoji_url The remote emoji URL.
125 * @param string|null $updated Optional. The remote emoji's updated timestamp (ISO 8601).
126 * If provided and newer than cached version, re-downloads.
127 *
128 * @return string|false The local emoji URL on success, false on failure.
129 */
130 public static function import( $emoji_url, $updated = null ) {
131 if ( empty( $emoji_url ) || ! \filter_var( $emoji_url, FILTER_VALIDATE_URL ) ) {
132 return false;
133 }
134
135 /**
136 * Filters the result of emoji import before processing.
137 *
138 * Allows short-circuiting the emoji import, useful for testing.
139 *
140 * @since 5.6.0
141 *
142 * @param string|false|null $result The import result. Return a URL string to short-circuit,
143 * false to indicate failure, or null to proceed normally.
144 * @param string $emoji_url The remote emoji URL being imported.
145 * @param string|null $updated The remote emoji's updated timestamp.
146 */
147 $pre_import = \apply_filters( 'activitypub_pre_import_emoji', null, $emoji_url, $updated );
148 if ( null !== $pre_import ) {
149 return $pre_import;
150 }
151
152 $domain = \wp_parse_url( $emoji_url, PHP_URL_HOST );
153 if ( empty( $domain ) ) {
154 return false;
155 }
156
157 $options = array( 'max_dimension' => self::MAX_DIMENSION );
158 if ( $updated ) {
159 $options['updated'] = $updated;
160 }
161
162 return self::get_or_cache( $emoji_url, $domain, $options );
163 }
164
165 /**
166 * Generate a hash for an emoji URL.
167 *
168 * Uses full URL path hash to prevent collisions between emoji with the same
169 * filename but different paths (e.g., /set1/kappa.png vs /set2/kappa.png).
170 *
171 * @param string $url The URL to hash.
172 *
173 * @return string The hash string.
174 */
175 protected static function generate_hash( $url ) {
176 $url_path = \wp_parse_url( $url, PHP_URL_PATH );
177 if ( $url_path ) {
178 return \md5( $url_path );
179 }
180
181 // Fall back to full URL hash.
182 return parent::generate_hash( $url );
183 }
184 }
185