modified file bootstrap-buttons.css

This commit is contained in:
2023-12-08 23:23:36 +00:00
committed by Gitium
parent 33d18af972
commit 3f4d8b933f
2304 changed files with 24432 additions and 417943 deletions

View File

@ -1,221 +0,0 @@
<?php
/**
* The abstract class for module definition.
*
* @package ThemeIsleSDK
* @subpackage Loader
* @copyright Copyright (c) 2017, Marius Cristea
* @license http://opensource.org/licenses/gpl-3.0.php GNU Public License
* @since 3.0.0
*/
namespace ThemeisleSDK\Common;
use ThemeisleSDK\Product;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Class Abstract_Module.
*
* @package ThemeisleSDK\Common
*/
abstract class Abstract_Module {
/**
* Plugin paths.
*
* @var string[] $plugin_paths Plugin paths.
*/
private $plugin_paths = [
'otter-blocks' => 'otter-blocks/otter-blocks.php',
'optimole-wp' => 'optimole-wp/optimole-wp.php',
'tweet-old-post' => 'tweet-old-post/tweet-old-post.php',
'feedzy-rss-feeds' => 'feedzy-rss-feeds/feedzy-rss-feed.php',
'woocommerce-product-addon' => 'woocommerce-product-addon/woocommerce-product-addon.php',
'visualizer' => 'visualizer/index.php',
'wp-landing-kit' => 'wp-landing-kit/wp-landing-kit.php',
'multiple-pages-generator-by-porthas' => 'multiple-pages-generator-by-porthas/porthas-multi-pages-generator.php',
'sparks-for-woocommerce' => 'sparks-for-woocommerce/sparks-for-woocommerce.php',
'templates-patterns-collection' => 'templates-patterns-collection/templates-patterns-collection.php',
];
/**
* Product which use the module.
*
* @var Product $product Product object.
*/
protected $product = null;
/**
* Can load the module for the selected product.
*
* @param Product $product Product data.
*
* @return bool Should load module?
*/
abstract public function can_load( $product );
/**
* Bootstrap the module.
*
* @param Product $product Product object.
*/
abstract public function load( $product );
/**
* Check if the product is from partner.
*
* @param Product $product Product data.
*
* @return bool Is product from partner.
*/
public function is_from_partner( $product ) {
foreach ( Module_Factory::$domains as $partner_domain ) {
if ( strpos( $product->get_store_url(), $partner_domain ) !== false ) {
return true;
}
}
return array_key_exists( $product->get_slug(), Module_Factory::$slugs );
}
/**
* Wrapper for wp_remote_get on VIP environments.
*
* @param string $url Url to check.
* @param array $args Option params.
*
* @return array|\WP_Error
*/
public function safe_get( $url, $args = array() ) {
return function_exists( 'vip_safe_wp_remote_get' )
? vip_safe_wp_remote_get( $url )
: wp_remote_get( //phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.wp_remote_get_wp_remote_get, Already used.
$url,
$args
);
}
/**
* Get the SDK base url.
*
* @return string
*/
public function get_sdk_uri() {
global $themeisle_sdk_max_path;
/**
* $themeisle_sdk_max_path can point to the theme when the theme version is higher.
* hence we also need to check that the path does not point to the theme else this will break the URL.
* References: https://github.com/Codeinwp/neve-pro-addon/issues/2403
*/
if ( $this->product->is_plugin() && false === strpos( $themeisle_sdk_max_path, get_template_directory() ) ) {
return plugins_url( '/', $themeisle_sdk_max_path . '/themeisle-sdk/' );
};
return get_template_directory_uri() . '/vendor/codeinwp/themeisle-sdk/';
}
/**
* Call plugin api
*
* @param string $slug plugin slug.
*
* @return array|mixed|object
*/
public function call_plugin_api( $slug ) {
include_once ABSPATH . 'wp-admin/includes/plugin-install.php';
$call_api = get_transient( 'ti_plugin_info_' . $slug );
if ( false === $call_api ) {
$call_api = plugins_api(
'plugin_information',
array(
'slug' => $slug,
'fields' => array(
'downloaded' => false,
'rating' => false,
'description' => false,
'short_description' => true,
'donate_link' => false,
'tags' => false,
'sections' => true,
'homepage' => true,
'added' => false,
'last_updated' => false,
'compatibility' => false,
'tested' => false,
'requires' => false,
'downloadlink' => false,
'icons' => true,
'banners' => true,
),
)
);
set_transient( 'ti_plugin_info_' . $slug, $call_api, 30 * MINUTE_IN_SECONDS );
}
return $call_api;
}
/**
* Get the plugin status.
*
* @param string $plugin Plugin slug.
*
* @return bool
*/
public function is_plugin_installed( $plugin ) {
if ( ! isset( $this->plugin_paths[ $plugin ] ) ) {
return false;
}
if ( file_exists( WP_CONTENT_DIR . '/plugins/' . $this->plugin_paths[ $plugin ] ) ) {
return true;
}
return false;
}
/**
* Get plugin activation link.
*
* @param string $slug The plugin slug.
*
* @return string
*/
public function get_plugin_activation_link( $slug ) {
$reference_key = $slug === 'otter-blocks' ? 'reference_key' : 'optimole_reference_key';
$plugin = isset( $this->plugin_paths[ $slug ] ) ? $this->plugin_paths[ $slug ] : $slug . '/' . $slug . '.php';
return add_query_arg(
array(
'plugin_status' => 'all',
'paged' => '1',
'action' => 'activate',
$reference_key => $this->product->get_key(),
'plugin' => rawurlencode( $plugin ),
'_wpnonce' => wp_create_nonce( 'activate-plugin_' . $plugin ),
),
admin_url( 'plugins.php' )
);
}
/**
* Checks if a plugin is active.
*
* @param string $plugin plugin slug.
*
* @return bool
*/
public function is_plugin_active( $plugin ) {
include_once ABSPATH . 'wp-admin/includes/plugin.php';
$plugin = isset( $this->plugin_paths[ $plugin ] ) ? $this->plugin_paths[ $plugin ] : $plugin . '/' . $plugin . '.php';
return is_plugin_active( $plugin );
}
}

View File

@ -1,108 +0,0 @@
<?php
/**
* The module factory class.
*
* @package ThemeIsleSDK
* @subpackage Loader
* @copyright Copyright (c) 2017, Marius Cristea
* @license http://opensource.org/licenses/gpl-3.0.php GNU Public License
* @since 3.0.0
*/
namespace ThemeisleSDK\Common;
use ThemeisleSDK\Product;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Class Job_Factory
*
* @package ThemeisleSDK\Common
*/
class Module_Factory {
/**
* Partners slugs.
*
* @var array $SLUGS Partners product slugs.
*/
public static $slugs = [
'zermatt' => true,
'neto' => true,
'olsen' => true,
'benson' => true,
'romero' => true,
'carmack' => true,
'puzzle' => true,
'broadsheet' => true,
'girlywp' => true,
'veggie' => true,
'zeko' => true,
'maishawp' => true,
'didi' => true,
'liber' => true,
'medicpress-pt' => true,
'adrenaline-pt' => true,
'consultpress-pt' => true,
'legalpress-pt' => true,
'gympress-pt' => true,
'readable-pt' => true,
'bolts-pt' => true,
];
/**
* Partners domains.
*
* @var array $DOMAINS Partners domains.
*/
public static $domains = [
'proteusthemes.com',
'anarieldesign.com',
'prothemedesign.com',
'cssigniter.com',
];
/**
* Map which contains all the modules loaded for each product.
*
* @var array Mapping array.
*/
private static $modules_attached = [];
/**
* Load availabe modules for the selected product.
*
* @param Product $product Loaded product.
* @param array $modules List of modules.
*/
public static function attach( $product, $modules ) {
if ( ! isset( self::$modules_attached[ $product->get_slug() ] ) ) {
self::$modules_attached[ $product->get_slug() ] = [];
}
foreach ( $modules as $module ) {
$class = 'ThemeisleSDK\\Modules\\' . ucwords( $module, '_' );
/**
* Module object.
*
* @var Abstract_Module $module_object Module instance.
*/
$module_object = new $class( $product );
if ( ! $module_object->can_load( $product ) ) {
continue;
}
self::$modules_attached[ $product->get_slug() ][ $module ] = $module_object->load( $product );
}
}
/**
* Products/Modules loaded map.
*
* @return array Modules map.
*/
public static function get_modules_map() {
return self::$modules_attached;
}
}

View File

@ -1,150 +0,0 @@
<?php
/**
* The main loader class for ThemeIsle SDK
*
* @package ThemeIsleSDK
* @subpackage Loader
* @copyright Copyright (c) 2017, Marius Cristea
* @license http://opensource.org/licenses/gpl-3.0.php GNU Public License
* @since 1.0.0
*/
namespace ThemeisleSDK;
use ThemeisleSDK\Common\Module_Factory;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Singleton loader for ThemeIsle SDK.
*/
final class Loader {
/**
* Singleton instance.
*
* @var Loader instance The singleton instance
*/
private static $instance;
/**
* Current loader version.
*
* @var string $version The class version.
*/
private static $version = '2.0.0';
/**
* Holds registered products.
*
* @var array The products which use the SDK.
*/
private static $products = [];
/**
* Holds available modules to load.
*
* @var array The modules which SDK will be using.
*/
private static $available_modules = [
'dashboard_widget',
'rollback',
'uninstall_feedback',
'licenser',
'logger',
'translate',
'review',
'recommendation',
'notification',
'promotions',
'welcome',
'compatibilities',
'about_us',
];
/**
* Initialize the sdk logic.
*/
public static function init() {
if ( ! isset( self::$instance ) && ! ( self::$instance instanceof Loader ) ) {
self::$instance = new Loader();
$modules = array_merge( self::$available_modules, apply_filters( 'themeisle_sdk_modules', [] ) );
foreach ( $modules as $key => $module ) {
if ( ! class_exists( 'ThemeisleSDK\\Modules\\' . ucwords( $module, '_' ) ) ) {
unset( $modules[ $key ] );
}
}
self::$available_modules = $modules;
}
}
/**
* Get cache token used in API requests.
*
* @return string Cache token.
*/
public static function get_cache_token() {
$cache_token = get_transient( 'themeisle_sdk_cache_token' );
if ( false === $cache_token ) {
$cache_token = wp_generate_password( 6, false );
set_transient( $cache_token, WEEK_IN_SECONDS );
}
return $cache_token;
}
/**
* Clear cache token.
*/
public static function clear_cache_token() {
delete_transient( 'themeisle_sdk_cache_token' );
}
/**
* Register product into SDK.
*
* @param string $base_file The product base file.
*
* @return Loader The singleton object.
*/
public static function add_product( $base_file ) {
if ( ! is_file( $base_file ) ) {
return self::$instance;
}
$product = new Product( $base_file );
Module_Factory::attach( $product, self::get_modules() );
self::$products[ $product->get_slug() ] = $product;
return self::$instance;
}
/**
* Get all registered modules by the SDK.
*
* @return array Modules available.
*/
public static function get_modules() {
return self::$available_modules;
}
/**
* Get all products using the SDK.
*
* @return array Products available.
*/
public static function get_products() {
return self::$products;
}
/**
* Get the version of the SDK.
*
* @return string The version.
*/
public static function get_version() {
return self::$version;
}
}

View File

@ -1,323 +0,0 @@
<?php
/**
* The about page model class for ThemeIsle SDK
*
* Here's how to hook it in your plugin:
*
* add_filter( <product_slug>_about_us_metadata', 'add_about_meta' );
*
* function add_about_meta($data) {
* return [
* 'location' => <top level page - e.g. themes.php>,
* 'logo' => <logo url>,
* 'page_menu' => [['text' => '', 'url' => '']], // optional
* 'has_upgrade_menu' => <condition>,
* 'upgrade_link' => <url>,
* 'upgrade_text' => 'Get Pro Version',
* ]
* }
*
* @package ThemeIsleSDK
* @subpackage Modules
* @copyright Copyright (c) 2023, Andrei Baicus
* @license http://opensource.org/licenses/gpl-3.0.php GNU Public License
* @since 3.2.42
*/
namespace ThemeisleSDK\Modules;
use ThemeisleSDK\Common\Abstract_Module;
use ThemeisleSDK\Product;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Promotions module for ThemeIsle SDK.
*/
class About_Us extends Abstract_Module {
/**
* About data.
*
* @var array $about_data About page data, received from the filter.
*
* Shape of the $about_data property array:
* [
* 'location' => 'top level page',
* 'logo' => 'logo path',
* 'page_menu' => [['text' => '', 'url' => '']], // Optional
* 'has_upgrade_menu' => !defined('NEVE_PRO_VERSION'),
* 'upgrade_link' => 'upgrade url',
* 'upgrade_text' => 'Get Pro Version',
* ]
*/
private $about_data = array();
/**
* Should we load this module.
*
* @param Product $product Product object.
*
* @return bool
*/
public function can_load( $product ) {
if ( $this->is_from_partner( $product ) ) {
return false;
}
$this->about_data = apply_filters( $product->get_key() . '_about_us_metadata', array() );
return ! empty( $this->about_data );
}
/**
* Registers the hooks.
*
* @param Product $product Product to load.
*/
public function load( $product ) {
$this->product = $product;
add_action( 'admin_menu', [ $this, 'add_submenu_pages' ] );
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_about_page_script' ] );
}
/**
* Adds submenu pages.
*
* @return void
*/
public function add_submenu_pages() {
if ( ! isset( $this->about_data['location'] ) ) {
return;
}
add_submenu_page(
$this->about_data['location'],
__( 'About Us', 'textdomain' ),
__( 'About Us', 'textdomain' ),
'manage_options',
$this->get_about_page_slug(),
array( $this, 'render_about_us_page' ),
100
);
if ( ! isset( $this->about_data['has_upgrade_menu'] ) ) {
return;
}
if ( $this->about_data['has_upgrade_menu'] !== true ) {
return;
}
if ( ! isset( $this->about_data['upgrade_link'] ) ) {
return;
}
if ( ! isset( $this->about_data['upgrade_text'] ) ) {
return;
}
add_submenu_page(
$this->about_data['location'],
$this->about_data['upgrade_text'],
$this->about_data['upgrade_text'],
'manage_options',
$this->about_data['upgrade_link'],
'',
101
);
}
/**
* Render page content.
*
* @return void
*/
public function render_about_us_page() {
echo '<div id="ti-sdk-about"></div>';
}
/**
* Enqueue scripts & styles.
*
* @return void
*/
public function enqueue_about_page_script() {
$current_screen = get_current_screen();
if ( ! isset( $current_screen->id ) ) {
return;
}
if ( strpos( $current_screen->id, $this->get_about_page_slug() ) === false ) {
return;
}
global $themeisle_sdk_max_path;
$handle = 'ti-sdk-about-' . $this->product->get_key();
$asset_file = require $themeisle_sdk_max_path . '/assets/js/build/about/about.asset.php';
$deps = array_merge( $asset_file['dependencies'], [ 'updates' ] );
wp_register_script( $handle, $this->get_sdk_uri() . 'assets/js/build/about/about.js', $deps, $asset_file['version'], true );
wp_localize_script( $handle, 'tiSDKAboutData', $this->get_about_localization_data() );
wp_enqueue_script( $handle );
wp_enqueue_style( $handle, $this->get_sdk_uri() . 'assets/js/build/about/about.css', [ 'wp-components' ], $asset_file['version'] );
}
/**
* Get localized data.
*
* @return array
*/
private function get_about_localization_data() {
$links = isset( $this->about_data['page_menu'] ) ? $this->about_data['page_menu'] : [];
return [
'links' => $links,
'logoUrl' => $this->about_data['logo'],
'products' => $this->get_other_products_data(),
'homeUrl' => esc_url( home_url() ),
'pageSlug' => $this->get_about_page_slug(),
'currentProduct' => [
'slug' => $this->product->get_key(),
'name' => $this->product->get_name(),
],
'teamImage' => $this->get_sdk_uri() . 'assets/images/team.jpg',
'strings' => [
'aboutUs' => __( 'About us', 'textdomain' ),
'heroHeader' => __( 'Our Story', 'textdomain' ),
'heroTextFirst' => __( 'Themeisle was founded in 2012 by a group of passionate developers who wanted to create beautiful and functional WordPress themes and plugins. Since then, we have grown into a team of over 20 dedicated professionals who are committed to delivering the best possible products to our customers.', 'textdomain' ),
'heroTextSecond' => __( 'At Themeisle, we offer a wide range of WordPress themes and plugins that are designed to meet the needs of both beginners and advanced users. Our products are feature-rich, easy to use, and are designed to help you create beautiful and functional websites.', 'textdomain' ),
'teamImageCaption' => __( 'Our team in WCEU2022 in Portugal', 'textdomain' ),
'newsHeading' => __( 'Stay connected for news & updates!', 'textdomain' ),
'emailPlaceholder' => __( 'Your email address', 'textdomain' ),
'signMeUp' => __( 'Sign me up', 'textdomain' ),
'installNow' => __( 'Install Now', 'textdomain' ),
'activate' => __( 'Activate', 'textdomain' ),
'learnMore' => __( 'Learn More', 'textdomain' ),
'installed' => __( 'Installed', 'textdomain' ),
'notInstalled' => __( 'Not Installed', 'textdomain' ),
'active' => __( 'Active', 'textdomain' ),
],
];
}
/**
* Get products data.
*
* @return array
*/
private function get_other_products_data() {
$products = [
'optimole-wp' => [
'name' => 'Optimole',
'description' => 'Optimole is an image optimization service that automatically optimizes your images and serves them to your visitors via a global CDN, making your website lighter, faster and helping you reduce your bandwidth usage.',
],
'neve' => [
'skip_api' => true,
'name' => 'Neve',
'description' => __( 'A fast, lightweight, customizable WordPress theme offering responsive design, speed, and flexibility for various website types.', 'textdomain' ),
'icon' => $this->get_sdk_uri() . 'assets/images/neve.png',
],
'otter-blocks' => [
'name' => 'Otter',
],
'tweet-old-post' => [
'name' => 'Revive Old Post',
],
'feedzy-rss-feeds' => [
'name' => 'Feedzy',
],
'woocommerce-product-addon' => [
'name' => 'PPOM',
'condition' => class_exists( 'WooCommerce', false ),
],
'visualizer' => [
'name' => 'Visualizer',
],
'wp-landing-kit' => [
'skip_api' => true,
'premiumUrl' => tsdk_utmify( 'https://themeisle.com/plugins/wp-landing-kit', $this->get_about_page_slug() ),
'name' => 'WP Landing Kit',
'description' => __( 'Turn WordPress into a landing page powerhouse with Landing Kit, map domains to pages or any other published resource.', 'textdomain' ),
'icon' => $this->get_sdk_uri() . 'assets/images/wplk.png',
],
'multiple-pages-generator-by-porthas' => [
'name' => 'MPG',
],
'sparks-for-woocommerce' => [
'skip_api' => true,
'premiumUrl' => tsdk_utmify( 'https://themeisle.com/plugins/sparks-for-woocommerce', $this->get_about_page_slug() ),
'name' => 'Sparks',
'description' => __( 'Extend your store functionality with 8 ultra-performant features like product comparisons, variation swatches, wishlist, and more.', 'textdomain' ),
'icon' => $this->get_sdk_uri() . 'assets/images/sparks.png',
'condition' => class_exists( 'WooCommerce', false ),
],
'templates-patterns-collection' => [
'name' => 'Template Cloud',
'description' => __( 'Ultimate Free Templates Cloud for WordPress, for blocks, patters of full pages.', 'textdomain' ),
],
];
foreach ( $products as $slug => $product ) {
if ( isset( $product['condition'] ) && ! $product['condition'] ) {
unset( $products[ $slug ] );
continue;
}
if ( $slug === 'neve' ) {
$theme = get_template();
$themes = wp_get_themes();
$products[ $slug ]['status'] = isset( $themes['neve'] ) ? 'installed' : 'not-installed';
$products[ $slug ]['status'] = $theme === 'neve' ? 'active' : $products[ $slug ]['status'];
$products[ $slug ]['activationLink'] = add_query_arg(
[
'stylesheet' => 'neve',
'action' => 'activate',
'_wpnonce' => wp_create_nonce( 'switch-theme_neve' ),
],
admin_url( 'themes.php' )
);
continue;
}
$products[ $slug ]['status'] = $this->is_plugin_installed( $slug ) ? 'installed' : 'not-installed';
$products[ $slug ]['status'] = $this->is_plugin_active( $slug ) ? 'active' : $products[ $slug ]['status'];
$products[ $slug ]['activationLink'] = $this->get_plugin_activation_link( $slug );
if ( isset( $product['skip_api'] ) ) {
continue;
}
$api_data = $this->call_plugin_api( $slug );
if ( ! isset( $product['icon'] ) ) {
$products[ $slug ]['icon'] = isset( $api_data->icons['2x'] ) ? $api_data->icons['2x'] : $api_data->icons['1x'];
}
if ( ! isset( $product['description'] ) ) {
$products[ $slug ]['description'] = $api_data->short_description;
}
if ( ! isset( $product['name'] ) ) {
$products[ $slug ]['name'] = $api_data->name;
}
}
return $products;
}
/**
* Get the page slug.
*
* @return string
*/
private function get_about_page_slug() {
return 'ti-about-' . $this->product->get_key();
}
}

View File

@ -1,236 +0,0 @@
<?php
/**
* The compatibilities model class for ThemeIsle SDK
*
* @package ThemeIsleSDK
* @subpackage Modules
* @copyright Copyright (c) 2017, Marius Cristea
* @license http://opensource.org/licenses/gpl-3.0.php GNU Public License
* @since 1.0.0
*/
namespace ThemeisleSDK\Modules;
use ThemeisleSDK\Common\Abstract_Module;
use ThemeisleSDK\Product;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Promotions module for ThemeIsle SDK.
*/
class Compatibilities extends Abstract_Module {
const REQUIRED = 'required';
const TESTED_UP = 'tested_up';
/**
* Should we load this module.
*
* @param Product $product Product object.
*
* @return bool
*/
public function can_load( $product ) {
if ( $this->is_from_partner( $product ) ) {
return false;
}
if ( $product->is_theme() && ! current_user_can( 'switch_themes' ) ) {
return false;
}
if ( $product->is_plugin() && ! current_user_can( 'install_plugins' ) ) {
return false;
}
return true;
}
/**
* Registers the hooks.
*
* @param Product $product Product to load.
*
* @throws \Exception If the configuration is invalid.
*
* @return Compatibilities Module instance.
*/
public function load( $product ) {
$this->product = $product;
$compatibilities = apply_filters( 'themeisle_sdk_compatibilities/' . $this->product->get_slug(), [] );
if ( empty( $compatibilities ) ) {
return $this;
}
$requirement = null;
$check_type = null;
foreach ( $compatibilities as $compatibility ) {
if ( empty( $compatibility['basefile'] ) ) {
return $this;
}
$requirement = new Product( $compatibility['basefile'] );
$tested_up = isset( $compatibility[ self::TESTED_UP ] ) ? $compatibility[ self::TESTED_UP ] : '999';
$required = $compatibility[ self::REQUIRED ];
if ( ! version_compare( $required, $tested_up, '<' ) ) {
throw new \Exception( sprintf( 'Invalid required/tested_up configuration. Required version %s should be lower than tested_up %s.', $required, $tested_up ) );
}
$check_type = self::REQUIRED;
if ( ! version_compare( $requirement->get_version(), $required, '<' ) ) {
$check_type = self::TESTED_UP;
if ( version_compare( $requirement->get_version(), $tested_up . '.9999', '<' ) ) {
return $this;
}
}
break;
}
if ( empty( $requirement ) ) {
return $this;
}
if ( $check_type === self::REQUIRED ) {
$this->mark_required( $product, $requirement );
}
if ( $check_type === self::TESTED_UP ) {
$this->mark_testedup( $product, $requirement );
}
return $this;
}
/**
* Mark the product tested up.
*
* @param Product $product Product object.
* @param Product $requirement Requirement object.
*
* @return void
*/
public function mark_testedup( $product, $requirement ) {
add_action(
'admin_head',
function () use ( $product, $requirement ) {
$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : '';
if ( empty( $screen ) || ! isset( $screen->id ) ) {
return;
}
if ( $requirement->is_theme() && $screen->id === 'themes' ) {
?>
<script type="text/javascript">
jQuery(document).ready(function ($) {
setInterval(checkTheme, 500);
function checkTheme() {
var theme = jQuery( '.theme.active[data-slug="<?php echo esc_attr( $requirement->get_slug() ); ?>"]' );
var notice_id = 'testedup<?php echo esc_attr( $requirement->get_slug() . $product->get_slug() ); ?>';
var product_name = '<?php echo esc_attr( $product->get_friendly_name() ); ?>';
if (theme.length > 0 && jQuery('#' + notice_id).length === 0) {
theme.find('.theme-id-container').prepend('<div style="bottom:100%;top:auto;" id="'+notice_id+'" class="notice notice-warning"><strong>Warning:</strong> This theme has not been tested with your current version of <strong>' + product_name +'</strong>. Please update '+product_name+' plugin.</div>');
}
if (theme.length > 0 && jQuery('#' + notice_id + 'overlay').length === 0) {
jQuery('.theme-overlay.active .theme-author').after('<div style="bottom:100%;top:auto;" id="'+notice_id+'overlay" class="notice notice-warning"><p><strong>Warning:</strong> This theme has not been tested with your current version of <strong>' + product_name +'</strong>. Please update '+product_name+' plugin.</p></div>');
}
}
})
</script>
<?php
}
if ( $requirement->is_plugin() && $screen->id === 'plugins' ) {
?>
<script type="text/javascript">
jQuery(document).ready(function ($) {
setInterval(checkPlugin, 500);
function checkPlugin() {
var plugin = jQuery( '.plugins .active[data-slug="<?php echo esc_attr( $requirement->get_slug() ); ?>"]' );
var notice_id = 'testedup<?php echo esc_attr( $requirement->get_slug() . $product->get_slug() ); ?>';
var product_name = '<?php echo esc_attr( $product->get_friendly_name() ); ?>';
var product_type = '<?php echo ( $product->is_plugin() ? 'plugin' : 'theme' ); ?>';
if (plugin.length > 0 && jQuery('#' + notice_id).length === 0) {
plugin.find('.column-description').append('<div style="bottom:100%;top:auto;" id="'+notice_id+'" class="notice notice-warning notice-alt notice-inline"><strong>Warning:</strong> This plugin has not been tested with your current version of <strong>' + product_name +'</strong>. Please update '+product_name+' '+product_type+'.</div>');
}
}
})
</script>
<?php
}
}
);
}
/**
* Mark the product requirements.
*
* @param Product $product Product object.
* @param Product $requirement Requirement object.
*
* @return void
*/
public function mark_required( $product, $requirement ) {
add_filter(
'upgrader_pre_download',
function ( $return, $package, $upgrader ) use ( $product, $requirement ) {
/**
* Upgrader object.
*
* @var \WP_Upgrader $upgrader Upgrader object.
*/
$should_block = false;
if ( $product->is_theme()
&& property_exists( $upgrader, 'skin' )
&& property_exists( $upgrader->skin, 'theme_info' )
&& $upgrader->skin->theme_info->template === $product->get_slug() ) {
$should_block = true;
}
if ( ! $should_block && $product->is_plugin()
&& property_exists( $upgrader, 'skin' )
&& property_exists( $upgrader->skin, 'plugin_info' )
&& $upgrader->skin->plugin_info['Name'] === $product->get_name() ) {
$should_block = true;
}
if ( $should_block ) {
echo( sprintf(
'%s update requires a newer version of %s. Please %supdate%s %s %s.',
esc_attr( $product->get_friendly_name() ),
esc_attr( $requirement->get_friendly_name() ),
'<a href="' . esc_url( admin_url( $requirement->is_theme() ? 'themes.php' : 'plugins.php' ) ) . '">',
'</a>',
esc_attr( $requirement->get_friendly_name() ),
esc_attr( $requirement->is_theme() ? 'theme' : 'plugin' )
) );
$upgrader->maintenance_mode( false );
die();
}
return $return;
},
10,
3
);
add_action(
'admin_notices',
function () use ( $product, $requirement ) {
echo '<div class="notice notice-error "><p>';
echo( sprintf(
'%s requires a newer version of %s. Please %supdate%s %s %s to the latest version.',
'<strong>' . esc_attr( $product->get_friendly_name() ) . '</strong>',
'<strong>' . esc_attr( $requirement->get_friendly_name() ) . '</strong>',
'<a href="' . esc_url( admin_url( $requirement->is_theme() ? 'themes.php' : 'plugins.php' ) ) . '">',
'</a>',
'<strong>' . esc_attr( $requirement->get_friendly_name() ) . '</strong>',
esc_attr( $requirement->is_theme() ? 'theme' : 'plugin' )
) );
echo '</p></div>';
}
);
}
}

View File

@ -1,474 +0,0 @@
<?php
/**
* The blog dashboard model class for ThemeIsle SDK
*
* @package ThemeIsleSDK
* @subpackage Modules
* @copyright Copyright (c) 2017, Marius Cristea
* @license http://opensource.org/licenses/gpl-3.0.php GNU Public License
* @since 1.0.0
*/
namespace ThemeisleSDK\Modules;
use ThemeisleSDK\Common\Abstract_Module;
use ThemeisleSDK\Product;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Blog dashboard widget module for ThemeIsle SDK.
*/
class Dashboard_Widget extends Abstract_Module {
/**
* Fetched feeds items.
*
* @var array Feed items.
*/
private $items = array();
/**
* Dashboard widget title.
*
* @var string $dashboard_name Dashboard name.
*/
private $dashboard_name = '';
/**
* Dashboard widget feed sources.
*
* @var array $feeds Feed url.
*/
private $feeds = [];
/**
* Should we load this module.
*
* @param Product $product Product object.
*
* @return bool
*/
public function can_load( $product ) {
if ( $this->is_from_partner( $product ) ) {
return false;
}
if ( ! apply_filters( $product->get_slug() . '_load_dashboard_widget', true ) ) {
return false;
}
return true;
}
/**
* Registers the hooks.
*
* @param Product $product Product to load.
*
* @return Dashboard_Widget Module instance.
*/
public function load( $product ) {
if ( apply_filters( 'themeisle_sdk_hide_dashboard_widget', false ) ) {
return;
}
$this->product = $product;
$this->dashboard_name = apply_filters( 'themeisle_sdk_dashboard_widget_name', 'WordPress Guides/Tutorials' );
$this->feeds = apply_filters(
'themeisle_sdk_dashboard_widget_feeds',
[
'https://themeisle.com/blog/feed',
'https://www.codeinwp.com/blog/feed',
'https://wpshout.com/feed',
]
);
add_action( 'wp_dashboard_setup', array( &$this, 'add_widget' ) );
add_action( 'wp_network_dashboard_setup', array( &$this, 'add_widget' ) );
add_filter( 'themeisle_sdk_recommend_plugin_or_theme', array( &$this, 'recommend_plugin_or_theme' ) );
return $this;
}
/**
* Add widget to the dashboard
*
* @return string|void
*/
public function add_widget() {
global $wp_meta_boxes;
if ( isset( $wp_meta_boxes['dashboard']['normal']['core']['themeisle'] ) ) {
return;
}
wp_add_dashboard_widget(
'themeisle',
$this->dashboard_name,
[
$this,
'render_dashboard_widget',
]
);
}
/**
* Render widget content
*/
public function render_dashboard_widget() {
$this->setup_feeds();
if ( empty( $this->items ) || ! is_array( $this->items ) ) {
return;
}
?>
<style type="text/css">
#themeisle ul li.ti-dw-recommend-item {
padding-left: 7px;
border-top: 1px solid #eee;
margin-bottom: 0px;
padding-top: 6px;
}
#themeisle h2.hndle {
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABbCAMAAADncTNAAAAAtFBMVEVHcEyAgIB/f3+xsbGgoaGBgYGCgoKKioqAgIC1tbW5ubnFx8iAgIDU1taBgYGCgoKAgIC0tLXW19jW2NiAgIC3uLiBgYHLzMy4uLhycnLW19d/f3/T1NW0tLTX19mVlZWvr6+BgYHl5eWKiottbW5JSUnW2Nm5ubnh4eHT1NWVlZVjY2N4eHh9fX6pqqq+v79PT0/39/fu7u7Nzc7Z2ttYWFgBAQHDw8P////JysoZGRk0NTZqJc/sAAAAIXRSTlMA0FL7/oEnEPL6eibivm9gwJya76/enFq2CXI+2lFAyM8GATmPAAADj0lEQVR4Xu2YaW/iOhSGAwRCWDosnXa6znjJvm8svf//f12TuARyhiR2pfnUR6gSEnr0+uT4xK7yRb755pvhHePli5K7Bfpkuhoq8ozRJdMH+WWha6Z3sqYparCSLRJqspjImVbANJU03cNMMpofAwQZCGsmpQYyFvVM0Q00OQ9koMl5IPcCoro+RA1Dt2Ea9n9eZ0+YHJLkgIlkDywQx00wCTyaReiKH8LbNU9ybJOdkchV6QFxyCFLbVvdfaREqgUWg/tx2UbqIcK2Hex2TdGLwFTjIj3XP3YfCZFsb23KRZn/3263oymSFI0/a5S4PqUBjoBIJBDjeEhCN0wxQSRybIxtJ3K5SGzuE/vAwIQc8ZmMMJFAIM4oikZItfEFtorGgoE43FObwqHU68OtPCnOz8KZ2Jbl5LgkSW0Tc7YyIz/EFWmS4jMbiZU5mJOmKRaJpKGGyLZtDJh3iyaNUu/3+xyKnrtFL71EG+FTiMpENhQtxUQ8kSOXCIr2tnCNhg/gTX0SHYFp0t7TCwQZ7U841yoHrW6rtGroUwTWVnLMssxx+H4bgZcSOFf5MYx0Ae8FghomMDyC2EBNImBywPkNTDNqGLQpIg2TjUNU8tBy9DQMo0DAZF16rAi7vJAtFTIYFAHUc6hIRW6OuOhJgaCSwmDEAYK4oa7ro+qIEyJU/US7KTJKPNSFT9tFgVFBu0SF1y7yjX4masRA9Da7EFGj28R/BkQz6xGIOurkx38T/bKs9Uk8aIiMwm/Jw0VP1yLrJwt13xAxvABBgsK4KWLov35DkRF7ZaqgzuZ7MQ8MOntmVYyAqKTwaICKqvSUFnVccMN5sziEP/5+xGDTahbH5Q3ZB76zr8fI+nJtvUUU3t3ml5GKviK/npCg3CGodnuJ4JVkfRFJYGVDBZrqKnn9RLf+CzDTS5PaN5J38+auzX4ykU4Qoj0rdKfcYs5ijfo9OL/uRUgZyQr7NCWtWwiUSLc4arfJa7lpszTA1OJZAQ8w8dXFrR5YHzCWSnS3pZ18tOi4Ps4vl/c7i/6qomjRecN+UubrPyPGn/VEMU3T0UFHkaPzpgjxmJsnjmrtionlMDZiog0TsY/DPtn8SXtlBvbtxKtwopy7lqW3smQO+yoGE1Uu55GJ3pmI8ygoejZNnqj0vnIRCyTKfLstRdtStGQi09myUsvwvlkuzSUXbV+Xz5ryBebV33fln/A/moud69FZiEYAAAAASUVORK5CYII=');
background-repeat: no-repeat;
background-position: 2% 50%;
background-size: 25px;
padding-left: 39px;
}
#themeisle .inside {
padding: 0;
}
.ti-feed-list {
padding: 0 12px 5px;
margin-bottom: 10px;
border-bottom: 1px solid #eee;
}
.ti-dw-feed-item a {
display: flex;
align-items: center;
margin-bottom: 5px;
padding: 5px;
transition: .2s ease;
border-radius: 3px;
}
.ti-dw-feed-item a:hover {
background-color: #f8f8f8;
}
.ti-dw-feed-item a:hover .ti-dw-date-container {
opacity: .9;
}
.ti-dw-feed-item .ti-dw-month-container {
margin-top: -5px;
text-transform: uppercase;
font-size: 10px;
letter-spacing: 1px;
font-weight: 700;
}
.ti-dw-feed-item .ti-dw-date-container {
border-radius: 3px;
transition: .2s ease;
min-height: 35px;
margin-right: 5px;
min-width: 35px;
text-align: center;
border: 1px solid #2a6f97;
color: #fff;
background: #2ea2cc;
display: flex;
flex-direction: column;
justify-content: center;
}
.ti-dw-footer {
padding: 0 12px 5px;
text-align: center;
}
.ti-dw-recommend-item {
display: block;
}
.ti-dw-recommend-item span {
color: #72777c;
}
.ti-dw-powered-by {
font-size: 11px;
margin-top: 3px;
display: block;
color: #72777c;
}
.ti-dw-powered-by span {
font-weight: 600;
}
</style>
<?php do_action( 'themeisle_sdk_dashboard_widget_before', $this->product ); ?>
<ul class="ti-feed-list">
<?php
foreach ( $this->items as $item ) {
?>
<li class="ti-dw-feed-item">
<a href="
<?php
echo esc_url(
add_query_arg(
array(
'utm_source' => 'wpadmin',
'utm_campaign' => 'feed',
'utm_medium' => 'dashboard_widget',
),
$item['link']
)
);
?>
" target="_blank">
<span class="ti-dw-date-container"><span
class="ti-dw-day-container"><?php echo esc_attr( gmdate( 'd', $item['date'] ) ); ?></span> <span
class="ti-dw-month-container"><?php echo esc_attr( substr( gmdate( 'M', $item['date'] ), 0, 3 ) ); ?></span></span><?php echo esc_attr( $item['title'] ); ?>
</a>
</li>
<?php
}
?>
</ul>
<?php
$recommend = apply_filters( 'themeisle_sdk_recommend_plugin_or_theme', array() );
if ( ! is_array( $recommend ) || empty( $recommend ) ) {
return;
}
$type = $recommend['type'];
if ( ( 'theme' === $type && ! current_user_can( 'install_themes' ) ) ) {
return;
}
if ( ( 'plugin' === $type && ! current_user_can( 'install_plugins' ) ) ) {
return;
}
add_thickbox();
$url = add_query_arg(
[
'theme' => $recommend['slug'],
],
network_admin_url( 'theme-install.php' )
);
if ( 'plugin' === $type ) {
$url = add_query_arg(
array(
'tab' => 'plugin-information',
'plugin' => $recommend['slug'],
),
network_admin_url( 'plugin-install.php' )
);
}
?>
<div class="ti-dw-footer">
<span class="ti-dw-recommend-item ">
<span class="ti-dw-recommend"><?php echo esc_attr( apply_filters( 'themeisle_sdk_dashboard_popular_label', sprintf( 'Popular %s', ucwords( $type ) ) ) ); ?>
: </span>
<?php
echo esc_attr(
trim(
str_replace(
array(
'lite',
'Lite',
'(Lite)',
'(lite)',
),
'',
$recommend['name']
)
)
);
?>
(<a class="thickbox open-plugin-details-modal"
href="<?php echo esc_url( $url . '&TB_iframe=true&width=600&height=500' ); ?>"><?php echo esc_attr( apply_filters( 'themeisle_sdk_dashboard_install_label', 'Install' ) ); ?></a>)
</span>
<span class="ti-dw-powered-by"><span><?php echo esc_attr( apply_filters( 'themeisle_sdk_dashboard_widget_powered_by', sprintf( 'Powered by %s', $this->product->get_friendly_name() ) ) ); ?></span></span>
</div>
<?php
}
/**
* Setup feed items.
*/
private function setup_feeds() {
if ( false === ( $items_normalized = get_transient( 'themeisle_sdk_feed_items' ) ) ) { //phpcs:ignore Squiz.PHP.DisallowMultipleAssignments.FoundInControlStructure
// Load SimplePie Instance.
$feed = fetch_feed( $this->feeds );
// TODO report error when is an error loading the feed.
if ( is_wp_error( $feed ) ) {
return;
}
$items = $feed->get_items( 0, 5 );
$items = is_array( $items ) ? $items : [];
foreach ( $items as $item ) {
$items_normalized[] = array(
'title' => $item->get_title(),
'date' => $item->get_date( 'U' ),
'link' => $item->get_permalink(),
);
}
set_transient( 'themeisle_sdk_feed_items', $items_normalized, 48 * HOUR_IN_SECONDS );
}
$this->items = $items_normalized;
}
/**
* Either the current product is installed or not.
*
* @param array $val The current recommended product.
*
* @return bool Either we should exclude the plugin or not.
*/
public function remove_current_products( $val ) {
if ( 'theme' === $val['type'] ) {
$exist = wp_get_theme( $val['slug'] );
return ! $exist->exists();
} else {
$all_plugins = array_keys( get_plugins() );
foreach ( $all_plugins as $slug ) {
if ( strpos( $slug, $val['slug'] ) !== false ) {
return false;
}
}
return true;
}
}
/**
* Contact the API and fetch the recommended plugins/themes
*/
public function recommend_plugin_or_theme() {
$products = $this->get_product_from_api();
if ( ! is_array( $products ) ) {
$products = array();
}
$products = array_filter( $products, array( $this, 'remove_current_products' ) );
$products = array_merge( $products );
if ( count( $products ) > 1 ) {
shuffle( $products );
$products = array_slice( $products, 0, 1 );
}
$to_recommend = isset( $products[0] ) ? $products[0] : $products;
return $to_recommend;
}
/**
* Fetch products from the recomended section.
*
* @return array|mixed The list of products to use in recomended section.
*/
public function get_product_from_api() {
if ( false === ( $products = get_transient( 'themeisle_sdk_products' ) ) ) { //phpcs:ignore Squiz.PHP.DisallowMultipleAssignments.FoundInControlStructure
$products = array();
$all_themes = $this->get_themes_from_wporg( 'themeisle' );
$all_plugins = $this->get_plugins_from_wporg( 'themeisle' );
static $allowed_products = [
'hestia' => true,
'neve' => true,
'visualizer' => true,
'feedzy-rss-feeds' => true,
'wp-product-review' => true,
'otter-blocks' => true,
'themeisle-companion' => true,
];
foreach ( $all_themes as $theme ) {
if ( $theme->active_installs < 4999 ) {
continue;
}
if ( ! isset( $allowed_products[ $theme->slug ] ) ) {
continue;
}
$products[] = array(
'name' => $theme->name,
'type' => 'theme',
'slug' => $theme->slug,
'installs' => $theme->active_installs,
);
}
foreach ( $all_plugins as $plugin ) {
if ( $plugin->active_installs < 4999 ) {
continue;
}
if ( ! isset( $allowed_products[ $plugin->slug ] ) ) {
continue;
}
$products[] = array(
'name' => $plugin->name,
'type' => 'plugin',
'slug' => $plugin->slug,
'installs' => $plugin->active_installs,
);
}
set_transient( 'themeisle_sdk_products', $products, 6 * HOUR_IN_SECONDS );
}
return $products;
}
/**
* Fetch themes from wporg api.
*
* @param string $author The author name.
*
* @return array The list of themes.
*/
public function get_themes_from_wporg( $author ) {
$products = $this->safe_get(
'https://api.wordpress.org/themes/info/1.1/?action=query_themes&request[author]=' . $author . '&request[per_page]=30&request[fields][active_installs]=true'
);
$products = json_decode( wp_remote_retrieve_body( $products ) );
if ( is_object( $products ) ) {
$products = isset( $products->themes ) ? $products->themes : array();
} else {
$products = array();
}
return (array) $products;
}
/**
* Fetch plugin from wporg api.
*
* @param string $author The author slug.
*
* @return array The list of plugins for the selected author.
*/
public function get_plugins_from_wporg( $author ) {
$products = $this->safe_get(
'https://api.wordpress.org/plugins/info/1.1/?action=query_plugins&request[author]=' . $author . '&request[per_page]=40&request[fields][active_installs]=true'
);
$products = json_decode( wp_remote_retrieve_body( $products ) );
if ( is_object( $products ) ) {
$products = isset( $products->plugins ) ? $products->plugins : array();
} else {
$products = array();
}
return (array) $products;
}
}

View File

@ -1,179 +0,0 @@
<?php
/**
* The logger model class for ThemeIsle SDK
*
* @package ThemeIsleSDK
* @subpackage Modules
* @copyright Copyright (c) 2017, Marius Cristea
* @license http://opensource.org/licenses/gpl-3.0.php GNU Public License
* @since 1.0.0
*/
namespace ThemeisleSDK\Modules;
use ThemeisleSDK\Common\Abstract_Module;
use ThemeisleSDK\Loader;
use ThemeisleSDK\Product;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Logger module for ThemeIsle SDK.
*/
class Logger extends Abstract_Module {
/**
* Endpoint where to collect logs.
*/
const TRACKING_ENDPOINT = 'https://api.themeisle.com/tracking/log';
/**
* Check if we should load the module for this product.
*
* @param Product $product Product to load the module for.
*
* @return bool Should we load ?
*/
public function can_load( $product ) {
return apply_filters( $product->get_slug() . '_sdk_enable_logger', true );
}
/**
* Load module logic.
*
* @param Product $product Product to load.
*
* @return Logger Module object.
*/
public function load( $product ) {
$this->product = $product;
$this->setup_notification();
$this->setup_actions();
return $this;
}
/**
* Setup notification on admin.
*/
public function setup_notification() {
if ( ! $this->product->is_wordpress_available() ) {
return;
}
add_filter( 'themeisle_sdk_registered_notifications', [ $this, 'add_notification' ] );
}
/**
* Setup tracking actions.
*/
public function setup_actions() {
if ( ! $this->is_logger_active() ) {
return;
}
$action_key = $this->product->get_key() . '_log_activity';
if ( ! wp_next_scheduled( $action_key ) ) {
wp_schedule_single_event( time() + ( wp_rand( 1, 24 ) * 3600 ), $action_key );
}
add_action( $action_key, array( $this, 'send_log' ) );
}
/**
* Check if the logger is active.
*
* @return bool Is logger active?
*/
private function is_logger_active() {
$default = 'no';
if ( ! $this->product->is_wordpress_available() ) {
$default = 'yes';
} else {
$pro_slug = $this->product->get_pro_slug();
if ( ! empty( $pro_slug ) ) {
$all_products = Loader::get_products();
if ( isset( $all_products[ $pro_slug ] ) ) {
$default = 'yes';
}
}
}
return ( get_option( $this->product->get_key() . '_logger_flag', $default ) === 'yes' );
}
/**
* Add notification to queue.
*
* @param array $all_notifications Previous notification.
*
* @return array All notifications.
*/
public function add_notification( $all_notifications ) {
$message = apply_filters( $this->product->get_key() . '_logger_heading', 'Do you enjoy <b>{product}</b>? Become a contributor by opting in to our anonymous data tracking. We guarantee no sensitive data is collected.' );
$message = str_replace(
array( '{product}' ),
$this->product->get_friendly_name(),
$message
);
$button_submit = apply_filters( $this->product->get_key() . '_logger_button_submit', 'Sure, I would love to help.' );
$button_cancel = apply_filters( $this->product->get_key() . '_logger_button_cancel', 'No, thanks.' );
$all_notifications[] = [
'id' => $this->product->get_key() . '_logger_flag',
'message' => $message,
'ctas' => [
'confirm' => [
'link' => '#',
'text' => $button_submit,
],
'cancel' => [
'link' => '#',
'text' => $button_cancel,
],
],
];
return $all_notifications;
}
/**
* Send the statistics to the api endpoint.
*/
public function send_log() {
$environment = array();
$theme = wp_get_theme();
$environment['theme'] = array();
$environment['theme']['name'] = $theme->get( 'Name' );
$environment['theme']['author'] = $theme->get( 'Author' );
$environment['theme']['parent'] = $theme->parent() !== false ? $theme->parent()->get( 'Name' ) : $theme->get( 'Name' );
$environment['plugins'] = get_option( 'active_plugins' );
global $wp_version;
wp_remote_post(
self::TRACKING_ENDPOINT,
array(
'method' => 'POST',
'timeout' => 3,
'redirection' => 5,
'body' => array(
'site' => get_site_url(),
'slug' => $this->product->get_slug(),
'version' => $this->product->get_version(),
'wp_version' => $wp_version,
'locale' => get_locale(),
'data' => apply_filters( $this->product->get_key() . '_logger_data', array() ),
'environment' => $environment,
'license' => apply_filters( $this->product->get_key() . '_license_status', '' ),
),
)
);
}
}

View File

@ -1,515 +0,0 @@
<?php
/**
* The notification model class for ThemeIsle SDK
*
* @package ThemeIsleSDK
* @subpackage Modules
* @copyright Copyright (c) 2017, Marius Cristea
* @license http://opensource.org/licenses/gpl-3.0.php GNU Public License
* @since 1.0.0
*/
namespace ThemeisleSDK\Modules;
use ThemeisleSDK\Common\Abstract_Module;
use ThemeisleSDK\Product;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Notification module for ThemeIsle SDK.
*/
class Notification extends Abstract_Module {
/**
* Show notifications only after the user has the product installed after this amount of time, in hours.
*/
const MIN_INSTALL_TIME = 100;
/**
* How much time should we show the notification, in days.
*/
const MAX_TIME_TO_LIVE = 7;
/**
* Number of days between notifications.
*/
const TIME_BETWEEN_NOTIFICATIONS = 5;
/**
* Holds a possible notification list.
*
* @var array Notifications list.
*/
private static $notifications = [];
/**
* Show notification data.
*/
public static function show_notification() {
$current_notification = self::get_last_notification();
$notification_details = [];
// Check if the saved notification is still present among the possible ones.
if ( ! empty( $current_notification ) ) {
$notification_details = self::get_notification_details( $current_notification );
if ( empty( $notification_details ) ) {
$current_notification = [];
}
}
// Check if the notificatin is expired.
if ( ! empty( $current_notification ) && self::is_notification_expired( $current_notification ) ) {
update_option( $current_notification['id'], 'no' );
self::set_last_active_notification_timestamp();
$current_notification = [];
}
// If we don't have any saved notification, get a new one.
if ( empty( $current_notification ) ) {
$notification_details = self::get_random_notification();
if ( empty( $notification_details ) ) {
return;
}
self::set_active_notification(
[
'id' => $notification_details['id'],
'display_at' => time(),
]
);
}
if ( empty( $notification_details ) ) {
return;
}
$notification_html = self::get_notification_html( $notification_details );
do_action( $notification_details['id'] . '_before_render' );
echo $notification_html; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, already escaped internally.
do_action( $notification_details['id'] . '_after_render' );
self::render_snippets();
}
/**
* Get last notification details.
*
* @return array Last notification details.
*/
private static function get_last_notification() {
$notification = self::get_notifications_metadata();
return isset( $notification['last_notification'] ) ? $notification['last_notification'] : [];
}
/**
* Get notification center details.
*
* @return array Notification center details.
*/
private static function get_notifications_metadata() {
$data = get_option(
'themeisle_sdk_notifications',
[
'last_notification' => [],
'last_notification_active' => 0,
]
);
return $data;
}
/**
* Check if the notification is still possible.
*
* @param array $notification Notification to check.
*
* @return array Either is still active or not.
*/
private static function get_notification_details( $notification ) {
$notifications = array_filter(
self::$notifications,
function ( $value ) use ( $notification ) {
if ( isset( $value['id'] ) && isset( $notification['id'] ) && $value['id'] === $notification['id'] ) {
return true;
}
return false;
}
);
return ! empty( $notifications ) ? reset( $notifications ) : [];
}
/**
* Check if the notification is expired.
*
* @param array $notification Notification to check.
*
* @return bool Either the notification is due.
*/
private static function is_notification_expired( $notification ) {
if ( ! isset( $notification['display_at'] ) ) {
return true;
}
$notifications = array_filter(
self::$notifications,
function ( $value ) use ( $notification ) {
if ( isset( $value['id'] ) && isset( $notification['id'] ) && $value['id'] === $notification['id'] ) {
return true;
}
return false;
}
);
if ( empty( $notifications ) ) {
return true;
}
$notification_definition = reset( $notifications );
$when_to_expire = isset( $notification_definition['expires_at'] )
? $notification_definition['expires_at'] :
( isset( $notification_definition['expires'] )
? ( $notification['display_at'] + $notification_definition['expires'] ) :
( $notification['display_at'] + self::MAX_TIME_TO_LIVE * DAY_IN_SECONDS )
);
return ( $when_to_expire - time() ) < 0;
}
/**
* Set last notification details.
*/
private static function set_last_active_notification_timestamp() {
$metadata = self::get_notifications_metadata();
$metadata['last_notification_active'] = time();
update_option( 'themeisle_sdk_notifications', $metadata );
}
/**
* Return notification to show.
*
* @return array Notification data.
*/
public static function get_random_notification() {
if ( ( time() - self::get_last_active_notification_timestamp() ) < self::TIME_BETWEEN_NOTIFICATIONS * DAY_IN_SECONDS ) {
return [];
}
$notifications = self::$notifications;
$notifications = array_filter(
$notifications,
function ( $value ) {
if ( isset( $value['sticky'] ) && true === $value['sticky'] ) {
return true;
}
return false;
}
);
// No priority notifications, use all.
if ( empty( $notifications ) ) {
$notifications = self::$notifications;
}
if ( empty( $notifications ) ) {
return [];
}
$notifications = array_values( $notifications );
return $notifications[ array_rand( $notifications, 1 ) ];
}
/**
* Get last notification details.
*
* @return int Last notification details.
*/
private static function get_last_active_notification_timestamp() {
$notification = self::get_notifications_metadata();
return isset( $notification['last_notification_active'] ) ? $notification['last_notification_active'] : 0;
}
/**
* Get last notification details.
*
* @param array $notification Notification data.
*/
private static function set_active_notification( $notification ) {
$metadata = self::get_notifications_metadata();
$metadata['last_notification'] = $notification;
update_option( 'themeisle_sdk_notifications', $metadata );
}
/**
* Get notification html.
*
* @param array $notification_details Notification details.
*
* @return string Html for notice.
*/
public static function get_notification_html( $notification_details ) {
$default = [
'id' => '',
'heading' => '',
'img_src' => '',
'message' => '',
'ctas' => [
'confirm' => [
'link' => '#',
'text' => '',
],
'cancel' => [
'link' => '#',
'text' => '',
],
],
'type' => 'success',
];
$notification_details = wp_parse_args( $notification_details, $default );
global $pagenow;
$type = in_array( $notification_details['type'], [ 'success', 'info', 'warning', 'error' ], true ) ? $notification_details['type'] : 'success';
$notification_details['ctas']['cancel']['link'] = wp_nonce_url( add_query_arg( [ 'nid' => $notification_details['id'] ], admin_url( $pagenow ) ), $notification_details['id'], 'tsdk_dismiss_nonce' );
$notification_html = '<div class="notice notice-' . $type . ' is-dismissible themeisle-sdk-notice" data-notification-id="' . esc_attr( $notification_details['id'] ) . '" id="' . esc_attr( $notification_details['id'] ) . '-notification"> <div class="themeisle-sdk-notification-box">';
if ( ! empty( $notification_details['heading'] ) ) {
$notification_html .= sprintf( '<h4>%s</h4>', wp_kses_post( $notification_details['heading'] ) );
}
if ( ! empty( $notification_details['img_src'] ) ) {
$notification_html .= '<div class="wrap-flex">';
$notification_html .= sprintf( '<img src="%s" alt="%s" />', esc_attr( $notification_details['img_src'] ), esc_attr( $notification_details['heading'] ) );
}
if ( ! empty( $notification_details['message'] ) ) {
$notification_html .= wp_kses_post( $notification_details['message'] );
if ( ! empty( $notification_details['img_src'] ) ) {
$notification_html .= '</div>';
}
}
$notification_html .= '<div class="actions">';
if ( ! empty( $notification_details['ctas']['confirm']['text'] ) ) {
$notification_html .= sprintf(
'<a href="%s" target="_blank" class=" button button-primary %s" data-confirm="yes" >%s</a>',
esc_url( $notification_details['ctas']['confirm']['link'] ),
esc_attr( $notification_details['id'] . '_confirm' ),
wp_kses_post( $notification_details['ctas']['confirm']['text'] )
);
}
if ( ! empty( $notification_details['ctas']['cancel']['text'] ) ) {
$notification_html .= sprintf(
'<a href="%s" class=" button %s" data-confirm="no">%s</a>',
esc_url( $notification_details['ctas']['cancel']['link'] ),
esc_attr( $notification_details['id'] ) . '_cancel',
wp_kses_post( $notification_details['ctas']['cancel']['text'] )
);
}
$notification_html .= '</div>';
$notification_html .= ' </div>';
$notification_html .= ' </div>';
return $notification_html;
}
/**
* Adds js snippet for hiding the notice.
*/
public static function render_snippets() {
?>
<style type="text/css">
.themeisle-sdk-notification-box {
padding: 3px;
}
.themeisle-sdk-notification-box .wrap-flex {
display: flex;
align-items: center;
justify-content: start;
gap: 12px;
}
.themeisle-sdk-notification-box .wrap-flex img {
width: 42px;
object-fit: cover;
}
.themeisle-sdk-notification-box .actions {
margin-top: 6px;
margin-bottom: 4px;
}
.themeisle-sdk-notification-box .button {
margin-right: 5px;
}
</style>
<script type="text/javascript">
(function ($) {
$(document).ready(function () {
$('#wpbody-content').on('click', ".themeisle-sdk-notice a.button, .themeisle-sdk-notice .notice-dismiss", function (e) {
var container = $('.themeisle-sdk-notice');
var link = $(this);
var notification_id = container.attr('data-notification-id');
var confirm = link.attr('data-confirm');
if (typeof confirm === "undefined") {
confirm = 'no';
}
$.post(
ajaxurl,
{
'nonce': '<?php echo esc_attr( wp_create_nonce( (string) __CLASS__ ) ); ?>',
'action': 'themeisle_sdk_dismiss_notice',
'id': notification_id,
'confirm': confirm,
},
).fail(function() {
location.href = encodeURI(link.attr('href'));
});
if (confirm === 'yes') {
$(this).trigger('themeisle-sdk:confirmed');
} else {
$(this).trigger('themeisle-sdk:canceled');
}
container.hide();
if (confirm === 'no' || link.attr('href') === '#') {
return false;
}
});
});
})(jQuery);
</script>
<?php
}
/**
* Dismiss the notification.
*/
public static function dismiss() {
check_ajax_referer( (string) __CLASS__, 'nonce' );
$id = isset( $_POST['id'] ) ? sanitize_text_field( $_POST['id'] ) : '';
$confirm = isset( $_POST['confirm'] ) ? sanitize_text_field( $_POST['confirm'] ) : 'no';
if ( empty( $id ) ) {
wp_send_json( [] );
}
self::setup_notifications();
$ids = wp_list_pluck( self::$notifications, 'id' );
if ( ! in_array( $id, $ids, true ) ) {
wp_send_json( [] );
}
self::set_last_active_notification_timestamp();
update_option( $id, $confirm );
do_action( $id . '_process_confirm', $confirm );
wp_send_json( [] );
}
/**
* Dismiss the notification.
*/
public static function dismiss_get() {
$is_nonce_dismiss = sanitize_text_field( isset( $_GET['tsdk_dismiss_nonce'] ) ? $_GET['tsdk_dismiss_nonce'] : '' );
if ( strlen( $is_nonce_dismiss ) < 5 ) {
return;
}
$id = sanitize_text_field( isset( $_GET['nid'] ) ? $_GET['nid'] : '' );
if ( empty( $id ) ) {
return;
}
$nonce = wp_verify_nonce( sanitize_text_field( $_GET['tsdk_dismiss_nonce'] ), $id );
if ( $nonce !== 1 ) {
return;
}
$ids = wp_list_pluck( self::$notifications, 'id' );
if ( ! in_array( $id, $ids, true ) ) {
return;
}
$confirm = 'no';
self::set_last_active_notification_timestamp();
update_option( $id, $confirm );
do_action( $id . '_process_confirm', $confirm );
}
/**
* Check if we should load the notification module.
*
* @param Product $product Product to check.
*
* @return bool Should we load this?
*/
public function can_load( $product ) {
if ( $this->is_from_partner( $product ) ) {
return false;
}
if ( ! current_user_can( 'manage_options' ) ) {
return false;
}
if ( ( time() - $product->get_install_time() ) < ( self::MIN_INSTALL_TIME * HOUR_IN_SECONDS ) ) {
return false;
}
return true;
}
/**
* Setup notifications queue.
*/
public static function setup_notifications() {
$notifications = apply_filters( 'themeisle_sdk_registered_notifications', [] );
$notifications = array_filter(
$notifications,
function ( $value ) {
if ( ! isset( $value['id'] ) ) {
return false;
}
if ( get_option( $value['id'], '' ) !== '' ) {
return false;
}
return apply_filters( $value['id'] . '_should_show', true );
}
);
self::$notifications = $notifications;
}
/**
* Load the module logic.
*
* @param Product $product Product to load the module for.
*
* @return Notification Module instance.
*/
public function load( $product ) {
if ( apply_filters( 'themeisle_sdk_hide_notifications', false ) ) {
return;
}
$this->product = $product;
$notifications = apply_filters( 'themeisle_sdk_registered_notifications', [] );
$notifications = array_filter(
$notifications,
function ( $value ) {
if ( ! isset( $value['id'] ) ) {
return false;
}
if ( get_option( $value['id'], '' ) !== '' ) {
return false;
}
return apply_filters( $value['id'] . '_should_show', true );
}
);
self::$notifications = $notifications;
add_action( 'admin_notices', array( __CLASS__, 'show_notification' ) );
add_action( 'wp_ajax_themeisle_sdk_dismiss_notice', array( __CLASS__, 'dismiss' ) );
add_action( 'admin_head', array( __CLASS__, 'dismiss_get' ) );
add_action( 'admin_head', array( __CLASS__, 'setup_notifications' ) );
return $this;
}
}

View File

@ -1,334 +0,0 @@
<?php
/**
* The class that exposes hooks for recommend.
*
* @package ThemeIsleSDK
* @subpackage Rollback
* @copyright Copyright (c) 2017, Marius Cristea
* @license http://opensource.org/licenses/gpl-3.0.php GNU Public License
* @since 1.0.0
*/
namespace ThemeisleSDK\Modules;
// Exit if accessed directly.
use ThemeisleSDK\Common\Abstract_Module;
use ThemeisleSDK\Product;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Expose endpoints for ThemeIsle SDK.
*/
class Recommendation extends Abstract_Module {
/**
* Load module logic.
*
* @param Product $product Product to load.
*/
public function load( $product ) {
$this->product = $product;
$this->setup_hooks();
return $this;
}
/**
* Setup endpoints.
*/
private function setup_hooks() {
add_action( $this->product->get_key() . '_recommend_products', array( $this, 'render_products_box' ), 10, 4 );
add_action( 'admin_head', array( $this, 'enqueue' ) );
}
/**
* Check if we should load the module for this product.
*
* @param Product $product Product data.
*
* @return bool Should we load the module?
*/
public function can_load( $product ) {
return true;
}
/**
* Render products box content.
*
* @param array $plugins_list - list of useful plugins (in slug => nicename format).
* @param array $themes_list - list of useful themes (in slug => nicename format).
* @param array $strings - list of translated strings.
* @param array $preferences - list of preferences.
*/
public function render_products_box( $plugins_list, $themes_list, $strings, $preferences = array() ) {
if ( empty( $plugins_list ) && empty( $themes_list ) ) {
return;
}
if ( ! empty( $plugins_list ) && ! current_user_can( 'install_plugins' ) ) {
return;
}
if ( ! empty( $themes_list ) && ! current_user_can( 'install_themes' ) ) {
return;
}
add_thickbox();
if ( ! empty( $themes_list ) ) {
$list = $this->get_themes( $themes_list, $preferences );
if ( has_action( $this->product->get_key() . '_recommend_products_theme_template' ) ) {
do_action( $this->product->get_key() . '_recommend_products_theme_template', $list, $strings, $preferences );
} else {
echo '<div class="recommend-product">';
foreach ( $list as $theme ) {
echo '<div class="plugin_box">';
echo ' <img class="theme-banner" src="' . esc_url( $theme->screenshot_url ) . '">';
echo ' <div class="title-action-wrapper">';
echo ' <span class="plugin-name">' . esc_html( $theme->custom_name ) . '</span>';
if ( ! isset( $preferences['description'] ) || ( isset( $preferences['description'] ) && $preferences['description'] ) ) {
echo '<span class="plugin-desc">' . esc_html( substr( $theme->description, 0, strpos( $theme->description, '.' ) ) ) . '.</span>';
}
echo ' </div>';
echo '<div class="plugin-box-footer">';
echo ' <div class="button-wrap">';
echo ' <a class="button button-primary " href="' . esc_url( $theme->custom_url ) . '"><span class="dashicons dashicons-external"></span>' . esc_html( $strings['install'] ) . '</a>';
echo ' </div>';
echo ' </div>';
echo '</div>';
}
echo '</div>';
}
}
if ( ! empty( $plugins_list ) ) {
$list = $this->get_plugins( $plugins_list, $preferences );
if ( has_action( $this->product->get_key() . '_recommend_products_plugin_template' ) ) {
do_action( $this->product->get_key() . '_recommend_products_plugin_template', $list, $strings, $preferences );
} else {
echo '<div class="recommend-product">';
foreach ( $list as $current_plugin ) {
echo '<div class="plugin_box">';
echo ' <img class="plugin-banner" src="' . esc_url( $current_plugin->custom_image ) . '">';
echo ' <div class="title-action-wrapper">';
echo ' <span class="plugin-name">' . esc_html( $current_plugin->custom_name ) . '</span>';
if ( ! isset( $preferences['description'] ) || ( isset( $preferences['description'] ) && $preferences['description'] ) ) {
echo '<span class="plugin-desc">' . esc_html( substr( $current_plugin->short_description, 0, strpos( $current_plugin->short_description, '.' ) ) ) . '. </span>';
}
echo ' </div>';
echo ' <div class="plugin-box-footer">';
echo ' <a class="button button-primary thickbox open-plugin-details-modal" href="' . esc_url( $current_plugin->custom_url ) . '"><span class="dashicons dashicons-external"></span>' . esc_html( $strings['install'] ) . '</a>';
echo ' </div>';
echo '</div>';
}
echo '</div>';
}
}
}
/**
* Collect all the information for the themes list.
*
* @param array $themes_list - list of useful themes (in slug => nicename format).
* @param array $preferences - list of preferences.
*
* @return array
*/
private function get_themes( $themes_list, $preferences ) {
$list = array();
foreach ( $themes_list as $slug => $nicename ) {
$theme = $this->call_theme_api( $slug );
if ( ! $theme ) {
continue;
}
$url = add_query_arg(
array(
'theme' => $theme->slug,
),
network_admin_url( 'theme-install.php' )
);
$name = empty( $nicename ) ? $theme->name : $nicename;
$theme->custom_url = $url;
$theme->custom_name = $name;
$list[] = $theme;
}
return $list;
}
/**
* Call theme api
*
* @param string $slug theme slug.
*
* @return array|mixed|object
*/
private function call_theme_api( $slug ) {
$theme = get_transient( 'ti_theme_info_' . $slug );
if ( false !== $theme ) {
return $theme;
}
$products = $this->safe_get(
'https://api.wordpress.org/themes/info/1.1/?action=query_themes&request[theme]=' . $slug . '&request[per_page]=1'
);
$products = json_decode( wp_remote_retrieve_body( $products ) );
if ( is_object( $products ) ) {
$theme = $products->themes[0];
set_transient( 'ti_theme_info_' . $slug, $theme, 6 * HOUR_IN_SECONDS );
}
return $theme;
}
/**
* Collect all the information for the plugins list.
*
* @param array $plugins_list - list of useful plugins (in slug => nicename format).
* @param array $preferences - list of preferences.
*
* @return array
*/
private function get_plugins( $plugins_list, $preferences ) {
$list = array();
foreach ( $plugins_list as $plugin => $nicename ) {
$current_plugin = $this->call_plugin_api( $plugin );
$name = empty( $nicename ) ? $current_plugin->name : $nicename;
$image = $current_plugin->banners['low'];
if ( isset( $preferences['image'] ) && 'icon' === $preferences['image'] ) {
$image = $current_plugin->icons['1x'];
}
$url = add_query_arg(
array(
'tab' => 'plugin-information',
'plugin' => $current_plugin->slug,
'TB_iframe' => true,
'width' => 800,
'height' => 800,
),
network_admin_url( 'plugin-install.php' )
);
$current_plugin->custom_url = $url;
$current_plugin->custom_name = $name;
$current_plugin->custom_image = $image;
$list[] = $current_plugin;
}
return $list;
}
/**
* Load css and scripts for the plugin recommend page.
*/
public function enqueue() {
$screen = get_current_screen();
if ( ! isset( $screen->id ) ) {
return;
}
if ( false === apply_filters( $this->product->get_key() . '_enqueue_recommend', false, $screen->id ) ) {
return;
}
?>
<style type="text/css">
.recommend-product {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
}
.recommend-product .theme-banner {
width: 200px;
margin: auto;
}
.recommend-product .plugin-banner {
width: 100px;
margin: auto;
}
.recommend-product .plugin_box .button span {
margin-top: 2px;
margin-right: 7px;
}
.recommend-product .plugin_box .button {
margin-bottom: 10px;
}
.recommend-product .plugin_box {
margin-bottom: 20px;
padding-top: 5px;
display: flex;
box-shadow: 0px 0px 10px -5px rgba(0, 0, 0, 0.55);
background: #fff;
border-radius: 5px;
flex-direction: column;
justify-content: flex-start;
width: 95%;
}
.recommend-product .title-action-wrapper {
padding: 15px 20px 5px 20px;
}
.recommend-product .plugin-name {
font-size: 18px;
display: block;
white-space: nowrap;
text-overflow: ellipsis;
margin-bottom: 10px;
overflow: hidden;
line-height: normal;
}
.recommend-product .plugin-desc {
display: block;
margin-bottom: 10px;
font-size: 13px;
color: #777;
line-height: 1.6;
}
.recommend-product .button-wrap > div {
padding: 0;
margin: 0;
}
.plugin-box-footer {
display: flex;
justify-content: space-around;
vertical-align: middle;
align-items: center;
padding: 0px 10px 5px;
flex: 1;
margin-top: auto;
}
</style>
<?php
}
}

View File

@ -1,117 +0,0 @@
<?php
/**
* The Review model class for ThemeIsle SDK
*
* @package ThemeIsleSDK
* @subpackage Modules
* @copyright Copyright (c) 2017, Marius Cristea
* @license http://opensource.org/licenses/gpl-3.0.php GNU Public License
* @since 1.0.0
*/
namespace ThemeisleSDK\Modules;
use ThemeisleSDK\Common\Abstract_Module;
use ThemeisleSDK\Product;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Review module for ThemeIsle SDK.
*/
class Review extends Abstract_Module {
/**
* Check if we should load module for this.
*
* @param Product $product Product to check.
*
* @return bool Should load ?
*/
public function can_load( $product ) {
if ( $this->is_from_partner( $product ) ) {
return false;
}
if ( ! $product->is_wordpress_available() ) {
return false;
}
return apply_filters( $product->get_slug() . '_sdk_should_review', true );
}
/**
* Add notification to queue.
*
* @param array $all_notifications Previous notification.
*
* @return array All notifications.
*/
public function add_notification( $all_notifications ) {
$developers = [
'Bogdan',
'Marius',
'Hardeep',
'Rodica',
'Stefan',
'Uriahs',
'Madalin',
'Cristi',
'Silviu',
'Andrei',
];
$link = 'https://wordpress.org/support/' . $this->product->get_type() . '/' . $this->product->get_slug() . '/reviews/#wporg-footer';
$message = apply_filters( $this->product->get_key() . '_feedback_review_message', '<p>Hey, it\'s great to see you have <b>{product}</b> active for a few days now. How is everything going? If you can spare a few moments to rate it on WordPress.org it would help us a lot (and boost my motivation). Cheers! <br/> <br/>~ {developer}, developer of {product}</p>' );
$button_submit = apply_filters( $this->product->get_key() . '_feedback_review_button_do', 'Ok, I will gladly help.' );
$button_cancel = apply_filters( $this->product->get_key() . '_feedback_review_button_cancel', 'No, thanks.' );
$message = str_replace(
[ '{product}', '{developer}' ],
[
$this->product->get_friendly_name(),
$developers[ strlen( get_site_url() ) % 10 ],
],
$message
);
$all_notifications[] = [
'id' => $this->product->get_key() . '_review_flag',
'message' => $message,
'ctas' => [
'confirm' => [
'link' => $link,
'text' => $button_submit,
],
'cancel' => [
'link' => '#',
'text' => $button_cancel,
],
],
];
return $all_notifications;
}
/**
* Load module logic.
*
* @param Product $product Product to load.
*
* @return Review Module instance.
*/
public function load( $product ) {
$this->product = $product;
add_filter( 'themeisle_sdk_registered_notifications', [ $this, 'add_notification' ] );
return $this;
}
}

View File

@ -1,400 +0,0 @@
<?php
/**
* The rollback class for ThemeIsle SDK.
*
* @package ThemeIsleSDK
* @subpackage Rollback
* @copyright Copyright (c) 2017, Marius Cristea
* @license http://opensource.org/licenses/gpl-3.0.php GNU Public License
* @since 1.0.0
*/
namespace ThemeisleSDK\Modules;
// Exit if accessed directly.
use ThemeisleSDK\Common\Abstract_Module;
use ThemeisleSDK\Product;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Rollback for ThemeIsle SDK.
*/
class Rollback extends Abstract_Module {
/**
* Add js scripts for themes rollback.
*/
public function add_footer() {
$screen = get_current_screen();
if ( ! isset( $screen->parent_file ) ) {
return;
}
if ( 'themes.php' !== $screen->parent_file ) {
return;
}
if ( ! $this->product->is_theme() ) {
return;
}
$version = $this->get_rollback();
if ( empty( $version ) ) {
return;
}
?>
<script type="text/javascript">
jQuery(document).ready(function ($) {
setInterval(checkTheme, 500);
function checkTheme() {
var theme = '<?php echo esc_attr( $this->product->get_slug() ); ?>-action';
if (jQuery('#' + theme).length > 0) {
if (jQuery('.theme-overlay.active').is(':visible')) {
if (jQuery('#' + theme + '-rollback').length === 0) {
jQuery('.theme-actions .active-theme').prepend('<a class="button" style="float:left" id="' + theme + '-rollback" href="<?php echo esc_url( wp_nonce_url( admin_url( 'admin-post.php?action=' . $this->product->get_key() . '_rollback' ), $this->product->get_key() . '_rollback' ) ); ?>">Rollback to v<?php echo esc_attr( $version['version'] ); ?></a>')
}
}
}
}
})
</script>
<?php
}
/**
* Get the last rollback for this product.
*
* @return array The rollback version.
*/
public function get_rollback() {
$rollback = array();
$versions = $this->get_api_versions();
$versions = apply_filters( $this->product->get_key() . '_rollbacks', $versions );
if ( empty( $versions ) ) {
return $rollback;
}
if ( $versions ) {
usort( $versions, array( $this, 'sort_rollback_array' ) );
foreach ( $versions as $version ) {
if ( isset( $version['version'] ) && isset( $version['url'] ) && version_compare( $this->product->get_version(), $version['version'], '>' ) ) {
$rollback = $version;
break;
}
}
}
return $rollback;
}
/**
* Get versions array from wp.org
*
* @return array Array of versions.
*/
private function get_api_versions() {
$cache_key = $this->product->get_cache_key();
$cache_versions = get_transient( $cache_key );
if ( false === $cache_versions ) {
$versions = $this->get_remote_versions();
set_transient( $cache_key, $versions, 5 * DAY_IN_SECONDS );
} else {
$versions = is_array( $cache_versions ) ? $cache_versions : array();
}
return $versions;
}
/**
* Get remote versions zips.
*
* @return array Array of available versions.
*/
private function get_remote_versions() {
$url = $this->get_versions_api_url();
if ( empty( $url ) ) {
return [];
}
$response = function_exists( 'wp_remote_get_wp_remote_get' )
? wp_remote_get_wp_remote_get( $url )
: wp_remote_get( $url ); //phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.wp_remote_get_wp_remote_get
if ( is_wp_error( $response ) ) {
return array();
}
$response = wp_remote_retrieve_body( $response );
if ( is_serialized( $response ) ) {
$response = maybe_unserialize( $response );
} else {
$response = json_decode( $response );
}
if ( ! is_object( $response ) ) {
return array();
}
if ( ! isset( $response->versions ) ) {
return array();
}
$versions = array();
foreach ( $response->versions as $key => $value ) {
$versions[] = array(
'version' => is_object( $value ) ? $value->version : $key,
'url' => is_object( $value ) ? $value->file : $value,
);
}
return $versions;
}
/**
* Return url where to check for versions.
*
* @return string Url where to check for versions.
*/
private function get_versions_api_url() {
if ( $this->product->is_wordpress_available() && $this->product->is_plugin() ) {
return sprintf( 'https://api.wordpress.org/plugins/info/1.0/%s', $this->product->get_slug() );
}
if ( $this->product->is_wordpress_available() && $this->product->is_theme() ) {
return sprintf( 'https://api.wordpress.org/themes/info/1.1/?action=theme_information&request[slug]=%s&request[fields][versions]=true', $this->product->get_slug() );
}
$license = $this->product->get_license();
if ( $this->product->requires_license() && strlen( $license ) < 10 ) {
return '';
}
return sprintf( '%slicense/versions/%s/%s/%s/%s', Product::API_URL, rawurlencode( $this->product->get_name() ), $license, urlencode( get_site_url() ), $this->product->get_version() );
}
/**
* Show the rollback links in the plugin page.
*
* @param array $links Plugin links.
*
* @return array $links Altered links.
*/
public function add_rollback_link( $links ) {
$version = $this->get_rollback();
if ( empty( $version ) ) {
return $links;
}
$links[] = '<a href="' . wp_nonce_url( admin_url( 'admin-post.php?action=' . $this->product->get_key() . '_rollback' ), $this->product->get_key() . '_rollback' ) . '">' . sprintf( apply_filters( $this->product->get_key() . '_rollback_label', 'Rollback to v%s' ), $version['version'] ) . '</a>';
return $links;
}
/**
* Start the rollback operation.
*/
public function start_rollback() {
if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'], $this->product->get_key() . '_rollback' ) ) { //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
wp_nonce_ays( '' );
}
if ( $this->product->is_plugin() ) {
$this->start_rollback_plugin();
return;
}
if ( $this->product->is_theme() ) {
$this->start_rollback_theme();
return;
}
}
/**
* Start the rollback operation for the plugin.
*/
private function start_rollback_plugin() {
$rollback = $this->get_rollback();
$plugin_transient = get_site_transient( 'update_plugins' );
$plugin_folder = $this->product->get_slug();
$plugin_file = $this->product->get_file();
$version = $rollback['version'];
$temp_array = array(
'slug' => $plugin_folder,
'new_version' => $version,
'package' => $rollback['url'],
);
$temp_object = (object) $temp_array;
$plugin_transient->response[ $plugin_folder . '/' . $plugin_file ] = $temp_object;
set_site_transient( 'update_plugins', $plugin_transient );
$transient = get_transient( $this->product->get_key() . '_warning_rollback' );
// Style fix for the api link that gets outside the content.
echo '<style>body#error-page{word-break:break-word;}</style>';
if ( false === $transient ) {
set_transient( $this->product->get_key() . '_warning_rollback', 'in progress', 30 );
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
$title = sprintf( apply_filters( $this->product->get_key() . '_rollback_message', 'Rolling back %s to v%s' ), $this->product->get_name(), $version );
$plugin = $plugin_folder . '/' . $plugin_file;
$nonce = 'upgrade-plugin_' . $plugin;
$url = 'update.php?action=upgrade-plugin&plugin=' . urlencode( $plugin );
$upgrader_skin = new \Plugin_Upgrader_Skin( compact( 'title', 'nonce', 'url', 'plugin' ) );
$upgrader = new \Plugin_Upgrader( $upgrader_skin );
$upgrader->upgrade( $plugin );
delete_transient( $this->product->get_key() . '_warning_rollback' );
wp_die(
'',
esc_attr( $title ),
array(
'response' => 200,
)
);
}
}
/**
* Start the rollback operation for the theme.
*/
private function start_rollback_theme() {
add_filter( 'update_theme_complete_actions', array( $this, 'alter_links_theme_upgrade' ) );
$rollback = $this->get_rollback();
$transient = get_site_transient( 'update_themes' );
$folder = $this->product->get_slug();
$version = $rollback['version'];
$temp_array = array(
'new_version' => $version,
'package' => $rollback['url'],
);
$transient->response[ $folder . '/style.css' ] = $temp_array;
set_site_transient( 'update_themes', $transient );
$transient = get_transient( $this->product->get_key() . '_warning_rollback' );
// Style fix for the api link that gets outside the content.
echo '<style>body#error-page{word-break:break-word;}</style>';
if ( false === $transient ) {
set_transient( $this->product->get_key() . '_warning_rollback', 'in progress', 30 );
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
$title = sprintf( apply_filters( $this->product->get_key() . '_rollback_message', 'Rolling back %s to v%s' ), $this->product->get_name(), $version );
$theme = $folder . '/style.css';
$nonce = 'upgrade-theme_' . $theme;
$url = 'update.php?action=upgrade-theme&theme=' . urlencode( $theme );
$upgrader = new \Theme_Upgrader( new \Theme_Upgrader_Skin( compact( 'title', 'nonce', 'url', 'theme' ) ) );
$upgrader->upgrade( $theme );
delete_transient( $this->product->get_key() . '_warning_rollback' );
wp_die(
'',
esc_attr( $title ),
array(
'response' => 200,
)
);
}
}
/**
* Alter links and remove duplicate customize message.
*
* @param array $links Array of old links.
*
* @return mixed Array of links.
*/
public function alter_links_theme_upgrade( $links ) {
if ( isset( $links['preview'] ) ) {
$links['preview'] = str_replace( '<span aria-hidden="true">Customize</span>', '', $links['preview'] );
}
return $links;
}
/**
* Loads product object.
*
* @param Product $product Product object.
*
* @return bool Should we load the module?
*/
public function can_load( $product ) {
if ( $this->is_from_partner( $product ) ) {
return false;
}
if ( $product->is_theme() && ! current_user_can( 'switch_themes' ) ) {
return false;
}
if ( $product->is_plugin() && ! current_user_can( 'install_plugins' ) ) {
return false;
}
return true;
}
/**
* Sort the rollbacks array in descending order.
*
* @param mixed $a First version to compare.
* @param mixed $b Second version to compare.
*
* @return bool Which version is greater?
*/
public function sort_rollback_array( $a, $b ) {
return version_compare( $b['version'], $a['version'] );
}
/**
* Load module logic.
*
* @param Product $product Product object.
*
* @return $this Module object.
*/
public function load( $product ) {
$this->product = $product;
$this->show_link();
$this->add_hooks();
return $this;
}
/**
* If product can be rolled back, show the link to rollback.
*/
private function show_link() {
add_filter(
'plugin_action_links_' . plugin_basename( $this->product->get_basefile() ),
array(
$this,
'add_rollback_link',
)
);
}
/**
* Fires after the option has been updated.
*
* @param mixed $old_value The old option value.
* @param mixed $value The new option value.
* @param string $option Option name.
*/
public function update_active_plugins_action( $old_value, $value, $option ) {
delete_site_transient( 'update_plugins' );
wp_cache_delete( 'plugins', 'plugins' );
}
/**
* Set the rollback hook. Strangely, this does not work if placed in the ThemeIsle_SDK_Rollback class, so it is being called from there instead.
*/
public function add_hooks() {
add_action( 'admin_post_' . $this->product->get_key() . '_rollback', array( $this, 'start_rollback' ) );
add_action( 'admin_footer', array( $this, 'add_footer' ) );
// This hook will be invoked after the plugin activation.
// We use this to force an update of the cache so that Update is present immediate after a rollback.
add_action( 'update_option_active_plugins', array( $this, 'update_active_plugins_action' ), 10, 3 );
}
}

View File

@ -1,918 +0,0 @@
<?php
/**
* The translate model class for ThemeIsle SDK
*
* @package ThemeIsleSDK
* @subpackage Modules
* @copyright Copyright (c) 2017, Marius Cristea
* @license http://opensource.org/licenses/gpl-3.0.php GNU Public License
* @since 1.0.0
*/
namespace ThemeisleSDK\Modules;
use ThemeisleSDK\Common\Abstract_Module;
use ThemeisleSDK\Product;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Translate module for ThemeIsle SDK.
*/
class Translate extends Abstract_Module {
/**
* List of available locales.
*
* @var array Array of available locals.
*/
private static $locales = array(
'af' => array(
'slug' => 'af',
'name' => 'Afrikaans',
),
'ak' => array(
'slug' => 'ak',
'name' => 'Akan',
),
'am' => array(
'slug' => 'am',
'name' => 'Amharic',
),
'ar' => array(
'slug' => 'ar',
'name' => 'Arabic',
),
'arq' => array(
'slug' => 'arq',
'name' => 'Algerian Arabic',
),
'ary' => array(
'slug' => 'ary',
'name' => 'Moroccan Arabic',
),
'as' => array(
'slug' => 'as',
'name' => 'Assamese',
),
'ast' => array(
'slug' => 'ast',
'name' => 'Asturian',
),
'az' => array(
'slug' => 'az',
'name' => 'Azerbaijani',
),
'azb' => array(
'slug' => 'azb',
'name' => 'South Azerbaijani',
),
'az_TR' => array(
'slug' => 'az-tr',
'name' => 'Azerbaijani (Turkey)',
),
'ba' => array(
'slug' => 'ba',
'name' => 'Bashkir',
),
'bal' => array(
'slug' => 'bal',
'name' => 'Catalan (Balear)',
),
'bcc' => array(
'slug' => 'bcc',
'name' => 'Balochi Southern',
),
'bel' => array(
'slug' => 'bel',
'name' => 'Belarusian',
),
'bg_BG' => array(
'slug' => 'bg',
'name' => 'Bulgarian',
),
'bn_BD' => array(
'slug' => 'bn',
'name' => 'Bengali',
),
'bo' => array(
'slug' => 'bo',
'name' => 'Tibetan',
),
'bre' => array(
'slug' => 'br',
'name' => 'Breton',
),
'bs_BA' => array(
'slug' => 'bs',
'name' => 'Bosnian',
),
'ca' => array(
'slug' => 'ca',
'name' => 'Catalan',
),
'ceb' => array(
'slug' => 'ceb',
'name' => 'Cebuano',
),
'ckb' => array(
'slug' => 'ckb',
'name' => 'Kurdish (Sorani)',
),
'co' => array(
'slug' => 'co',
'name' => 'Corsican',
),
'cs_CZ' => array(
'slug' => 'cs',
'name' => 'Czech',
),
'cy' => array(
'slug' => 'cy',
'name' => 'Welsh',
),
'da_DK' => array(
'slug' => 'da',
'name' => 'Danish',
),
'de_DE' => array(
'slug' => 'de',
'name' => 'German',
),
'de_CH' => array(
'slug' => 'de-ch',
'name' => 'German (Switzerland)',
),
'dv' => array(
'slug' => 'dv',
'name' => 'Dhivehi',
),
'dzo' => array(
'slug' => 'dzo',
'name' => 'Dzongkha',
),
'el' => array(
'slug' => 'el',
'name' => 'Greek',
),
'art_xemoji' => array(
'slug' => 'art-xemoji',
'name' => 'Emoji',
),
'en_US' => array(
'slug' => 'en',
'name' => 'English',
),
'en_AU' => array(
'slug' => 'en-au',
'name' => 'English (Australia)',
),
'en_CA' => array(
'slug' => 'en-ca',
'name' => 'English (Canada)',
),
'en_GB' => array(
'slug' => 'en-gb',
'name' => 'English (UK)',
),
'en_NZ' => array(
'slug' => 'en-nz',
'name' => 'English (New Zealand)',
),
'en_ZA' => array(
'slug' => 'en-za',
'name' => 'English (South Africa)',
),
'eo' => array(
'slug' => 'eo',
'name' => 'Esperanto',
),
'es_ES' => array(
'slug' => 'es',
'name' => 'Spanish (Spain)',
),
'es_AR' => array(
'slug' => 'es-ar',
'name' => 'Spanish (Argentina)',
),
'es_CL' => array(
'slug' => 'es-cl',
'name' => 'Spanish (Chile)',
),
'es_CO' => array(
'slug' => 'es-co',
'name' => 'Spanish (Colombia)',
),
'es_CR' => array(
'slug' => 'es-cr',
'name' => 'Spanish (Costa Rica)',
),
'es_GT' => array(
'slug' => 'es-gt',
'name' => 'Spanish (Guatemala)',
),
'es_MX' => array(
'slug' => 'es-mx',
'name' => 'Spanish (Mexico)',
),
'es_PE' => array(
'slug' => 'es-pe',
'name' => 'Spanish (Peru)',
),
'es_PR' => array(
'slug' => 'es-pr',
'name' => 'Spanish (Puerto Rico)',
),
'es_VE' => array(
'slug' => 'es-ve',
'name' => 'Spanish (Venezuela)',
),
'et' => array(
'slug' => 'et',
'name' => 'Estonian',
),
'eu' => array(
'slug' => 'eu',
'name' => 'Basque',
),
'fa_IR' => array(
'slug' => 'fa',
'name' => 'Persian',
),
'fa_AF' => array(
'slug' => 'fa-af',
'name' => 'Persian (Afghanistan)',
),
'fuc' => array(
'slug' => 'fuc',
'name' => 'Fulah',
),
'fi' => array(
'slug' => 'fi',
'name' => 'Finnish',
),
'fo' => array(
'slug' => 'fo',
'name' => 'Faroese',
),
'fr_FR' => array(
'slug' => 'fr',
'name' => 'French (France)',
),
'fr_BE' => array(
'slug' => 'fr-be',
'name' => 'French (Belgium)',
),
'fr_CA' => array(
'slug' => 'fr-ca',
'name' => 'French (Canada)',
),
'frp' => array(
'slug' => 'frp',
'name' => 'Arpitan',
),
'fur' => array(
'slug' => 'fur',
'name' => 'Friulian',
),
'fy' => array(
'slug' => 'fy',
'name' => 'Frisian',
),
'ga' => array(
'slug' => 'ga',
'name' => 'Irish',
),
'gd' => array(
'slug' => 'gd',
'name' => 'Scottish Gaelic',
),
'gl_ES' => array(
'slug' => 'gl',
'name' => 'Galician',
),
'gn' => array(
'slug' => 'gn',
'name' => 'Guarani',
),
'gsw' => array(
'slug' => 'gsw',
'name' => 'Swiss German',
),
'gu' => array(
'slug' => 'gu',
'name' => 'Gujarati',
),
'hat' => array(
'slug' => 'hat',
'name' => 'Haitian Creole',
),
'hau' => array(
'slug' => 'hau',
'name' => 'Hausa',
),
'haw_US' => array(
'slug' => 'haw',
'name' => 'Hawaiian',
),
'haz' => array(
'slug' => 'haz',
'name' => 'Hazaragi',
),
'he_IL' => array(
'slug' => 'he',
'name' => 'Hebrew',
),
'hi_IN' => array(
'slug' => 'hi',
'name' => 'Hindi',
),
'hr' => array(
'slug' => 'hr',
'name' => 'Croatian',
),
'hu_HU' => array(
'slug' => 'hu',
'name' => 'Hungarian',
),
'hy' => array(
'slug' => 'hy',
'name' => 'Armenian',
),
'id_ID' => array(
'slug' => 'id',
'name' => 'Indonesian',
),
'ido' => array(
'slug' => 'ido',
'name' => 'Ido',
),
'is_IS' => array(
'slug' => 'is',
'name' => 'Icelandic',
),
'it_IT' => array(
'slug' => 'it',
'name' => 'Italian',
),
'ja' => array(
'slug' => 'ja',
'name' => 'Japanese',
),
'jv_ID' => array(
'slug' => 'jv',
'name' => 'Javanese',
),
'ka_GE' => array(
'slug' => 'ka',
'name' => 'Georgian',
),
'kab' => array(
'slug' => 'kab',
'name' => 'Kabyle',
),
'kal' => array(
'slug' => 'kal',
'name' => 'Greenlandic',
),
'kin' => array(
'slug' => 'kin',
'name' => 'Kinyarwanda',
),
'kk' => array(
'slug' => 'kk',
'name' => 'Kazakh',
),
'km' => array(
'slug' => 'km',
'name' => 'Khmer',
),
'kn' => array(
'slug' => 'kn',
'name' => 'Kannada',
),
'ko_KR' => array(
'slug' => 'ko',
'name' => 'Korean',
),
'kir' => array(
'slug' => 'kir',
'name' => 'Kyrgyz',
),
'lb_LU' => array(
'slug' => 'lb',
'name' => 'Luxembourgish',
),
'li' => array(
'slug' => 'li',
'name' => 'Limburgish',
),
'lin' => array(
'slug' => 'lin',
'name' => 'Lingala',
),
'lo' => array(
'slug' => 'lo',
'name' => 'Lao',
),
'lt_LT' => array(
'slug' => 'lt',
'name' => 'Lithuanian',
),
'lv' => array(
'slug' => 'lv',
'name' => 'Latvian',
),
'me_ME' => array(
'slug' => 'me',
'name' => 'Montenegrin',
),
'mg_MG' => array(
'slug' => 'mg',
'name' => 'Malagasy',
),
'mk_MK' => array(
'slug' => 'mk',
'name' => 'Macedonian',
),
'ml_IN' => array(
'slug' => 'ml',
'name' => 'Malayalam',
),
'mlt' => array(
'slug' => 'mlt',
'name' => 'Maltese',
),
'mn' => array(
'slug' => 'mn',
'name' => 'Mongolian',
),
'mr' => array(
'slug' => 'mr',
'name' => 'Marathi',
),
'mri' => array(
'slug' => 'mri',
'name' => 'Maori',
),
'ms_MY' => array(
'slug' => 'ms',
'name' => 'Malay',
),
'my_MM' => array(
'slug' => 'mya',
'name' => 'Myanmar (Burmese)',
),
'ne_NP' => array(
'slug' => 'ne',
'name' => 'Nepali',
),
'nb_NO' => array(
'slug' => 'nb',
'name' => 'Norwegian (Bokmal)',
),
'nl_NL' => array(
'slug' => 'nl',
'name' => 'Dutch',
),
'nl_BE' => array(
'slug' => 'nl-be',
'name' => 'Dutch (Belgium)',
),
'nn_NO' => array(
'slug' => 'nn',
'name' => 'Norwegian (Nynorsk)',
),
'oci' => array(
'slug' => 'oci',
'name' => 'Occitan',
),
'ory' => array(
'slug' => 'ory',
'name' => 'Oriya',
),
'os' => array(
'slug' => 'os',
'name' => 'Ossetic',
),
'pa_IN' => array(
'slug' => 'pa',
'name' => 'Punjabi',
),
'pl_PL' => array(
'slug' => 'pl',
'name' => 'Polish',
),
'pt_BR' => array(
'slug' => 'pt-br',
'name' => 'Portuguese (Brazil)',
),
'pt_PT' => array(
'slug' => 'pt',
'name' => 'Portuguese (Portugal)',
),
'ps' => array(
'slug' => 'ps',
'name' => 'Pashto',
),
'rhg' => array(
'slug' => 'rhg',
'name' => 'Rohingya',
),
'ro_RO' => array(
'slug' => 'ro',
'name' => 'Romanian',
),
'roh' => array(
'slug' => 'roh',
'name' => 'Romansh',
),
'ru_RU' => array(
'slug' => 'ru',
'name' => 'Russian',
),
'rue' => array(
'slug' => 'rue',
'name' => 'Rusyn',
),
'rup_MK' => array(
'slug' => 'rup',
'name' => 'Aromanian',
),
'sah' => array(
'slug' => 'sah',
'name' => 'Sakha',
),
'sa_IN' => array(
'slug' => 'sa-in',
'name' => 'Sanskrit',
),
'scn' => array(
'slug' => 'scn',
'name' => 'Sicilian',
),
'si_LK' => array(
'slug' => 'si',
'name' => 'Sinhala',
),
'sk_SK' => array(
'slug' => 'sk',
'name' => 'Slovak',
),
'sl_SI' => array(
'slug' => 'sl',
'name' => 'Slovenian',
),
'sna' => array(
'slug' => 'sna',
'name' => 'Shona',
),
'snd' => array(
'slug' => 'snd',
'name' => 'Sindhi',
),
'so_SO' => array(
'slug' => 'so',
'name' => 'Somali',
),
'sq' => array(
'slug' => 'sq',
'name' => 'Albanian',
),
'sq_XK' => array(
'slug' => 'sq-xk',
'name' => 'Shqip (Kosovo)',
),
'sr_RS' => array(
'slug' => 'sr',
'name' => 'Serbian',
),
'srd' => array(
'slug' => 'srd',
'name' => 'Sardinian',
),
'su_ID' => array(
'slug' => 'su',
'name' => 'Sundanese',
),
'sv_SE' => array(
'slug' => 'sv',
'name' => 'Swedish',
),
'sw' => array(
'slug' => 'sw',
'name' => 'Swahili',
),
'syr' => array(
'slug' => 'syr',
'name' => 'Syriac',
),
'szl' => array(
'slug' => 'szl',
'name' => 'Silesian',
),
'ta_IN' => array(
'slug' => 'ta',
'name' => 'Tamil',
),
'ta_LK' => array(
'slug' => 'ta-lk',
'name' => 'Tamil (Sri Lanka)',
),
'tah' => array(
'slug' => 'tah',
'name' => 'Tahitian',
),
'te' => array(
'slug' => 'te',
'name' => 'Telugu',
),
'tg' => array(
'slug' => 'tg',
'name' => 'Tajik',
),
'th' => array(
'slug' => 'th',
'name' => 'Thai',
),
'tir' => array(
'slug' => 'tir',
'name' => 'Tigrinya',
),
'tl' => array(
'slug' => 'tl',
'name' => 'Tagalog',
),
'tr_TR' => array(
'slug' => 'tr',
'name' => 'Turkish',
),
'tt_RU' => array(
'slug' => 'tt',
'name' => 'Tatar',
),
'tuk' => array(
'slug' => 'tuk',
'name' => 'Turkmen',
),
'twd' => array(
'slug' => 'twd',
'name' => 'Tweants',
),
'tzm' => array(
'slug' => 'tzm',
'name' => 'Tamazight (Central Atlas)',
),
'ug_CN' => array(
'slug' => 'ug',
'name' => 'Uighur',
),
'uk' => array(
'slug' => 'uk',
'name' => 'Ukrainian',
),
'ur' => array(
'slug' => 'ur',
'name' => 'Urdu',
),
'uz_UZ' => array(
'slug' => 'uz',
'name' => 'Uzbek',
),
'vi' => array(
'slug' => 'vi',
'name' => 'Vietnamese',
),
'wa' => array(
'slug' => 'wa',
'name' => 'Walloon',
),
'xho' => array(
'slug' => 'xho',
'name' => 'Xhosa',
),
'xmf' => array(
'slug' => 'xmf',
'name' => 'Mingrelian',
),
'yor' => array(
'slug' => 'yor',
'name' => 'Yoruba',
),
'zh_CN' => array(
'slug' => 'zh-cn',
'name' => 'Chinese (China)',
),
'zh_HK' => array(
'slug' => 'zh-hk',
'name' => 'Chinese (Hong Kong)',
),
'zh_TW' => array(
'slug' => 'zh-tw',
'name' => 'Chinese (Taiwan)',
),
'de_DE_formal' => array(
'slug' => 'de/formal',
'name' => 'German (Formal)',
),
'nl_NL_formal' => array(
'slug' => 'nl/formal',
'name' => 'Dutch (Formal)',
),
'de_CH_informal' => array(
'slug' => 'de-ch/informal',
'name' => 'Chinese (Taiwan)',
),
'pt_PT_ao90' => array(
'slug' => 'pt/ao90',
'name' => 'Portuguese (Portugal, AO90)',
),
);
/**
* Check if we should load module for this.
*
* @param Product $product Product to check.
*
* @return bool Should load ?
*/
public function can_load( $product ) {
if ( $this->is_from_partner( $product ) ) {
return false;
}
if ( ! $product->is_wordpress_available() ) {
return false;
}
$lang = $this->get_user_locale();
if ( 'en_US' === $lang ) {
return false;
}
$languages = $this->get_translations( $product );
if ( ! is_array( $languages ) ) {
return false;
}
if ( ! isset( $languages['translations'] ) ) {
return false;
}
$languages = $languages['translations'];
$available = wp_list_pluck( $languages, 'language' );
if ( in_array( $lang, $available ) ) {
return false;
}
if ( ! isset( self::$locales[ $lang ] ) ) {
return false;
}
return apply_filters( $product->get_slug() . '_sdk_enable_translate', true );
}
/**
* Get the user's locale.
*/
private function get_user_locale() {
global $wp_version;
if ( version_compare( $wp_version, '4.7.0', '>=' ) ) {
return get_user_locale();
}
$user = wp_get_current_user();
if ( $user ) {
$locale = $user->locale;
}
return $locale ? $locale : get_locale();
}
/**
* Fetch translations from api.
*
* @param Product $product Product to check.
*
* @return mixed Translation array.
*/
private function get_translations( $product ) {
$cache_key = $product->get_key() . '_all_languages';
$translations = get_transient( $cache_key );
if ( false === $translations ) {
require_once ABSPATH . 'wp-admin/includes/translation-install.php';
$translations = translations_api(
$product->get_type() . 's',
array(
'slug' => $product->get_slug(),
'version' => $product->get_version(),
)
);
set_transient( $cache_key, $translations, WEEK_IN_SECONDS );
}
return $translations;
}
/**
* Add notification to queue.
*
* @param array $all_notifications Previous notification.
*
* @return array All notifications.
*/
public function add_notification( $all_notifications ) {
$lang = $this->get_user_locale();
$link = $this->get_locale_paths( $lang );
$language_meta = self::$locales[ $lang ];
$heading = apply_filters( $this->product->get_key() . '_feedback_translate_heading', 'Improve {product}' );
$heading = str_replace(
array( '{product}' ),
$this->product->get_friendly_name(),
$heading
);
$message = apply_filters(
$this->product->get_key() . '_feedback_translation',
'Translating <b>{product}</b> into as many languages as possible is a huge project. We still need help with a lot of them, so if you are good at translating into <b>{language}</b>, it would be greatly appreciated.
The process is easy, and you can join by following the link below!'
);
$message = str_replace(
[ '{product}', '{language}' ],
[
$this->product->get_friendly_name(),
$language_meta['name'],
],
$message
);
$button_submit = apply_filters( $this->product->get_key() . '_feedback_translate_button_do', 'Ok, I will gladly help.' );
$button_cancel = apply_filters( $this->product->get_key() . '_feedback_translate_button_cancel', 'No, thanks.' );
$all_notifications[] = [
'id' => $this->product->get_key() . '_translate_flag',
'heading' => $heading,
'message' => $message,
'ctas' => [
'confirm' => [
'link' => $link,
'text' => $button_submit,
],
'cancel' => [
'link' => '#',
'text' => $button_cancel,
],
],
];
return $all_notifications;
}
/**
* Return the locale path.
*
* @param string $locale Locale code.
*
* @return string Locale path.
*/
private function get_locale_paths( $locale ) {
if ( empty( $locale ) ) {
return '';
}
$slug = isset( self::$locales[ $locale ] ) ? self::$locales[ $locale ]['slug'] : '';
if ( empty( $slug ) ) {
return '';
}
if ( strpos( $slug, '/' ) === false ) {
$slug .= '/default';
}
$url = 'https://translate.wordpress.org/projects/wp-' . $this->product->get_type() . 's/' . $this->product->get_slug() . '/' . ( $this->product->get_type() === 'plugin' ? 'dev/' : '' ) . $slug . '?filters%5Bstatus%5D=untranslated&sort%5Bby%5D=random';
return $url;
}
/**
* Load module logic.
*
* @param Product $product Product to load.
*
* @return Translate Module instance.
*/
public function load( $product ) {
$this->product = $product;
add_filter( 'themeisle_sdk_registered_notifications', [ $this, 'add_notification' ] );
return $this;
}
}

View File

@ -1,860 +0,0 @@
<?php
/**
* The deactivate feedback model class for ThemeIsle SDK
*
* @package ThemeIsleSDK
* @subpackage Feedback
* @copyright Copyright (c) 2017, Marius Cristea
* @license http://opensource.org/licenses/gpl-3.0.php GNU Public License
* @since 1.0.0
*/
namespace ThemeisleSDK\Modules;
use ThemeisleSDK\Common\Abstract_Module;
use ThemeisleSDK\Product;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Uninstall feedback module for ThemeIsle SDK.
*/
class Uninstall_Feedback extends Abstract_Module {
/**
* How many seconds before the deactivation window is triggered for themes?
*
* @var int Number of days.
*/
const AUTO_TRIGGER_DEACTIVATE_WINDOW_SECONDS = 3;
/**
* How many days before the deactivation window pops up again for the theme?
*
* @var int Number of days.
*/
const PAUSE_DEACTIVATE_WINDOW_DAYS = 100;
/**
* Where to send the data.
*
* @var string Endpoint url.
*/
const FEEDBACK_ENDPOINT = 'https://api.themeisle.com/tracking/uninstall';
/**
* Default options for plugins.
*
* @var array $options_plugin The main options list for plugins.
*/
private $options_plugin = array(
'I found a better plugin' => array(
'id' => 3,
'type' => 'text',
'placeholder' => 'What\'s the plugin\'s name?',
),
'I could not get the plugin to work' => array(
'type' => 'textarea',
'placeholder' => 'What problem are you experiencing?',
'id' => 4,
),
'I no longer need the plugin' => array(
'id' => 5,
'type' => 'textarea',
'placeholder' => 'If you could improve one thing about our product, what would it be?',
),
'It\'s a temporary deactivation. I\'m just debugging an issue.' => array(
'type' => 'textarea',
'placeholder' => 'What problem are you experiencing?',
'id' => 6,
),
);
/**
* Default options for theme.
*
* @var array $options_theme The main options list for themes.
*/
private $options_theme = array(
'I don\'t know how to make it look like demo' => array(
'id' => 7,
),
'It lacks options' => array(
'placeholder' => 'What option is missing?',
'type' => 'text',
'id' => 8,
),
'Is not working with a plugin that I need' => array(
'id' => 9,
'type' => 'text',
'placeholder' => 'What is the name of the plugin',
),
'I want to try a new design, I don\'t like {theme} style' => array(
'id' => 10,
),
);
/**
* Default other option.
*
* @var array $other The other option
*/
private $other = array(
'Other' => array(
'id' => 999,
'type' => 'textarea',
'placeholder' => 'What can we do better?',
),
);
/**
* Default heading for plugin.
*
* @var string $heading_plugin The heading of the modal
*/
private $heading_plugin = 'What\'s wrong?';
/**
* Default heading for theme.
*
* @var string $heading_theme The heading of the modal
*/
private $heading_theme = 'What does not work for you in {theme}?';
/**
* Default submit button action text.
*
* @var string $button_submit The text of the deactivate button
*/
private $button_submit = 'Submit &amp; Deactivate';
/**
* Default cancel button.
*
* @var string $button_cancel The text of the cancel button
*/
private $button_cancel = 'Skip &amp; Deactivate';
/**
* Loads the additional resources
*/
public function load_resources() {
$screen = get_current_screen();
if ( ! $screen || ! in_array( $screen->id, array( 'theme-install', 'plugins' ) ) ) {
return;
}
$this->add_feedback_popup_style();
if ( $this->product->get_type() === 'theme' ) {
$this->add_theme_feedback_drawer_js();
$this->render_theme_feedback_popup();
return;
}
$this->add_plugin_feedback_popup_js();
$this->render_plugin_feedback_popup();
}
/**
* Render theme feedback drawer.
*/
private function render_theme_feedback_popup() {
$heading = str_replace( '{theme}', $this->product->get_name(), $this->heading_theme );
$button_submit = apply_filters( $this->product->get_key() . '_feedback_deactivate_button_submit', 'Submit' );
$options = $this->options_theme;
$options = $this->randomize_options( apply_filters( $this->product->get_key() . '_feedback_deactivate_options', $options ) );
$info_disclosure_link = '<a href="#" class="info-disclosure-link">' . apply_filters( $this->product->get_slug() . '_themeisle_sdk_info_collect_cta', 'What info do we collect?' ) . '</a>';
$options += $this->other;
?>
<div class="ti-theme-uninstall-feedback-drawer ti-feedback">
<div class="popup--header">
<h5><?php echo wp_kses( $heading, array( 'span' => true ) ); ?> </h5>
<button class="toggle"><span>&times;</span></button>
</div><!--/.popup--header-->
<div class="popup--body">
<?php $this->render_options_list( $options ); ?>
</div><!--/.popup--body-->
<div class="popup--footer">
<div class="actions">
<?php
echo wp_kses_post( $info_disclosure_link );
echo wp_kses_post( $this->get_disclosure_labels() );
echo '<div class="buttons">';
echo get_submit_button( //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, Function has an internal sanitization.
$button_submit,
'secondary',
$this->product->get_key() . 'ti-deactivate-yes',
false,
array(
'data-after-text' => $button_submit,
'disabled' => true,
)
);
echo '</div>';
?>
</div><!--/.actions-->
</div><!--/.popup--footer-->
</div>
<?php
}
/**
* Add feedback styles.
*/
private function add_feedback_popup_style() {
?>
<style>
.ti-feedback {
background: #fff;
max-width: 400px;
z-index: 10000;
box-shadow: 0 0 15px -5px rgba(0, 0, 0, .5);
transition: all .3s ease-out;
}
.ti-feedback .popup--header {
position: relative;
background-color: #23A1CE;
}
.ti-feedback .popup--header h5 {
margin: 0;
font-size: 16px;
padding: 15px;
color: #fff;
font-weight: 600;
text-align: center;
letter-spacing: .3px;
}
.ti-feedback .popup--body {
padding: 15px;
}
.ti-feedback .popup--form {
margin: 0;
font-size: 13px;
}
.ti-feedback .popup--form input[type="radio"] {
<?php echo is_rtl() ? 'margin: 0 0 0 10px;' : 'margin: 0 10px 0 0;'; ?>
}
.ti-feedback .popup--form input[type="radio"]:checked ~ textarea {
display: block;
}
.ti-feedback .popup--form textarea {
width: 100%;
margin: 10px 0 0;
display: none;
max-height: 150px;
}
.ti-feedback li {
display: flex;
align-items: center;
margin-bottom: 15px;
flex-wrap: wrap;
}
.ti-feedback li label {
max-width: 90%;
}
.ti-feedback li:last-child {
margin-bottom: 0;
}
.ti-feedback .popup--footer {
padding: 0 15px 15px;
}
.ti-feedback .actions {
display: flex;
flex-wrap: wrap;
}
.info-disclosure-link {
width: 100%;
margin-bottom: 15px;
}
.ti-feedback .info-disclosure-content {
max-height: 0;
overflow: hidden;
width: 100%;
transition: .3s ease;
}
.ti-feedback .info-disclosure-content.active {
max-height: 300px;
}
.ti-feedback .info-disclosure-content p {
margin: 0;
}
.ti-feedback .info-disclosure-content ul {
margin: 10px 0;
border-radius: 3px;
}
.ti-feedback .info-disclosure-content ul li {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 0;
padding: 5px 0;
border-bottom: 1px solid #ccc;
}
.ti-feedback .buttons {
display: flex;
width: 100%;
}
.ti-feedback .buttons input:last-child {
<?php echo is_rtl() ? 'margin-right: auto;' : 'margin-left: auto;'; ?>
}
.ti-theme-uninstall-feedback-drawer {
border-top-left-radius: 5px;
position: fixed;
top: 100%;
right: 15px;
}
.ti-theme-uninstall-feedback-drawer.active {
transform: translateY(-100%);
}
.ti-theme-uninstall-feedback-drawer .popup--header {
border-top-left-radius: 5px;
}
.ti-theme-uninstall-feedback-drawer .popup--header .toggle {
position: absolute;
padding: 3px 0;
width: 30px;
top: -26px;
right: 0;
cursor: pointer;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
font-size: 20px;
background-color: #23A1CE;
color: #fff;
border: none;
line-height: 20px;
}
.ti-theme-uninstall-feedback-drawer .toggle span {
margin: 0;
display: inline-block;
}
.ti-theme-uninstall-feedback-drawer:not(.active) .toggle span {
transform: rotate(45deg);
}
.ti-theme-uninstall-feedback-drawer .popup--header .toggle:hover {
background-color: #1880a5;
}
.ti-plugin-uninstall-feedback-popup .popup--header:before {
content: "";
display: block;
position: absolute;
top: 50%;
transform: translateY(-50%);
<?php
echo is_rtl() ?
'right: -10px;
border-top: 20px solid transparent;
border-left: 20px solid #23A1CE;
border-bottom: 20px solid transparent;' :
'left: -10px;
border-top: 20px solid transparent;
border-right: 20px solid #23A1CE;
border-bottom: 20px solid transparent;';
?>
}
.ti-plugin-uninstall-feedback-popup {
display: none;
position: absolute;
white-space: normal;
width: 400px;
<?php echo is_rtl() ? 'right: calc( 100% + 15px );' : 'left: calc( 100% + 15px );'; ?>
top: -15px;
}
.ti-plugin-uninstall-feedback-popup.sending-feedback .popup--body i {
animation: rotation 2s infinite linear;
display: block;
float: none;
align-items: center;
width: 100%;
margin: 0 auto;
height: 100%;
background: transparent;
padding: 0;
}
.ti-plugin-uninstall-feedback-popup.sending-feedback .popup--body i:before {
padding: 0;
background: transparent;
box-shadow: none;
color: #b4b9be
}
.ti-plugin-uninstall-feedback-popup.active {
display: block;
}
tr[data-plugin^="<?php echo esc_attr( $this->product->get_slug() ); ?>"] .deactivate {
position: relative;
}
body.ti-feedback-open .ti-feedback-overlay {
content: "";
display: block;
background-color: rgba(0, 0, 0, 0.5);
top: 0;
bottom: 0;
right: 0;
left: 0;
z-index: 10000;
position: fixed;
}
@media (max-width: 768px) {
.ti-plugin-uninstall-feedback-popup {
position: fixed;
max-width: 100%;
margin: 0 auto;
left: 50%;
top: 50px;
transform: translateX(-50%);
}
.ti-plugin-uninstall-feedback-popup .popup--header:before {
display: none;
}
}
</style>
<?php
}
/**
* Theme feedback drawer JS.
*/
private function add_theme_feedback_drawer_js() {
$key = $this->product->get_key();
?>
<script type="text/javascript" id="ti-deactivate-js">
(function ($) {
$(document).ready(function () {
setTimeout(function () {
$('.ti-theme-uninstall-feedback-drawer').addClass('active');
}, <?php echo absint( self::AUTO_TRIGGER_DEACTIVATE_WINDOW_SECONDS * 1000 ); ?> );
$('.ti-theme-uninstall-feedback-drawer .toggle').on('click', function (e) {
e.preventDefault();
$('.ti-theme-uninstall-feedback-drawer').toggleClass('active');
});
$('.info-disclosure-link').on('click', function (e) {
e.preventDefault();
$('.info-disclosure-content').toggleClass('active');
});
$('.ti-theme-uninstall-feedback-drawer input[type="radio"]').on('change', function () {
var radio = $(this);
if (radio.parent().find('textarea').length > 0 &&
radio.parent().find('textarea').val().length === 0) {
$('#<?php echo esc_attr( $key ); ?>ti-deactivate-yes').attr('disabled', 'disabled');
radio.parent().find('textarea').on('keyup', function (e) {
if ($(this).val().length === 0) {
$('#<?php echo esc_attr( $key ); ?>ti-deactivate-yes').attr('disabled', 'disabled');
} else {
$('#<?php echo esc_attr( $key ); ?>ti-deactivate-yes').removeAttr('disabled');
}
});
} else {
$('#<?php echo esc_attr( $key ); ?>ti-deactivate-yes').removeAttr('disabled');
}
});
$('#<?php echo esc_attr( $key ); ?>ti-deactivate-yes').on('click', function (e) {
e.preventDefault();
e.stopPropagation();
var selectedOption = $(
'.ti-theme-uninstall-feedback-drawer input[name="ti-deactivate-option"]:checked');
$.post(ajaxurl, {
'action': '<?php echo esc_attr( $key ) . '_uninstall_feedback'; ?>',
'nonce': '<?php echo esc_attr( wp_create_nonce( (string) __CLASS__ ) ); ?>',
'id': selectedOption.parent().attr('ti-option-id'),
'msg': selectedOption.parent().find('textarea').val(),
'type': 'theme',
'key': '<?php echo esc_attr( $key ); ?>'
});
$('.ti-theme-uninstall-feedback-drawer').fadeOut();
});
});
})(jQuery);
</script>
<?php
do_action( $this->product->get_key() . '_uninstall_feedback_after_js' );
}
/**
* Render the options list.
*
* @param array $options the options for the feedback form.
*/
private function render_options_list( $options ) {
$key = $this->product->get_key();
$inputs_row_map = [
'text' => 1,
'textarea' => 2,
];
?>
<ul class="popup--form">
<?php foreach ( $options as $title => $attributes ) { ?>
<li ti-option-id="<?php echo esc_attr( $attributes['id'] ); ?>">
<input type="radio" name="ti-deactivate-option" id="<?php echo esc_attr( $key . $attributes['id'] ); ?>">
<label for="<?php echo esc_attr( $key . $attributes['id'] ); ?>">
<?php echo esc_attr( str_replace( '{theme}', $this->product->get_name(), $title ) ); ?>
</label>
<?php
if ( array_key_exists( 'type', $attributes ) ) {
$placeholder = array_key_exists( 'placeholder', $attributes ) ? $attributes['placeholder'] : '';
echo '<textarea width="100%" rows="' . esc_attr( $inputs_row_map[ $attributes['type'] ] ) . '" name="comments" placeholder="' . esc_attr( $placeholder ) . '"></textarea>';
}
?>
</li>
<?php } ?>
</ul>
<?php
}
/**
* Render plugin feedback popup.
*/
private function render_plugin_feedback_popup() {
$button_cancel = apply_filters( $this->product->get_key() . '_feedback_deactivate_button_cancel', $this->button_cancel );
$button_submit = apply_filters( $this->product->get_key() . '_feedback_deactivate_button_submit', $this->button_submit );
$options = $this->randomize_options( apply_filters( $this->product->get_key() . '_feedback_deactivate_options', $this->options_plugin ) );
$info_disclosure_link = '<a href="#" class="info-disclosure-link">' . apply_filters( $this->product->get_slug() . '_themeisle_sdk_info_collect_cta', 'What info do we collect?' ) . '</a>';
$options += $this->other;
?>
<div class="ti-plugin-uninstall-feedback-popup ti-feedback" id="<?php echo esc_attr( $this->product->get_slug() . '_uninstall_feedback_popup' ); ?>">
<div class="popup--header">
<h5><?php echo wp_kses( $this->heading_plugin, array( 'span' => true ) ); ?> </h5>
</div><!--/.popup--header-->
<div class="popup--body">
<?php $this->render_options_list( $options ); ?>
</div><!--/.popup--body-->
<div class="popup--footer">
<div class="actions">
<?php
echo wp_kses_post( $info_disclosure_link );
echo wp_kses_post( $this->get_disclosure_labels() );
echo '<div class="buttons">';
echo get_submit_button( //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, Function internals are escaped.
$button_cancel,
'secondary',
$this->product->get_key() . 'ti-deactivate-no',
false
);
echo get_submit_button( //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, Function internals are escaped.
$button_submit,
'primary',
$this->product->get_key() . 'ti-deactivate-yes',
false,
array(
'data-after-text' => $button_submit,
'disabled' => true,
)
);
echo '</div>';
?>
</div><!--/.actions-->
</div><!--/.popup--footer-->
</div>
<?php
}
/**
* Add plugin feedback popup JS
*/
private function add_plugin_feedback_popup_js() {
$popup_id = '#' . $this->product->get_slug() . '_uninstall_feedback_popup';
$key = $this->product->get_key();
?>
<script type="text/javascript" id="ti-deactivate-js">
(function ($) {
$(document).ready(function () {
var targetElement = 'tr[data-plugin^="<?php echo esc_attr( $this->product->get_slug() ); ?>/"] span.deactivate a';
var redirectUrl = $(targetElement).attr('href');
if ($('.ti-feedback-overlay').length === 0) {
$('body').prepend('<div class="ti-feedback-overlay"></div>');
}
$('<?php echo esc_attr( $popup_id ); ?> ').appendTo($(targetElement).parent());
$(targetElement).on('click', function (e) {
e.preventDefault();
$('<?php echo esc_attr( $popup_id ); ?> ').addClass('active');
$('body').addClass('ti-feedback-open');
$('.ti-feedback-overlay').on('click', function () {
$('<?php echo esc_attr( $popup_id ); ?> ').removeClass('active');
$('body').removeClass('ti-feedback-open');
});
});
$('<?php echo esc_attr( $popup_id ); ?> .info-disclosure-link').on('click', function (e) {
e.preventDefault();
$(this).parent().find('.info-disclosure-content').toggleClass('active');
});
$('<?php echo esc_attr( $popup_id ); ?> input[type="radio"]').on('change', function () {
var radio = $(this);
if (radio.parent().find('textarea').length > 0 &&
radio.parent().find('textarea').val().length === 0) {
$('<?php echo esc_attr( $popup_id ); ?> #<?php echo esc_attr( $key ); ?>ti-deactivate-yes').attr('disabled', 'disabled');
radio.parent().find('textarea').on('keyup', function (e) {
if ($(this).val().length === 0) {
$('<?php echo esc_attr( $popup_id ); ?> #<?php echo esc_attr( $key ); ?>ti-deactivate-yes').attr('disabled', 'disabled');
} else {
$('<?php echo esc_attr( $popup_id ); ?> #<?php echo esc_attr( $key ); ?>ti-deactivate-yes').removeAttr('disabled');
}
});
} else {
$('<?php echo esc_attr( $popup_id ); ?> #<?php echo esc_attr( $key ); ?>ti-deactivate-yes').removeAttr('disabled');
}
});
$('<?php echo esc_attr( $popup_id ); ?> #<?php echo esc_attr( $key ); ?>ti-deactivate-no').on('click', function (e) {
e.preventDefault();
e.stopPropagation();
$(targetElement).unbind('click');
$('body').removeClass('ti-feedback-open');
$('<?php echo esc_attr( $popup_id ); ?>').remove();
if (redirectUrl !== '') {
location.href = redirectUrl;
}
});
$('<?php echo esc_attr( $popup_id ); ?> #<?php echo esc_attr( $key ); ?>ti-deactivate-yes').on('click', function (e) {
e.preventDefault();
e.stopPropagation();
$(targetElement).unbind('click');
var selectedOption = $(
'<?php echo esc_attr( $popup_id ); ?> input[name="ti-deactivate-option"]:checked');
var data = {
'action': '<?php echo esc_attr( $key ) . '_uninstall_feedback'; ?>',
'nonce': '<?php echo esc_attr( wp_create_nonce( (string) __CLASS__ ) ); ?>',
'id': selectedOption.parent().attr('ti-option-id'),
'msg': selectedOption.parent().find('textarea').val(),
'type': 'plugin',
'key': '<?php echo esc_attr( $key ); ?>'
};
$.ajax({
type: 'POST',
url: ajaxurl,
data: data,
complete() {
$('body').removeClass('ti-feedback-open');
$('<?php echo esc_attr( $popup_id ); ?>').remove();
if (redirectUrl !== '') {
location.href = redirectUrl;
}
},
beforeSend() {
$('<?php echo esc_attr( $popup_id ); ?>').addClass('sending-feedback');
$('<?php echo esc_attr( $popup_id ); ?> .popup--footer').remove();
$('<?php echo esc_attr( $popup_id ); ?> .popup--body').html('<i class="dashicons dashicons-update-alt"></i>');
}
});
});
});
})(jQuery);
</script>
<?php
do_action( $this->product->get_key() . '_uninstall_feedback_after_js' );
}
/**
* Get the disclosure labels markup.
*
* @return string
*/
private function get_disclosure_labels() {
$disclosure_new_labels = apply_filters( $this->product->get_slug() . '_themeisle_sdk_disclosure_content_labels', [], $this->product );
$disclosure_labels = array_merge(
[
'title' => 'Below is a detailed view of all data that ThemeIsle will receive if you fill in this survey. No email address or IP addresses are transmitted after you submit the survey.',
'items' => [
sprintf( '%s %s version %s %s %s %s', '<strong>', ucwords( $this->product->get_type() ), '</strong>', '<code>', $this->product->get_version(), '</code>' ),
sprintf( '%sCurrent website:%s %s %s %s', '<strong>', '</strong>', '<code>', get_site_url(), '</code>' ),
sprintf( '%sUsage time:%s %s %s%s', '<strong>', '</strong>', '<code>', ( time() - $this->product->get_install_time() ), 's</code>' ),
sprintf( '%s Uninstall reason %s %s Selected reason from the above survey %s ', '<strong>', '</strong>', '<i>', '</i>' ),
],
],
$disclosure_new_labels
);
$info_disclosure_content = '<div class="info-disclosure-content"><p>' . wp_kses_post( $disclosure_labels['title'] ) . '</p><ul>';
foreach ( $disclosure_labels['items'] as $disclosure_item ) {
$info_disclosure_content .= sprintf( '<li>%s</li>', wp_kses_post( $disclosure_item ) );
}
$info_disclosure_content .= '</ul></div>';
return $info_disclosure_content;
}
/**
* Randomizes the options array.
*
* @param array $options The options array.
*/
public function randomize_options( $options ) {
$new = array();
$keys = array_keys( $options );
shuffle( $keys );
foreach ( $keys as $key ) {
$new[ $key ] = $options[ $key ];
}
return $new;
}
/**
* Called when the deactivate button is clicked.
*/
public function post_deactivate() {
check_ajax_referer( (string) __CLASS__, 'nonce' );
$this->post_deactivate_or_cancel();
if ( empty( $_POST['id'] ) ) {
wp_send_json( [] );
return;
}
$this->call_api(
array(
'type' => 'deactivate',
'id' => sanitize_key( $_POST['id'] ),
'comment' => isset( $_POST['msg'] ) ? sanitize_textarea_field( $_POST['msg'] ) : '',
)
);
wp_send_json( [] );
}
/**
* Called when the deactivate/cancel button is clicked.
*/
private function post_deactivate_or_cancel() {
if ( ! isset( $_POST['type'] ) || ! isset( $_POST['key'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Missing, Nonce already present in caller function.
return;
}
if ( 'theme' !== $_POST['type'] ) { //phpcs:ignore WordPress.Security.NonceVerification.Missing, Nonce already present in caller function.
return;
}
set_transient( 'ti_sdk_pause_' . sanitize_text_field( $_POST['key'] ), true, self::PAUSE_DEACTIVATE_WINDOW_DAYS * DAY_IN_SECONDS );//phpcs:ignore WordPress.Security.NonceVerification.Missing, Nonce already present in caller function.
}
/**
* Calls the API
*
* @param array $attributes The attributes of the post body.
*
* @return bool Is the request succesfull?
*/
protected function call_api( $attributes ) {
$slug = $this->product->get_slug();
$version = $this->product->get_version();
$attributes['slug'] = $slug;
$attributes['version'] = $version;
$attributes['url'] = get_site_url();
$attributes['active_time'] = ( time() - $this->product->get_install_time() );
$response = wp_remote_post(
self::FEEDBACK_ENDPOINT,
array(
'body' => $attributes,
)
);
return is_wp_error( $response );
}
/**
* Should we load this object?.
*
* @param Product $product Product object.
*
* @return bool Should we load the module?
*/
public function can_load( $product ) {
if ( $this->is_from_partner( $product ) ) {
return false;
}
if ( $product->is_theme() && ( false !== get_transient( 'ti_sdk_pause_' . $product->get_key(), false ) ) ) {
return false;
}
if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
return true;
}
global $pagenow;
if ( ! isset( $pagenow ) || empty( $pagenow ) ) {
return false;
}
if ( $product->is_plugin() && 'plugins.php' !== $pagenow ) {
return false;
}
if ( $product->is_theme() && 'theme-install.php' !== $pagenow ) {
return false;
}
return true;
}
/**
* Loads module hooks.
*
* @param Product $product Product details.
*
* @return Uninstall_Feedback Current module instance.
*/
public function load( $product ) {
if ( apply_filters( $product->get_key() . '_hide_uninstall_feedback', false ) ) {
return;
}
$this->product = $product;
add_action( 'admin_head', array( $this, 'load_resources' ) );
add_action( 'wp_ajax_' . $this->product->get_key() . '_uninstall_feedback', array( $this, 'post_deactivate' ) );
return $this;
}
}

View File

@ -1,193 +0,0 @@
<?php
/**
* The welcome model class for ThemeIsle SDK
*
* Here's how to hook it in your plugin or theme:
* ```php
* add_filter( '<product_slug>_welcome_metadata', function() {
* return [
* 'is_enabled' => <condition_if_pro_available>,
* 'pro_name' => 'Product PRO name',
* 'logo' => '<path_to_logo>',
* 'cta_link' => tsdk_utmify( 'https://link_to_upgrade.with/?discount=<discountCode>')
* ];
* } );
* ```
*
* @package ThemeIsleSDK
* @subpackage Modules
* @copyright Copyright (c) 2023, Bogdan Preda
* @license http://opensource.org/licenses/gpl-3.0.php GNU Public License
* @since 1.0.0
*/
namespace ThemeisleSDK\Modules;
// Exit if accessed directly.
use ThemeisleSDK\Common\Abstract_Module;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Promotions module for ThemeIsle SDK.
*/
class Welcome extends Abstract_Module {
/**
* Debug mode.
*
* @var bool
*/
private $debug = false;
/**
* Welcome metadata.
*
* @var array
*/
private $welcome_discounts = array();
/**
* Check that we can load this module.
*
* @param \ThemeisleSDK\Product $product The product.
*
* @return bool
*/
public function can_load( $product ) {
$this->debug = apply_filters( 'themeisle_sdk_welcome_debug', $this->debug );
$welcome_metadata = apply_filters( $product->get_key() . '_welcome_metadata', array() );
$is_welcome_enabled = $this->is_welcome_meta_valid( $welcome_metadata );
if ( $is_welcome_enabled ) {
$this->welcome_discounts[ $product->get_key() ] = $welcome_metadata;
}
return $this->debug || $is_welcome_enabled;
}
/**
* Check that the metadata is valid and the welcome is enabled.
*
* @param array $welcome_metadata The metadata to validate.
*
* @return bool
*/
private function is_welcome_meta_valid( $welcome_metadata ) {
return ! empty( $welcome_metadata ) && isset( $welcome_metadata['is_enabled'] ) && $welcome_metadata['is_enabled'];
}
/**
* Load the module.
*
* @param \ThemeisleSDK\Product $product The product.
*
* @return $this
*/
public function load( $product ) {
if ( ! current_user_can( 'install_plugins' ) ) {
return;
}
$this->product = $product;
if ( ! $this->is_time_to_show_welcome() && $this->debug === false ) {
return;
}
add_filter( 'themeisle_sdk_registered_notifications', [ $this, 'add_notification' ], 99, 1 );
return $this;
}
/**
* Check if it's time to show the welcome.
*
* @return bool
*/
private function is_time_to_show_welcome() {
// if 7 days from install have not passed, don't show the welcome.
if ( $this->product->get_install_time() + 7 * DAY_IN_SECONDS > time() ) {
return false;
}
// if 12 days from install have passed, don't show the welcome ( after 7 days for 5 days ).
if ( $this->product->get_install_time() + 12 * DAY_IN_SECONDS < time() ) {
return false;
}
return true;
}
/**
* Add the welcome notification.
* Will block all other notifications if a welcome notification is present.
*
* @return array
*/
public function add_notification( $all_notifications ) {
if ( empty( $this->welcome_discounts ) ) {
return $all_notifications;
}
if ( ! isset( $this->welcome_discounts[ $this->product->get_key() ] ) ) {
return $all_notifications;
}
// filter out the notifications that are not welcome upsells
// if we arrived here we will have at least one welcome upsell
$all_notifications = array_filter(
$all_notifications,
function( $notification ) {
return strpos( $notification['id'], '_welcome_upsell_flag' ) !== false;
}
);
$offer = $this->welcome_discounts[ $this->product->get_key() ];
$response = [];
$logo = isset( $offer['logo'] ) ? $offer['logo'] : '';
$pro_name = isset( $offer['pro_name'] ) ? $offer['pro_name'] : $this->product->get_friendly_name() . ' PRO';
$link = $offer['cta_link'];
$message = apply_filters( $this->product->get_key() . '_welcome_upsell_message', '<p>You\'ve been using <b>{product}</b> for 7 days now and we appreciate your loyalty! We also want to make sure you\'re getting the most out of our product. That\'s why we\'re offering you a special deal - upgrade to <b>{pro_product}</b> in the next 5 days and receive a discount of <b>up to 30%</b>. <a href="{cta_link}" target="_blank">Upgrade now</a> and unlock all the amazing features of <b>{pro_product}</b>!</p>' );
$button_submit = apply_filters( $this->product->get_key() . '_feedback_review_button_do', 'Upgrade Now!' );
$button_cancel = apply_filters( $this->product->get_key() . '_feedback_review_button_cancel', 'No, thanks.' );
$message = str_replace(
[ '{product}', '{pro_product}', '{cta_link}' ],
[
$this->product->get_friendly_name(),
$pro_name,
$link,
],
$message
);
$all_notifications[] = [
'id' => $this->product->get_key() . '_welcome_upsell_flag',
'message' => $message,
'img_src' => $logo,
'ctas' => [
'confirm' => [
'link' => $link,
'text' => $button_submit,
],
'cancel' => [
'link' => '#',
'text' => $button_cancel,
],
],
'type' => 'info',
];
$key = array_rand( $all_notifications );
$response[] = $all_notifications[ $key ];
return $response;
}
}

View File

@ -1,460 +0,0 @@
<?php
/**
* The product model class for ThemeIsle SDK
*
* @package ThemeIsleSDK
* @subpackage Product
* @copyright Copyright (c) 2017, Marius Cristea
* @license http://opensource.org/licenses/gpl-3.0.php GNU Public License
* @since 1.0.0
*/
namespace ThemeisleSDK;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Product model for ThemeIsle SDK.
*/
class Product {
/**
* Define plugin type string.
*/
const PLUGIN_TYPE = 'plugin';
/**
* Define theme type string.
*/
const THEME_TYPE = 'theme';
/**
* If the product has a pro version, contains the pro slug.
*
* @var string $pro_slug Pro slug, if available.
*/
public $pro_slug;
/**
* Current product slug.
*
* @var string $slug THe product slug.
*/
private $slug;
/**
* Product basefile, with the proper metadata.
*
* @var string $basefile The file with headers.
*/
private $basefile;
/**
* Type of the product.
*
* @var string $type The product type ( plugin | theme ).
*/
private $type;
/**
* The file name.
*
* @var string $file The file name.
*/
private $file;
/**
* Product name, fetched from the file headers.
*
* @var string $name The product name.
*/
private $name;
/**
* Product normalized key.
*
* @var string $key The product ready key.
*/
private $key;
/**
* Author URL
*
* @var string $author_url The author url.
*/
private $author_url;
/**
* Product store url.
*
* @var string $store_url The store url.
*/
private $store_url;
/**
* Product install timestamp.
*
* @var int $install The date of install.
*/
private $install;
/**
* Product store/author name.
*
* @var string $store_name The store name.
*/
private $store_name;
/**
* Does the product requires license.
*
* @var bool $requires_license Either user needs to activate it with license.
*/
private $requires_license;
/**
* Is the product available on wordpress.org
*
* @var bool $wordpress_available Either is available on WordPress or not.
*/
private $wordpress_available;
/**
* Current version of the product.
*
* @var string $version The product version.
*/
private $version;
/**
* Holds a map of loaded products objects.
*
* @var array Array of loaded products.
*/
private static $cached_products = [];
/**
* Root api endpoint.
*/
const API_URL = 'https://api.themeisle.com/';
/**
* ThemeIsle_SDK_Product constructor.
*
* @param string $basefile Product basefile.
*/
public function __construct( $basefile ) {
if ( ! empty( $basefile ) ) {
if ( is_file( $basefile ) ) {
$this->basefile = $basefile;
$this->setup_from_path();
$this->setup_from_fileheaders();
}
}
$install = get_option( $this->get_key() . '_install', 0 );
if ( 0 === $install ) {
$install = time();
update_option( $this->get_key() . '_install', time() );
}
$this->install = $install;
self::$cached_products[ crc32( $basefile ) ] = $this;
}
/**
* Return a product.
*
* @param string $basefile Product basefile.
*
* @return Product Product Object.
*/
public static function get( $basefile ) {
$key = crc32( $basefile );
if ( isset( self::$cached_products[ $key ] ) ) {
return self::$cached_products[ $key ];
}
self::$cached_products[ $key ] = new Product( $basefile );
return self::$cached_products[ $key ];
}
/**
* Setup props from path.
*/
public function setup_from_path() {
$this->file = basename( $this->basefile );
$dir = dirname( $this->basefile );
$this->slug = basename( $dir );
$exts = explode( '.', $this->basefile );
$ext = $exts[ count( $exts ) - 1 ];
if ( 'css' === $ext ) {
$this->type = 'theme';
}
if ( 'php' === $ext ) {
$this->type = 'plugin';
}
$this->key = self::key_ready_name( $this->slug );
}
/**
* Normalize string.
*
* @param string $string the String to be normalized for cron handler.
*
* @return string $name The normalized string.
*/
public static function key_ready_name( $string ) {
return str_replace( '-', '_', strtolower( trim( $string ) ) );
}
/**
* Setup props from fileheaders.
*/
public function setup_from_fileheaders() {
$file_headers = array(
'Requires License' => 'Requires License',
'WordPress Available' => 'WordPress Available',
'Pro Slug' => 'Pro Slug',
'Version' => 'Version',
);
if ( 'plugin' === $this->type ) {
$file_headers['Name'] = 'Plugin Name';
$file_headers['AuthorName'] = 'Author';
$file_headers['AuthorURI'] = 'Author URI';
}
if ( 'theme' === $this->type ) {
$file_headers['Name'] = 'Theme Name';
$file_headers['AuthorName'] = 'Author';
$file_headers['AuthorURI'] = 'Author URI';
}
$file_headers = get_file_data( $this->basefile, $file_headers );
$this->name = $file_headers['Name'];
$this->store_name = $file_headers['AuthorName'];
$this->author_url = $file_headers['AuthorURI'];
$this->store_url = $file_headers['AuthorURI'];
$this->requires_license = ( 'yes' === $file_headers['Requires License'] ) ? true : false;
$this->wordpress_available = ( 'yes' === $file_headers['WordPress Available'] ) ? true : false;
$this->pro_slug = ! empty( $file_headers['Pro Slug'] ) ? $file_headers['Pro Slug'] : '';
$this->version = $file_headers['Version'];
}
/**
* Return the product key.
*
* @return string The product key.
*/
public function get_key() {
return $this->key;
}
/**
* Check if the product is either theme or plugin.
*
* @return string Product type.
*/
public function get_type() {
return $this->type;
}
/**
* Return if the product is used as a plugin.
*
* @return bool Is plugin?
*/
public function is_plugin() {
return self::PLUGIN_TYPE === $this->type;
}
/**
* Return if the product is used as a theme.
*
* @return bool Is theme ?
*/
public function is_theme() {
return self::THEME_TYPE === $this->type;
}
/**
* Returns the product slug.
*
* @return string The product slug.
*/
public function get_slug() {
return $this->slug;
}
/**
* The magic var_dump info method.
*
* @return array Debug info.
*/
public function __debugInfo() {
return array(
'name' => $this->name,
'slug' => $this->slug,
'version' => $this->version,
'basefile' => $this->basefile,
'key' => $this->key,
'type' => $this->type,
'store_name' => $this->store_name,
'store_url' => $this->store_url,
'wordpress_available' => $this->wordpress_available,
'requires_license' => $this->requires_license,
);
}
/**
* Getter for product version.
*
* @return string The product version.
*/
public function get_version() {
return $this->version;
}
/**
* Returns current product license, if available.
*
* @return string Return license key, if available.
*/
public function get_license() {
if ( ! $this->requires_license() && ! $this->is_wordpress_available() ) {
return 'free';
}
$license_data = get_option( $this->get_key() . '_license_data', '' );
if ( empty( $license_data ) ) {
return get_option( $this->get_key() . '_license', '' );
}
if ( ! isset( $license_data->key ) ) {
return get_option( $this->get_key() . '_license', '' );
}
return $license_data->key;
}
/**
* Either the product requires license or not.
*
* @return bool Either requires license or not.
*/
public function requires_license() {
return $this->requires_license;
}
/**
* If product is available on wordpress.org or not.
*
* @return bool Either is wp available or not.
*/
public function is_wordpress_available() {
return $this->wordpress_available;
}
/**
* Return friendly name.
*
* @return string Friendly name.
*/
public function get_friendly_name() {
$name = apply_filters( $this->get_key() . '_friendly_name', trim( str_replace( 'Lite', '', $this->get_name() ) ) );
$name = rtrim( $name, '- ()' );
return $name;
}
/**
* Return the product version cache key.
*
* @return string The product version cache key.
*/
public function get_cache_key() {
return $this->get_key() . '_' . preg_replace( '/[^0-9a-zA-Z ]/m', '', $this->get_version() ) . 'versions';
}
/**
* Getter for product name.
*
* @return string The product name.
*/
public function get_name() {
return $this->name;
}
/**
* Returns the Store name.
*
* @return string Store name.
*/
public function get_store_name() {
return $this->store_name;
}
/**
* Returns the store url.
*
* @return string The store url.
*/
public function get_store_url() {
if ( strpos( $this->store_url, '/themeisle.com' ) !== false ) {
return 'https://store.themeisle.com/';
}
return $this->store_url;
}
/**
* Returns product basefile, which holds the metaheaders.
*
* @return string The product basefile.
*/
public function get_basefile() {
return $this->basefile;
}
/**
* Get changelog url.
*
* @return string Changelog url.
*/
public function get_changelog() {
return add_query_arg(
[
'name' => rawurlencode( $this->get_name() ),
'edd_action' => 'view_changelog',
],
$this->get_store_url()
);
}
/**
* Returns product filename.
*
* @return string The product filename.
*/
public function get_file() {
return $this->file;
}
/**
* Returns the pro slug, if available.
*
* @return string The pro slug.
*/
public function get_pro_slug() {
return $this->pro_slug;
}
/**
* Return the install timestamp.
*
* @return int The install timestamp.
*/
public function get_install_time() {
return $this->install;
}
/**
* Returns the URL of the product base file.
*
* @param string $path The path to the file.
*
* @return string The URL of the product base file.
*/
public function get_base_url( $path = '/' ) {
if ( $this->type ) {
return plugins_url( $path, $this->basefile );
}
}
}