This commit is contained in:
2024-05-20 15:37:46 +03:00
commit 00b7dbd0b7
10404 changed files with 3285853 additions and 0 deletions

View File

@ -0,0 +1,166 @@
# WDEV Free Notices Module #
WPMU DEV Free Notices module (short wpmu-free-notices) is used in our free plugins hosted on wordpress.org
It will display,
* A welcome message upon plugin activation that offers the user a 5-day introduction email course for the plugin.
* After 7 days a message asking the user to rate the plugin on wordpress.org.
* After 2 days a giveaway notice asking for email subscription.
# How to use it #
1. Insert this repository as **sub-module** into the existing project
2. Include the file `module.php` in your main plugin file.
3. Call the action `wpmudev_register_notices` with the params mentioned below.
4. Done!
# Upgrading to v2.0
The 2.0 release is backward incompatible with the 1.x versions. To accommodate new functionality and fix WordPress coding standards violations, a lot of the hooks/filters have been refactored.
Make sure to change the following:
1. Update the `do_action` hook name from `wdev-register-plugin` to `wpmudev_register_notices`.
2. Update the params to new format (See example below).
3. Both `wdev-email-message-` and `wdev-rating-message-` filters have been changed to `wdev_email_title_`/`wdev_email_message_` and `wdev_rating_title_`/`wdev_rating_message_`
## IMPORTANT:
DO NOT include this submodule in Pro plugins. These notices are only for wp.org versions.
## Code Example : Registering a plugin (from Smush) ##
```
#!php
<?php
add_action( 'admin_init', 'mycustom_free_notices_init' );
function mycustom_free_notices_init() {
// Load the notices module.
include_once 'external/free-notices/module.php';
// Register the current plugin for notices.
do_action(
'wpmudev_register_notices',
'smush', // Required: plugin id. Get from the below list.
array(
'basename' => WP_SMUSH_BASENAME, // Required: Plugin basename (for backward compat).
'title' => 'Smush', // Plugin title.
'wp_slug' => 'wp-smushit', // Plugin slug on wp.org
'cta_email' => __( 'Get Fast!', 'ga_trans' ), // Email button CTA.
'installed_on' => time(), // Plugin installed time (timestamp). Default to current time.
'screens' => array( // Screen IDs of plugin pages.
'toplevel_page_smush',
'smush_page_smush-bulk',
'smush_page_smush-directory',
),
)
);
}
```
> IMPORTANT: Make sure to initialize this on a hook which is executed in admin-ajax requests too. The recommended hook is `admin_init`
## Plugins and IDs
Only wp.org plugins are listed below.
| Plugin | ID |
|-------------|-------------|
| Smush | smush |
| Hummingbird | hummingbird |
| Defender | defender |
| SmartCrawl | smartcrawl |
| Forminator | forminator |
| Hustle | hustle |
| Snapshot | snapshot |
| Branda | branda |
| Beehive | beehive |
## Testing Notices
To see the notices before the due time, you can fake the current time by appending `&wpmudev_notice_time=CUSTOMTIMESTAMP` to the url on a page where the notice should be visible. Please make sure you are using a timestamp after the due time.
## Optional: Customize the notices via filters ##
```
<?php
// The email message contains 1 variable: plugin-name
add_filter(
'wdev_email_message_smush', // change plugin id.
'custom_email_message'
);
function custom_email_message( $message ) {
$message = 'You installed %s! This is a custom <u>email message</u>';
return $message;
}
```
```
<?php
// The rating message contains 2 variables: user-name, plugin-name
add_filter(
'wdev_rating_message_smush', // Change plugin id.
'custom_rating_message'
);
function custom_rating_message( $message ) {
$message = 'Hi %s, you used %s for a while now! This is a custom <u>rating message</u>';
return $message;
}
```
```
<?php
// To disable or enable a notice type.
add_filter(
'wpmudev_notices_is_disabled',
'custom_rating_message',
10,
2
);
function disable_rating_message( $disabled, $type, $plugin ) {
if ( 'rate' === $type && 'beehive' === $plugin ) {
return true;
}
return $disabled;
}
```
# Development
Do not commit anything directly to `master` branch. The `master` branch should always be production ready. All plugins will be using it as a submodule.
## Build Tasks (npm)
Everything should be handled by npm. Note that you don't need to interact with Gulp in a direct way.
| Command | Action |
|----------------------|--------------------------------------------------------|
| `npm run watch` | Compiles and watch for changes. |
| `npm run compile` | Compile production ready assets. |
| `npm run build` | Build production ready submodule inside `/build/` folder |
## Git Workflow
- Create a new branch from `dev` branch: `git checkout -b branch-name`. Try to give it a descriptive name. For example:
- `release/X.X.X` for next releases
- `new/some-feature` for new features
- `enhance/some-enhancement` for enhancements
- `fix/some-bug` for bug fixing
- Make your commits and push the new branch: `git push -u origin branch-name`
- File the new Pull Request against `dev` branch
- Assign somebody to review your code.
- Once the PR is approved and finished, merge it in `dev` branch.
- Checkout `dev` branch.
- Run `npm run build` and copy all files and folders from the `build` folder.
- Checkout `master` branch and replace all files and folders with copied content from the build folder.
- Commit and push the `master` branch changes.
- Inform all devs to update the submodule.

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
body[class*=sui-2-] .sui-wrap .giveaway-banner{padding:20px}@media(min-width: 783px){body[class*=sui-2-] .sui-wrap .giveaway-banner{padding:15px;display:flex;flex-flow:row nowrap}}body[class*=sui-2-] .sui-wrap .giveaway-banner__image{max-width:112px;margin:0 auto 20px}@media(min-width: 783px){body[class*=sui-2-] .sui-wrap .giveaway-banner__image{flex:0 0 auto;margin:15px}}body[class*=sui-2-] .sui-wrap .giveaway-banner__image img{max-width:100%;max-height:100%;display:block;margin:0}@media(max-width: 782px){body[class*=sui-2-] .sui-wrap .giveaway-banner__content{text-align:center}}@media(min-width: 783px){body[class*=sui-2-] .sui-wrap .giveaway-banner__content{margin:15px}}body[class*=sui-2-] .sui-wrap .giveaway-banner__content p{margin-bottom:5px !important;font-size:13px;line-height:22px}body[class*=sui-2-] .sui-wrap .giveaway-banner__content p:last-child{margin:0 !important}body[class*=sui-2-] .sui-wrap .giveaway-banner__content .sui-form-control{height:30px;padding:7px 12px;font-size:12px;line-height:16px}body[class*=sui-2-] .sui-wrap .giveaway-banner p.giveaway-banner__title{margin-bottom:10px !important;padding:0;color:#333;font-size:15px;line-height:30px;font-weight:700}body[class*=sui-2-] .sui-wrap .giveaway-banner p.giveaway-banner__title:last-child{margin:0 !important}body[class*=sui-2-] .sui-wrap .giveaway-banner__dismiss,body[class*=sui-2-] .sui-wrap .giveaway-banner button.giveaway-banner__dismiss{position:absolute;z-index:1;top:16px;right:16px}body[class*=sui-2-] .sui-wrap .giveaway-banner__dismiss:not(:last-child),body[class*=sui-2-] .sui-wrap .giveaway-banner button.giveaway-banner__dismiss:not(:last-child){margin-right:0}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,777 @@
<?php
/**
* Plugin notices handler.
*
* This class will take care of registering, queuing and showing different
* notices across WP pages.
*
* @since 2.0
* @author Incsub (Joel James)
* @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* @copyright Copyright (c) 2022, Incsub
* @package WPMUDEV\Notices
* @subpackage Handler
*/
namespace WPMUDEV\Notices;
// If this file is called directly, abort.
defined( 'WPINC' ) || die;
if ( ! class_exists( __NAMESPACE__ . '\\Handler' ) ) {
/**
* Class Module
*
* @since 2.0
* @package WPMUDEV\Notices
*/
final class Handler {
/**
* Current version.
*
* @since 2.0
* @var string $version
*/
public $version = '2.0.5';
/**
* Option name to store data.
*
* @since 2.0
* @var string $option_name
*/
protected $option_name = 'wpmudev_notices';
/**
* Registered plugins for the opt-ins.
*
* @since 2.0
* @var array[] $plugins
*/
private $plugins = array();
/**
* WordPress screen IDs to show notices on.
*
* @since 2.0
* @var array[] $screens
*/
private $screens = array();
/**
* Disabled notice types.
*
* @since 2.0
* @var string[] $disabled
*/
private $disabled = array( 'email', 'giveaway' );
/**
* Registered plugin notices data from db.
*
* @since 2.0
* @var array|null $queue
*/
private $stored = null;
/**
* Notice types that are shown on WP Dashboard.
*
* @since 2.0
* @var array $wp_notices
*/
private $wp_notices = array(
'email' => '\WPMUDEV\Notices\Notices\Email',
'rate' => '\WPMUDEV\Notices\Notices\Rating',
);
/**
* Notice type that are shown within plugin pages.
*
* @since 2.0
* @var array $plugin_notices
*/
private $plugin_notices = array(
'giveaway' => '\WPMUDEV\Notices\Notices\Giveaway',
);
/**
* Construct handler class.
*
* @since 2.0
*/
protected function __construct() {
// Register plugins.
add_action( 'wpmudev_register_notices', array( $this, 'register' ), 10, 2 );
// Always setup ajax actions.
add_action( 'wp_ajax_wpmudev_notices_action', array( $this, 'process_action' ) );
// Render admin notices.
add_action( 'load-index.php', array( $this, 'admin_notice' ) );
}
/**
* Initializes and returns the singleton instance.
*
* @since 2.0
*
* @return static
*/
public static function instance() {
static $instance = null;
if ( null === $instance ) {
$instance = new self();
}
return $instance;
}
/**
* Register an active plugin for notices.
*
* ```php
* do_action(
* 'wpmudev_register_notices',
* 'beehive', // Plugin id.
* array(
* 'basename' => plugin_basename( BEEHIVE_PLUGIN_FILE ), // Required: Plugin basename (for backward compat).
* 'title' => 'Beehive', // Required: Plugin title.
* 'wp_slug' => 'beehive-analytics', // Required: wp.org slug of the plugin.
* 'cta_email' => __( 'Get Fast!', 'ga_trans' ), // Email button CTA.
* 'installed_on' => time(), // Optional: Plugin activated time.
* 'screens' => array( // Required: Plugin screen ids.
* 'toplevel_page_beehive',
* 'dashboard_page_beehive-accounts',
* 'dashboard_page_beehive-settings',
* 'dashboard_page_beehive-tutorials',
* 'dashboard_page_beehive-google-analytics',
* 'dashboard_page_beehive-google-tag-manager',
* ),
* )
* );
* ```
*
* @since 2.0
*
* @param array $options Options.
*
* @param string $plugin_id Plugin ID.
*/
public function register( $plugin_id, array $options = array() ) {
// Plugin ID can't be empty.
if ( empty( $plugin_id ) ) {
return;
}
// Add to the plugins list.
$this->plugins[ $plugin_id ] = $options;
// Setup screens.
if ( ! empty( $options['screens'] ) ) {
$this->add_to_screens( $plugin_id, $options['screens'] );
}
}
/**
* Show an admin notice on WP page (not plugin's SUI pages).
*
* @since 2.0
*
* @return void
*/
public function admin_notice() {
if ( is_super_admin() ) {
add_action( 'all_admin_notices', array( $this, 'render_admin_notice' ) );
}
}
/**
* Show a notice on current plugin page.
*
* @since 2.0
*
* @return void
*/
public function plugin_notice() {
if ( is_super_admin() ) {
add_action( 'all_admin_notices', array( $this, 'render_plugin_notice' ) );
}
}
/**
* Render admin notice content.
*
* @since 2.0
*
* @return void
*/
public function render_admin_notice() {
$this->render( false, array_keys( $this->wp_notices ) );
}
/**
* Render a plugin notice content.
*
* @since 2.0
*
* @return void
*/
public function render_plugin_notice() {
// Get current screen.
$screen = get_current_screen();
// Continue only if registered screen.
if ( empty( $screen->id ) || empty( $this->screens[ $screen->id ] ) ) {
return;
}
$this->render(
$this->screens[ $screen->id ],
array_keys( $this->plugin_notices )
);
}
/**
* Process a notice action.
*
* All ajax requests from the notice are processed here.
* After nonce verification the action will be processed if a matching
* method is already defined.
*
* @since 2.0
*/
public function process_action() {
// Check required fields.
if ( ! isset( $_POST['plugin_id'], $_POST['notice_action'], $_POST['notice_type'], $_POST['nonce'] ) ) {
wp_die( esc_html__( 'Required fields are missing.', 'wdev_frash' ) );
}
// Only admins can do this.
if ( ! is_super_admin() ) {
wp_die( esc_html__( 'Access check failed.', 'wdev_frash' ) );
}
// Get request data.
$plugin = sanitize_text_field( wp_unslash( $_POST['plugin_id'] ) );
$action = sanitize_text_field( wp_unslash( $_POST['notice_action'] ) );
$type = sanitize_text_field( wp_unslash( $_POST['notice_type'] ) );
$nonce = sanitize_text_field( wp_unslash( $_POST['nonce'] ) );
// Verify nonce.
if ( ! wp_verify_nonce( $nonce, 'wpmudev_notices_action' ) ) {
wp_die( esc_html__( 'Nonce verification failed.', 'wdev_frash' ) );
}
// Initialize the options.
$this->init_option();
// Get the notice class.
$notice = $this->get_notice( $plugin, $type );
// Process action if defined on this class.
if ( method_exists( $this, $action ) ) {
call_user_func( array( $this, $action ), $plugin, $type );
} elseif ( is_object( $notice ) && method_exists( $notice, $action ) ) {
// Process action if defined on the notice class.
call_user_func( array( $notice, $action ), $plugin );
}
/**
* Action hook to do something after a notice action is performed.
*
* @since 2.0
*
* @param string $plugin Plugin ID.
* @param string $type Notice type.
*
* @param string $action Action.
*/
do_action( 'wpmudev_notices_after_notice_action', $action, $plugin, $type );
wp_die();
}
/**
* Remove a notice from the queue.
*
* @since 2.0
*
* @param string $type Notice type.
*
* @param string $plugin Plugin ID.
*
* @return void
*/
public function dismiss_notice( $plugin, $type ) {
// Remove from the queue.
if ( isset( $this->stored['queue'][ $plugin ][ $type ] ) ) {
unset( $this->stored['queue'][ $plugin ][ $type ] );
}
// Setup done list.
if ( ! isset( $this->stored['done'][ $plugin ] ) ) {
$this->stored['done'][ $plugin ] = array();
}
// Mark as done.
$this->stored['done'][ $plugin ][ $type ] = time();
// Update the queue.
$this->update_option();
}
/**
* Getter for the queue data.
*
* @since 2.0
*
* @return array
*/
public function get_option() {
$this->init_option();
return $this->stored;
}
/**
* Update the notices stored data in db.
*
* @since 2.0
*
* @param array $data Option data (optional).
*
* @return bool
*/
public function update_option( $data = false ) {
// If new data is provided use it.
if ( ! empty( $data ) ) {
$this->stored = $data;
}
// Update the data.
return update_site_option( $this->option_name, $this->stored );
}
/**
* Render notice for the current screen.
*
* @since 2.0
*
* @param array $types Notice types to render.
*
* @param string|false $plugin_id Plugin id (false to check all plugins).
*
* @return void|string
*/
protected function render( $plugin_id = false, $types = array() ) {
// Setup queue when required.
$this->setup_queue();
if ( empty( $plugin_id ) ) {
// Get a random notice.
$notice = $this->get_random_notice( $types, $plugin_id );
} else {
// Get a plugin's notice.
$notice = $this->get_plugin_notice( $plugin_id, $types );
}
// Render if notice found.
if ( ! empty( $notice ) && method_exists( $notice, 'render' ) ) {
return call_user_func( array( $notice, 'render' ), $plugin_id );
}
}
/**
* Set screen IDs for the notices.
*
* NOTE: Only one plugin can use one screen id.
*
* @since 2.0
*
* @param array $screens Screen IDs.
*
* @param string $plugin_id Plugin ID.
*
* @return void
*/
protected function add_to_screens( $plugin_id, array $screens ) {
// Set the screens.
if ( ! empty( $screens ) ) {
foreach ( $screens as $screen_id ) {
$this->screens[ $screen_id ] = $plugin_id;
// Remove network suffix for page hook.
if ( is_multisite() ) {
$screen_id = str_replace( '-network', '', $screen_id );
}
// Register screen notice.
add_action( "load-$screen_id", array( $this, 'plugin_notice' ) );
}
}
}
/**
* Setup the notices queue when ready.
*
* To avoid calling db queries we need to do this only before
* a notice is being rendered.
*
* @since 2.0
*
* @return void
*/
protected function setup_queue() {
// Initialize data.
$this->init_option();
// Setup all registered plugins to in queue.
foreach ( $this->plugins as $plugin_id => $options ) {
$this->add_to_queue( $plugin_id, $options );
}
}
/**
* Set the queue for the plugin if required.
*
* We should always schedule all notice types even if they
* are disabled. Then only we can enable it later easily.
* Disabled notices won't be considered when taken from the queue.
*
* @since 2.0
*
* @param array $options Options.
*
* @param string $plugin_id Plugin ID.
*
* @return void
*/
protected function add_to_queue( $plugin_id, array $options ) {
// Store to notice queue if not saved already.
if ( ! isset( $this->stored['plugins'][ $plugin_id ] ) ) {
// Register plugin.
$this->stored['plugins'][ $plugin_id ] = time();
$this->stored['queue'][ $plugin_id ] = array();
// Add notices to queue.
foreach ( $this->get_types() as $type => $class_name ) {
// Notice class.
$notice = $this->get_notice( $plugin_id, $type );
// Schedule notice.
if ( ! empty( $notice ) ) {
$this->stored['queue'][ $plugin_id ][ $type ] = $notice->get_next_schedule( $options['installed_on'] );
}
}
// Upgrade if required.
if ( ! empty( $options['basename'] ) ) {
$this->maybe_upgrade( $plugin_id, $options['basename'] );
}
// Update the stored data.
$this->update_option();
}
}
/**
* Init the notices stored data.
*
* Get from the db only if not already initialized.
*
* @since 2.0
*/
protected function init_option() {
if ( null === $this->stored ) {
$queue = (array) get_site_option( $this->option_name, array() );
$this->stored = wp_parse_args(
$queue,
array(
'plugins' => array(),
'queue' => array(),
'done' => array(),
)
);
}
}
/**
* Get a notice object from the entire due list.
*
* This is usually used for a common WP page where all plugins'
* notices are shown. Eg: WP Dashboard page.
*
* @since 2.0
*
* @param string|bool $plugin_id Plugin ID.
*
* @param array $types Notice types.
*
* @return object|false
*/
protected function get_random_notice( array $types = array(), &$plugin_id = false ) {
if ( ! empty( $this->stored['queue'] ) ) {
// Check all due items.
foreach ( $this->stored['queue'] as $plugin => $notices ) {
if ( ! empty( $notices ) ) {
// Chose one with priority.
$notice = $this->choose_notice( $plugin, $notices, $types );
// Return only if a valid notice is selected.
if ( ! empty( $notice ) ) {
// Set the plugin id.
$plugin_id = $plugin;
return $notice;
}
}
}
}
return false;
}
/**
* Get a notice object for the plugin.
*
* Select one with priority from the due list for the plugin.
*
* @since 2.0
*
* @param array $types Notice types.
*
* @param string $plugin_id Plugin ID.
*
* @return object|false
*/
protected function get_plugin_notice( $plugin_id, array $types = array() ) {
// Choose one notice from the due list.
if ( ! empty( $this->stored['queue'][ $plugin_id ] ) ) {
return $this->choose_notice(
$plugin_id,
$this->stored['queue'][ $plugin_id ],
$types
);
}
return false;
}
/**
* Choose a notice from the due list.
*
* Notice will be selected based on the order it's defined
* in the $types property of this class.
*
* @since 2.0
*
* @param array $notices Notices array.
* @param array $types Notice types.
*
* @param string $plugin_id Plugin ID.
*
* @return object|false
*/
protected function choose_notice( $plugin_id, array $notices, array $types = array() ) {
foreach ( $this->get_types() as $type => $class ) {
// Not in the list.
if ( ! isset( $notices[ $type ] ) ) {
continue;
}
// Not a desired type, skip.
if ( ! empty( $types ) && ! in_array( $type, $types, true ) ) {
continue;
}
// Disabled type, skip.
if ( $this->is_disabled( $type, $plugin_id ) ) {
continue;
}
// Due time reached or passed.
if ( $this->get_current_time() >= (int) $notices[ $type ] ) {
// Get the notice class instance.
$notice = $this->get_notice( $plugin_id, $type );
// Return the notice object.
if ( ! empty( $notice ) && $notice->can_show( $plugin_id ) ) {
return $notice;
}
}
}
return false;
}
/**
* Get the notice type class instance.
*
* @since 2.0
*
* @param string $type Notice type.
*
* @param string $plugin_id Plugin ID.
*
* @return bool|object
*/
protected function get_notice( $plugin_id, $type ) {
$types = $this->get_types();
// If a valid class found for the type.
if (
isset( $types[ $type ] )
&& isset( $this->plugins[ $plugin_id ] )
&& class_exists( $types[ $type ] )
) {
/**
* Notice class name.
*
* @var Notices\Notice $class_name
*/
$class_name = $types[ $type ];
return $class_name::instance( $this->plugins[ $plugin_id ] );
}
return false;
}
/**
* Get available notice types and classes.
*
* @since 2.0
*
* @return array
*/
protected function get_types() {
return array_merge(
$this->wp_notices,
$this->plugin_notices
);
}
/**
* Get current time to use for due date check.
*
* This is used to enable custom time fot testing.
*
* @since 2.0
*
* @return int
*/
protected function get_current_time() {
// Get custom time.
$time = filter_input( INPUT_GET, 'wpmudev_notice_time', FILTER_SANITIZE_SPECIAL_CHARS );
return empty( $time ) ? time() : (int) $time;
}
/**
* Check if a notice type is disabled.
*
* @since 2.0.3
*
* @param string $type Notice type.
* @param string $plugin Plugin ID.
*
* @return bool
*/
protected function is_disabled( $type, $plugin ) {
/**
* Filter to modify disabled notices list.
*
* @param array $disabled Disabled list.
* @param string $plugin Plugin ID.
*/
$disabled = apply_filters( 'wpmudev_notices_disabled_notices', $this->disabled, $plugin );
// Check if notice type is disabled.
$is_disabled = in_array( $type, $disabled, true );
/**
* Filter to enable/disable a notice type.
*
* @param bool $is_disabled Is disabled.
* @param string $type Notice type.
* @param string $plugin Plugin ID.
*/
return apply_filters( 'wpmudev_notices_is_disabled', $is_disabled, $type, $plugin );
}
/**
* Optional upgrade from old version (WDEV Frash).
*
* If old data exist, we need to use it before registering new time.
* Used only when registering for the first time.
*
* @since 2.0
* @deprecated 2.0 We may remove this in future.
*
* @param string $plugin_id Plugin ID.
* @param string $basename Plugin basename (used in old plugins).
*
* @return void
*/
protected function maybe_upgrade( $plugin_id, $basename ) {
// Old settings data.
$deprecated = get_site_option( 'wdev-frash' );
// Old notice exists, upgrade it.
if ( ! empty( $deprecated ) ) {
$deprecated = wp_parse_args(
(array) $deprecated,
array(
'plugins' => array(),
'queue' => array(),
'done' => array(),
)
);
// Not found in old settings.
if ( ! isset( $deprecated['plugins'][ $basename ] ) ) {
return;
}
// Use old registered time.
$this->stored['plugins'][ $plugin_id ] = $deprecated['plugins'][ $basename ];
// Existing plugin, so show giveaway right away.
$this->stored['queue'][ $plugin_id ]['giveaway'] = time();
// Only email and rate types.
foreach ( array( 'email', 'rate' ) as $type ) {
// Old key hash.
$hash = md5( $basename . '-' . $type );
if ( isset( $deprecated['queue'][ $hash ]['show_at'] ) ) {
// Use the existing time.
$this->stored['queue'][ $plugin_id ][ $type ] = $deprecated['queue'][ $hash ]['show_at'];
// Remove from old settings.
unset( $deprecated['queue'][ $hash ] );
} else {
// Check if notice type found in dismissed list.
$dismissed = array_filter(
$deprecated['done'],
function ( $item ) use ( $basename, $type ) {
return $item['plugin'] === $basename && $item['type'] === $type;
}
);
// Already shown and dismissed, remove it.
if ( ! empty( $dismissed[0]['handled_at'] ) ) {
// Remove from queue.
unset( $this->stored['queue'][ $plugin_id ][ $type ] );
// Move to done list.
$this->stored['done'][ $plugin_id ][ $type ] = $dismissed[0]['handled_at'];
}
}
}
// Do not delete it yet for backward compatibility.
update_site_option( 'wdev-frash', $deprecated );
}
}
}
}

View File

@ -0,0 +1,218 @@
<?php
/**
* Email notice class.
*
* @since 2.0
* @author Incsub (Joel James)
* @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* @copyright Copyright (c) 2022, Incsub
* @package WPMUDEV\Notices
*/
namespace WPMUDEV\Notices\Notices;
// If this file is called directly, abort.
defined( 'WPINC' ) || die;
use WPMUDEV\Notices\Handler;
if ( ! class_exists( __NAMESPACE__ . '\\Email' ) ) {
/**
* Class Email
*
* @since 2.0
* @package WPMUDEV\Notices
*/
class Email extends Notice {
/**
* Current notice type.
*
* @since 2.0
* @var string $type
*/
protected $type = 'email';
/**
* User id /API Key for Mailchimp subscriber list
*
* @since 1.2
* @var string $mc_user_id
*/
private $mc_user_id = '53a1e972a043d1264ed082a5b';
/**
* Render a notice type content.
*
* @since 2.0
*
* @param string $plugin Plugin ID.
*
* @return void
*/
public function render( $plugin ) {
$this->enqueue_assets( $plugin );
$admin_email = get_site_option( 'admin_email' );
/* translators: %s - plugin name */
$title = __( "We're happy that you've chosen to install %s!", 'wdev_frash' );
$title = apply_filters( 'wdev_email_title_' . $plugin, $title );
/* translators: %s - plugin name */
$message = __( 'Are you interested in how to make the most of this plugin? How would you like a quick 5 day email crash course with actionable advice on building your membership site? Only the info you want, no subscription!', 'wdev_frash' );
$message = apply_filters( 'wdev_email_message_' . $plugin, $message );
// Plugin title.
$plugin_title = $this->get_option( 'title', __( 'Plugin', 'wdev_frash' ) );
?>
<div class="notice notice-info frash-notice frash-notice-<?php echo esc_attr( $this->type ); ?> hidden">
<?php $this->render_hidden_fields( $plugin ); ?>
<div class="frash-notice-logo <?php echo esc_attr( $plugin ); ?>"><span></span></div>
<div class="frash-notice-message">
<p class="notice-title"><?php printf( esc_html( $title ), esc_html( $plugin_title ) ); ?></p>
<p><?php printf( esc_html( $message ), esc_html( $plugin_title ) ); ?></p>
<div class="frash-notice-cta">
<?php
/**
* Fires before subscribe form renders.
*
* @since 1.3
* @since 2.0.4 Mailchimp ID deprecated.
*
* @param int $mc_list_id Mailchimp list ID (deprecated).
*/
do_action( 'frash_before_subscribe_form_render', '' );
?>
<form action="<?php echo esc_url( $this->api_url( 'mailjet/v1/plugin' ) ); ?>" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank">
<label for="wpmudev-email" class="hidden"><?php esc_html_e( 'Email', 'wdev_frash' ); ?></label>
<input type="email" name="email" class="email" id="wpmudev-email" value="<?php echo esc_attr( $admin_email ); ?>" required="required"/>
<input type="hidden" name="source" id="wpmudev-source" value="<?php echo esc_attr( $plugin ); ?>"/>
<button class="frash-notice-act button-primary" data-msg="<?php esc_attr_e( 'Thanks :)', 'wdev_frash' ); ?>" type="submit">
<?php echo esc_html( $this->get_option( 'cta_email', __( 'Get Fast!', 'wdev_frash' ) ) ); ?>
</button>
<span class="frash-notice-cta-divider">|</span>
<a href="#" class="frash-notice-dismiss" data-msg="<?php esc_attr_e( 'Saving', 'wdev_frash' ); ?>">
<?php esc_html_e( 'No thanks', 'wdev_frash' ); ?>
</a>
<?php
/**
* Fires after subscribe form fields are rendered.
* Use this hook to add additional fields for on the sub form.
*
* Make sure that the additional field has is also present on the
* actual MC subscribe form.
*
* @since 1.3
* @since 2.0.4 Mailchimp ID deprecated.
*
* @param int $mc_list_id Mailchimp list ID (deprecated).
*/
do_action( 'frash_subscribe_form_fields', '' );
?>
</form>
<?php
/**
* Fires after subscribe form is rendered
*
* @since 1.3
* @since 2.0.4 Mailchimp ID deprecated.
*
* @param int $mc_list_id Mailchimp list ID (deprecated).
*/
do_action( 'frash_before_subscribe_form_render', '' );
?>
</div>
</div>
</div>
<?php
}
/**
* Render a notice type content.
*
* @since 2.0
*
* @param string $plugin Plugin ID.
*
* @return void
*/
protected function render_hidden_fields( $plugin ) {
$wp_url = $this->get_option( 'wp_slug' );
if ( false === strpos( $wp_url, '://' ) ) {
$wp_url = 'https://wordpress.org/plugins/' . trim( $wp_url, '/' );
}
?>
<input type="hidden" name="type" value="<?php echo esc_attr( $this->type ); ?>"/>
<input type="hidden" name="plugin_id" value="<?php echo esc_attr( $plugin ); ?>"/>
<input type="hidden" name="url_wp" value="<?php echo esc_attr( $wp_url ); ?>"/>
<?php wp_nonce_field( 'wpmudev_notices_action', 'notice_nonce' ); ?>
<?php
}
/**
* Enqueue assets for a notice if required.
*
* @since 2.0
*
* @param string $plugin Plugin ID.
*
* @return void
*/
protected function enqueue_assets( $plugin ) {
$handle = 'wpmudev-notices-dashboard';
wp_enqueue_style(
$handle,
$this->assets_url( 'css/dashboard-notices.min.css' ),
array(),
Handler::instance()->version
);
wp_enqueue_script(
$handle,
$this->assets_url( 'js/dashboard-notices.min.js' ),
array(),
Handler::instance()->version,
true
);
}
/**
* Check if current notice is allowed for the plugin.
*
* @since 2.0
*
* @param string $plugin Plugin ID.
*
* @return bool
*/
public function can_show( $plugin ) {
// Show only on dashboard.
return 'dashboard' === $this->screen_id();
}
/**
* Parse options for the notice.
*
* @since 2.0
*
* @param array $options Plugin options.
*
* @return array
*/
protected function parse_options( array $options ) {
return wp_parse_args(
$options,
array(
'title' => __( 'Plugin', 'wdev_frash' ),
'wp_slug' => '',
'cta_email' => __( 'Get Fast!', 'wdev_frash' ),
)
);
}
}
}

View File

@ -0,0 +1,273 @@
<?php
/**
* Giveaway notice class.
*
* @since 2.0
* @author Incsub (Joel James)
* @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* @copyright Copyright (c) 2022, Incsub
* @package WPMUDEV\Notices\Notices
*/
namespace WPMUDEV\Notices\Notices;
// If this file is called directly, abort.
defined( 'WPINC' ) || die;
use WPMUDEV\Notices\Handler;
if ( ! class_exists( __NAMESPACE__ . '\\Giveaway' ) ) {
/**
* Class Giveaway
*
* @since 2.0
* @package WPMUDEV\Notices
*/
class Giveaway extends Notice {
/**
* Current notice type.
*
* @since 2.0
* @var string $type
*/
protected $type = 'giveaway';
/**
* Show after 2 days
*
* @since 2.0
* @var string $type
*/
protected $time = DAY_IN_SECONDS * 2; // After 2 days.
/**
* Allowed plugin IDs for the notice.
*
* @since 2.0
* @var string[] $allowed_plugins
*/
protected $allowed_plugins = array(
'defender',
'smartcrawl',
'forminator',
'hustle',
'branda',
'beehive',
);
/**
* Initializes and returns the notice instance.
*
* @since 2.0
*
* @param array $options Plugin options.
*/
protected function __construct( array $options ) {
parent::__construct( $options );
// Dismiss all plugin notices once giveaway is dismissed.
add_action( 'wpmudev_notices_after_notice_action', array( $this, 'maybe_dismiss_all' ), 10, 3 );
}
/**
* Render a notice type content.
*
* @since 2.0
*
* @param string $plugin Plugin ID.
*
* @return void
*/
public function render( $plugin ) {
$this->enqueue_assets( $plugin );
echo '<div id="wpmudev-plugin-notices" class="wpmudev-plugin-notices sui-wrap"></div>';
}
/**
* Enqueue assets for a notice if required.
*
* @since 2.0
*
* @param string $plugin Plugin ID.
*
* @return void
*/
protected function enqueue_assets( $plugin ) {
$script_handle = 'wpmudev-notices-giveaway';
wp_enqueue_style(
$script_handle,
$this->assets_url( 'css/giveaway-banner.min.css' ),
array(),
Handler::instance()->version
);
wp_enqueue_script(
$script_handle,
$this->assets_url( 'js/giveaway-banner.min.js' ),
array( 'wp-element', 'wp-i18n' ),
Handler::instance()->version,
true
);
// Script vars.
wp_localize_script(
$script_handle,
'wpmudevNoticeGiveaway',
array(
'pluginId' => $plugin,
'apiUrl' => $this->api_url( 'giveaway/v1/plugin' ),
'images' => array(
'form' => $this->assets_url( 'images/giveaway/form/' . $plugin . '.png' ),
'form2x' => $this->assets_url( 'images/giveaway/form/' . $plugin . '@2x.png' ),
'success' => $this->assets_url( 'images/giveaway/success/common.png' ),
'success2x' => $this->assets_url( 'images/giveaway/success/common@2x.png' ),
),
'nonce' => wp_create_nonce( 'wpmudev_notices_action' ),
)
);
}
/**
* Check if current notice is allowed for the plugin.
*
* @since 2.0
*
* @param string $plugin Plugin ID.
*
* @return bool
*/
public function can_show( $plugin ) {
// Should be a valid plugin and not on dashboard.
$allowed_plugins = in_array( $plugin, $this->allowed_plugins, true );
// Check if WPMUDEV Dashboard plugin is active.
$dash_installed = class_exists( '\WPMUDEV_Dashboard' );
return $allowed_plugins && ! $dash_installed && ! $this->is_dismissed();
}
/**
* Check if any of the plugins has already dismissed the notice.
*
* @since 2.0.1
*
* @return bool
*/
private function is_dismissed() {
$option = Handler::instance()->get_option();
if ( ! empty( $option['done'] ) ) {
foreach ( $option['done'] as $notices ) {
// Remove from the queue.
if ( isset( $notices[ $this->type ] ) ) {
// Make sure to dismiss all.
$this->dismiss_all();
return true;
}
}
}
return false;
}
/**
* Parse options for the notice.
*
* @since 2.0
*
* @param array $options Plugin options.
*
* @return array
*/
protected function parse_options( array $options ) {
return wp_parse_args(
$options,
array(
'installed_on' => time() + $this->time,
)
);
}
/**
* Remove a notice from the queue.
*
* If a giveaway notice is dismissed permanently, we need to hide
* all plugins' giveaway notices.
*
* @since 2.0.1
*
* @param string $plugin Plugin ID.
* @param string $type Notice type.
*
* @param string $action Action.
*
* @return void
*/
public function maybe_dismiss_all( $action, $plugin, $type ) {
// Not a giveaway notice.
if ( $this->type === $type && 'dismiss_notice' === $action ) {
$this->dismiss_all();
}
}
/**
* Mark all plugins' giveaway notices as done.
*
* @since 2.0.1
*
* @return void
*/
private function dismiss_all() {
$option = Handler::instance()->get_option();
if ( ! empty( $option['queue'] ) ) {
foreach ( $option['queue'] as $plugin_id => $notices ) {
// Remove from the queue.
if ( isset( $notices[ $this->type ] ) ) {
unset( $option['queue'][ $plugin_id ][ $this->type ] );
// Add to done list.
if ( ! isset( $option['done'][ $plugin_id ] ) ) {
$option['done'][ $plugin_id ] = array();
}
$option['done'][ $plugin_id ][ $this->type ] = time();
}
}
// Update the queue.
Handler::instance()->update_option( $option );
}
}
/**
* Extend a notice to future time.
*
* If notice not found in queue, it will be added.
*
* @since 2.0
*
* @param string $plugin Plugin ID.
*
* @return void
*/
public function extend_notice( $plugin ) {
$option = Handler::instance()->get_option();
if (
isset( $option['plugins'][ $plugin ] ) // Only if already registered.
&& ! isset( $option['done'][ $plugin ][ $this->type ] ) // Should not be in done list.
) {
// Extend to future.
$option['queue'][ $plugin ][ $this->type ] = $this->get_next_schedule(
false,
DAY_IN_SECONDS * 30 // Extend 30 days.
);
// Update queue.
Handler::instance()->update_option( $option );
}
}
}
}

View File

@ -0,0 +1,235 @@
<?php
/**
* Plugin notice base class.
*
* @since 2.0
* @author Incsub (Joel James)
* @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* @copyright Copyright (c) 2022, Incsub
* @package WPMUDEV\Notices\Notices
*/
namespace WPMUDEV\Notices\Notices;
// If this file is called directly, abort.
defined( 'WPINC' ) || die;
use WPMUDEV\Notices\Handler;
if ( ! class_exists( __NAMESPACE__ . '\\Notice' ) ) {
/**
* Class Notice
*
* @since 2.0
* @package WPMUDEV\Notices
*/
abstract class Notice {
/**
* Current notice type (Should override in sub class).
*
* @var string $type
*
* @since 2.0
*/
protected $type;
/**
* Time to start showing notice.
*
* This will be added to the current time.
*
* @var string $type
*
* @since 2.0
*/
protected $time = 0; // Right now.
/**
* Current plugin's notice options.
*
* @var string $plugin_id
*
* @since 2.0
*/
protected $options = array();
/**
* Initializes and returns the notice instance.
*
* @param array $options Plugin options.
*
* @since 2.0
*/
protected function __construct( array $options ) {
// Set options.
$this->options = $this->parse_options( $options );
}
/**
* Initializes and returns the singleton instance.
*
* @param array $options Plugin options.
*
* @since 2.0
*
* @return static
*/
public static function instance( array $options = array() ) {
static $instance = null;
if ( null === $instance ) {
$called_class = get_called_class();
$instance = new $called_class( $options );
}
return $instance;
}
/**
* Render a notice type content.
*
* @param string $plugin Plugin ID.
*
* @since 2.0
*
* @return void
*/
abstract public function render( $plugin );
/**
* Check if current notice is allowed for the plugin.
*
* @param string $plugin Plugin ID.
*
* @since 2.0
*
* @return bool
*/
public function can_show( $plugin ) {
return true;
}
/**
* Enqueue assets for a notice if required.
*
* @param string $plugin Plugin ID.
*
* @since 2.0
*
* @return void
*/
protected function enqueue_assets( $plugin ) {
// Override to enqueue.
}
/**
* Parse options for the notice.
*
* @param array $options Plugin options.
*
* @since 2.0
*
* @return array
*/
protected function parse_options( array $options ) {
return $options;
}
/**
* Get a notice option value.
*
* @param string $key Option name.
* @param mixed $default Default value.
*
* @since 2.0
*
* @return array
*/
protected function get_option( $key, $default = '' ) {
if ( isset( $this->options[ $key ] ) ) {
return $this->options[ $key ];
}
return $default;
}
/**
* Get next scheduled time to show notice.
*
* @param int $time Current time.
* @param int $extend How many days to extend.
*
* @since 2.0
*
* @return int
*/
public function get_next_schedule( $time = false, $extend = false ) {
// Use current time.
if ( ! is_int( $time ) ) {
$time = time();
}
// Use extension time.
if ( ! is_int( $extend ) ) {
$extend = $this->time;
}
return $time + $extend;
}
/**
* Get full url to an asset.
*
* @param string $path Path to append.
*
* @since 2.0
*
* @return string
*/
protected function assets_url( $path ) {
return plugin_dir_url( WPMUDEV_NOTICES_FILE ) . 'assets/' . $path;
}
/**
* Get the full url to the API endpoint.
*
* @param string $endpoint API endpoint.
*
* @since 2.0
*
* @return string
*/
protected function api_url( $endpoint ) {
$base = 'https://wpmudev.com/';
// Support custom API base.
if ( defined( 'WPMUDEV_CUSTOM_API_SERVER' ) && ! empty( WPMUDEV_CUSTOM_API_SERVER ) ) {
$base = trailingslashit( WPMUDEV_CUSTOM_API_SERVER );
}
// Append endpoint.
return $base . 'api/' . $endpoint;
}
/**
* Get current screen id.
*
* @since 2.0
*
* @return string
*/
protected function screen_id() {
// Screen not defined yet.
if ( ! function_exists( 'get_current_screen' ) ) {
return '';
}
// Get current screen.
$screen = get_current_screen();
// Return current screen id.
return empty( $screen->id ) ? '' : $screen->id;
}
}
}

View File

@ -0,0 +1,107 @@
<?php
/**
* Rating notice class.
*
* Rating notice is almost same as email notice. Only the notice
* content is different.
*
* @since 2.0
* @author Incsub (Joel James)
* @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* @copyright Copyright (c) 2022, Incsub
* @package WPMUDEV\Notices\Notices
*/
namespace WPMUDEV\Notices\Notices;
// If this file is called directly, abort.
defined( 'WPINC' ) || die;
if ( ! class_exists( __NAMESPACE__ . '\\Rating' ) ) {
/**
* Class Rating
*
* @since 2.0
* @package WPMUDEV\Notices
*/
class Rating extends Email {
/**
* Current notice type.
*
* @since 2.0
* @var string $type
*/
protected $type = 'rate';
/**
* Show after 1 week.
*
* @since 2.0
* @var string $time
*/
protected $time = WEEK_IN_SECONDS; // After 1 week.
/**
* Render a notice type content.
*
* @since 2.0
*
* @param string $plugin Plugin ID.
*
* @return void
*/
public function render( $plugin ) {
$this->enqueue_assets( $plugin );
/* translators: %s - plugin name */
$title = __( 'Enjoying %s? Wed love to hear your feedback!', 'wdev_frash' );
$title = apply_filters( 'wdev_rating_title_' . $plugin, $title );
/* translators: %s - plugin name */
$message = __( 'Youve been using %s for over a week now, and wed love to hear about your experience! Weve spent countless hours developing it for you, and your feedback is important to us. Wed really appreciate your rating.', 'wdev_frash' );
$message = apply_filters( 'wdev_rating_message_' . $plugin, $message );
// Plugin title.
$plugin_title = $this->get_option( 'title', __( 'Plugin', 'wdev_frash' ) );
?>
<div class="notice notice-info frash-notice frash-notice-<?php echo esc_attr( $this->type ); ?> hidden">
<?php $this->render_hidden_fields( $plugin ); // Render hidden fields. ?>
<div class="frash-notice-logo <?php echo esc_attr( $plugin ); ?>"><span></span></div>
<div class="frash-notice-message">
<p class="notice-title"><?php printf( esc_html( $title ), esc_html( $plugin_title ) ); ?></p>
<p><?php printf( esc_html( $message ), esc_html( $plugin_title ) ); ?></p>
<div class="frash-notice-actions">
<a href="#" class="frash-notice-act frash-stars" data-msg="<?php esc_attr_e( 'Thanks :)', 'wdev_frash' ); ?>">
<span>★</span><span>★</span><span>★</span><span>★</span><span>★</span>
</a>
<span class="frash-notice-cta-divider">|</span>
<a href="#" class="frash-notice-dismiss" data-msg="<?php esc_attr_e( 'Saving', 'wdev_frash' ); ?>">
<?php esc_html_e( 'Dismiss', 'wdev_frash' ); ?>
</a>
</div>
</div>
</div>
<?php
}
/**
* Check if current notice is allowed for the plugin.
*
* @since 2.0
*
* @param string $plugin Plugin ID.
*
* @return bool
*/
public function can_show( $plugin ) {
// Mailchimp list id is required.
$wp_slug = $this->get_option( 'wp_slug' );
// Show only on dashboard.
return 'dashboard' === $this->screen_id() && ! empty( $wp_slug );
}
}
}

View File

@ -0,0 +1,29 @@
<?php
/**
* WPMUDEV Notices - Notices module for WPMUDEV free plugins.
*
* Used by wordpress.org hosted plugins to show email optins, rating notice
* and giveaway notices.
*
* @since 2.0
* @author Incsub (Philipp Stracker, Joel James)
* @package WPMUDEV\Notices
*/
if ( ! class_exists( 'WPMUDEV\Notices\Handler' ) ) {
// Base file.
if ( ! defined( 'WPMUDEV_NOTICES_FILE' ) ) {
define( 'WPMUDEV_NOTICES_FILE', __FILE__ );
}
// Include main module.
require_once 'classes/class-handler.php';
// Include notices.
require_once 'classes/notices/class-notice.php';
require_once 'classes/notices/class-email.php';
require_once 'classes/notices/class-rating.php';
require_once 'classes/notices/class-giveaway.php';
// Initialize notices.
WPMUDEV\Notices\Handler::instance();
}

View File

@ -0,0 +1,61 @@
# Usage Examples
## Minimum requirement
```
<?php
require_once 'recommended-plugins-notice/notice.php';
do_action(
'wpmudev-recommended-plugins-register-notice',
plugin_basename(__FILE__), // Plugin basename
'My Plugin Name', // Plugin Name
array(
'top_level_page_screen_id' // Screen IDs
),
);
```
# Development Mode
## Always ON
This code below will always show the notice on every page.
```
<?php
require_once 'recommended-plugins-notice/notice.php';
add_filter( 'wpmudev-recommended-plugins-is-displayable', '__return_true' );
add_filter(
'wpmudev-recommended-plugin-active-registered',
function () {
$active = new WPMUDEV_Recommended_Plugins_Notice_Registered_Plugin( 'basename' );
$active->selector = array( 'after', '.sui-wrap .sui-header' );
$active->name = 'Sample';
return $active;
}
);
```
## Custom time trigger
Default of notice to be displayed in plugin page(s) is **14** days after its registered.
You can decrease or even increase this because why not.
```
<?php
add_filter(
'wpmudev-recommended-plugins-notice-display-seconds-after-registered',
function ( $time_trigger ) {
// 1 minute trigger
$time_trigger = 1 * MINUTE_IN_SECONDS;
return $time_trigger;
}
);
```
## Un-dismiss
Accidentally or purposed-ly dismiss the notice for whatever reason ? this below code can undo that.
```
<?php
add_action(
'wpmudev-recommended-plugins-before-display',
function () {
WPMUDEV_Recommended_Plugins_Notice::get_instance()->un_dismiss();
}
);
```

View File

@ -0,0 +1,134 @@
@font-face {
font-family: 'wpmudev-close-icon';
src: url('../fonts/wpmudev-close-icon.eot?v4kr59');
src: url('../fonts/wpmudev-close-icon.eot?v4kr59#iefix') format('embedded-opentype'),
url('../fonts/wpmudev-close-icon.woff2?v4kr59') format('woff2'),
url('../fonts/wpmudev-close-icon.ttf?v4kr59') format('truetype'),
url('../fonts/wpmudev-close-icon.woff?v4kr59') format('woff'),
url('../fonts/wpmudev-close-icon.svg?v4kr59#wpmudev-close-icon') format('svg');
font-weight: normal;
font-style: normal;
}
[class^="icon-"], [class*=" icon-"] {
/* use !important to prevent issues with browser extensions that change fonts */
font-family: 'wpmudev-close-icon' !important;
speak: none;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
/* Better Font Rendering =========== */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-close:before {
content: "\29";
color: #AAAAAA;
-webkit-transition: ease-in-out .2s;
transition: ease-in-out .2s;
}
.wpmudev-recommended-plugins {
position: relative;
border-radius: 4px;
background-color: #FFFFFF;
padding: 25px 30px 30px;
-webkit-box-shadow: 0 2px 0 #E6E6E6;
box-shadow: 0 2px 0 #E6E6E6;
margin-bottom: 30px;
-webkit-transition: ease-in-out .2s;
transition: ease-in-out .2s;
}
.wpmudev-recommended-plugins .wpmudev-recommended-plugins-dismiss {
position: absolute;
top: 20px;
right: 20px;
}
.wpmudev-recommended-plugins .wpmudev-recommended-plugins-dismiss a {
display: flex;
width: 30px;
height: 30px;
justify-content: center;
align-items: center;
}
.wpmudev-recommended-plugins .wpmudev-recommended-plugins-dismiss:hover .icon-close:before {
color: #888888;
}
div.wpmudev-recommended-plugins p.wpmudev-notice-status {
font-size: 13px;
color: #888888;
margin-bottom: 25px !important;
line-height: 22px;
}
.wpmudev-recommended-plugins h3.wpmudev-plugin-name {
font-family: "Roboto", Arial, sans-serif;
font-size: 13px;
line-height: 22px;
margin-bottom: 2px;
-webkit-transition: ease-in-out .2s;
transition: ease-in-out .2s;
}
.wpmudev-recommended-plugins p.wpmudev-plugin-description {
font-family: "Roboto", Arial, sans-serif;
color: #888888;
font-size: 13px;
letter-spacing: -0.25px;
line-height: 18px;
margin: 0;
}
.wpmudev-recommended-plugin-blocks {
display: flex;
}
.wpmudev-recommended-plugin-block {
width: 50%;
min-height: 60px;
}
.wpmudev-recommended-plugin-block-image {
position: absolute;
}
.wpmudev-recommended-plugin-block-detail {
margin: 0 15px 0 75px;
}
.wpmudev-recommended-plugin-block-detail {
vertical-align: middle;
}
.wpmudev-recommended-plugin-block-image img {
border-radius: 4px;
width: 60px;
-webkit-transition: ease-in-out .2s;
transition: ease-in-out .2s;
}
.wpmudev-recommended-plugin-block:hover img {
opacity: .8;
}
.wpmudev-recommended-plugin-block:hover h3.wpmudev-plugin-name {
color: #666666;
}
/* Don't display the notice on screens smaller than 782px */
@media only screen and (max-width: 782px) {
.wpmudev-recommended-plugins {
display: none;
}
}
/* Color accessible close icon */
.sui-wrap.sui-color-accessible .wpmudev-recommended-plugins .wpmudev-recommended-plugins-dismiss .icon-close:before {
color: #000000;
}

View File

@ -0,0 +1,11 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Generated by IcoMoon</metadata>
<defs>
<font id="wpmudev-close-icon" horiz-adv-x="1024">
<font-face units-per-em="1024" ascent="960" descent="-64" />
<missing-glyph horiz-adv-x="1024" />
<glyph unicode="&#x20;" horiz-adv-x="512" d="" />
<glyph unicode="&#x29;" glyph-name="close" d="M620.102 448.032l234.834 234.834c7.407 7.444 11.987 17.708 11.987 29.041s-4.579 21.597-11.988 29.042l-50.879 50.097c-7.444 7.407-17.708 11.987-29.041 11.987s-21.597-4.579-29.042-11.988l-234.832-234.832-234.834 234.834c-7.377 7.168-17.458 11.588-28.572 11.588s-21.195-4.42-28.581-11.598l-50.558-50.401c-7.315-7.428-11.832-17.628-11.832-28.885s4.517-21.457 11.836-28.89l234.828-234.828-234.834-234.834c-7.407-7.444-11.987-17.708-11.987-29.041s4.579-21.597 11.988-29.042l50.566-50.097c7.427-7.445 17.696-12.051 29.041-12.051s21.615 4.606 29.041 12.050l234.835 234.835 234.834-234.834c7.444-7.407 17.708-11.987 29.041-11.987s21.597 4.579 29.042 11.988l50.409 50.409c7.407 7.444 11.987 17.708 11.987 29.041s-4.579 21.597-11.988 29.042z" />
</font></defs></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -0,0 +1,45 @@
(function ($, window) {
var notice = $('.wpmudev-recommended-plugins'),
selectorEl = notice.data('selectorEl'),
selectorFn = notice.data('selectorFn');
// customize placement
if ('' !== selectorEl && '' !== selectorFn) {
try {
$.fn[selectorFn].call($(selectorEl), notice);
} catch (e) {
}
}
notice.show();
$('.wpmudev-recommended-plugins .dismiss').on('click', function (e) {
var pointer = $(this).data('pointer'),
action = $(this).data('action');
e.preventDefault();
if (window.ajaxurl) {
$.ajax({
url: window.ajaxurl,
method: 'POST',
data: {
pointer: pointer,
action: action,
},
}).always(function () {
// ALWAYS CLOSE WHATEVER AJAX RESULT
removeNotice();
});
} else {
// ALWAYS CLOSE EVEN AJAX NOT POSSIBLE
removeNotice();
}
return false;
});
function removeNotice() {
$('.wpmudev-recommended-plugins').remove();
}
}(jQuery, window));

View File

@ -0,0 +1,757 @@
<?php
/**
* WPMUDEV - Recommended Plugins Notice
*
* @author WPMUDEV (https://wpmudev.com)
* @license GPLv2
* @package WPMUDEV_Recommended_Plugins_Notice_Registered_Plugin
*/
if ( ! class_exists( 'WPMUDEV_Recommended_Plugins_Notice_Registered_Plugin' ) ) {
/**
* Class WPMUDEV_Recommended_Plugins_Notice_Registered_Plugin
*
* Hold registered plugin as object
*/
class WPMUDEV_Recommended_Plugins_Notice_Registered_Plugin { //phpcs:ignore
/**
* Plugin basename
* plugin-dir/plugin-name.php
*
* @var string
*/
protected $basename = '';
/**
* Plugin nice name
*
* `My Plugin`
*
* @var string
*/
public $name = '';
/**
* Screens which notice should be displayed
*
* @var array
*/
public $screens = array();
/**
* Time the plugin registered to notice
*
* @var int
*/
public $registered_at = 0;
/**
* Element selector which notice should be append-ed
*
* @var array
*/
public $selector = array();
/**
* Current active screen being displayed
*
* @var string
*/
protected $active_screen = '';
/**
* WPMUDEV_Recommended_Plugins_Notice_Registered_Plugin constructor.
*
* @param string $basename Plugin basename.
*/
public function __construct( $basename ) {
$this->basename = $basename;
}
/**
* Get plugin basename
*
* @return string
*/
public function get_basename() {
return $this->basename;
}
/**
* Build object properties from array
*
* @param array $data Notice data.
*/
public function from_array( $data ) {
if ( is_array( $data ) ) {
if ( isset( $data['registered_at'] ) ) {
$this->registered_at = (int) $data['registered_at'];
}
}
}
/**
* Export to array
*
* @return array
*/
public function to_array() {
return array(
'registered_at' => $this->registered_at,
);
}
/**
* Check if screen is listen on plugin screen
*
* @param string $screen_id Screen ID.
*
* @return bool
*/
public function is_my_screen( $screen_id ) {
foreach ( $this->screens as $screen ) {
if ( $screen_id === $screen ) {
$this->active_screen = $screen_id;
return true;
}
}
return false;
}
/**
* Get where notice should be moved
*
* @return array
*/
public function get_selector() {
$selector = $this->selector;
$active_screen = $this->active_screen;
/**
* Filter selector which notice should be moved to
*
* @param array $selector
* @param string $active_screen
*
* @return array
*/
return apply_filters( "wpmudev-recommended-plugin-$this->basename-notice-selector", $selector, $active_screen );
}
/**
* Check whether now is the time to display
*
* @return bool
*/
public function is_time_to_display_notice() {
$active_screen = $this->active_screen;
$seconds_after_registered = 14 * DAY_IN_SECONDS;
/**
* Filter how many seconds after registered before notice displayed
*
* This filter is globally used.
*
* @param int $seconds_after_registered
* @param string $active_screen
*
* @return string
*/
$seconds_after_registered = apply_filters( "wpmudev-recommended-plugins-notice-display-seconds-after-registered", $seconds_after_registered, $active_screen );
/**
* Filter how many seconds after registered before notice displayed
*
* This filter is for plugin based, overriding global value.
*
* @param int $seconds_after_registered
* @param string $active_screen
*
* @return string
*/
$seconds_after_registered = apply_filters( "wpmudev-recommended-plugin-{$this->basename}-notice-display-seconds-after-registered", $seconds_after_registered, $active_screen );
$now = time();
if ( $now >= ( $this->registered_at + $seconds_after_registered ) ) {
return true;
}
return false;
}
/**
* Get pre text on displayed notice
*
* @return string
*/
public function get_pre_text_notice() {
$pre_text_notice = sprintf( /* translators: %s - plugin name */
__( 'Enjoying %s? Try out a few of our other popular free plugins...', 'wpmudev_recommended_plugins_notice' ),
$this->name
);
/**
* Filter pre text on displayed notice
*
* @param string $pre_text_notice
*
* @return string
*/
return apply_filters( "wpmudev-recommended-plugin-$this->basename-pre-text-notice", $pre_text_notice );
}
}
}
if ( ! class_exists( 'WPMUDEV_Recommended_Plugins_Notice' ) ) {
/**
* Class WPMUDEV_Recommended_Plugins_Notice
*
* @internal
*/
class WPMUDEV_Recommended_Plugins_Notice {//phpcs:ignore
/**
* Class instance.
*
* @var WPMUDEV_Recommended_Plugins_Notice
*/
private static $instance = null;
/**
* Collection of recommended plugins
*
* @var array
*/
protected $recommended_plugins = array();
/**
* Version
*/
const VERSION = '1.0.0';
/**
* Pointer name
*/
const POINTER_NAME = 'wpmudev_recommended_plugins';
/**
* Registered plugins
*/
const OPTION_NAME = 'wpmudev_recommended_plugins_registered';
/**
* Collection of registered plugins to use this notice
*
* @var WPMUDEV_Recommended_Plugins_Notice_Registered_Plugin[]
*/
protected $registered_plugins = array();
/**
* Active registered plugin on this screen
*
* @var null
*/
protected $active_registered_plugin = null;
/**
* WPMUDEV_Recommended_Plugins_Notice constructor.
*/
public function __construct() {
// Only do things when its on admin screen.
if ( is_admin() ) {
$this->init_recommended_plugins();
$this->parse_saved_registered_plugins();
add_action( 'wpmudev-recommended-plugins-register-notice', array( $this, 'register' ), 10, 4 );
add_action( 'all_admin_notices', array( $this, 'display' ), 6 );
}
}
/**
* Init recommended plugins
*
* @return void
*/
private function init_recommended_plugins() {
$recommended_plugins = array(
array(
'name' => 'Smush Image Compression',
'desc' => __( 'Resize, optimize and compress all of your images to the max.', 'wpmudev_recommended_plugins_notice' ),
'image' => trailingslashit( plugin_dir_url( __FILE__ ) ) . '/assets/images/plugins-smush.png',
'free_slug' => 'wp-smushit/wp-smush.php',
'pro_slug' => 'wp-smush-pro/wp-smush.php',
'install_link' => 'https://wordpress.org/plugins/wp-smushit/',
),
array(
'name' => 'Hummingbird Performance',
'desc' => __( 'Add powerful caching and optimize your assets.', 'wpmudev_recommended_plugins_notice' ),
'image' => trailingslashit( plugin_dir_url( __FILE__ ) ) . '/assets/images/plugins-hummingbird.png',
'free_slug' => 'hummingbird-performance/wp-hummingbird.php',
'pro_slug' => 'wp-hummingbird/wp-hummingbird.php',
'install_link' => 'https://wordpress.org/plugins/hummingbird-performance/',
),
array(
'name' => 'Defender Security',
'desc' => __( 'Secure and protect your site from malicious hackers and bots.', 'wpmudev_recommended_plugins_notice' ),
'image' => trailingslashit( plugin_dir_url( __FILE__ ) ) . '/assets/images/plugins-defender.png',
'free_slug' => 'defender-security/wp-defender.php',
'pro_slug' => 'wp-defender/wp-defender.php',
'install_link' => 'https://wordpress.org/plugins/defender-security/',
),
array(
'name' => 'SmartCrawl SEO',
'desc' => __( 'Configure your markup for optimal page and social ranking.', 'wpmudev_recommended_plugins_notice' ),
'image' => trailingslashit( plugin_dir_url( __FILE__ ) ) . '/assets/images/plugins-smartcrawl.png',
'free_slug' => 'smartcrawl-seo/wpmu-dev-seo.php',
'pro_slug' => 'wpmu-dev-seo/wpmu-dev-seo.php',
'install_link' => 'https://wordpress.org/plugins/smartcrawl-seo/',
),
array(
'name' => 'Forminator Forms, Polls & Quizzes',
'desc' => __( 'Create dynamic forms easily and quickly with our form builder.', 'wpmudev_recommended_plugins_notice' ),
'image' => trailingslashit( plugin_dir_url( __FILE__ ) ) . '/assets/images/plugins-forminator.png',
'free_slug' => 'forminator/forminator.php',
'pro_slug' => '',
'install_link' => 'https://wordpress.org/plugins/forminator/',
),
array(
'name' => 'Hustle Marketing',
'desc' => __( 'Generate leads with pop-ups, slide-ins and email opt-ins.', 'wpmudev_recommended_plugins_notice' ),
'image' => trailingslashit( plugin_dir_url( __FILE__ ) ) . '/assets/images/plugins-hustle.png',
'free_slug' => 'wordpress-popup/popover.php',
'pro_slug' => 'hustle/opt-in.php',
'install_link' => 'https://wordpress.org/plugins/wordpress-popup/',
),
);
$recommended_plugins = apply_filters( 'wpmudev-all-recommended-plugins', $recommended_plugins );
$this->recommended_plugins = $recommended_plugins;
}
/**
* Get recommended plugins to be displayed on notice
*
* This function will only return recommended plugins that `not installed` yet
*
* @param int $min Minimum plugins to be displayed.
* @param int $max Maximum plugins to be displayed.
*
* @return array
*/
protected function get_recommended_plugins_for_notice( $min = 2, $max = 2 ) {
$recommended_plugins_for_notice = array();
$recommended_plugins = $this->recommended_plugins;
foreach ( $recommended_plugins as $recommended_plugin ) {
if ( $this->is_plugin_installed( $recommended_plugin ) ) {
continue;
}
$recommended_plugins_for_notice[] = $recommended_plugin;
// Stop when we reached max.
if ( count( $recommended_plugins_for_notice ) >= $max ) {
break;
}
}
// Not enough!
if ( count( $recommended_plugins_for_notice ) < $min ) {
$recommended_plugins_for_notice = array();
}
/**
* Filter recommended plugins to be displayed on notice
*
* @param array $recommended_plugins_for_notice recommended plugins to be displayed
* @param array $recommended_plugins all recommended plugins
* @param int $min minimum plugins to be displayed
* @param int $max maximum plugins to be displayed
*
* @return array
*/
return apply_filters( 'wpmudev-recommended-plugins', $recommended_plugins_for_notice, $recommended_plugins, $min, $max );
}
/**
* Check whether plugin is installed
*
* @uses get_plugins()
*
* @param array $plugin_data Plugin data.
*
* @return bool
*/
protected function is_plugin_installed( $plugin_data ) {
$is_installed = false;
if ( ! function_exists( 'get_plugins' ) ) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
$installed_plugins = get_plugins();
// Check the free one.
if ( isset( $plugin_data['free_slug'] ) && ! empty( $plugin_data['free_slug'] ) ) {
if ( isset( $installed_plugins[ $plugin_data['free_slug'] ] ) ) {
$is_installed = true;
}
}
// Check the pro one.
if ( ! $is_installed ) {
if ( isset( $plugin_data['pro_slug'] ) && ! empty( $plugin_data['pro_slug'] ) ) {
if ( isset( $installed_plugins[ $plugin_data['pro_slug'] ] ) ) {
$is_installed = true;
}
}
}
/**
* Filter is_installed status of recommended plugin
*
* @param bool $is_installed
* @param array $plugin_data plugin to be check
* @param array $installed_plugins current installed plugins
*
* @return bool
*/
return apply_filters( 'wpmudev-recommended-plugin-is-installed', $is_installed, $plugin_data, $installed_plugins );
}
/**
* Display the notice
*
* @return void
*/
public function display() {
/**
* Fires before displaying notice
*
* This action fired before any check done.
*/
do_action( 'wpmudev-recommended-plugins-before-display' );
$is_displayable = $this->is_displayable();
/**
* Filter whether notice is displayable
*
* @param bool $is_displayable
*
* @return bool
*/
$is_displayable = apply_filters( 'wpmudev-recommended-plugins-is-displayable', $is_displayable );
if ( ! $is_displayable ) {
return;
}
$active_registered_plugin = $this->active_registered_plugin;
/**
* Filter whether notice is displayable
*
* @param bool $is_displayable
*
* @return bool
*/
$active_registered_plugin = apply_filters( 'wpmudev-recommended-plugin-active-registered', $active_registered_plugin );
if ( ! $active_registered_plugin instanceof WPMUDEV_Recommended_Plugins_Notice_Registered_Plugin ) {
return;
}
$recommended_plugins = $this->get_recommended_plugins_for_notice();
// no plugins to be recommended.
if ( empty( $recommended_plugins ) ) {
return;
}
wp_register_style( 'wpmudev-recommended-plugins-css', trailingslashit( plugin_dir_url( __FILE__ ) ) . 'assets/css/notice.css', array(), self::VERSION );
wp_enqueue_style( 'wpmudev-recommended-plugins-css' );
wp_register_script( 'wpmudev-recommended-plugins-js', trailingslashit( plugin_dir_url( __FILE__ ) ) . 'assets/js/notice.js', array( 'jquery' ), self::VERSION, true );
wp_enqueue_script( 'wpmudev-recommended-plugins-js' );
$dismissed_text = __( 'Dismiss', 'wpmudev_recommended_plugins_notice' );
/**
* Filter dismissed text
*
* @param string $dismissed_text
*
* @return string
*/
$dismissed_text = apply_filters( 'wpmudev-recommended-plugins-dismissed-text', $dismissed_text );
// placement customizer.
$selector = $active_registered_plugin->get_selector();
$data_selector_el = '';
$data_selector_fn = '';
if ( is_array( $selector ) ) {
if ( isset( $selector[0] ) ) {
$data_selector_fn = (string) $selector[0];
}
if ( isset( $selector[1] ) ) {
$data_selector_el = (string) $selector[1];
}
}
?>
<div class="wpmudev-recommended-plugins" style="display: none"
data-selector-el="<?php echo esc_attr( $data_selector_el ); ?>"
data-selector-fn="<?php echo esc_attr( $data_selector_fn ); ?>">
<p class="wpmudev-notice-status"><?php echo wp_kses_post( $active_registered_plugin->get_pre_text_notice() ); ?></p>
<div class="wpmudev-recommended-plugin-blocks">
<?php foreach ( $recommended_plugins as $recommended_plugin ) : ?>
<div class="wpmudev-recommended-plugin-block">
<a href="<?php echo esc_attr( $recommended_plugin['install_link'] ); ?>" target="_blank">
<div class="wpmudev-recommended-plugin-block-image">
<img src="<?php echo esc_url( $recommended_plugin['image'] ); ?>" alt="<?php echo esc_attr( $recommended_plugin['name'] ); ?>"/>
</div>
<div class="wpmudev-recommended-plugin-block-detail">
<h3 class="wpmudev-plugin-name"><?php echo esc_html( $recommended_plugin['name'] ); ?></h3>
<p class="wpmudev-plugin-description"><?php echo esc_html( $recommended_plugin['desc'] ); ?></p>
</div>
</a>
</div>
<?php endforeach; ?>
</div>
<div class="wpmudev-recommended-plugins-dismiss">
<a class="dismiss"
href="#"
aria-label="Dismiss"
data-action="dismiss-wp-pointer"
data-pointer="<?php echo esc_attr( self::POINTER_NAME ); ?>">
<i class="icon-close" aria-hidden="true"></i>
</a>
</div>
</div>
<?php
/**
* Fires after displaying notice
*
* This action fired after notice markup sent if any check above fails, this action won't fire.
*/
do_action( 'wpmudev-recommended-plugins-after-display' );
}
/**
* Check if notice can be displayed
*
* @uses current_user_can()
*
* @return bool
*/
public function is_displayable() {
/**
* CHECK #1 whether dismissed previously ?
*
* @uses dismissed_wp_pointers use builtin dismissed_wp_pointers to hold data, to avoid add more rows on WP
*/
$dismissed_wp_pointers = get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true );
$dismissed_wp_pointers = explode( ',', (string) $dismissed_wp_pointers );
$dismissed = ( is_array( $dismissed_wp_pointers ) && in_array( self::POINTER_NAME, $dismissed_wp_pointers, true ) );
/**
* Filter flag of dismissed status
*
* @param bool $dismissed
*
* @return bool
*/
$dismissed = apply_filters( 'wpmudev-recommended-plugins-dismissed', $dismissed, $dismissed_wp_pointers );
if ( $dismissed ) {
return false;
}
/**
* CHECK #2 Cap check
* default is for user that capable of `install_plugins`, which make sense because this notice will suggest user to install plugin
*/
$capability_to_check = 'install_plugins';
/**
* Filter user capability which will be shown this notice
*
* @param string $capability_to_check
*
* @return string
*/
$capability_to_check = apply_filters( 'wpmudev-recommended-plugins-capability', $capability_to_check );
if ( ! current_user_can( $capability_to_check ) ) {
return false;
}
/**
* CHECK #3 is_wpmudev_member
*
* This guy is a pro!!
* - wpmudev dash activated and or membership type = full
*/
if ( function_exists( 'is_wpmudev_member' ) ) {
if ( is_wpmudev_member() ) {
return false;
}
}
/**
* CHECK #4 only display on screens that defined by plugin
*/
$current_screen = get_current_screen();
if ( ! $current_screen instanceof WP_Screen || ! isset( $current_screen->id ) ) {
return false;
}
$active_registered_plugin = null;
foreach ( $this->registered_plugins as $registered_plugin ) {
if ( $registered_plugin->is_my_screen( $current_screen->id ) ) {
$active_registered_plugin = $registered_plugin;
break;
}
}
if ( is_null( $active_registered_plugin ) ) {
return false;
}
/**
* CHECK #5 time after register to display notice
*/
if ( ! $active_registered_plugin->is_time_to_display_notice() ) {
return false;
}
$this->active_registered_plugin = $active_registered_plugin;
return true;
}
/**
* Register plugin for notice to be added
*
* @param string $plugin_basename Plugin basename.
* @param string $plugin_name Plugin name.
* @param array $screens Screens.
* @param array $selector [jqueryFn, jQuerySelector].
*/
public function register( $plugin_basename, $plugin_name, $screens = array(), $selector = array() ) {
// Invalid register.
if ( empty( $plugin_basename ) || empty( $plugin_name ) ) {
return;
}
if ( ! isset( $this->registered_plugins[ $plugin_basename ] ) ) {
$registered_plugin_object = new WPMUDEV_Recommended_Plugins_Notice_Registered_Plugin( $plugin_basename );
$registered_plugin_object->registered_at = time();
$this->registered_plugins[ $plugin_basename ] = $registered_plugin_object;
$this->save_registered_plugins();
}
$registered_plugin_object = $this->registered_plugins[ $plugin_basename ];
$registered_plugin_object->name = $plugin_name;
$registered_plugin_object->screens = $screens;
$registered_plugin_object->selector = $selector;
$this->registered_plugins[ $plugin_basename ] = $registered_plugin_object;
}
/**
* Parse registered plugins from storage
*/
public function parse_saved_registered_plugins() {
/**
* Fired before saved registered plugins being parse
*/
do_action( 'wpmudev-recommended-plugins-before-parse-saved-registered-plugins' );
$registered_plugins = get_site_option( self::OPTION_NAME, array() );
if ( is_array( $registered_plugins ) ) {
foreach ( $registered_plugins as $plugin_basename => $registered_plugin ) {
$registered_plugin_object = new WPMUDEV_Recommended_Plugins_Notice_Registered_Plugin( $plugin_basename );
$registered_plugin_object->from_array( $registered_plugin );
$this->registered_plugins[ $plugin_basename ] = $registered_plugin_object;
}
}
}
/**
* Save registered plugins to storage
*/
public function save_registered_plugins() {
$registered_plugins = array();
foreach ( $this->registered_plugins as $registered_plugin ) {
$registered_plugins[ $registered_plugin->get_basename() ] = $registered_plugin->to_array();
}
update_site_option( self::OPTION_NAME, $registered_plugins );
}
/**
* Un-dismiss the notice
*
* @internal
*/
public function un_dismiss() {
$dismissed_wp_pointers = get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true );
$dismissed_wp_pointers = explode( ',', (string) $dismissed_wp_pointers );
$dismissed = ( is_array( $dismissed_wp_pointers ) && in_array( self::POINTER_NAME, $dismissed_wp_pointers, true ) );
if ( $dismissed ) {
foreach ( $dismissed_wp_pointers as $key => $dismissed_wp_pointer ) {
if ( self::POINTER_NAME === $dismissed_wp_pointer ) {
unset( $dismissed_wp_pointers[ $key ] );
}
}
$dismissed_wp_pointers = implode( ',', $dismissed_wp_pointers );
update_user_meta( get_current_user_id(), 'dismissed_wp_pointers', $dismissed_wp_pointers );
}
}
/**
* Reset saved registered plugins
*
* @internal
*/
public function reset_registered_plugins() {
delete_site_option( self::OPTION_NAME );
}
/**
* Get class instance.
*
* @return WPMUDEV_Recommended_Plugins_Notice
*/
public static function get_instance() {
if ( is_null( self::$instance ) ) {
self::$instance = new self();
}
return self::$instance;
}
}
$GLOBALS['WPMUDEV_Recommended_Plugins_Notice'] = WPMUDEV_Recommended_Plugins_Notice::get_instance();
}

View File

@ -0,0 +1,158 @@
# WDEV Logger #
WPMU DEV Logger - A simple logger module.
It's created based on Hummingbird\Core\Logger.
This logger lib will handle the old messages based on the expected size.
This means, it will try to get rid of the old messages if the file size is larger than the max size of the log file.
# How to use it #
1. Insert this repository as **sub-module** into the existing project
2. Include the file `wdev-logger.php` in your main plugin file.
3. Register a logger instance via method: WDEV_Logger::create
## Code Example ##
```
#!php
<?php
// Load the WDEV Logger module.
include_once 'lib/wdev-logger/wdev-logger.php';
$logger = WDEV_Logger::create(array(
'max_log_size' => 10,//10MB
'expected_log_size_in_percent' => 0.7,//70%
'log_dir' => 'uploads/your_plugin_name',
'add_subsite_dir' => true,//For MU site, @see self::get_log_directory(),
'is_private' => false,
'modules' => array(
'foo' => array(
'is_private' => true,//-log.php,
'log_dir' => 'uploads/specific/log_dir',
),
//uploads/your_plugin_name/bazz-debug.log
'baz' => array(
'name' => 'bazz',
'max_log_size' => 5,//5MB,
),
//It's not required to register a main module, by default it will be index ($logger->index()->...)
'main_module' => array(
'is_global_module' => true,
'log_level' => LOG_DEBUG,// @see self::$log_level
)
)
), $your_plugin_key_or_null);
// We will use this name ($your_plugin_key_or_null) to save the settings, we can leave it empty to use the folder plugin name.
// @see WDEV_Logger::get_option_key() for more detail.
$logger->foo()->error('Log an error.');// result: [...DATE...] Error: Log an error. File path: [uploads/specific/log_dir/foo-log.php]
$logger->foo()->warning('Log a warning');
$logger->foo()->notice('Log a notice');
$logger->foo()->info('Log info');
//Main Module.
$logger->main_module()->error('Log an error for main module');
//Or
$logger->error('Log an error');
//Delete log file.
$logger->foo()->delete();//Delete the log file of module Foo.
$logger->delete();//Delete log file for the main module.
/**
* By default, Logger only log when the user enable WP_DEBUG_LOG, but we can overwrite it via setting
* or via methods.
*
* 1. Log mode:
* define('WP_DEBUG_LOG', LOG_DEBUG );
*
* LOG_ERR or 3 => Only log Error type.
* LOG_WARNING or 4 => Only log Warning type.
* LOG_NOTICE or 5 => Only log Notice type.
* LOG_INFO or 6 => Only log Info type.
* LOG_DEBUG or 7 => Log Error, Warning and Notice type.
* self::WPMUDEV_DEBUG_LEVEL or 10 or TRUE => for all message types.
*/
// or set log mode via set_log_level();
$logger->set_log_level( LOG_DEBUG );// It will set for global module, and other modules can inherit from global module.
// We can do like this to only activate it for the main module.
$logger->main_module()->set_log_level( true );// While setting the log level via method, we will convert true to WPMUDEV_DEBUG_LEVEL.
/**
* 2. Debug mode.
* define('WP_DEBUG', LOG_DEBUG );
*
* We use this config to enable option to log the backtrace:
* LOG_ERR or 3 => Only for Error type.
* LOG_WARNING or 4 => Only for Warning type.
* LOG_NOTICE or 5 => Only for Notice type.
* LOG_INFO or 6 => Only for Info type.
* LOG_DEBUG or 7 => For Error, Warning and Notice type.
* self::WPMUDEV_DEBUG_LEVEL or 10 => for all message types.
*
* Or via method.
*/
$logger->set_debug_level( LOG_DEBUG );
/**
* Add a new module.
*/
$logger->add_module('new-module', array(
'is_private' => true,
'log_level' => LOG_DEBUG
));
/**
* Update a module (it will also inherit from the old module option)
* @see WDEV_Logger::update_module()
*/
$logger->update_module('exist-module', array(
'is_private' => true,
'log_level' => LOG_DEBUG,
'max_log_size' => 5,
));
/**
* Get the download link
*/
$download_link_foo_log = $logger->foo()->get_download_link();
/**
* Delete the log file via ajax.
*/
add_action( 'admin_footer', 'wpmudev_logger_ajax' ); // Write our JS below here
function wpmudev_logger_ajax() {
?>
<script type="text/javascript" >
jQuery(document).ready(function($) {
var data = {
'action': 'wdev_logger_action',
'log_action': 'delete',
'log_module': 'foo',
<?php echo WDEV_Logger::NONCE_NAME;?>: '<?php echo wp_create_nonce('action_[your_option_key]');?>'
};
jQuery.post(ajaxurl, data, function(response) {
console.log(response);
});
});
</script> <?php
}
}
/**
* Cleanup.
*/
$logger->cleanup();
// End.
```

File diff suppressed because it is too large Load Diff