Action Hooks
*
* Fires the following WordPress action hooks:
*
* - `font_awesome_preferences`
*
* Fired when the plugin is ready for clients to register their preferences.
*
* Client plugins and themes should normally use this action hook to call {@see FontAwesome::register()}
* with their preferences.
*
* The hook passes no parameters to its callbacks.
*
* - `font_awesome_enqueued`
*
* Called when a version of Font Awesome has been successfully prepared for enqueuing.
*
* Clients should register a callback on this action to be notified when it is valid to query the FontAwesome
* plugin's metadata using methods such as:
* - {@see FontAwesome::version()} to discover the version of Font Awesome being loaded
* - {@see FontAwesome::pro()} to discover whether a version with Pro icons is being loaded
* - {@see FontAwesome::pseudo_elements()} to discover whether Font Awesome is being loaded with support for svg pseudo-elements
*
*
Internal Use vs. Public API
*
* Developers should take care to notice which functions, methods, classes,
* constants, defines, REST routes, or data structures are indicated as part of
* this plugin's public API and which are not.
*
* A method, for example, being declared in PHP with `public` visibility does not
* indicate its inclusion in the plugin's _public API_.
*
* A method may be declared with public visibility in PHP in order to satisfy
* the language's requirements for access across code modules, or for callbacks.
* Yet this does not mean it can be relied upon as a stable interface by client
* code.
*
* A method that is part of _this plugin's public API_ can be relied upon to
* change, or not change, according to [semantic versioning best practices](https://semver.org/).
* No such conventions apply to a method that is for internal use only, even
* if it is declared `public` in PHP.
*
* Generally, public API members are accessed only from this `FontAwesome` class.
*
* For example, the {@see FontAwesome::releases_refreshed_at()} method provides a way
* to find out when releases metadata were last fetched from `api.fontawesome.com`.
* It delegates to another class internally. But that other class and its methods
* are not part of this plugin's public API. They may change significantly from
* one patch release to another, but no breaking changes would be made to
* {@see FontAwesome::releases_refreshed_at()} without a major version change.
*
* References to "API" in this section refer to this plugin's PHP code or REST
* routes, not to the Font Awesome GraphQL API at `api.fontawesome.com`.
*
* @since 4.0.0
*/
class FontAwesome {
/**
* Name of this plugin's shortcode tag.
*
* @since 4.0.0
*/
public const SHORTCODE_TAG = 'icon';
/**
* Default style prefix.
*
* @since 4.0.0
*/
public const DEFAULT_PREFIX = 'fas';
/**
* Key where this plugin's saved options data are stored in the WordPress options table.
*
* Internal use only, not part of this plugin's public API.
*
* @ignore
* @internal
*/
public const OPTIONS_KEY = 'font-awesome';
/**
* Key where this plugin stores conflict detection data in the WordPress options table.
*
* Internal use only, not part of this plugin's public API.
*
* @internal
* @ignore
*/
public const CONFLICT_DETECTION_OPTIONS_KEY = 'font-awesome-conflict-detection';
/**
* The unique WordPress plugin slug for this plugin.
*
* @since 4.0.0
*/
public const PLUGIN_NAME = 'font-awesome';
/**
* The version of this WordPress plugin.
*
* @since 4.0.0
*/
public const PLUGIN_VERSION = '5.1.4';
/**
* The namespace for this plugin's REST API.
*
* @internal
* @deprecated
* @ignore
*/
public const REST_API_NAMESPACE = self::PLUGIN_NAME . '/v1';
/**
* The name of this plugin's options page, or WordPress admin dashboard page.
*
* @since 4.0.0
*/
public const OPTIONS_PAGE = 'font-awesome';
/**
* GET param used for linking to a particular starting tab in the admin UI.
*
* @ignore
* @internal
*/
public const ADMIN_TAB_QUERY_VAR = 'tab';
/**
* The handle used when enqueuing this plugin's resulting resource.
* Used when this plugin calls either `wp_enqueue_script` or `wp_enqueue_style` to enqueue Font Awesome assets.
*
* @since 4.0.0
*/
public const RESOURCE_HANDLE = 'font-awesome-official';
/**
* The handle used when enqueuing the v4shim.
*
* @since 4.0.0
*/
public const RESOURCE_HANDLE_V4SHIM = 'font-awesome-official-v4shim';
/**
* The handle used when enqueuing the conflict detector.
*
* @ignore
* @internal
*/
public const RESOURCE_HANDLE_CONFLICT_DETECTOR = 'font-awesome-official-conflict-detector';
/**
* The handle used when enqueuing the icon chooser.
*
* @ignore
* @internal
*/
public const RESOURCE_HANDLE_ICON_CHOOSER = 'font-awesome-official-icon-chooser';
/**
* The handle used when enqueuing the bundle for supporting the Classic Editor.
*
* @ignore
* @internal
*/
public const RESOURCE_HANDLE_CLASSIC_EDITOR = 'font-awesome-official-classic-editor';
/**
* The handle used when enqueuing block editor assets.
*
* @ignore
* @internal
*/
public const RESOURCE_HANDLE_FA_BLOCKS = 'font-awesome-official-blocks';
/**
* The source URL for the conflict detector, a feature introduced in Font Awesome 5.10.0.
*
* @ignore
* @internal
*/
public const CONFLICT_DETECTOR_SOURCE = 'https://use.fontawesome.com/releases/v6.6.0/js/conflict-detection.js';
/**
* The custom data attribute added to script, link, and style elements enqueued
* by this plugin when conflict detection is enabled, in order for them to be
* ignored by the conflict detector.
*
* @internal
* @ignore
*/
public const CONFLICT_DETECTION_IGNORE_ATTR = 'data-fa-detection-ignore';
/**
* The base name of the handle used for enqueuing this plugin's admin assets, those required for running
* the admin settings page.
*
* @ignore
* @internal
*/
public const ADMIN_RESOURCE_HANDLE = self::RESOURCE_HANDLE . '-admin';
/**
* Name used for inline data attached to the JavaScript admin bundle.
* Not part of this plugin's public API.
*
* @internal
* @ignore
*/
public const ADMIN_RESOURCE_LOCALIZATION_NAME = '__FontAwesomeOfficialPlugin__';
/**
* Refresh the ReleaseProvider automatically no more often than this
* number of seconds.
*
* Internal use only. Not part of this plugin's public API.
*
* @ignore
* @internal
*/
public const RELEASES_REFRESH_INTERVAL = 10 * 60;
/**
* We will not use a default for version, since we want the version stored in the options
* to always be resolved to an actual version number, which requires that the release
* provider successfully runs at least once. We'll do that upon plugin activation.
*
* @ignore
* @internal
*/
public const DEFAULT_USER_OPTIONS = array(
'usePro' => false,
'compat' => true,
'technology' => 'webfont',
'pseudoElements' => true,
'kitToken' => null,
// whether the token is present, not the token's value.
'apiToken' => false,
'dataVersion' => 4,
);
/**
* Default conflict detection options.
*
* @ignore
* @internal
*/
public const DEFAULT_CONFLICT_DETECTION_OPTIONS = array(
'detectConflictsUntil' => 0,
'unregisteredClients' => array(),
);
/**
* @internal
* @ignore
*/
protected static $instance = null;
/**
* @internal
* @ignore
*/
protected $client_preferences = array();
/**
* @internal
* @ignore
*/
protected $icon_chooser_screens = array( 'post.php', 'post-new.php', 'site-editor.php' );
/**
* @internal
* @ignore
*/
protected $conflicts_by_client = null;
/**
* @internal
* @ignore
*/
protected $screen_id = null;
/**
* This tracks the state of whether, when we process options after the
* plugin upgrades from using the v1 options schema to v2, the former
* removeUnregisteredClients option was set. If so we use some automatic
* conflict detection and resolution, like that old feature worked.
*
* Internal use only, not part of this plugin's public API.
*
* @deprecated
* @internal
* @ignore
*/
protected $old_remove_unregistered_clients = false;
/**
* If true, features required for Block Editor support will be disabled.
*
* Internal use only, not part of this plugin's public API.
*
* @deprecated
* @internal
* @ignore
*/
protected $disable_block_editor_support = false;
/**
* Returns the singleton instance of the FontAwesome plugin.
*
* @since 4.0.0
*
* @see fa()
* @return FontAwesome
*/
public static function instance() {
if ( is_null( self::$instance ) ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Internal use only, not part of this plugin's public API.
*
* @internal
* @ignore
*/
private function __construct() {
/**
* Determines whether to disable features required for supporting the Block Editor.
*
* @return bool if `true`, disable Block Editor support
* @since 5.0.2
*/
$this->disable_block_editor_support = apply_filters( 'font_awesome_disable_block_editor_support', false );
}
/**
* Returns this plugin's admin page's screen_id. Only valid after the admin_menu hook has run.
*
* Internal only, not part of this plugin's public API.
*
* @ignore
* @internal
*/
public function admin_screen_id() {
return $this->screen_id;
}
/**
* Callback for init.
*
* Main entry point for running the plugin. Called automatically when the plugin is loaded.
*
* Internal use only.
*
* @ignore
* @internal
*/
public function init() {
try {
$this->try_upgrade();
$this->validate_options( fa()->options() );
$this->initialize_rest_api();
if ( is_admin() ) {
$this->initialize_admin();
}
add_shortcode(
self::SHORTCODE_TAG,
array( $this, 'process_shortcode' )
);
if ( $this->is_block_editor_support_enabled() ) {
FontAwesome_SVG_Styles_Manager::register_svg_styles( $this );
block_init();
add_action(
'enqueue_block_assets',
function () {
wp_enqueue_style( FontAwesome_SVG_Styles_Manager::RESOURCE_HANDLE_SVG_STYLES );
}
);
}
try {
$this->gather_preferences();
// phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
} catch ( PreferenceRegistrationException $e ) {
/**
* Ignore this on normal page loads.
* If something seems amiss, the site owner may try to look
* into it on the plugin settings page where some additional
* diagnostic information may be found.
*/
}
$this->maybe_enqueue_admin_assets();
// Setup JavaScript internationalization if we're on WordPress 5.0+.
if ( function_exists( 'wp_set_script_translations' ) ) {
wp_set_script_translations( self::ADMIN_RESOURCE_HANDLE, 'font-awesome' );
}
if ( $this->using_kit() ) {
if ( $this->skip_enqueue_kit() ) {
// Normally, conflict detection is built into a kit.
// However, when not enqueuing the kit, we must enqueue conflict detection separately.
$this->maybe_enqueue_conflict_detection();
} else {
$this->enqueue_kit( $this->options()['kitToken'] );
}
} else {
$resource_collection = $this->cdn_resource_collection_for_current_options();
$this->enqueue_cdn( $this->options(), $resource_collection );
}
} catch ( Exception $e ) {
notify_admin_fatal_error( $e );
} catch ( Error $e ) {
notify_admin_fatal_error( $e );
}
}
/**
* Indicates whether to enqueue the kit.
*
* Internal use only, not part of the plugin's public API.
*
* However, this depends on the `font_awesome_skip_enqueue_kit` filter
* which is part of the public API.
*
* @internal
* @ignore
*/
protected function skip_enqueue_kit() {
/**
* Determines whether to skip the kit enqueue.
*
* When the plugin is configured to use a kit, the normal behavior is
* to use `wp_enqueue_script()` to enqueue the kit's embed code, a JavaScript
* loaded from the Font Awesome Kits CDN. The kit being loaded on front end page
* renderings enables rendering `` tags as Font Awesome icons, for example.
*
* By setting this to `true`, that `wp_enqueue_script()` will be skipped. Thus, the
* kit will not be loaded from the Font Awesome CDN on front end page loads. As a
* consequence, this plugin will not render `` tags as Font Awesome icons.
*
* You may prefer to skip loading the kit if:
*
* 1. You only use the block editor.
*
* As of version 5.0.0 of this plugin, icons are added in the
* block editor as `` elements and require no further rendering like `` tags.
*
* 2. You want to avoid using a CDN for front end page loads.
*
* Even when disabling the use of the CDN for front end page loads, the CDN is still used
* when editing pages on the back with the icon chooser. The icon chooser loads SVG icons
* from the CDN. When you choose one, the SVG content for that icon is added to your page.
*
* Default: false (that is, by default, enqueue the kit to be loaded from the CDN)
*
* @since 5.0.0
*/
return apply_filters( 'font_awesome_skip_enqueue_kit', false );
}
/**
* Not part of this plugin's public API.
*
* @ignore
* @internal
* @throws ConfigCorruptionException
* @return array
*/
public function cdn_resource_collection_for_current_options() {
return FontAwesome_Release_Provider::get_resource_collection(
$this->options()['version'],
array(
'use_pro' => $this->pro(),
'use_svg' => 'svg' === $this->technology(),
'use_compatibility' => $this->v4_compatibility(),
)
);
}
/**
* Detects whether upgrade is necessary and performs upgrade if so.
*
* Internal use only.
*
* @throws UpgradeException
* @throws ApiRequestException
* @throws ApiResponseException
* @throws ReleaseProviderStorageException
* @throws ReleaseMetadataMissingException
* @throws ConfigCorruptionException if options are invalid
* @internal
* @ignore
*/
public function try_upgrade() {
$options = get_option( self::OPTIONS_KEY );
if ( ! $options || ! is_array( $options ) ) {
throw new ConfigCorruptionException();
}
$should_upgrade = false;
// Upgrade from v1 schema: 4.0.0-rc13 or earlier.
if ( isset( $options['lockedLoadSpec'] ) || isset( $options['adminClientLoadSpec'] ) ) {
if ( isset( $options['removeUnregisteredClients'] ) && $options['removeUnregisteredClients'] ) {
$this->old_remove_unregistered_clients = true;
}
$upgraded_options = $this->convert_options_from_v1( $options );
/**
* If the version is still not set for some reason, set it to a
* default of the latest available version.
*/
if ( ! isset( $upgraded_options['version'] ) ) {
$upgraded_options['version'] = fa()->latest_version_6();
}
$should_upgrade = true;
$options = $upgraded_options;
}
if ( ! isset( $options['dataVersion'] ) || $options['dataVersion'] < 4 ) {
if ( ! isset( $options['compat'] ) && isset( $options['v4Compat'] ) ) {
$v4_compat = boolval( $options['v4Compat'] );
$options['compat'] = $v4_compat;
unset( $options['v4Compat'] );
} else {
$options['compat'] = self::DEFAULT_USER_OPTIONS['compat'];
}
if ( isset( $options['v4Compat'] ) ) {
unset( $options['v4Compat'] );
}
$options['dataVersion'] = 4;
$should_upgrade = true;
}
if ( $should_upgrade ) {
$this->validate_options( $options );
$this->maybe_update_last_used_release_schema_for_upgrade();
$this->maybe_move_release_metadata_for_upgrade();
/**
* Delete the main option to make sure it's removed entirely, including
* from the autoload cache.
*
* Function delete_option() returns false when it fails, including when the
* option does not exist. We know the option exists, because we just
* queried it above. So any other failure should halt the upgrade
* process to avoid inconsistent states.
*/
if ( ! delete_option( self::OPTIONS_KEY ) ) {
throw UpgradeException::main_option_delete();
}
update_option( self::OPTIONS_KEY, $options );
}
}
/**
* Some upgrades have involved changing how we store release metadata.
*
* If the plugin's backing data was in a valid sate before upgrade, then
* it should always be possible to apply any fixups to how the release
* metadata are stored without issuing a request to the API server for
* fresh metadata. Since issuing such a blocking request upon upgrade
* is known to cause load problems and request timeouts, let's never
* do it on upgrade.
*
* Internal use only.
*
* @throws ReleaseMetadataMissingException
* @ignore
* @internal
*/
private function maybe_move_release_metadata_for_upgrade() {
if ( boolval( get_option( FontAwesome_Release_Provider::OPTIONS_KEY ) ) ) {
// If this option is set, then we're all caught up.
return;
}
// It used to be stored in one of these.
$release_metadata = get_site_transient( 'font-awesome-releases' );
if ( ! $release_metadata ) {
$release_metadata = get_transient( 'font-awesome-releases' );
}
// Move it into where it belongs now.
if ( boolval( $release_metadata ) ) {
update_option( FontAwesome_Release_Provider::OPTIONS_KEY, $release_metadata, false );
}
/**
* Delete the old release metadata transient, if it exists.
* It's no longer stored as a transient.
*/
delete_transient( 'font-awesome-releases' );
/**
* Delete the old font-awesome-last-used-release site transient, if it exists.
* It's no longer stored as a site (network-wide) transient.
*/
delete_site_transient( 'font-awesome-last-used-release' );
/**
* Now we'll reset the release provider.
*
* If we've fallen through to this point, and we haven't found the release
* metadata stored in one of the previous locations, then this will throw an
* exception.
*/
FontAwesome_Release_Provider::reset();
}
/**
* With 4.1.0, the name of one of the keys in the LAST_USED_RELEASE_TRANSIENT changed.
* We can fix it up.
*
* Internal use only.
*
* @throws ReleaseMetadataMissingException
* @ignore
* @internal
*/
private function maybe_update_last_used_release_schema_for_upgrade() {
$last_used_transient = get_site_transient( FontAwesome_Release_Provider::LAST_USED_RELEASE_TRANSIENT );
if ( ! $last_used_transient ) {
$last_used_transient = get_transient( FontAwesome_Release_Provider::LAST_USED_RELEASE_TRANSIENT );
}
if ( $last_used_transient && isset( $last_used_transient['use_shim'] ) ) {
$compat = $last_used_transient['use_shim'];
unset( $last_used_transient['use_shim'] );
$last_used_transient['use_compatibility'] = $compat;
set_site_transient( FontAwesome_Release_Provider::LAST_USED_RELEASE_TRANSIENT, $last_used_transient, FontAwesome_Release_Provider::LAST_USED_RELEASE_TRANSIENT_EXPIRY );
}
}
/**
* Returns boolean indicating whether the plugin is currently configured
* to run the client-side conflict detection scanner.
*
* @since 4.0.0
* @return bool
*/
public function detecting_conflicts() {
$conflict_detection = get_option( self::CONFLICT_DETECTION_OPTIONS_KEY );
if ( isset( $conflict_detection['detectConflictsUntil'] ) && is_integer( $conflict_detection['detectConflictsUntil'] ) ) {
return time() < $conflict_detection['detectConflictsUntil'];
} else {
return false;
}
}
/**
* Returns boolean indicating whether a kit is configured.
*
* It normally shouldn't make a difference to other theme's or plugins
* as to whether Font Awesome is configured to use the standard CDN or a kit.
* Yet this is a valid way to determine that.
*
* @since 4.0.0
* @throws ConfigCorruptionException
* @return bool
*/
public function using_kit() {
$options = $this->options();
$this->validate_options( $options );
return self::using_kit_given_options( $options );
}
/**
* Internal use only, not part of this plugin's public API.
*
* @internal
* @ignore
* @return bool
*/
public static function using_kit_given_options( $options ) {
return isset( $options['kitToken'] )
&& isset( $options['apiToken'] )
&& $options['apiToken']
&& is_string( $options['kitToken'] );
}
/**
* Internal use only, not part of this plugin's public API.
*
* @internal
* @ignore
*/
protected function stringify_constraints( $constraints ) {
$flipped_concat_each = array_map(
function ( $constraint ) {
return "$constraint[1] $constraint[0]";
},
$constraints
);
return implode( ' and ', $flipped_concat_each );
}
/**
* Internal use only, not part of this plugin's public API.
*
* @internal
* @ignore
*/
private function initialize_rest_api() {
add_action(
'rest_api_init',
array(
new FontAwesome_API_Controller( self::PLUGIN_NAME, self::REST_API_NAMESPACE ),
'register_routes',
)
);
add_action(
'rest_api_init',
array(
new FontAwesome_Config_Controller( self::PLUGIN_NAME, self::REST_API_NAMESPACE ),
'register_routes',
)
);
add_action(
'rest_api_init',
array(
new FontAwesome_Preference_Check_Controller( self::PLUGIN_NAME, self::REST_API_NAMESPACE ),
'register_routes',
)
);
add_action(
'rest_api_init',
array(
new FontAwesome_Conflict_Detection_Controller( self::PLUGIN_NAME, self::REST_API_NAMESPACE ),
'register_routes',
)
);
}
/**
* Returns the latest available full release version of Font Awesome 5 as a string,
* or null if the releases metadata has not yet been successfully retrieved from the
* API server.
*
* As of the release of Font Awesome 6.0.0-beta1, this API is being deprecated,
* because the symbolic version "latest" is being deprecated. It now just means
* "the latest full release of Font Awesome with major version 5." Therefore,
* it may not be very useful any more as Font Awesome 6 is released.
*
* The recommended way to resolve the symbolic versions 'latest',
* '5.x', or '6.x' into their current concrete values is to query the GraphQL
* API like this:
*
* ```
* query { release(version: "5.x") { version } }
* ```
*
* The `version` argument on the `release` field can accept any of these symbolic
* version values. So that release's `version` field will be the corresponding
* current concrete version value at the time the query is run.
*
* This query could be issued from a front-end script through `FontAwesome_API_Controller`
* like this, assuming `@wordpress/api-fetch` is at `wp.apiFetch`,
* and you've [setup a nonce](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-api-fetch/#built-in-middlewares) correctly,
* and the logged in user has the appropriate permissions.
*
* ```
* wp.apiFetch( {
* path: '/font-awesome/v1/api',
* method: 'POST',
* headers: {'Content-Type': 'application/json'},
* body: '{ "query": "query Version5x($ver: String!) { release(version: $ver){ version } }", "variables": {"ver": "5.x"} }'
* } ).then( res => {
* console.log( res );
* } )
* ```
*
* Or you could issue your own `POST` request directly `api.fontawesome.com`.
* [See the Font Awesome GraphQL API reference here](https://fontawesome.com/v5.15/how-to-use/graphql-api/intro/getting-started).
*
* @since 4.0.0
* @deprecated
*
* @return null|string
*/
public function latest_version() {
return $this->release_provider()->latest_version();
}
/**
* Returns the latest available full release version of Font Awesome 5 as a string,
* or null if the releases metadata has not yet been successfully retrieved from the
* API server.
*
* @since 4.2.0
*
* @return null|string
*/
public function latest_version_5() {
return $this->release_provider()->latest_version_5();
}
/**
* Returns the latest available full release version of Font Awesome 6 as a string,
* or null if the releases metadata has not yet been successfully retrieved from the
* API server.
*
* @since 4.2.0
*
* @return null|string
*/
public function latest_version_6() {
return $this->release_provider()->latest_version_6();
}
/**
* Returns the latest available version of Font Awesome 7 as a string,
* or null if the releases metadata has not yet been successfully retrieved from the
* API server.
*
* @since 5.1.0
*
* @return null|string
*/
public function latest_version_7() {
return $this->release_provider()->latest_version_7();
}
/**
* Queries the Font Awesome API to load releases metadata. Results are stored
* in the wp database.
*
* This is the metadata that supports API
* methods like {@see FontAwesome::latest_version()}
* and all other metadata required to enqueue Font Awesome when configured
* to use the standard CDN (non-kits).
*
* This has been deprecated to discourage themes or plugins from invoking
* it as a blocking network request during front-end page loads. If we find
* that functionality like this is still needed for some use cases, let's
* design an alternative API that encourages best-practice use while
* discouraging anti-patterns.
*
* @since 4.0.0
* @deprecated
* @ignore
* @throws ApiRequestException
* @throws ApiResponseException
* @throws ReleaseProviderStorageException
*/
public function refresh_releases() {
$this->release_provider()->load_releases();
}
/**
* Returns the time when releases metadata was last
* refreshed.
*
* @since 4.0.0
* @return integer|null the time in unix epoch seconds or null if never
*/
public function releases_refreshed_at() {
return $this->release_provider()->refreshed_at();
}
/**
* Refreshes releases only if it's a been a while.
*
* Internal use only, not part of this plugin's public API.
*
* @ignore
* @internal
* @return WP_Error|1 error if there was a problem, otherwise 1.
*/
protected function maybe_refresh_releases() {
$refreshed_at = $this->releases_refreshed_at();
/**
* If we've just upgraded from an older plugin version that didn't have this metadata value,
* then we should refresh to get it.
*/
$latest_version_6 = $this->latest_version_6();
if ( is_null( $latest_version_6 ) || is_null( $refreshed_at ) || ( time() - $refreshed_at ) > self::RELEASES_REFRESH_INTERVAL ) {
return FontAwesome_Release_Provider::load_releases();
} else {
return 1;
}
}
/**
* URL for this plugin's admin settings page.
*
* Internal use only, not part of this plugin's public API.
*
* @ignore
* @internal
*/
private function settings_page_url() {
return admin_url( 'admin.php?page=' . self::OPTIONS_PAGE );
}
/**
* The value of the "ts" GET param given for this page request, or null if none.
*
* Internal use only, not part of this plugin's public API.
*
* We'll be super-strict validating what values we'll accept, insead of passing
* through whatever is on the query string.
*
* @ignore
* @internal
* @return string|null
*/
private function active_admin_tab() {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( ! isset( $_REQUEST[ self::ADMIN_TAB_QUERY_VAR ] ) || empty( $_REQUEST[ self::ADMIN_TAB_QUERY_VAR ] ) ) {
return null;
}
// phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$value = $_REQUEST[ self::ADMIN_TAB_QUERY_VAR ];
// These values are defined in the Redux reducer module of the admin JS React app.
switch ( $value ) {
case 'ts':
return 'ADMIN_TAB_TROUBLESHOOT';
case 's':
return 'ADMIN_TAB_SETTINGS';
default:
return null;
}
}
/**
* Initalizes everything about the admin environment except the React app
* bundle, which is handled in maybe_enqueue_admin_assets().
*
* Internal use only, not part of this plugin's public API.
*
* @ignore
* @internal
*/
public function initialize_admin() {
$admin_menu_command = new FontAwesome_Command(
function () {
fa()->screen_id = add_options_page(
/* translators: add_options_page page_title */
esc_html__( 'Font Awesome Settings', 'font-awesome' ),
/* translators: add_options_page menu_title */
esc_html__( 'Font Awesome', 'font-awesome' ),
'manage_options',
self::OPTIONS_PAGE,
array( fa(), 'create_admin_page' )
);
}
);
add_action(
'admin_menu',
array( $admin_menu_command, 'run' )
);
$plugin_action_links_command = new FontAwesome_Command(
function ( $links ) {
$mylinks = array(
/* translators: label for link to settings page on plugin listing */
'' . esc_html__( 'Settings', 'font-awesome' ) . ' ',
);
return array_merge( $links, $mylinks );
}
);
add_filter(
'plugin_action_links_' . FONTAWESOME_PLUGIN_FILE,
array( $plugin_action_links_command, 'run' )
);
$multi_version_warning_command = new FontAwesome_Command(
function ( $plugin_file, $plugin_data ) {
if ( version_compare( FontAwesome::PLUGIN_VERSION, $plugin_data['Version'], 'ne' ) ) {
$loader_version = FontAwesome_Loader::instance()->loaded_path();
?>
' . esc_html( $loader_version ) . '',
'ver. ' . esc_html( FontAwesome::PLUGIN_VERSION ) . ' '
);
?>
screen_id ) {
$this->maybe_refresh_releases();
}
} catch ( Exception $e ) {
notify_admin_warning( $e );
} catch ( Error $e ) {
notify_admin_warning( $e );
}
}
);
if ( $this->is_block_editor_support_enabled() ) {
try {
if ( ! FontAwesome_SVG_Styles_Manager::is_svg_stylesheet_present( $this ) ) {
FontAwesome_SVG_Styles_Manager::ensure_svg_styles_with_admin_notice_warning( $this, $this->release_provider() );
}
} catch ( Exception $e ) {
notify_admin_warning( $e );
} catch ( Error $e ) {
notify_admin_warning( $e );
}
}
}
/**
* Returns current options.
*
* Internal use only. Not part of this plugin's public API.
*
* @throws ConfigCorruptionException
* @internal
* @ignore
* @return array
*/
public function options() {
$options = get_option( self::OPTIONS_KEY );
if ( ! $options ) {
throw new ConfigCorruptionException();
}
return $options;
}
/**
* Validates options.
*
* Internal use only. Not part of this plugin's public API.
*
* @ignore
* @internal
* @throws ConfigCorruptionException if options are invalid
*/
public function validate_options( $options ) {
$using_kit = self::using_kit_given_options( $options );
$kit_token = isset( $options['kitToken'] ) ? $options['kitToken'] : null;
$api_token = isset( $options['apiToken'] ) ? $options['apiToken'] : null;
$version = isset( $options['version'] ) ? $options['version'] : null;
if ( ! isset( $options['usePro'] ) || ! is_bool( $options['usePro'] ) ) {
throw new ConfigCorruptionException();
}
if ( $using_kit ) {
if ( ! boolval( $api_token ) ) {
throw new ConfigCorruptionException();
}
if ( ! is_string( $kit_token ) ) {
throw new ConfigCorruptionException();
}
if ( ! is_string( $version ) ) {
throw new ConfigCorruptionException();
}
} else {
/**
* If we're not using a kit, then the version cannot be "latest",
* "5.x", or "6.x" at this point. It must have already been resolved
* into a concrete version.
*/
$version_is_concrete = self::version_is_concrete( $version );
if ( ! $version_is_concrete ) {
throw new ConfigCorruptionException();
}
$version_is_v6 = is_string( $version )
&& 1 === preg_match( '/^6\./', $version );
$is_pro = boolval( $options['usePro'] );
/**
* Pro Version 6 CDN is not supported.
*/
if ( $version_is_v6 && $is_pro ) {
throw new ConfigCorruptionException();
}
}
if ( ! isset( $options['compat'] ) || ! is_bool( $options['compat'] ) ) {
throw new ConfigCorruptionException();
}
if ( ! isset( $options['pseudoElements'] ) || ! is_bool( $options['pseudoElements'] ) ) {
throw new ConfigCorruptionException();
}
if (
! isset( $options['technology'] ) ||
! is_string( $options['technology'] ) ||
false === array_search( $options['technology'], array( 'svg', 'webfont' ), true )
) {
throw new ConfigCorruptionException();
}
}
/**
* An array of md5 hashes that identify detected conflicting versions of
* Font Awesome that the site owner has chosen to block from being enqueued.
*
* It is managed through the plugin's settings page.
*
* @since 4.0.0
* @return array
*/
public function blocklist() {
$conflict_detection = get_option( self::CONFLICT_DETECTION_OPTIONS_KEY );
$unregistered_clients = (
isset( $conflict_detection['unregisteredClients'] )
&& is_array( $conflict_detection['unregisteredClients'] )
)
? $conflict_detection['unregisteredClients']
: array();
$blocklist = array_reduce(
array_keys( $unregistered_clients ),
function ( $carry, $md5 ) use ( $unregistered_clients ) {
if (
isset( $unregistered_clients[ $md5 ]['blocked'] )
&& boolval( $unregistered_clients[ $md5 ]['blocked'] )
) {
array_push( $carry, $md5 );
}
return $carry;
},
array()
);
return $blocklist;
}
/**
* Gets the current value of detectConflictsUntil from the conflict detection
* option key in the database.
*
* Returns 0 if that value is unset in the db.
*
* Internal use only, not part of this plugin's public API.
*
* @ignore
* @internal
* @return integer
*/
protected function detect_conflicts_until() {
$conflict_detection = get_option(
self::CONFLICT_DETECTION_OPTIONS_KEY,
self::DEFAULT_CONFLICT_DETECTION_OPTIONS
);
return isset( $conflict_detection['detectConflictsUntil'] )
? $conflict_detection['detectConflictsUntil'] : 0;
}
/**
* Converts a given options array with a v1 schema to one with a v2 schema.
* There are significant changes from the schema used by 4.0.0-rc9 and before.
*
* Internal use only, not part of this plugin's public API.
*
* @internal
* @ignore
* @param $options
* @return array
*/
public function convert_options_from_v1( $options ) {
$converted_options = self::DEFAULT_USER_OPTIONS;
if ( isset( $options['usePro'] ) ) {
$converted_options['usePro'] = $options['usePro'];
} else {
$converted_options['usePro'] = self::DEFAULT_USER_OPTIONS['usePro'];
}
if ( isset( $options['version'] ) ) {
$converted_options['version'] = $options['version'];
}
if ( isset( $options['lockedLoadSpec'] ) ) {
$converted_options['technology'] = isset( $options['lockedLoadSpec']['method'] )
? $options['lockedLoadSpec']['method']
: 'webfont';
/**
* If technology is webfont, always coerce pseudo-elements to true.
* Otherwise, carry over whatever value it had before.
*/
$converted_options['pseudoElements'] =
'webfont' === $converted_options['technology']
? true
: (
isset( $options['lockedLoadSpec']['pseudoElements'] )
? $options['lockedLoadSpec']['pseudoElements']
: false
);
$converted_options['compat'] = $options['lockedLoadSpec']['v4shim'];
} elseif ( isset( $options['adminClientLoadSpec'] ) ) {
$converted_options['technology'] = $options['adminClientLoadSpec']['method'];
$converted_options['pseudoElements'] = 'svg' === $options['adminClientLoadSpec']['method']
&& $options['adminClientLoadSpec']['pseudoElements'];
$converted_options['compat'] = $options['adminClientLoadSpec']['v4shim'];
}
return $converted_options;
}
/**
* Callback function for creating the plugin's admin page.
*
* Internal use only, not part of this plugin's public API.
*
* @ignore
* @internal
*/
public function create_admin_page() {
include_once FONTAWESOME_DIR_PATH . 'admin/views/main.php';
}
/**
* Resets the singleton instance referenced by this class.
*
* Internal use only, not part of this plugin's public API.
*
* @ignore
* @internal
* @return FontAwesome
*/
public static function reset() {
self::$instance = null;
return fa();
}
/**
* Triggers the font_awesome_preferences action to gather preferences from clients.
*
* Internal use only, not part of this plugin's public API.
*
* @internal
* @ignore
* @throws PreferenceRegistrationException
*/
public function gather_preferences() {
/**
* Fired when the plugin is ready for clients to register their preferences.
*
* @since 4.0.0
*/
try {
do_action( 'font_awesome_preferences' );
} catch ( Exception $e ) {
// phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
throw PreferenceRegistrationException::with_thrown( $e );
} catch ( Error $e ) {
// phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
throw PreferenceRegistrationException::with_thrown( $e );
}
}
/**
* Returns current preferences conflicts, keyed by option name.
*
* Internal use only, not part of this plugin's public API.
*
* Should normally only be called after the `font_awesome_enqueued` action has triggered, indicating that all
* client preferences have been registered and processed.
*
* The returned array includes all conflicts between the options configured for this plugin by the site owner
* and any preferences registered by themes or plugins.
*
* The presence of conflicts will not stop this plugin from loading Font Awesome according to its
* configured options, but they will be presented to the site owner in the plugin's admin settings page to
* aid in troubleshooting.
*
* @ignore
* @internal
* @param $options options to use for comparison. Uses $this->options() by default.
* @see FontAwesome::register() register() documents all client preference keys
* @return array
*/
public function conflicts_by_option( $options = null ) {
$conflicts = array();
$options_for_comparison = is_null( $options ) ? $this->options() : $options;
foreach ( $this->conflicts_by_client( $options_for_comparison ) as $client_name => $client_conflicts ) {
foreach ( $client_conflicts as $conflicted_option ) {
// Initialize the key with an empty array if it doesn't already have something in it.
$conflicts[ $conflicted_option ] = isset( $conflicts[ $conflicted_option ] )
? $conflicts[ $conflicted_option ]
: array();
// Push the current client onto that array.
array_push( $conflicts[ $conflicted_option ], $client_name );
}
}
return $conflicts;
}
/**
* Returns current preferences conflicts, keyed by client name.
*
* Internal use only, not part of this plugin's public API.
*
* Should normally only be called after the `font_awesome_enqueued` action has triggered, indicating that all
* client preferences have been registered and processed.
*
* The returned array includes all conflicts between the given options and any preferences registered
* by themes or plugins.
*
* The presence of conflicts will not stop this plugin from loading Font Awesome according to its
* configured options, but they will be presented to the site owner in the plugin's admin settings page to
* aid in troubleshooting.
*
* @ignore
* @internal
* @param $options options to use for comparison. Uses $this->options() by default.
* @see FontAwesome::register() register() documents all client preference keys
* @return array
*/
public function conflicts_by_client( $options = null ) {
if ( is_null( $this->conflicts_by_client ) ) {
$conflicts = array();
$options_for_comparison = is_null( $options ) ? $this->options() : $options;
foreach ( $this->client_preferences as $client_name => $client_preferences ) {
$current_conflicts = FontAwesome_Preference_Conflict_Detector::detect(
$options_for_comparison,
$client_preferences,
$this->latest_version_5(),
$this->latest_version_6(),
$this->latest_version_7()
);
if ( count( $current_conflicts ) > 0 ) {
$conflicts[ $client_name ] = $current_conflicts;
}
}
$this->conflicts_by_client = $conflicts;
return $conflicts;
} else {
return $this->conflicts_by_client;
}
}
/**
* Return current client preferences for all registered clients.
*
* Internal use only, not part of this plugin's public API.
*
* The website owner (i.e. the one who uses the WordPress admin dashboard) is considered a registered client.
* So that owner's preferences will be represented here. But note that these preferences do not include
* the `options`, as returned by {@see FortAwesome\FontAwesome::options()} which also help determine the
* final result of how the Font Awesome assets are loaded.
*
* Each element of the array has the same shape as the preferences given to {@see FortAwesome\FontAwesome::register()}.
*
* @ignore
* @internal
* @see FortAwesome\FontAwesome::register()
* @return array
*/
public function client_preferences() {
return $this->client_preferences;
}
/**
* Return unregistered clients that have been detected and stored in the WordPress db.
*
* Internal use only, not part of this plugin's public API.
*
* Unregistered clients are those for which the in-browser conflict detector
* detects the presence of a Font Awesome version that is not being loaded by
* this plugin, and therefore is likely causing a conflict.
*
* Client-side conflict detection is enabled in this plugin's setting page in WP admin.
*
* @ignore
* @internal
* @return array
*/
public function unregistered_clients() {
$conflict_detection = get_option( self::CONFLICT_DETECTION_OPTIONS_KEY );
if ( isset( $conflict_detection['unregisteredClients'] ) && is_array( $conflict_detection['unregisteredClients'] ) ) {
return $conflict_detection['unregisteredClients'];
} else {
return array();
}
}
/**
* Indicates whether Font Awesome Pro is being loaded.
*
* It's a handy way to toggle the use of Pro icons in client theme or plugin template code.
*
* @since 4.0.0
* @throws ConfigCorruptionException
*
* @return boolean
*/
public function pro() {
$options = $this->options();
$this->validate_options( $options );
return $options['usePro'];
}
/**
* Indicates which Font Awesome technology is configured: 'webfont' or 'svg'.
*
* @since 4.0.0
* @throws ConfigCorruptionException
*
* @return string
*/
public function technology() {
$options = $this->options();
$this->validate_options( $options );
return $options['technology'];
}
/**
* Reports the version of Font Awesome assets being loaded, which may have
* a symbolic value like "latest" (deprecated), "5.x", or "6.x" if configured
* for a kit. If not configured for a kit, the version is guaranteed to be
* a concrete, semver parseable value, like 5.15.3.
*
* Your theme or plugin can call this method in order to determine
* whether all of the icons used in your templates will be available,
* especially if you tend to use newer icons.
*
* It should be really easy for site owners to update to a new Font Awesome
* version to accommodate your templates--just a simple dropdown selection
* on the Font Awesome plugin settings page. You might need to show an admin
* notice to nudge them to do so if you detect that the current version of
* Font Awesome being loaded is older than you'd like.
*
* When Font Awesome is configured to use a kit, that kit may be configured
* to load the "latest" version. The resolution of that symoblic "latest"
* version happens internal to the kit's own loading logic, which is
* outside the scope of this plugin.
*
* Before the release of Font Awesome 6.0.0-beta1, the symbolic version "latest"
* on a kit always meant: "the latest stable release with major version 5."
* Using "latest" for kits has been deprecated, but where it is still present
* on a kit, it will continue to mean just "the latest stable release with
* major version 5."
*
* New symbolic major version ranges have been introduced instead "5.x" and "6.x".
* These mean, respectively: "the latest stable release with major version 5",
* and "the latest stable release with major version 6, when available, otherwise
* the latest pre-release with major version 6."
*
* So if this function does not return a semver parseable version, then
* it must be one of these symbolic versions.
*
* The recommended way to resolve the symbolic versions 'latest',
* '5.x', or '6.x' into their current concrete values is to query the GraphQL
* API like this:
*
* ```
* query { release(version: "5.x") { version } }
* ```
*
* @since 4.0.0
* @see FontAwesome::latest_version()
* @see FontAwesome::releases_refreshed_at()
* @throws ConfigCorruptionException
* @return string|null null if no version has yet been saved in the options
* in the db. Otherwise, 5.x, or 6.x, or a semantic version string.
*/
public function version() {
$options = $this->options();
$this->validate_options( $options );
return $options['version'];
}
/**
* Returns the currently configured kit token, if the plugin is currently
* configured to load a kit.
*
* @since 4.0.0
* @throws ConfigCorruptionException
* @return string|null kit token if present, or null
*/
public function kit_token() {
$options = $this->options();
$this->validate_options( $options );
return isset( $options['kitToken'] ) ? $options['kitToken'] : null;
}
/**
* Indicates whether Font Awesome is being loaded with version 4 compatibility.
*
* Its result is valid only after the `font_awesome_enqueued` has been triggered.
*
* @since 4.0.0
* @throws ConfigCorruptionException
* @deprecated
*
* @return boolean
*/
public function v4_compatibility() {
return $this->compatibility();
}
/**
* Indicates whether Font Awesome is being loaded with older version compatibility.
*
* Its result is valid only after the `font_awesome_enqueued` has been triggered.
*
* @since 4.1.0
* @throws ConfigCorruptionException
*
* @return boolean
*/
public function compatibility() {
$options = $this->options();
$this->validate_options( $options );
return $options['compat'];
}
/**
* Indicates whether Font Awesome is being loaded with support for pseudo-elements.
*
* Its results are only valid after the `font_awesome_enqueued` action has been triggered.
*
* There are known performance problems with this SVG and pseudo-elements,
* but it is provided for added compatibility where pseudo-elements must be used.
*
* Always returns true if technology() === 'webfont', because pseudo-elements
* are always inherently supported by the CSS/Webfont technology.
*
* @since 4.0.0
* @link https://fontawesome.com/how-to-use/on-the-web/advanced/css-pseudo-elements CSS Pseudo-Elements and Font Awesome
* @throws ConfigCorruptionException
* @return boolean
*/
public function pseudo_elements() {
$options = $this->options();
$this->validate_options( $options );
return $options['pseudoElements'];
}
/**
* Internal use only, not part of this plugin's public API.
*
* @internal
* @ignore
*/
protected function specified_preference_or_default( $preference, $default_value ) {
return array_key_exists( 'value', $preference ) ? $preference['value'] : $default_value;
}
/**
* Enqueues the JavaScript bundle that is the React app for the admin
* settings page as well as the conflict detection reporter.
*
* The same bundle will be enqueued for both purposes. When enqueued, it
* must be configured to indicate which React components to mount in the DOM,
* which may be either, both, or neither.
*
* Internal use only, not part of this plugin's public API.
*
* @internal
* @ignore
*/
public function maybe_enqueue_admin_assets() {
add_action(
'admin_enqueue_scripts',
function ( $hook ) {
$should_enable_icon_chooser = $this->should_icon_chooser_be_enabled( $hook );
wp_register_script(
self::RESOURCE_HANDLE_ICON_CHOOSER,
trailingslashit( FONTAWESOME_DIR_URL ) . 'icon-chooser/build/index.js',
array( self::ADMIN_RESOURCE_HANDLE ),
self::PLUGIN_VERSION,
true
);
/** While this plugin's Classic Editor support does depend on tinymce
* being loaded, the wp-tinymce dependency has intentionally been omitted
* here due to compatibility problems with Toolset.
* See: https://wordpress.org/support/topic/cant-edit-post_content-in-classic-editor-if-plugin-v5-0-1-or-v5-0-2-is-active/
*/
wp_register_script(
self::RESOURCE_HANDLE_CLASSIC_EDITOR,
trailingslashit( FONTAWESOME_DIR_URL ) . 'classic-editor/build/index.js',
array(
self::ADMIN_RESOURCE_HANDLE,
self::RESOURCE_HANDLE_ICON_CHOOSER,
'jquery',
),
self::PLUGIN_VERSION,
true
);
try {
if ( $this->detecting_conflicts() || $hook === $this->screen_id || $should_enable_icon_chooser ) {
$this->enqueue_admin_js_assets( $should_enable_icon_chooser );
}
if ( $hook === $this->screen_id ) {
wp_localize_script(
self::ADMIN_RESOURCE_HANDLE,
self::ADMIN_RESOURCE_LOCALIZATION_NAME,
array_merge(
$this->common_data_for_js_bundle(),
array(
'showAdmin' => true,
'onSettingsPage' => true,
'clientPreferences' => $this->client_preferences(),
'releases' => array(
'available' => $this->release_provider()->versions(),
'latest_version_5' => $this->latest_version_5(),
'latest_version_6' => $this->latest_version_6(),
'latest_version_7' => $this->latest_version_7(),
),
'pluginVersion' => FontAwesome::PLUGIN_VERSION,
'preferenceConflicts' => $this->conflicts_by_option(),
)
)
);
} elseif ( $should_enable_icon_chooser ) {
wp_localize_script(
self::ADMIN_RESOURCE_HANDLE,
self::ADMIN_RESOURCE_LOCALIZATION_NAME,
$this->common_data_for_js_bundle()
);
wp_enqueue_script( self::RESOURCE_HANDLE_ICON_CHOOSER );
/**
* TODO: re-enable the possibility of integrating with TinyMCE
* even on pages where Gutenberg is also present.
* This is an initial fix for GitHub Issue: #133
* https://github.com/FortAwesome/wordpress-fontawesome/issues/133
*
* UPATE: Now that the bundles are building differently
* and external dependencies are working correctly, this
* is close to working (try loading a new post in Gutenberg
* with integration plugin-nu enabled). It requires disabling
* the dynamic import of the components style.css. In the
* case where we're already on Gutenberg page, it is not
* necessary to load it, so it should be easy to check for that.
*
* The remaining issue seems to be just determining into
* which editor the shortcode should be inserted. When
* running plugin-nu, activating the Icon Chooser from either
* Gutenberg or the plugin-nu's TinyMCE editor, the shortcode
* is always inserted into to the TinyMCE editors. Seems
* like that should be easy to resolve when there's time
* and priority to continue investigating.
*/
if ( ! is_gutenberg_page() ) {
add_action(
'media_buttons',
function ( $editor_id ) {
printf(
/* translators: 1: open button tag, 2: editor id value, 3: data attribute for editor id, 4: editor id value, 5: closing data attribute value quote, 6: remaining button tag open and icon svg tag, 7: close button tag */
esc_html__(
'%1$s%2$s%3$s%4$s%5$s%6$sAdd Font Awesome%7$s',
'font-awesome'
),
' ',
' '
);
},
99,
1
);
add_action(
'after_wp_tiny_mce',
function ( $mce_settings ) {
$editor_ids = array_keys( $mce_settings );
?>
common_data_for_js_bundle()
);
}
} catch ( Exception $e ) {
notify_admin_fatal_error( $e );
} catch ( Error $e ) {
notify_admin_fatal_error( $e );
}
}
);
if ( $this->detecting_conflicts() && current_user_can( 'manage_options' ) ) {
foreach ( array( 'wp_enqueue_scripts', 'login_enqueue_scripts' ) as $action ) {
add_action(
$action,
function () {
try {
$current_screen = get_current_screen();
$should_enable_icon_chooser = $this->should_icon_chooser_be_enabled( $current_screen );
$this->enqueue_admin_js_assets( $should_enable_icon_chooser );
wp_localize_script(
self::ADMIN_RESOURCE_HANDLE,
self::ADMIN_RESOURCE_LOCALIZATION_NAME,
array_merge(
$this->common_data_for_js_bundle(),
array(
'onSettingsPage' => false,
'showAdmin' => false,
'showConflictDetectionReporter' => true,
)
)
);
} catch ( Exception $e ) {
notify_admin_fatal_error( $e );
} catch ( Error $e ) {
notify_admin_fatal_error( $e );
}
}
);
}
}
}
/**
* Enqueues the entrypoint JavaScript index.js, and declares relevant js
* dependencies.
*
* @param bool $with_icon_chooser
* @ignore
* @internal
* @return string $main_js_handle
*/
private function enqueue_admin_js_assets( $with_icon_chooser ) {
global $wp_version;
$enable_icon_chooser = boolval( $with_icon_chooser );
$deps = array();
$deps = array_merge( $deps, array( 'react', 'react-dom', 'wp-i18n', 'wp-element', 'wp-components', 'wp-api-fetch', 'lodash' ) );
/**
* We don't need these Gutenberg dependencies unless we're on a Gutenberg
* page. Declaring them unnecessarily (when not on a Gutenberg page)
* has resulted in conflict for at least one other plugin: RankMath.
*
* See: https://wordpress.org/support/topic/plugin-conflicts-with-rankmath
*/
if ( $enable_icon_chooser && is_gutenberg_page() && $this->is_block_editor_support_enabled() ) {
$deps = array_merge( $deps, array( 'wp-blocks', 'wp-editor', 'wp-rich-text', 'wp-block-editor' ) );
}
wp_enqueue_script(
self::ADMIN_RESOURCE_HANDLE,
trailingslashit( $this->get_webpack_asset_url_base() ) . 'index.js',
$deps,
self::PLUGIN_VERSION,
true
);
}
/**
* Internal use only, not part of this plugin's public API.
*
* @ignore
* @internal
*/
public function common_data_for_js_bundle() {
return array(
'apiNonce' => wp_create_nonce( 'wp_rest' ),
'apiUrl' => rest_url( self::REST_API_NAMESPACE ),
'faApiUrl' => FONTAWESOME_API_URL,
'restApiNamespace' => self::REST_API_NAMESPACE,
'rootUrl' => rest_url(),
'detectConflictsUntil' => $this->detect_conflicts_until(),
'unregisteredClients' => $this->unregistered_clients(),
'showConflictDetectionReporter' => $this->detecting_conflicts(),
'settingsPageUrl' => $this->settings_page_url(),
'activeAdminTab' => $this->active_admin_tab(),
'options' => $this->options(),
'webpackPublicPath' => trailingslashit( FONTAWESOME_DIR_URL ) . 'admin/build/',
'disableRichTextIcons' => $this->disable_rich_text_icons(),
'assetsBaseUrlOverride' => FONTAWESOME_ASSETS_BASE_URL_OVERRIDE,
);
}
/**
* Enqueues a kit loader
*
* where deadbeef00 is the kitToken
*
* @ignore
* @internal
* @throws ConfigCorruptionException if the kit_token is not a string
*/
public function enqueue_kit( $kit_token ) {
if ( ! is_string( $kit_token ) ) {
throw new ConfigCorruptionException();
}
foreach ( array( 'wp_enqueue_scripts', 'admin_enqueue_scripts', 'login_enqueue_scripts' ) as $action ) {
$enqueue_command = new FontAwesome_Command(
function () use ( $kit_token ) {
try {
wp_enqueue_script(
FontAwesome::RESOURCE_HANDLE,
trailingslashit( FONTAWESOME_KIT_LOADER_BASE_URL ) . $kit_token . '.js',
array(),
// phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
null,
false
);
/**
* Kits have built-in support for detecting conflicts, but we need to
* inject some configuration to turn it on. We will do that by manipulating
* the FontAwesomeKitConfig global property.
*/
if ( fa()->detecting_conflicts() ) {
/**
* Kits Conflict Detection expects this value to be in milliseconds
* since the unix epoch.
*/
$detect_conflicts_until = fa()->detect_conflicts_until() * 1000;
$script_content = <<< EOT
window.__FontAwesome__WP__KitConfig__ = {
detectConflictsUntil: {$detect_conflicts_until}
}
Object.defineProperty(window, 'FontAwesomeKitConfig', {
enumerable: true,
configurable: false,
get() { return window.__FontAwesome__WP__KitConfig__ },
set( newValue ) {
var newValueCopy = Object.assign({}, newValue)
window.__FontAwesome__WP__KitConfig__ = Object.assign(newValueCopy, window.__FontAwesome__WP__KitConfig__)
}
})
EOT;
wp_add_inline_script(
FontAwesome::RESOURCE_HANDLE,
$script_content,
'before'
);
}
} catch ( Exception $e ) {
notify_admin_fatal_error( $e );
} catch ( Error $e ) {
notify_admin_fatal_error( $e );
}
}
);
add_action(
$action,
array( $enqueue_command, 'run' )
);
}
$script_loader_tag_command = new FontAwesome_Command(
function ( $html, $handle ) {
$revised_html = $html;
/**
* Set the crossorigin attr to ensure that the Origin header is
* by the browser when the kit loader script is loaded.
* Needed for authorization.
*/
if ( self::RESOURCE_HANDLE === $handle ) {
$revised_html = preg_replace(
'/