This commit is contained in:
2026-04-26 23:47:49 +02:00
parent 1b95f03d1e
commit b073e009d8
5288 changed files with 1112699 additions and 55536 deletions

View File

@@ -0,0 +1,56 @@
# Recommended Blocks
The Recommended Blocks package adds a list of Gutenberg Blocks to your plugin. Each block will automatically install and activate the recommended plugin from the WordPress repository.
### Usage
```php
use Smashballoon\Framework\Packages\Blocks\RecommendedBlocks;
$recommended_blocks = new RecommendedBlocks();
$recommended_blocks->setup();
```
### Development
Run `npm install` and `npm run build` to build the JavaScript file. The built file will be located in the `build` directory. The built JavaScript and CSS file is enqueued in the `RecommendedBlocks` class.
The recommended blocks are defined in the `blocks.js` file in the `src` directory. This file contains an array of block objects. Each block object contains the following properties:
### Sample block object
```js
{
name: 'instagram-feed',
title: __('Instagram Feed', 'smashballoon'),
description: __('Display your Instagram feeds.', 'smashballoon'),
pluginPath: 'instagram-feed/instagram-feed.php',
proPluginPath: 'instagram-feed-pro/instagram-feed.php',
pluginDescription: __('Custom Instagram Feed is a highly customizable way to display feeds from your Instagram account. Promote your latest content and update your site content automatically.', 'smashballoon'),
keywords: [
__('Instagram', 'smashballoon'),
__('Photos', 'smashballoon'),
__('Social Media', 'smashballoon'),
],
}
```
Add a block object to the `recommendedBlocks` array in the `blocks.js` file to add a new block to the list.
The `name` property is the block name and should be unique.
The `title` property is the block title.
The `description` property is the block description.
The `pluginPath` property is the plugin path in the WordPress repository.
The `proPluginPath` property is the pro plugin path in the plugins repository. This is optional and will check if the pro plugin is installed before recommending the plugin from the WordPress.org
The `pluginDescription` property is the plugin description. This will be displayed in the plugin details modal when the user clicks on the Recommended block. This should contain a brief description of the plugin.
The `keywords` property is an array of keywords for the block. The user can search for blocks using these keywords.

View File

@@ -0,0 +1,108 @@
<?php
/**
* Description: Recommended blocks for suggesting other Awesome Motive plugins.
* Version: 1.0
* Author: Awesome Motive, Inc.
* Author URI: https://awesomemotive.com/
* License: GPL-2.0-or-later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Smashballoon\Framework\Packages\Blocks;
require_once \ABSPATH . 'wp-admin/includes/class-wp-upgrader-skin.php';
use Plugin_Installer_Skin;
use Plugin_Upgrader;
use WP_Error;
/**
* Recommended Blocks class.
* @internal
*/
class RecommendedBlocks
{
/**
* Setup.
*/
public function setup()
{
\add_action('wp_ajax_am_recommended_block_install', [$this, 'install_plugin']);
\add_action('enqueue_block_editor_assets', [$this, 'enqueue_block_assets']);
}
/**
* Enqueue the needed scripts.
*/
public function enqueue_block_assets()
{
$asset_file = \plugin_dir_path(__FILE__) . 'build/index.asset.php';
$asset = \file_exists($asset_file) ? require $asset_file : ['dependencies' => ['wp-i18n', 'wp-element', 'wp-components', 'wp-api-fetch'], 'version' => '1.0.0'];
\wp_enqueue_script('recommended-blocks', \plugin_dir_url(__FILE__) . 'build/index.js', $asset['dependencies'], $asset['version'], \true);
$active_plugins = (array) \get_option('active_plugins', array());
\wp_localize_script('recommended-blocks', 'recommendedBlocksData', ['siteUrl' => \admin_url('admin-ajax.php'), 'nonce' => \wp_create_nonce('am_recommended_block_install'), 'plugins' => $active_plugins]);
\wp_enqueue_style('recommended-blocks', \plugin_dir_url(__FILE__) . 'build/index.css', [], '1.0.0');
}
/**
* Install the plugin.
*/
public function install_plugin()
{
include_once \ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
include_once \ABSPATH . 'wp-admin/includes/plugin-install.php';
if (!\current_user_can('install_plugins')) {
$error = new WP_Error('no_permission', 'You do not have permission to install plugins.');
\wp_send_json_error($error);
}
if (empty($_REQUEST['nonce']) || !\wp_verify_nonce(\sanitize_text_field($_REQUEST['nonce']), 'am_recommended_block_install')) {
$error = new WP_Error('nonce_failure', 'The nonce was not valid.');
\wp_send_json_error($error);
}
if (empty($_REQUEST['plugin'])) {
$error = new WP_Error('missing_file', 'The plugin file was not specified.');
\wp_send_json_error($error);
}
$plugin_file = \sanitize_text_field($_REQUEST['plugin']);
$slug = \strtok($plugin_file, '/');
$plugin_dir = \WP_PLUGIN_DIR . '/' . $slug;
$plugin_path = \WP_PLUGIN_DIR . '/' . $plugin_file;
if (!\is_dir($plugin_dir)) {
$api = \plugins_api('plugin_information', ['slug' => $slug, 'fields' => ['short_description' => \false, 'sections' => \false, 'requires' => \false, 'rating' => \false, 'ratings' => \false, 'downloaded' => \false, 'last_updated' => \false, 'added' => \false, 'tags' => \false, 'compatibility' => \false, 'homepage' => \false, 'donate_link' => \false]]);
$skin = new Plugin_Installer_Skin(['api' => $api]);
$upgrader = new Plugin_Upgrader($skin);
$install = $upgrader->install($api->download_link);
if ($install !== \true) {
$error = new WP_Error('failed_install', 'The plugin install failed.');
\wp_send_json_error($error);
}
}
if (\file_exists($plugin_path)) {
\activate_plugin($plugin_path);
$this->disable_installed_plugins_redirect();
\wp_redirect(\get_permalink());
} else {
$error = new WP_Error('failed_activation', 'The plugin activation failed.');
\wp_send_json_error($error);
}
\wp_die();
}
/**
* Disable the redirect to the 3rd party plugin's welcome page.
*
* @return void
*/
public function disable_installed_plugins_redirect()
{
// Smash Balloon plugins.
$this->disable_smash_balloon_redirect();
}
/**
* Disable the redirect to Smash Balloon's plugin welcome page after activation.
*
* @return void
*/
public function disable_smash_balloon_redirect()
{
$smash_list = ['facebook' => 'cff_plugin_do_activation_redirect', 'instagram' => 'sbi_plugin_do_activation_redirect', 'youtube' => 'sby_plugin_do_activation_redirect', 'twitter' => 'ctf_plugin_do_activation_redirect', 'reviews' => 'sbr_plugin_do_activation_redirect'];
foreach ($smash_list as $plugin => $option) {
\delete_option($option);
}
}
}

View File

@@ -0,0 +1,5 @@
<?php
namespace SmashBalloon\YoutubeFeed\Vendor;
return array('dependencies' => array('react', 'wp-blocks', 'wp-components', 'wp-data', 'wp-i18n'), 'version' => 'bc802afce2e0b571354b');

View File

@@ -0,0 +1 @@
.am-recommended-block-modal{min-height:290px;position:relative;width:580px}.am-recommended-block-modal .am-modal-header{background:#f3f4f5;padding:20px 0 10px 20px;position:relative}.am-recommended-block-modal .am-modal-title-wrap{display:flex;gap:20px;justify-content:flex-start}.am-recommended-block-modal .am-modal-title-wrap .am-modal-title{align-items:flex-start;display:flex;flex-direction:column;justify-content:center}.am-recommended-block-modal .am-modal-title-wrap .am-modal-title .am-requires{color:#434960;font-size:12px;font-weight:700;line-height:19px}.am-recommended-block-modal .am-modal-title-wrap .am-title{align-items:center;display:flex;gap:10px}.am-recommended-block-modal .am-modal-title-wrap .am-title h2{color:#141b38;font-size:18px;font-weight:600;line-height:25px;margin:0}.am-recommended-block-modal .am-modal-title-wrap .am-title .am-plugin-tag{background-color:#59ab46;border-radius:2px;color:#fff;font-size:10px;font-weight:700;margin-top:-5px;padding:0 2px}.am-recommended-block-modal .am-modal-content{margin-left:86px;padding:20px}.am-recommended-block-modal .am-modal-content p{color:#434960;font-size:14px;font-weight:400;line-height:22px;margin:0;padding:0 20px 20px 0}.am-recommended-block-modal .am-modal-content .am-modal-cta-btn{align-items:center;display:flex;font-size:14px;font-weight:600;justify-content:center;line-height:22px;width:100%}.am-recommended-block-modal .am-modal-close{position:absolute;right:1%;top:1%}.am-recommended-block-modal .components-modal__content,.am-recommended-block-modal .components-modal__content.hide-header{padding:0}.am-recommended-block-modal .components-spinner{display:none;height:14px;margin-top:0;width:14px}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,295 @@
<?php
/**
* License Tier class.
*
* @package License_Tier
*/
namespace Smashballoon\Framework\Packages\License_Tier;
use function Smashballoon\Framework\flatten_array;
/** @internal */
abstract class License_Tier
{
/**
* This gets the license key
*
* @var string
*/
public $license_key_option_name;
/**
* This gets the license status
*
* @var string
*/
public $license_status_option_name;
/**
* This gets the license data
*
* @var string
*/
public $license_data_option_name;
/**
* Item ID of the basic, plus, elite and all access license tier
*
* @var integer
*/
public $item_id_basic;
public $item_id_plus;
public $item_id_elite;
public $item_id_all_access;
/**
* Is all access
*
* @var boolean
*/
public $is_all_access;
/**
* Name of the basic, plus, and elite license tier
*
* @var string
*/
public $license_tier_free_name;
public $license_tier_basic_name;
public $license_tier_plus_name;
public $license_tier_elite_name;
/**
* Legacy item IDs
*
* @var integer
*/
public $item_id_personal;
public $item_id_business;
public $item_id_developer;
/**
* Legacy license tier names
*
* @var string
*/
public $license_tier_personal_name;
public $license_tier_business_name;
public $license_tier_developer_name;
/**
* This holds the license data
*
* @var array
*/
public $license_data = [];
/**
* This holds the plugin features list
*
* @var array
*/
protected $plugin_features = [];
/**
* This holds the legacy features list
*
* @var array
*/
protected $legacy_features = [];
/**
* This holds the active license tier name
*
* @var string
*/
protected $license_tier;
public $edd_item_name;
/**
* Constructor
*/
public function __construct()
{
$this->features_list();
$this->legacy_features_list();
$this->license_data();
}
/**
* This defines the features list of the plugin
*
* @return void
*/
public abstract function features_list();
/**
* This defines the legacy features list of the plugin
*
* @return void
*/
public function legacy_features_list()
{
$this->legacy_features = [];
}
/**
* Get license data
*
* @return void
*/
public function license_data()
{
$license_data = (array) \get_option($this->license_data_option_name);
$license_tier = $this->license_tier_free_name;
if (\is_array($license_data) && isset($license_data['item_id']) && isset($license_data['price_id'])) {
$license_tier = $this->convert_to_readable_plan_name($license_data);
}
$this->license_data = $license_data;
$this->license_tier = $license_tier;
}
/**
* This gets the license tier plan name in a readable format
*
* @return string|null
*/
public function get_license_tier()
{
return $this->license_tier;
}
/**
* Returns list of available features for a given plan
*
* @return array $tier_features returns tier features or empty array
*/
public function tier_features()
{
$all_features = $this->plugin_features;
$plan_name = $this->get_license_tier();
$tier_features = [];
$legacy_plans = [$this->license_tier_personal_name, $this->license_tier_business_name, $this->license_tier_developer_name];
if (\in_array($plan_name, $legacy_plans)) {
$tier_features = $this->legacy_tier_features();
return $tier_features;
}
if ($plan_name == $this->license_tier_free_name) {
$tier_features = isset($all_features[$plan_name]) ? $all_features[$plan_name] : [];
}
if ($plan_name == $this->license_tier_basic_name) {
$tier_features = isset($all_features[$plan_name]) ? $all_features[$plan_name] : [];
}
if ($plan_name == $this->license_tier_plus_name) {
$tier_features = isset($all_features[$this->license_tier_basic_name]) && isset($all_features[$plan_name]) ? \array_merge($all_features[$this->license_tier_basic_name], $all_features[$plan_name]) : [];
}
if ($plan_name == $this->license_tier_elite_name || $this->is_all_access) {
$tier_features = flatten_array($all_features);
}
return $tier_features;
}
/**
* Returns list of available features for a given legacy plan
*
* @return array $tier_features returns tier features or empty array
*/
public function legacy_tier_features()
{
$all_features = $this->legacy_features;
$plan_name = $this->get_license_tier();
$tier_features = [];
if ($plan_name == $this->license_tier_personal_name) {
$tier_features = isset($all_features[$plan_name]) ? $all_features[$plan_name] : [];
}
if ($plan_name == $this->license_tier_business_name) {
$tier_features = isset($all_features[$this->license_tier_personal_name]) && isset($all_features[$plan_name]) ? \array_merge($all_features[$this->license_tier_personal_name], $all_features[$plan_name]) : [];
}
if ($plan_name == $this->license_tier_developer_name) {
$tier_features = flatten_array($all_features);
}
return $tier_features;
}
/**
* This is a helpful function to look for any specific feature on a given plan
*
* @params string $feature_name Expects a feature name as a string
* @params string $plan_name Expects a given pricing plan as a string
*
* @return boolean $plan_exists returns boolean value
*/
public function has_feature($feature_name, $plan_name)
{
$features_list = $this->plugin_features;
$plan_exists = \false;
if ($plan_name == $this->license_tier_basic_name) {
$plan_exists = \in_array($feature_name, $features_list[$this->license_tier_basic_name]);
}
if ($plan_name == $this->license_tier_plus_name) {
$plan_exists = \in_array($feature_name, $features_list[$this->license_tier_basic_name]) || \in_array($feature_name, $features_list[$this->license_tier_plus_name]);
}
if ($plan_name == $this->license_tier_elite_name) {
$plan_exists = \in_array($feature_name, $features_list[$this->license_tier_basic_name]) || \in_array($feature_name, $features_list[$this->license_tier_plus_name]) || \in_array($feature_name, $features_list[$this->license_tier_elite_name]);
}
return $plan_exists;
}
/**
* Map item id to tier name
*
* @return array
*/
public function item_id_to_tier_name()
{
$item_id_to_tier_name = [$this->item_id_basic => $this->license_tier_basic_name, $this->item_id_plus => $this->license_tier_plus_name, $this->item_id_elite => $this->license_tier_elite_name, $this->item_id_personal => $this->license_tier_personal_name, $this->item_id_business => $this->license_tier_business_name, $this->item_id_developer => $this->license_tier_developer_name];
return $item_id_to_tier_name;
}
/**
* Get tier name
*
* @since string $license_tier_name
*/
public function item_name_to_tier_name()
{
$license_tier_name = '';
if (\strpos($this->edd_item_name, 'Personal') !== \false) {
$license_tier_name = $this->license_tier_personal_name;
}
if (\strpos($this->edd_item_name, 'Business') !== \false) {
$license_tier_name = $this->license_tier_business_name;
}
if (\strpos($this->edd_item_name, 'Developer') !== \false) {
$license_tier_name = $this->license_tier_developer_name;
}
if (\strpos($this->edd_item_name, 'Basic') !== \false) {
$license_tier_name = $this->license_tier_basic_name;
}
if (\strpos($this->edd_item_name, 'Plus') !== \false) {
$license_tier_name = $this->license_tier_plus_name;
}
if (\strpos($this->edd_item_name, 'Elite') !== \false) {
$license_tier_name = $this->license_tier_elite_name;
}
return $license_tier_name;
}
/**
* This converts plan price id to a readable plan name
*
* @param array $license_data License data.
*
* @return string
*/
public function convert_to_readable_plan_name(array $license_data)
{
$item_id = (int) $license_data['item_id'];
$price_id = (int) $license_data['price_id'];
$item_name_to_tier_name = $this->item_name_to_tier_name();
$license_tier_name = $this->license_tier_basic_name;
if (!empty($item_name_to_tier_name)) {
// Check if it's all access.
if ($item_id === $this->item_id_elite) {
$license_tier_name = 'all_access';
$this->is_all_access = \true;
} else {
$license_tier_name = $item_name_to_tier_name;
}
}
return $license_tier_name;
}
/**
* Check if the feature is available for the current license tier
*
* @param string $feature Feature to check.
*
* @return boolean
*/
public function is_feature_available($feature)
{
$tier_features = $this->tier_features();
if (\in_array($feature, $tier_features)) {
return \true;
}
return \false;
}
}

View File

@@ -0,0 +1,51 @@
# Licnese Tier #
This package helps to differentiate license tier features in plugins. This package contains a `License_Tier.php` abastract class and `Sample_Plugin_License_Tier.php` class which is a sample class that can be used on a plugin level with modifications.
__Usage:__
1. Copy the class `Sample_Plugin_License_Tier.php` and paste in your plugin folder.
2. Change the class name to something like `Instagram_License_Tier.php`
3. Change `$license_key_option_name`, `$license_status_option_name`, `$license_data_option_name` properties to the respected license related options.
4. Open the main plugin file e.g. `instagram-feed.php` and get the plugins download ID's of three different tiers. e.g. from [this line](https://github.com/awesomemotive/instagram-feed-pro/blob/development/instagram-feed.php#L30)
5. Now change the item ID for basic, plus, and elite in the respected properties of `Sample_Plugin_License_Tier.php` class.
6. Update the license features for each tier under `features_list()` method.
Suppose, we are in the Feed_Builder.php file and need to get the list of features against the license data user already have in database.
```
// Instagram License Tier
$license_tier = new Instagram_License_Tier();
$license_tier_features = $license_tier->tier_features();
```
This will return only the features are listed for that specific tier that declared under the `features_list()` method.
We can pass it to `builder.js` and access the features list. Then we can restrict the other features in the create feed flow or feed customizer.
Inside `builder.js`, under `data()` please the below state
```
license_tier_features : sbi_builder.license_tier_features,
```
Now, let's add this helpful method to check if any feature exists in the features list array.
```
hasFeature : function ( feature_name ) {
var self = this;
return self.license_tier_features.includes( feature_name );
}
```
For all tiers, we do not need to restrict any features for basic tier as those features will be available to all tiers. Mainly, we need to restrict features from plus and elite tiers as those will be only available to those license groups. I'll give an example of how I restrict some features in the feed creation flow and customizer.
For example, carousel feed layout is only available to "plus" tier, that means "elite" tier can also have the feature.
We need to inside the Vue method that handles clicking on feed layout toggleset. And upon clicking, we need get what feed layout is selected, if carouel is selected then check if carousel_feed is in the features list.
```
self.hasFeature('carousel_feed')
```
Note: If carousel_feed is inside the 'plus' license tier, then if the user's license is personal then automatically carousel_feed will not be available and `self.hasFeature('carousel_feed')` will return false.
Leading that, if user's license is plus or elite, then `self.hasFeature('carousel_feed')` will return true.

View File

@@ -0,0 +1,54 @@
<?php
/**
* Sample Plugin License Tier
*
* @since 1.0
*/
namespace Smashballoon\Customizer;
use Smashballoon\Framework\Packages\License_Tier\License_Tier;
/** @internal */
class Sample_Plugin_License_Tier extends License_Tier
{
/**
* This gets the license key
*/
public $license_key_option_name = 'sbi_license_key';
/**
* This gets the license status
*/
public $license_status_option_name = 'sbi_license_status';
/**
* This gets the license data
*/
public $license_data_option_name = 'sbi_license_data';
public $item_id_basic = 762236;
// put item id for the basic tier
public $item_id_plus = 762320;
// put item id for the plus tier
public $item_id_elite = 762322;
// put item id for the elite tier
public $item_id_all_access = 789157;
// this is the all access item id, no need to change
public $license_tier_basic_name = 'personal';
// basic tier name
public $license_tier_plus_name = 'business';
// plus tier name
public $license_tier_elite_name = 'developer';
// elite tier name
public function __construct()
{
parent::__construct();
}
/**
* This defines the features list of the plugin
*
* @return void
*/
public function features_list()
{
$features_list = ['personal' => [], 'business' => [], 'developer' => []];
$this->plugin_features = $features_list;
}
}

View File

@@ -0,0 +1,100 @@
<?php
/**
* Admin notice class.
*
* @package Notices
*/
namespace Smashballoon\Framework\Packages\Notification\Notices;
use function Smashballoon\Framework\sb_get_template;
if (!\defined('ABSPATH')) {
exit;
// Exit if accessed directly.
}
/**
* Admin notice class.
* @internal
*/
class AdminNotice extends \Smashballoon\Framework\Packages\Notification\Notices\Notice
{
/**
* Display notice.
*
* @return void
*/
public function display()
{
$wrap_schema = $this->wrap_schema;
$fields = $this->fields;
// Display notice.
foreach ($fields as $field => $value) {
switch ($field) {
case 'id':
$fields['id'] = \Smashballoon\Framework\Packages\Notification\Notices\NoticeFields::get_id($this->id);
break;
case 'class':
$class = 'notice-' . $this->type . ' ' . $this->class;
if ($this->dismissible) {
$class .= ' is-dismissible';
}
$fields['class'] = \Smashballoon\Framework\Packages\Notification\Notices\NoticeFields::get_class($class);
break;
case 'wrap_id':
$fields['wrap_id'] = \Smashballoon\Framework\Packages\Notification\Notices\NoticeFields::get_id($this->wrap_id);
break;
case 'wrap_class':
$fields['wrap_class'] = \Smashballoon\Framework\Packages\Notification\Notices\NoticeFields::get_class($this->wrap_class);
break;
case 'styles':
$fields['styles'] = \Smashballoon\Framework\Packages\Notification\Notices\NoticeFields::get_styles($this->styles);
break;
case 'data':
$fields['data'] = \Smashballoon\Framework\Packages\Notification\Notices\NoticeFields::get_data($this->data);
break;
case 'image':
$fields['image'] = \Smashballoon\Framework\Packages\Notification\Notices\NoticeFields::get_image($this->image);
break;
case 'icon':
$fields['icon'] = \Smashballoon\Framework\Packages\Notification\Notices\NoticeFields::get_image($this->icon);
break;
case 'title':
$fields['title'] = \Smashballoon\Framework\Packages\Notification\Notices\NoticeFields::get_title($this->title);
break;
case 'message':
$fields['message'] = \Smashballoon\Framework\Packages\Notification\Notices\NoticeFields::get_content($this->message);
break;
case 'buttons':
$fields['buttons'] = \Smashballoon\Framework\Packages\Notification\Notices\NoticeFields::get_buttons($this->buttons, $this->buttons_wrap_start, $this->buttons_wrap_end);
break;
case 'dismiss':
$fields['dismiss'] = $this->dismissible ? \Smashballoon\Framework\Packages\Notification\Notices\NoticeFields::get_dismiss($this->dismiss) : '';
break;
case 'navigation':
$fields['navigation'] = $this->nav ? \Smashballoon\Framework\Packages\Notification\Notices\NoticeFields::get_navigation($this->navigation) : '';
break;
default:
$fields[$field] = $value;
break;
}
}
// Replace fields in wrap schema.
$notice = $this->replace_fields($wrap_schema, $fields);
$this->print_notice($notice);
}
/**
* Print notice.
*
* @param string $notice
*
* @return void
*/
public function print_notice($notice)
{
\ob_start();
sb_get_template('Notification/templates/' . $this->type . '.php', ['notice' => $notice, 'type' => $this->type, 'id' => $this->id]);
$notice_html = \ob_get_clean();
$notice_html = \apply_filters('sb_' . $this->type . '_notice_markup', $notice_html);
echo $notice_html;
}
}

View File

@@ -0,0 +1,201 @@
<?php
/**
* Notice class
*
* @package Notices
*/
namespace Smashballoon\Framework\Packages\Notification\Notices;
use Smashballoon\Framework\Packages\Notification\Notices\NoticeFields;
if (!\defined('ABSPATH')) {
exit;
// Exit if accessed directly.
}
/**
* Abstract Notice class.
* @internal
*/
abstract class Notice
{
/**
* Notice type
*
* @var string
*/
protected $type;
/**
* Notice message
*
* @var string
*/
protected $message;
/**
* Notice title
*
* @var array
*/
protected $title;
/**
* Notice icon
*
* @var string
*/
protected $icon;
/**
* Notice image
*
* @var string
*/
protected $image;
/**
* Notice class
*
* @var string
*/
protected $class;
/**
* Notice id
*
* @var string
*/
protected $id;
/**
* Notice wrap class
*
* @var string
*/
protected $wrap_class;
/**
* Notice wrap id
*
* @var string
*/
protected $wrap_id;
/**
* Notice data
*
* @var string
*/
protected $data;
/**
* Notice dismissible
*
* @var boolean
*/
protected $dismissible;
/**
* Notice dismiss
*
* @var string
*/
protected $dismiss;
/**
* Notice navigation
*
* @var boolean
*/
protected $nav;
/**
* Notice nav navigation
*
* @var string
*/
protected $navigation;
/**
* Notice buttons
*
* @var array
*/
protected $buttons;
/**
* Notice buttons wrap start
*
* @var string
*/
protected $buttons_wrap_start;
/**
* Notice buttons wrap end
*
* @var string
*/
protected $buttons_wrap_end;
/**
* Notice wrap schema
*
* @var string
*/
protected $wrap_schema;
/**
* Notice styles
*
* @var string
*/
protected $styles;
/**
* Notice fields
*
* @var array
*/
protected $fields = ['wrap_class' => '', 'wrap_id' => '', 'id' => '', 'class' => '', 'data' => '', 'icon' => '', 'image' => '', 'title' => '', 'message' => '', 'buttons' => '', 'dismiss' => '', 'navigation' => '', 'styles' => ''];
/**
* Current screen.
*
* @var string
*/
protected $screen;
/**
* Notice constructor.
*
* @param $args
*/
public function __construct($args)
{
$this->screen = isset($_GET['page']) ? \sanitize_text_field(\wp_unslash($_GET['page'])) : '';
$args = \wp_parse_args($args, ['type' => 'error', 'message' => '', 'title' => '', 'icon' => '', 'image' => '', 'class' => '', 'id' => '', 'dismissible' => \false, 'dismiss' => '', 'buttons' => [], 'buttons_wrap_start' => '', 'buttons_wrap_end' => '', 'wrap_schema' => '<div {id} {class}>{icon}{title}{message}{buttons}</div>', 'nav' => \false, 'navigation' => '', 'wrap_class' => '', 'wrap_id' => '', 'data' => '', 'styles' => '']);
$this->type = $args['type'];
$this->message = $args['message'];
$this->title = $args['title'];
$this->icon = $args['icon'];
$this->image = $args['image'];
$this->class = $args['class'];
$this->id = $args['id'];
$this->wrap_class = $args['wrap_class'];
$this->wrap_id = $args['wrap_id'];
$this->data = $args['data'];
$this->dismissible = $args['dismissible'];
$this->dismiss = $args['dismiss'];
$this->buttons = $args['buttons'];
$this->buttons_wrap_start = $args['buttons_wrap_start'];
$this->buttons_wrap_end = $args['buttons_wrap_end'];
$this->wrap_schema = $args['wrap_schema'];
$this->nav = $args['nav'];
$this->navigation = $args['navigation'];
$this->styles = $args['styles'];
NoticeFields::set_screen($this->screen);
}
/**
* Display notice
*
* @return void
*/
public abstract function display();
/**
* Replace fields in notice.
*
* @param string $notice
* @param array $fields
*
* @return string
*/
public function replace_fields($notice, $fields)
{
if (!empty($fields)) {
foreach ($fields as $key => $value) {
$notice = \str_replace('{' . $key . '}', $value, $notice);
}
$notice = \wp_kses($notice, NoticeFields::$allowed_tags);
}
return $notice;
}
}

View File

@@ -0,0 +1,376 @@
<?php
/**
* Notice fields class
*
* @package Notices
*/
namespace Smashballoon\Framework\Packages\Notification\Notices;
if (!\defined('ABSPATH')) {
exit;
// Exit if accessed directly.
}
/**
* Notice fields class.
* @internal
*/
class NoticeFields
{
/**
* Current screen.
*
* @var string
*/
protected static $screen;
/**
* Set current screen.
*
* @param string $screen Current screen.
*
* @return void
*/
public static function set_screen($screen)
{
self::$screen = $screen;
}
/**
* Content allowed tags
*
* @var array
*/
public static $allowed_tags = ['a' => ['href' => [], 'title' => [], 'target' => [], 'class' => [], 'id' => [], 'rel' => [], 'style' => [], 'data-*' => \true, 'v-on:click' => [], 'v-html' => [], 'v-bind:class' => []], 'br' => [], 'em' => [], 'strong' => [], 'code' => [], 'span' => ['class' => [], 'id' => [], 'style' => [], 'data-*' => \true], 'p' => ['class' => [], 'id' => [], 'style' => [], 'data-*' => \true], 'div' => ['class' => [], 'id' => [], 'style' => [], 'data-*' => \true], 'img' => ['src' => [], 'class' => [], 'id' => [], 'alt' => []], 'button' => ['class' => [], 'id' => [], 'type' => [], 'style' => [], 'data-*' => \true], 'h1' => ['class' => [], 'id' => [], 'style' => [], 'data-*' => \true], 'h2' => ['class' => [], 'id' => [], 'style' => [], 'data-*' => \true], 'h3' => ['class' => [], 'id' => [], 'style' => [], 'data-*' => \true], 'h4' => ['class' => [], 'id' => [], 'style' => [], 'data-*' => \true], 'h5' => ['class' => [], 'id' => [], 'style' => [], 'data-*' => \true], 'h6' => ['class' => [], 'id' => [], 'style' => [], 'data-*' => \true]];
/**
* Get id attribute for notice.
*
* @param string $id
*
* @return string
*/
public static function get_id($id)
{
return $id ? 'id="' . \esc_attr($id) . '"' : '';
}
/**
* Get class attribute for notice.
*
* @param string $class
*
* @return string
*/
public static function get_class($class)
{
return $class ? 'class="' . \esc_attr($class) . '"' : '';
}
/**
* Get style attribute for notice.
*
* @param mixed $styles
*
* @return string
*/
public static function get_styles($styles)
{
$styles_attr = '';
if (isset($styles) && \is_array($styles)) {
foreach ($styles as $styles_key => $styles_value) {
if (\is_array($styles_value) && isset($styles_value['condition'])) {
$check = self::check_condition($styles_value['condition']);
$styles_value = $check ? $styles_value['true'] : $styles_value['false'];
}
$styles_attr .= \esc_attr($styles_key) . ':' . \esc_attr($styles_value) . ';';
}
} else {
if (isset($styles) && \is_string($styles)) {
$styles_attr = $styles;
}
}
return $styles_attr ? 'style="' . $styles_attr . '"' : '';
}
/**
* Get data attribute for notice.
*
* @param mixed $data
*
* @return string
*/
public static function get_data($data)
{
$data_attr = '';
if (isset($data) && \is_array($data)) {
foreach ($data as $data_key => $data_value) {
if (\is_array($data_value) && isset($data_value['condition'])) {
$check = self::check_condition($data_value['condition']);
$data_value = $check ? $data_value['true'] : $data_value['false'];
}
$data_attr .= 'data-' . \esc_attr($data_key) . '="' . \esc_attr($data_value) . '" ';
}
}
return $data_attr;
}
/**
* Get image/icon for notice.
*
* @param mixed $image
*
* @return string
*/
public static function get_image($image)
{
// Check if image is a url.
if (isset($image) && \filter_var($image, \FILTER_VALIDATE_URL)) {
$image_html = '<img src="' . \esc_url($image) . '" />';
}
if (isset($image) && \is_array($image)) {
$src = isset($image['src']) ? 'src="' . \esc_url($image['src']) . '"' : '';
$alt = isset($image['alt']) ? ' alt="' . \esc_attr($image['alt']) . '"' : '';
$wrap = isset($image['wrap']) ? $image['wrap'] : '';
$overlay = isset($image['overlay']) ? $image['overlay'] : '';
$overlay_wrap = isset($image['overlay_wrap']) ? \esc_html($image['overlay_wrap']) : '';
$overlay_wrap = \str_replace('{overlay}', $overlay, $overlay_wrap);
if ($wrap) {
$wrap = \str_replace('{src}', $src, $wrap);
$wrap = \str_replace('{alt}', $alt, $wrap);
$wrap = \str_replace('{overlay}', $overlay_wrap, $wrap);
$image_html = \wp_kses($wrap, self::$allowed_tags);
} else {
$image_html = '<img src="' . \esc_url($src) . '" alt="' . \esc_attr($alt) . '" />';
}
} else {
if (isset($image) && \is_string($image)) {
$image_html = \wp_kses($image, self::$allowed_tags);
}
}
return $image_html;
}
/**
* Get title for notice.
*
* @param mixed $title
*
* @return string
*/
public static function get_title($title)
{
$title_html = '';
if (isset($title) && \is_array($title)) {
$title_tag = isset($title['tag']) ? $title['tag'] : 'h3';
$title_class = isset($title['class']) ? $title['class'] : '';
$title = isset($title['text']) ? $title['text'] : '';
$title_html = $title ? '<' . \esc_attr($title_tag) . ' class="' . \esc_attr($title_class) . '">' . \esc_html($title) . '</' . \esc_attr($title_tag) . '>' : '';
} else {
if (isset($title) && \is_string($title)) {
$title_html = '<h3>' . \esc_html($title) . '</h3>';
}
}
return $title_html;
}
/**
* Get content for notice.
*
* @param mixed $content
*
* @return string
*/
public static function get_content($content)
{
$content_html = '';
if (isset($content) && \is_array($content)) {
$content_tag = isset($content['tag']) ? $content['tag'] : 'p';
$content_class = isset($content['class']) ? $content['class'] : '';
$content = isset($content['text']) ? $content['text'] : '';
$content_html = $content ? '<' . \esc_attr($content_tag) . ' class="' . \esc_attr($content_class) . '">' . \esc_html($content) . '</' . \esc_attr($content_tag) . '>' : '';
} else {
if (isset($content) && \is_string($content)) {
$content_html = \wp_kses($content, self::$allowed_tags);
}
}
return $content_html;
}
/**
* Get button for notice.
*
* @param mixed $buttons
* @param string $wrap_start
* @param string $wrap_end
*
* @return string
*/
public static function get_buttons($buttons, $wrap_start, $wrap_end)
{
$buttons_html = '';
if (isset($buttons) && \is_array($buttons)) {
$buttons_html .= isset($wrap_start) ? \wp_kses($wrap_start, self::$allowed_tags) : '';
foreach ($buttons as $button) {
// conditional check for button.
if (isset($button['condition'])) {
if (!self::check_condition($button['condition'])) {
continue;
}
}
$btn_url = isset($button['url']) ? $button['url'] : '';
if (\is_array($btn_url) && isset($btn_url['args']) && isset($btn_url['action'])) {
$args = [];
foreach ($btn_url['args'] as $key => $value) {
$args[\sanitize_key($key)] = \sanitize_key($value);
}
$action = isset($btn_url['action']) ? \sanitize_key($btn_url['action']) : '';
$nonce = isset($btn_url['nonce']) ? \sanitize_key($btn_url['nonce']) : 'sbi_nonce';
$btn_url = \wp_nonce_url(\add_query_arg($args), $action, $nonce);
}
$tag = isset($button['tag']) ? $button['tag'] : 'a';
$btn_class = isset($button['class']) ? 'class="' . \esc_attr($button['class']) . '"' : '';
$btn_id = isset($button['id']) ? 'id="' . \esc_attr($button['id']) . '"' : '';
$btn_target = isset($button['target']) ? 'target="' . \esc_attr($button['target']) . '"' : '';
$btn_rel = isset($button['rel']) ? 'rel="' . \esc_attr($button['rel']) . '"' : '';
$btn_href = !empty($btn_url) ? 'href="' . \esc_url($btn_url) . '"' : '';
$btn_vue = isset($button['vue']) ? $button['vue'] : '';
$btn_text = isset($button['text']) ? \esc_html($button['text']) : '';
$btn_data = isset($button['data']) ? self::get_data($button['data']) : '';
$buttons_html .= '<' . \esc_attr($tag) . ' ' . $btn_class . ' ' . $btn_id . ' ' . $btn_target . ' ' . $btn_rel . ' ' . $btn_href . ' ' . $btn_vue . ' ' . $btn_data . '>' . $btn_text . '</' . \esc_attr($tag) . '>';
}
$buttons_html .= isset($wrap_end) ? \wp_kses($wrap_end, self::$allowed_tags) : '';
}
return $buttons_html;
}
/**
* Get dismiss button for notice.
*
* @param mixed $dismiss
*
* @return string
*/
public static function get_dismiss($dismiss)
{
$dismiss_html = '';
if (isset($dismiss) && \is_array($dismiss)) {
$icon = isset($dismiss['icon']) ? \esc_url($dismiss['icon']) : '';
$image_html = '<img src="' . \esc_url($icon) . '" />';
$title = isset($dismiss['title']) ? \esc_attr($dismiss['title']) : \esc_attr__('Dismiss this message', 'instagram-feed');
$class = isset($dismiss['class']) ? \esc_attr($dismiss['class']) : '';
$tag = isset($dismiss['tag']) ? \esc_attr($dismiss['tag']) : 'a';
$attr = isset($dismiss['attr']) ? $dismiss['attr'] : '';
$href = isset($dismiss['href']) ? $dismiss['href'] : '';
if (\is_array($href) && isset($href['args']) && isset($href['action'])) {
$args = [];
foreach ($href['args'] as $key => $value) {
$args[\sanitize_key($key)] = \sanitize_key($value);
}
$action = isset($href['action']) ? \sanitize_key($href['action']) : '';
$nonce = isset($href['nonce']) ? \sanitize_key($href['nonce']) : 'sbi_nonce';
$href = \wp_nonce_url(\add_query_arg($args), $action, $nonce);
}
$url = !empty($href) ? 'href="' . \esc_url($href) . '"' : '';
$dismiss_html = '<' . \esc_attr($tag) . ' ' . $url . ' ' . $attr . ' class="' . $class . '" title="' . $title . '">' . $image_html . '</' . \esc_attr($tag) . '>';
} else {
if (isset($dismiss) && \is_string($dismiss)) {
$dismiss_html = $dismiss;
}
}
$dismiss_html = \wp_kses($dismiss_html, self::$allowed_tags);
return $dismiss_html;
}
/**
* Get navigation for notice.
*
* @param mixed $navigation
*
* @return string
*/
public static function get_navigation($navigation)
{
$navigation_html = '';
if (isset($navigation) && \is_array($navigation)) {
$class = isset($navigation['class']) ? \esc_attr($navigation['class']) : '';
$tag = isset($navigation['tag']) ? \esc_attr($navigation['tag']) : 'a';
$item_html = '';
foreach ($navigation['items'] as $item) {
$item_class = isset($item['class']) ? \esc_attr($item['class']) : '';
$item_tag = isset($item['tag']) ? \esc_attr($item['tag']) : 'a';
$icon = isset($item['icon']) ? \esc_url($item['icon']) : '';
$image_html = '<img src="' . \esc_url($icon) . '" />';
$title = isset($item['title']) ? \esc_attr($item['title']) : '';
$attr = isset($item['attr']) ? \esc_attr($item['attr']) : '';
$item_html .= '<' . $item_tag . ' class="' . $item_class . '" title="' . $title . '" ' . $attr . '>' . $image_html . '</' . $item_tag . '>';
}
$navigation_html = '<' . $tag . ' class="' . $class . '">' . $item_html . '</' . $tag . '>';
} else {
if (isset($navigation) && \is_string($navigation)) {
$navigation_html = $navigation;
}
}
$navigation_html = \wp_kses($navigation_html, self::$allowed_tags);
return $navigation_html;
}
/**
* Check condition.
*
* @param array $condition
*
* @return boolean
*/
public static function check_condition($condition)
{
$check = \false;
switch ($condition['key']) {
case 'screen':
$check = self::check_screen($condition['compare'], $condition['value']);
break;
case 'option':
$check = self::check_option($condition);
break;
}
return $check;
}
/**
* Check screen.
*
* @param string $compare
* @param string $screen
*
* @return boolean
*/
public static function check_screen($compare, $screen)
{
$check = \false;
switch ($compare) {
case '===':
$check = $screen === self::$screen;
break;
case '!==':
$check = $screen !== self::$screen;
break;
}
return $check;
}
/**
* Check option.
*
* @param string $name
* @param string $compare
* @param string $value
*
* @return boolean
*/
public static function check_option($condition)
{
$name = isset($condition['name']) ? $condition['name'] : '';
$compare = isset($condition['compare']) ? $condition['compare'] : '===';
$value = isset($condition['value']) ? $condition['value'] : '';
$default = isset($condition['default']) ? $condition['default'] : \false;
if (!$name) {
return \false;
}
$check = \false;
$option = \get_option($name, $default);
switch ($compare) {
case '===':
$check = $option === $value;
break;
case '!==':
$check = $option !== $value;
break;
}
return $check;
}
}

View File

@@ -0,0 +1,376 @@
<?php
/**
* Notices class
*
* @package Notices
*/
namespace Smashballoon\Framework\Packages\Notification\Notices;
use Smashballoon\Framework\Packages\Notification\Notices\AdminNotice;
use function Smashballoon\Framework\sb_map_notice_hooks;
use function Smashballoon\Framework\sb_get_plugin_type;
if (!\defined('ABSPATH')) {
exit;
}
/**
* Get all notices and display error warning or success notices
* @internal
*/
class SBNotices
{
/**
* Notices
*
* @var array
*/
private $notices = [];
/**
* Group notices
*
* @var array
*/
private $group_notices = [];
/**
* Current screen.
*
* @var string
*/
private $screen;
/**
* Plugin - Example: instagram_feed
*
* @var string
*/
private $plugin;
/**
* Plugin slug name - Example: instagram-feed
*
* @var string
*/
private $plugin_slug;
/**
* Plugin type - Example: free or pro
*
* @var string
*/
private $plugin_type;
/**
* Notice options
*
* @var string
*/
private $notice_option;
private $group_notice_option;
/**
* The single instance of the class.
*
* @var SBNotices
*/
protected static $instance = null;
/**
* Main SBNotices Instance.
*
* Ensures only one instance of SBNotices is loaded or can be loaded.
*
* @since 1.0.0
* @static
* @return SBNotices - Main instance.
*/
public static function instance($plugin_slug = '')
{
if (\is_null(self::$instance)) {
self::$instance = new self($plugin_slug);
}
return self::$instance;
}
/**
* Constructor
*/
public function __construct($plugin_slug = '')
{
$this->plugin_slug = $plugin_slug;
$this->plugin_type = sb_get_plugin_type($this->plugin_slug);
$plugin_slug = \str_replace('-', '_', $plugin_slug);
$this->plugin = \str_replace('_pro', '', $plugin_slug);
$this->notice_option = \sanitize_key('sb_' . $this->plugin . '_notices');
$this->group_notice_option = \sanitize_key('sb_' . $this->plugin . '_group_notices');
$this->notices = \get_option($this->notice_option, []);
$this->screen = isset($_GET['page']) ? \sanitize_text_field(\wp_unslash($_GET['page'])) : '';
$this->group_notices = \get_option($this->group_notice_option, []);
$notice_hook = sb_map_notice_hooks($this->plugin_slug);
\add_action('admin_notices', [$this, 'display_notices']);
\add_action($notice_hook, [$this, 'display_notices']);
\add_action('admin_init', [$this, 'dismiss_notices'], 20);
\add_filter('safe_style_css', function ($styles) {
$styles[] = 'display';
return $styles;
});
}
/**
* Get all notices
*
* @return array
*/
public function get_notices()
{
return $this->notices;
}
/**
* Set notices
*
* @param array $notices Notices.
*
* @return void
*/
public function set_notices($notices)
{
$this->notices = $notices;
}
/**
* Get group notices
*
* @return array
*/
public function get_group_notices()
{
return $this->group_notices;
}
/**
* Set group notices
*
* @param array $group_notices Group notices.
*
* @return void
*/
public function set_group_notices($group_notices)
{
$this->group_notices = $group_notices;
}
/**
* Display notices
*
* @return void
*/
public function display_notices()
{
// Validate notifications.
$this->validate_notices();
if ($this->notices) {
foreach ($this->notices as $notice) {
// Check notice type.
switch ($notice['type']) {
// Admin notice.
case 'error':
case 'warning':
case 'information':
default:
$error = new AdminNotice($notice);
$error->display();
break;
}
}
}
}
/**
* Validate notices
*
* @return void
*/
private function validate_notices()
{
$notices = $this->get_notices();
$has_admin_errors = \apply_filters('sb_' . $this->plugin . '_has_admin_errors', \false);
if ($notices) {
foreach ($notices as $key => $notice) {
if (!isset($notice['type']) || !isset($notice['message'])) {
unset($notices[$key]);
}
// Check if critical error is present then unset 'information' notices.
if ($has_admin_errors && \in_array($notice['type'], ['information', 'warning'], \true)) {
unset($notices[$key]);
}
// Check start and end date, and unset if expired.
if ($notice['start_date'] && $notice['end_date']) {
if (\strtotime($notice['start_date']) > \time() || \strtotime($notice['end_date']) < \time()) {
unset($notices[$key]);
}
}
// Check page and unset if not match.
if (isset($notice['page']) && !empty($notice['page'])) {
$page = $notice['page'];
if (!\is_array($page)) {
$page = [$page];
}
if (!\in_array($this->screen, $page, \true)) {
unset($notices[$key]);
}
}
// If page has exclude then unset if match.
if (isset($notice['page_exclude']) && !empty($notice['page_exclude'])) {
$page_exclude = $notice['page_exclude'];
if (!\is_array($page_exclude)) {
$page_exclude = [$page_exclude];
}
if (\in_array($this->screen, $page_exclude, \true)) {
unset($notices[$key]);
}
}
// Check capability and unset if not match.
if (isset($notice['capability']) && !empty($notice['capability'])) {
$capability = $notice['capability'];
if (!\is_array($capability)) {
$capability = [$capability];
}
if (!\current_user_can($capability[0])) {
unset($notices[$key]);
}
}
// Check if notice is for free or pro version and unset if not match.
if (isset($notice['version']) && !empty($notice['version'])) {
if ($this->plugin_type !== $notice['version']) {
unset($notices[$key]);
}
}
}
// Notices are duplicate so unset them.
$notices = \array_unique($notices, \SORT_REGULAR);
// Sort notices as per priority value.
\uasort($notices, function ($a, $b) {
if (isset($a['priority']) && isset($b['priority'])) {
return $a['priority'] - $b['priority'];
}
return 255;
});
$notices = \apply_filters('sb_' . $this->plugin . '_admin_notices', $notices);
$this->set_notices($notices);
}
}
/**
* Get notice by id
*
* @param string $id
*
* @return array|boolean
*/
public function get_notice($id)
{
$notices = $this->get_notices();
return isset($notices[$id]) ? $notices[$id] : \false;
}
/**
* Add notice
*
* @param string $id
* @param string $type
* @param array $args
* @param string $group
*
* @return void
*/
public function add_notice($id, $type, $args, $group = \false)
{
if (empty($id) || empty($args['title']) && empty($args['message'])) {
return;
}
$type = \in_array($type, ['error', 'warning', 'information'], \true) ? $type : 'error';
$notices = $this->get_notices();
// Check if notice already exists.
if (isset($notices[$id])) {
return;
}
// Merge with defaults.
$notice = \wp_parse_args($args, ['id' => $id, 'type' => $type, 'message' => '', 'title' => '', 'icon' => '', 'class' => '', 'dismissible' => \false, 'priority' => 255, 'start_date' => \false, 'end_date' => \false]);
// Add notice to notices array.
$notices[$id] = $notice;
if ($group) {
// Add notice to group.
$notices[$id]['group'] = $group;
}
// Update notices.
$this->set_notices($notices);
\update_option($this->notice_option, $notices);
// Handle group notices.
if ($group) {
$group_notices = $this->get_group_notices();
if (!isset($group_notices[$group])) {
$group_notices[$group] = [];
}
$group_notices[$group][] = $id;
$this->set_group_notices($group_notices);
\update_option($this->group_notice_option, $group_notices);
}
}
/**
* Remove notice by id
*
* @param string $id
*
* @return void
*/
public function remove_notice($id)
{
$notices = $this->get_notices();
if (isset($notices[$id])) {
// Handle group notices.
$group_notices = $this->get_group_notices();
$is_group_notice = isset($notices[$id]['group']) ? $notices[$id]['group'] : \false;
if ($is_group_notice) {
$group_id = $notices[$id]['group'];
if (isset($group_notices[$group_id])) {
$group_notices[$group_id] = \array_diff($group_notices[$group_id], [$id]);
$this->set_group_notices($group_notices);
\update_option($this->group_notice_option, $group_notices);
}
}
unset($notices[$id]);
$this->set_notices($notices);
\update_option($this->notice_option, $notices);
}
}
/**
* Remove all notices
*
* @return void
*/
public function remove_all_notices()
{
$this->set_notices([]);
$this->set_group_notices([]);
\delete_option($this->notice_option);
\delete_option($this->group_notice_option);
}
/**
* Dismiss notices if the GET param is set.
*
* @return void
*/
public function dismiss_notices()
{
if (isset($_GET['sb-dismiss-notice']) && isset($_GET['_sb_notice_nonce'])) {
if (!\wp_verify_nonce(\wp_unslash($_GET['_sb_notice_nonce']), 'sb_dismiss_notice_nonce')) {
\wp_die(\esc_html__('Action failed. Please refresh the page and retry.', 'sb-notices'));
}
$notice_id = \sanitize_text_field(\wp_unslash($_GET['sb-dismiss-notice']));
$notices = $this->get_notices();
if (isset($notices[$notice_id])) {
$notice = $notices[$notice_id];
if (!$notice['dismissible']) {
\wp_die(\esc_html__('Notice cannot be dismissed.', 'sb-notices'));
}
if (isset($notice['capability']) && !empty($notice['capability'])) {
$capability = $notice['capability'];
if (!\is_array($capability)) {
$capability = [$capability];
}
if (!\current_user_can($capability[0])) {
\wp_die(\esc_html__('You do not have permission to dismiss the notice.', 'sb-notices'));
}
}
$this->remove_notice($notice_id);
\update_user_meta(\get_current_user_id(), 'sb_notice_' . $notice_id . '_dismissed', \true);
\do_action('sb_notice_' . $notice_id . '_dismissed', $notice_id);
}
}
}
}

View File

@@ -0,0 +1,72 @@
# Notices
Add in-plugin notifications to your plugin.
### Usage
```php
use Smashballoon\Framework\Packages\Notification\Notices\SBNotices;
$sbi_notices = SBNotices::instance( 'instagram-feed-pro' );
```
Replace the `instagram-feed-pro` with your plugin slug.
### Add a notice
```php
$sbi_notices->add_notice( 'notice-id', 'notice-type', 'notice-args' );
```
notice-id: Unique ID for the notice.<br>
notice-type: Type of notice. Can be `error`, `warning`, `information`.<br>
notice-args: Array of arguments for the notice.
### Remove a notice
```php
$sbi_notices->remove_notice( 'notice-id' );
```
### Sample notice arguments
```php
array(
'class' => 'notice notice-error',
'id' => 'notice-id',
'title' => array(
'text' => 'Notice Title',
'tag' => 'h3',
'class' => 'notice-title',
),
'message' => '<p>Notice message</p>',
'priority' => 10,
'dismissible' => true,
'dismiss' => array(
'class' => 'notice-dismiss',
'icon' => 'https://www.example.com/icon.png',
'tag' => 'button'
),
'page' => array('instagram-feed'),
'capability' => 'manage_options',
'buttons' => array(
array(
'text' => 'Button Text',
'class' => 'button button-primary',
'id' => 'button-id',
'url' => 'https://www.example.com',
'target' => '_blank',
'tag' => 'a',
),
),
'buttons_wrap_start' => '<div class="notice-buttons">',
'buttons_wrap_end' => '</div>',
'icon' => array(
'src' => 'https://www.example.com/icon.png',
'alt' => 'Icon Alt',
'wrap' => '<div class="notice-icon"><img {src} {alt}></div>',
),
'wrap_schema' => '<div {class} {id}><div>{dismiss}</div>{icon}{title}{message}{buttons}</div>',
)
```

View File

@@ -0,0 +1,34 @@
<?php
namespace SmashBalloon\YoutubeFeed\Vendor;
/**
* Show error messages
*
* This template can be overridden by copying it to yourtheme/smashballoon/Notification/templates/error.php.
*
* HOWEVER, on occasion Notices will need to update template files and you
* (the theme developer) will need to copy the new files to your theme to
* maintain compatibility. We try to do this as little as possible, but it does
* happen. When this occurs the version of the template file will be bumped and
* the readme will list any important changes.
*
* @see https://smashballoon.com/doc/
* @package Notification\templates
* @version 1.0.0
*/
if (!\defined('ABSPATH')) {
exit;
}
if (!$notice) {
return;
}
/*
* Fires before the notices are displayed.
*/
\do_action('sb_notices_before_error_notice');
echo $notice;
/*
* Fires after the notices are displayed.
*/
\do_action('sb_notices_after_error_notice');

View File

@@ -0,0 +1,34 @@
<?php
namespace SmashBalloon\YoutubeFeed\Vendor;
/**
* Show messages
*
* This template can be overridden by copying it to yourtheme/smashballoon/Notification/templates/information.php.
*
* HOWEVER, on occasion Notices will need to update template files and you
* (the theme developer) will need to copy the new files to your theme to
* maintain compatibility. We try to do this as little as possible, but it does
* happen. When this occurs the version of the template file will be bumped and
* the readme will list any important changes.
*
* @see https://smashballoon.com/doc/
* @package Notification\templates
* @version 1.0.0
*/
if (!\defined('ABSPATH')) {
exit;
}
if (!$notice) {
return;
}
/*
* Fires before the notices are displayed.
*/
\do_action('sb_notices_before_information_notice');
echo $notice;
/*
* Fires after the notices are displayed.
*/
\do_action('sb_notices_after_information_notice');

View File

@@ -0,0 +1,34 @@
<?php
namespace SmashBalloon\YoutubeFeed\Vendor;
/**
* Show messages
*
* This template can be overridden by copying it to yourtheme/smashballoon/Notification/templates/warning.php.
*
* HOWEVER, on occasion Notices will need to update template files and you
* (the theme developer) will need to copy the new files to your theme to
* maintain compatibility. We try to do this as little as possible, but it does
* happen. When this occurs the version of the template file will be bumped and
* the readme will list any important changes.
*
* @see https://smashballoon.com/doc/
* @package Notification\templates
* @version 1.0.0
*/
if (!\defined('ABSPATH')) {
exit;
}
if (!$notice) {
return;
}
/*
* Fires before the notices are displayed.
*/
\do_action('sbi_before_warning_notice');
echo $notice;
/*
* Fires after the notices are displayed.
*/
\do_action('sbi_after_warning_notice');