diff --git a/README.md b/README.md index 3d054ee..dbf67b2 100644 --- a/README.md +++ b/README.md @@ -493,6 +493,36 @@ dismissible notices. If you want the notice to be marked as dismissed, but not fade out, you can pass "clear" to the `customerCloserAttributes` method: e.g. `$elements->customCloserAttributes('clear')`. +## Custom scripts & styles + +Often times, especially for custom notices, you may want to enqueue custom scripts and styles, +however they should only be enqueued when the notice is displayed. This can be done by using the +`enqueueScript` and `enqueueStyle` methods on the notice. + +### `enqueueScript($src, $deps = [], $ver = false, $args = [])` + +Enqueues a script to be loaded when the notice is displayed, following the same parameters as +`wp_enqueue_script`. The only difference is that the loading strategy is "defer" by default. + +```php +use StellarWP\AdminNotices\AdminNotices; + +$notice = AdminNotices::show('my_notice', 'This is a notice') + ->enqueueScript('https://example.com/my-script.js', ['jquery']); +``` + +### `enqueueStyle($src, $deps = [], $ver = false, $media = 'all')` + +Enqueues a style to be loaded when the notice is displayed, following the same parameters as +`wp_enqueue_style`. + +```php +use StellarWP\AdminNotices\AdminNotices; + +$notice = AdminNotices::show('my_notice', 'This is a notice') + ->enqueueStylesheet('https://example.com/my-style.css'); +``` + ## Resetting dismissed notices For dismissible notices, when the user dismisses the notice, it is permanently dismissed. If you diff --git a/src/Actions/DisplayNoticesInAdmin.php b/src/Actions/DisplayNoticesInAdmin.php index 8416f62..5971a1b 100644 --- a/src/Actions/DisplayNoticesInAdmin.php +++ b/src/Actions/DisplayNoticesInAdmin.php @@ -5,8 +5,6 @@ namespace StellarWP\AdminNotices\Actions; -use DateTimeImmutable; -use DateTimeZone; use StellarWP\AdminNotices\AdminNotice; use StellarWP\AdminNotices\Traits\HasNamespace; @@ -31,156 +29,10 @@ public function __invoke(AdminNotice ...$notices) } foreach ($notices as $notice) { - if ($this->shouldDisplayNotice($notice)) { + if ((new NoticeShouldRender($this->namespace))($notice)) { echo (new RenderAdminNotice($this->namespace))($notice); } } } - - /** - * Checks whether the notice should be displayed based on the provided conditions. - * - * @since 1.0.0 - */ - private function shouldDisplayNotice(AdminNotice $notice): bool - { - return $this->passesDismissedConditions($notice) - && $this->passesDateLimits($notice) - && $this->passesWhenCallback($notice) - && $this->passesUserCapabilities($notice) - && $this->passesScreenConditions($notice); - } - - /** - * Checks whether the notice should be displayed based on the provided date limits. - * - * @since 1.0.0 - */ - private function passesDateLimits(AdminNotice $notice): bool - { - if (!$notice->getAfterDate() && !$notice->getUntilDate()) { - return true; - } - - $now = new DateTimeImmutable('now', new DateTimeZone('UTC')); - - if ($notice->getAfterDate() && $notice->getAfterDate() > $now) { - return false; - } - - if ($notice->getUntilDate() && $notice->getUntilDate() < $now) { - return false; - } - - return true; - } - - /** - * Checks whether the notice should be displayed based on the provided callback. - * - * @since 1.0.0 - */ - private function passesWhenCallback(AdminNotice $notice): bool - { - $callback = $notice->getWhenCallback(); - - if ($callback === null) { - return true; - } - - return $callback(); - } - - /** - * Checks whether user limits were provided and they pass. Only one capability is required to pass, allowing for - * multiple users have visibility. - * - * @since 1.0.0 - */ - private function passesUserCapabilities(AdminNotice $notice): bool - { - $capabilities = $notice->getUserCapabilities(); - - if (empty($capabilities)) { - return true; - } - - foreach ($capabilities as $capability) { - if ($capability->currentUserCan()) { - return true; - } - } - - return false; - } - - /** - * Checks whether the notice is limited to specific screens and the current screen matches the conditions. Only one - * screen condition is required to pass, allowing for the notice to appear on multiple screens. - * - * @since 1.0.0 - */ - private function passesScreenConditions(AdminNotice $notice): bool - { - $screenConditions = $notice->getOnConditions(); - - if (empty($screenConditions)) { - return true; - } - - $screen = get_current_screen(); - $currentUrl = get_admin_url(null, $_SERVER['REQUEST_URI']); - - foreach ($screenConditions as $screenCondition) { - $condition = $screenCondition->getCondition(); - - if ($screenCondition->isRegex()) { - // do a regex comparison on the current url - if (preg_match($condition, $currentUrl) === 1) { - return true; - } - } elseif (is_string($condition)) { - // do a string comparison on the current url - if (strpos($currentUrl, $condition) !== false) { - return true; - } - } else { - // compare the condition array against the WP_Screen object - foreach ($condition as $property => $value) { - if ($screen->$property === $value) { - return true; - } - } - } - } - - return false; - } - - /** - * Checks whether the notice has been dismissed by the user. - * - * @since 1.1.0 added namespacing to the preferences key - * @since 1.0.0 - */ - private function passesDismissedConditions(AdminNotice $notice): bool - { - global $wpdb; - - $userPreferences = get_user_meta(get_current_user_id(), $wpdb->get_blog_prefix() . 'persisted_preferences', true); - - $key = "stellarwp/admin-notices/$this->namespace"; - if (!is_array($userPreferences) || empty($userPreferences[$key])) { - return true; - } - - $dismissedNotices = $userPreferences[$key]; - - if (key_exists($notice->getId(), $dismissedNotices)) { - return false; - } - - return true; - } } diff --git a/src/Actions/EnqueueNoticesScriptsAndStyles.php b/src/Actions/EnqueueNoticesScriptsAndStyles.php new file mode 100644 index 0000000..3c08c0f --- /dev/null +++ b/src/Actions/EnqueueNoticesScriptsAndStyles.php @@ -0,0 +1,40 @@ +getScriptToEnqueue(); + $style = $notice->getStyleToEnqueue(); + + if (($script || $style) && (new NoticeShouldRender($this->namespace))($notice)) { + if ($script) { + $script->enqueue("stellarwp-{$this->namespace}-{$notice->getId()}"); + } + + if ($style) { + $style->enqueue("stellarwp-{$this->namespace}-{$notice->getId()}"); + } + } + } + } +} diff --git a/src/Actions/NoticeShouldRender.php b/src/Actions/NoticeShouldRender.php new file mode 100644 index 0000000..3931a5a --- /dev/null +++ b/src/Actions/NoticeShouldRender.php @@ -0,0 +1,174 @@ +passesDismissedConditions($notice) + && $this->passesDateLimits($notice) + && $this->passesWhenCallback($notice) + && $this->passesUserCapabilities($notice) + && $this->passesScreenConditions($notice); + } + + /** + * Checks whether the notice should be displayed based on the provided date limits. + * + * @unreleased moved to the NoticeShouldRender class + * @since 1.0.0 + */ + private function passesDateLimits(AdminNotice $notice): bool + { + if (!$notice->getAfterDate() && !$notice->getUntilDate()) { + return true; + } + + $now = new DateTimeImmutable('now', new DateTimeZone('UTC')); + + if ($notice->getAfterDate() && $notice->getAfterDate() > $now) { + return false; + } + + if ($notice->getUntilDate() && $notice->getUntilDate() < $now) { + return false; + } + + return true; + } + + /** + * Checks whether the notice should be displayed based on the provided callback. + * + * @unreleased moved to the NoticeShouldRender class + * @since 1.0.0 + */ + private function passesWhenCallback(AdminNotice $notice): bool + { + $callback = $notice->getWhenCallback(); + + if ($callback === null) { + return true; + } + + return $callback(); + } + + /** + * Checks whether user limits were provided and they pass. Only one capability is required to pass, allowing for + * multiple users have visibility. + * + * @unreleased moved to the NoticeShouldRender class + * @since 1.0.0 + */ + private function passesUserCapabilities(AdminNotice $notice): bool + { + $capabilities = $notice->getUserCapabilities(); + + if (empty($capabilities)) { + return true; + } + + foreach ($capabilities as $capability) { + if ($capability->currentUserCan()) { + return true; + } + } + + return false; + } + + /** + * Checks whether the notice is limited to specific screens and the current screen matches the conditions. Only one + * screen condition is required to pass, allowing for the notice to appear on multiple screens. + * + * @unreleased moved to the NoticeShouldRender class + * @since 1.0.0 + */ + private function passesScreenConditions(AdminNotice $notice): bool + { + $screenConditions = $notice->getOnConditions(); + + if (empty($screenConditions)) { + return true; + } + + $screen = get_current_screen(); + $currentUrl = get_admin_url(null, $_SERVER['REQUEST_URI']); + + foreach ($screenConditions as $screenCondition) { + $condition = $screenCondition->getCondition(); + + if ($screenCondition->isRegex()) { + // do a regex comparison on the current url + if (preg_match($condition, $currentUrl) === 1) { + return true; + } + } elseif (is_string($condition)) { + // do a string comparison on the current url + if (strpos($currentUrl, $condition) !== false) { + return true; + } + } else { + // compare the condition array against the WP_Screen object + foreach ($condition as $property => $value) { + if ($screen->$property === $value) { + return true; + } + } + } + } + + return false; + } + + /** + * Checks whether the notice has been dismissed by the user. + * + * @unreleased moved to the NoticeShouldRender class + * @since 1.1.0 added namespacing to the preferences key + * @since 1.0.0 + */ + private function passesDismissedConditions(AdminNotice $notice): bool + { + global $wpdb; + + $userPreferences = get_user_meta( + get_current_user_id(), + $wpdb->get_blog_prefix() . 'persisted_preferences', + true + ); + + $key = "stellarwp/admin-notices/$this->namespace"; + if (!is_array($userPreferences) || empty($userPreferences[$key])) { + return true; + } + + $dismissedNotices = $userPreferences[$key]; + + if (key_exists($notice->getId(), $dismissedNotices)) { + return false; + } + + return true; + } +} diff --git a/src/AdminNotice.php b/src/AdminNotice.php index 8a698b1..64c1ec4 100644 --- a/src/AdminNotice.php +++ b/src/AdminNotice.php @@ -12,6 +12,8 @@ use StellarWP\AdminNotices\ValueObjects\NoticeLocation; use StellarWP\AdminNotices\ValueObjects\NoticeUrgency; use StellarWP\AdminNotices\ValueObjects\ScreenCondition; +use StellarWP\AdminNotices\ValueObjects\Script; +use StellarWP\AdminNotices\ValueObjects\Style; use StellarWP\AdminNotices\ValueObjects\UserCapability; class AdminNotice @@ -81,6 +83,16 @@ class AdminNotice */ protected $location; + /** + * @var Script + */ + protected $scriptToEnqueue; + + /** + * @var Style + */ + protected $styleToEnqueue; + /** * @since 1.0.0 * @@ -404,6 +416,54 @@ public function getId(): string return $this->id; } + /** + * @unreleased + */ + public function enqueueScript( + string $source, + array $dependencies = [], + string $version = null, + array $args = null + ): self { + if ($args === null) { + $args = ['strategy' => 'defer']; + } + + $this->scriptToEnqueue = new Script($source, $dependencies, $version, $args); + + return $this; + } + + /** + * @unreleased + */ + public function getScriptToEnqueue(): ?Script + { + return $this->scriptToEnqueue; + } + + /** + * @unreleased + */ + public function enqueueStylesheet( + string $source, + array $dependencies = [], + string $version = null, + string $media = 'all' + ): self { + $this->styleToEnqueue = new Style($source, $dependencies, $version, $media); + + return $this; + } + + /** + * @unreleased + */ + public function getStyleToEnqueue(): ?Style + { + return $this->styleToEnqueue; + } + /** * Returns the text or callback used to render the notice * diff --git a/src/AdminNotices.php b/src/AdminNotices.php index ce8c6d8..4d1c85b 100644 --- a/src/AdminNotices.php +++ b/src/AdminNotices.php @@ -7,6 +7,7 @@ use Psr\Container\ContainerInterface; use RuntimeException; use StellarWP\AdminNotices\Actions\DisplayNoticesInAdmin; +use StellarWP\AdminNotices\Actions\EnqueueNoticesScriptsAndStyles; use StellarWP\AdminNotices\Contracts\NotificationsRegistrarInterface; class AdminNotices @@ -204,25 +205,15 @@ public static function enqueueScripts(): void $handle = "$namespace-admin-notices"; $version = filemtime(__DIR__ . '/resources/admin-notices.js'); - add_filter('script_loader_tag', static function ($tag, $tagHandle) use ($handle, $namespace) { - if ($handle !== $tagHandle) { - return $tag; - } - - $tag = str_replace(' src', ' defer src', $tag); - - $replacement = "