first commit

This commit is contained in:
2024-11-10 21:08:49 +01:00
commit 0d932ce5ee
14455 changed files with 2567501 additions and 0 deletions

View File

@@ -0,0 +1,47 @@
{
"name": "wpdesk\/ltv-dashboard-widget",
"description": "Library for displaying ltv widget in WordPress dashboard.",
"license": "MIT",
"keywords": [
"wordpress",
"notice",
"admin"
],
"minimum-stability": "stable",
"config": {
"platform": {
"php": "7.0"
}
},
"require": {
"php": ">=7.2"
},
"require-dev": {
"phpunit\/phpunit": "<7",
"wp-coding-standards\/wpcs": "^0.14.1",
"squizlabs\/php_codesniffer": "^3.0.2",
"mockery\/mockery": "*",
"10up\/wp_mock": "*",
"wimg\/php-compatibility": "^8"
},
"autoload": {
"psr-4": {
"FcfVendor\\WPDesk\\Dashboard\\": "src\/"
}
},
"extra": {
"text-domain": "wpdesk_ltv_dashboard_widget",
"translations-folder": "lang",
"po-files": {
"pl_PL": "wpdesk-ltv-dashboard-widget-pl_PL.po"
}
},
"autoload-dev": {},
"scripts": {
"phpcs": "phpcs",
"phpunit-unit": "phpunit --configuration phpunit-unit.xml --coverage-text --colors=never",
"phpunit-unit-fast": "phpunit --configuration phpunit-unit.xml --no-coverage",
"phpunit-integration": "phpunit --configuration phpunit-integration.xml --coverage-text --colors=never",
"phpunit-integration-fast": "phpunit --configuration phpunit-integration.xml --no-coverage"
}
}

View File

@@ -0,0 +1,157 @@
<?php
namespace FcfVendor\WPDesk\Dashboard;
final class DashboardWidget
{
const ID = 'flexible-checkout-fields';
const MUTEX_HOOK = 'wpdesk/ltvdashboard/initialized';
public function hooks()
{
if (\apply_filters(self::MUTEX_HOOK, \false) === \false) {
\add_filter(self::MUTEX_HOOK, '__return_true');
\add_action('wp_dashboard_setup', [$this, 'add_widget']);
}
}
public function add_widget()
{
\wp_add_dashboard_widget(self::ID, \__('Grow your business with WP Desk', 'flexible-checkout-fields'), [$this, 'widget_output'], null, null, 'normal', 'high');
}
private function get_all_plugins_dirs() : array
{
$all_plugins = \array_keys(\get_plugins());
return \array_map('dirname', $all_plugins);
}
private function filter_plugins_to_show(array $plugins) : array
{
\usort($plugins, static function ($a, $b) {
return \strnatcmp($a['priority'], $b['priority']);
});
$installed_plugins_dir = $this->get_all_plugins_dirs();
$plugins = \array_filter($plugins, static function ($plugin) use($installed_plugins_dir) {
return !\in_array($plugin['slug'], $installed_plugins_dir, \true);
});
return \array_slice($plugins, 0, 3);
}
private function get_server() : string
{
$locale = \get_user_locale();
if ($locale === 'pl_PL') {
return 'www.wpdesk.pl';
}
return 'www.wpdesk.net';
}
private function get_utm_base() : string
{
return 'utm_source=dashboard-metabox&utm_campaign=dashboard-metabox';
}
private function get_widget_data() : array
{
$cache_key = \sprintf('wpdesk_ltv_%1$s_%2$s', self::ID, \get_user_locale());
$cache_data = \get_transient($cache_key);
if ($cache_data) {
return $cache_data;
} elseif ($cache_data === \false) {
$response_data = $this->get_widget_data_from_remote();
if ($response_data !== null) {
\set_transient($cache_key, $response_data, 24 * 60 * 60);
return $response_data;
} else {
\set_transient($cache_key, null, 6 * 60 * 60);
}
}
return [];
}
/**
* @return array|null
*/
private function get_widget_data_from_remote()
{
$response = \wp_remote_get(\sprintf('https://%s?wpdesk_api=1&t=1', $this->get_server()), ['timeout' => 10, 'sslverify' => \false]);
if (!\is_array($response)) {
return null;
}
$ret = \json_decode($response['body'], \true);
if (!$ret || !\is_array($ret)) {
return null;
}
return ['header' => $ret['header'] ?? null, 'plugins' => $this->filter_plugins_to_show($ret['plugins'] ?? []), 'footer' => $ret['footer'] ?? null];
}
public function widget_output()
{
$widget_data = $this->get_widget_data();
$server = $this->get_server();
$utm_base = $this->get_utm_base();
if (!empty($widget_data)) {
echo '<div class="wpdesk_ltv_dashboard_widget">';
if ($widget_data['header']) {
echo \wp_kses_post($widget_data['header']);
}
echo '<ul class="ltv-rows">';
foreach ($widget_data['plugins'] as $plugin) {
$plugin_url = \sprintf('%1$s?%2$s&utm_medium=more-info-button&utm_term=%3$s', $plugin['url'], $utm_base, $plugin['slug']);
$add_to_cart_url = \sprintf('https://%1$s/?add-to-cart=%2$s&%3$s&utm_medium=buy-now-button&utm_term=%4$s', $server, $plugin['add_to_cart_id'], $utm_base, $plugin['slug']);
echo '<li class="ltv-row">';
if ($plugin['image']) {
echo '<img src="' . \esc_url($plugin['image']) . '" alt="" />';
}
echo '<p><strong>' . \esc_html($plugin['name']) . '</strong></p>';
echo '<div class="ltv-row-description">' . \wp_kses_post($plugin['description']) . '</div>';
echo '<div class="ltv-buttons">';
echo '<a class="button button-primary button-large" href="' . \esc_url($plugin_url) . '" target="_blank">' . \esc_html__('More info', 'flexible-checkout-fields') . '</a>';
echo '&nbsp;';
echo '<a class="button button-large" href="' . \esc_url($add_to_cart_url) . '" target="_blank">' . \esc_html__('Buy now', 'flexible-checkout-fields') . '</a>';
echo '</div>';
echo '</li>';
}
echo '</ul>';
echo '<div class="ltv-footer">';
if ($widget_data['footer']) {
echo \wp_kses_post($widget_data['footer']);
}
echo '</div>';
echo '</div>';
?>
<style>
.wpdesk_ltv_dashboard_widget .ltv-rows {
margin-left: -12px;
margin-right: -12px;
}
.wpdesk_ltv_dashboard_widget .ltv-row {
padding: 6px 12px 24px;
}
.wpdesk_ltv_dashboard_widget .ltv-row:nth-child(odd) {
background-color: #f6f7f7;
}
.wpdesk_ltv_dashboard_widget .ltv-row-description p {
margin-top: 6px;
}
.wpdesk_ltv_dashboard_widget img {
display: block;
margin: 0 auto 10px;
width: 250px;
max-width: 100%;
}
.wpdesk_ltv_dashboard_widget .ltv-buttons {
display: flex;
justify-content: space-around;
}
.wpdesk_ltv_dashboard_widget .ltv-footer {
margin: 0 -12px;
padding: 0 12px;
}
.wpdesk_ltv_dashboard_widget .ltv-footer p {
margin: 0;
}
</style>
<?php
}
}
}

View File

@@ -0,0 +1,47 @@
{
"name": "wpdesk\/wp-basic-requirements",
"authors": [
{
"name": "Krzysiek",
"email": "krzysiek@wpdesk.pl"
}
],
"require": {
"php": ">=5.3"
},
"require-dev": {
"php": ">=5.5",
"phpunit\/phpunit": "<7",
"wp-coding-standards\/wpcs": "^0.14.1",
"squizlabs\/php_codesniffer": "^3.0.2",
"mockery\/mockery": "*",
"10up\/wp_mock": "*",
"wimg\/php-compatibility": "^8"
},
"autoload": {},
"autoload-dev": {
"classmap": [
"src",
"tests"
]
},
"extra": {
"text-domain": "wp-basic-requirements",
"translations-folder": "lang",
"po-files": {
"pl_PL": "pl_PL.po",
"en_AU": "en_AU.po",
"en_CA": "en_CA.po",
"en_GB": "en_GB.po",
"de_DE": "de_DE.po"
}
},
"scripts": {
"phpcs": "phpcs",
"phpunit-unit": "phpunit --configuration phpunit-unit.xml --coverage-text --colors=never",
"phpunit-unit-fast": "phpunit --configuration phpunit-unit.xml --no-coverage",
"phpunit-integration": "phpunit --configuration phpunit-integration.xml --coverage-text --colors=never",
"phpunit-integration-fast": "phpunit --configuration phpunit-integration.xml --no-coverage",
"docs": "apigen generate"
}
}

View File

@@ -0,0 +1,558 @@
<?php
namespace FcfVendor;
if (!\interface_exists('FcfVendor\\WPDesk_Requirement_Checker')) {
require_once __DIR__ . '/Requirement_Checker.php';
}
if (!\class_exists('FcfVendor\\WPDesk_Basic_Requirement_Checker')) {
/**
* Checks requirements for plugin
* have to be compatible with PHP 5.3.x
*/
class WPDesk_Basic_Requirement_Checker implements \FcfVendor\WPDesk_Requirement_Checker
{
const EXTENSION_NAME_OPENSSL = 'openssl';
const HOOK_ADMIN_NOTICES_ACTION = 'admin_notices';
const HOOK_PLUGIN_DEACTIVATED_ACTION = 'deactivated_plugin';
const HOOK_PLUGIN_ACTIVATED_ACTION = 'activated_plugin';
const PLUGIN_INFO_KEY_NICE_NAME = 'nice_name';
const PLUGIN_INFO_KEY_NAME = 'name';
const PLUGIN_INFO_VERSION = 'version';
const PLUGIN_INFO_FAKE_REQUIRED_MINIMUM_VERSION = '0.0';
const PLUGIN_INFO_APPEND_PLUGIN_DATA = 'required_version';
const PLUGIN_INFO_TRANSIENT_NAME = 'require_plugins_data';
const PLUGIN_INFO_TRANSIENT_EXPIRATION_TIME = 16;
/** @var string */
protected $plugin_name;
/** @var string */
private $plugin_file;
/** @var string */
private $min_php_version;
/** @var string */
private $min_wp_version;
/** @var string|null */
private $min_wc_version = null;
/** @var int|null */
private $min_openssl_version = null;
/** @var array */
protected $plugin_require;
/** @var bool */
protected $should_check_plugin_versions = \false;
/** @var array */
private $module_require;
/** @var array */
private $setting_require;
/** @var array */
protected $notices;
/** @var @string */
private $text_domain;
/**
* @param string $plugin_file
* @param string $plugin_name
* @param string $text_domain
* @param string $php_version
* @param string $wp_version
*/
public function __construct($plugin_file, $plugin_name, $text_domain, $php_version, $wp_version)
{
$this->plugin_file = $plugin_file;
$this->plugin_name = $plugin_name;
$this->text_domain = $text_domain;
$this->set_min_php_require($php_version);
$this->set_min_wp_require($wp_version);
$this->plugin_require = array();
$this->module_require = array();
$this->setting_require = array();
$this->notices = array();
}
/**
* @param string $version
*
* @return $this
*/
public function set_min_php_require($version)
{
$this->min_php_version = $version;
return $this;
}
/**
* @param string $version
*
* @return $this
*/
public function set_min_wp_require($version)
{
$this->min_wp_version = $version;
return $this;
}
/**
* @param string $version
*
* @return $this
*/
public function set_min_wc_require($version)
{
$this->min_wc_version = $version;
return $this;
}
/**
* @param $version
*
* @return $this
*/
public function set_min_openssl_require($version)
{
$this->min_openssl_version = $version;
return $this;
}
/**
* @param string $plugin_name Name in wp format dir/file.php
* @param string $nice_plugin_name Nice plugin name for better looks in notice
* @param string $plugin_require_version required plugin minimum version
*
* @return $this
*/
public function add_plugin_require($plugin_name, $nice_plugin_name = null, $plugin_require_version = null)
{
if ($plugin_require_version) {
$this->should_check_plugin_versions = \true;
}
$this->plugin_require[$plugin_name] = array(self::PLUGIN_INFO_KEY_NAME => $plugin_name, self::PLUGIN_INFO_KEY_NICE_NAME => $nice_plugin_name === null ? $plugin_name : $nice_plugin_name, self::PLUGIN_INFO_VERSION => $plugin_require_version === null ? self::PLUGIN_INFO_FAKE_REQUIRED_MINIMUM_VERSION : $plugin_require_version);
return $this;
}
/**
* Add plugin to require list. Plugin is from repository so we can ask for installation.
*
* @param string $plugin_name Name in wp format dir/file.php
* @param string $version Required version of the plugin.
* @param string $nice_plugin_name Nice plugin name for better looks in notice
*
* @return $this
*/
public function add_plugin_repository_require($plugin_name, $version, $nice_plugin_name = null)
{
$this->plugin_require[$plugin_name] = array(self::PLUGIN_INFO_KEY_NAME => $plugin_name, self::PLUGIN_INFO_VERSION => $version, 'repository_url' => 'http://downloads.wordpress.org/plugin/' . \dirname($plugin_name) . '.latest-stable.zip', self::PLUGIN_INFO_KEY_NICE_NAME => $nice_plugin_name === null ? $plugin_name : $nice_plugin_name);
return $this;
}
/**
* @param string $module_name
* @param string $nice_name Nice module name for better looks in notice
*
* @return $this
*/
public function add_php_module_require($module_name, $nice_name = null)
{
if ($nice_name === null) {
$this->module_require[$module_name] = $module_name;
} else {
$this->module_require[$module_name] = $nice_name;
}
return $this;
}
/**
* @param string $setting
* @param mixed $value
*
* @return $this
*/
public function add_php_setting_require($setting, $value)
{
$this->setting_require[$setting] = $value;
return $this;
}
/**
* Returns true if are requirements are met.
*
* @return bool
*/
public function are_requirements_met()
{
$this->notices = $this->prepare_requirement_notices();
return \count($this->notices) === 0;
}
/**
* @return array
*/
private function prepare_requirement_notices()
{
$notices = array();
if (!self::is_php_at_least($this->min_php_version)) {
$notices[] = $this->prepare_notice_message(\sprintf(\__('The &#8220;%s&#8221; plugin cannot run on PHP versions older than %s. Please contact your host and ask them to upgrade.', $this->get_text_domain()), \esc_html($this->plugin_name), $this->min_php_version));
}
if (!self::is_wp_at_least($this->min_wp_version)) {
$notices[] = $this->prepare_notice_message(\sprintf(\__('The &#8220;%s&#8221; plugin cannot run on WordPress versions older than %s. Please update WordPress.', $this->get_text_domain()), \esc_html($this->plugin_name), $this->min_wp_version));
}
if ($this->min_wc_version !== null && $this->can_check_plugin_version() && !self::is_wc_at_least($this->min_wc_version)) {
$notices[] = $this->prepare_notice_message(\sprintf(\__('The &#8220;%s&#8221; plugin cannot run on WooCommerce versions older than %s. Please update WooCommerce.', $this->get_text_domain()), \esc_html($this->plugin_name), $this->min_wc_version));
}
if ($this->min_openssl_version !== null && !self::is_open_ssl_at_least($this->min_openssl_version)) {
$notices[] = $this->prepare_notice_message(\sprintf(\__('The &#8220;%s&#8221; plugin cannot run without OpenSSL module version at least %s. Please update OpenSSL module.', $this->get_text_domain()), \esc_html($this->plugin_name), '0x' . \dechex($this->min_openssl_version)));
}
$notices = $this->append_plugin_require_notices($notices);
$notices = $this->append_module_require_notices($notices);
$notices = $this->append_settings_require_notices($notices);
if ($this->should_check_plugin_versions) {
$notices = $this->check_minimum_require_plugins_version_and_append_notices($notices);
}
return $notices;
}
/**
* @param $min_version
*
* @return mixed
*/
public static function is_php_at_least($min_version)
{
return \version_compare(\PHP_VERSION, $min_version, '>=');
}
/**
* Prepares message in html format
*
* @param string $message
*
* @return string
*/
protected function prepare_notice_message($message)
{
return '<div class="error"><p>' . $message . '</p></div>';
}
public function get_text_domain()
{
return $this->text_domain;
}
/**
* @param string $min_version
*
* @return bool
*/
public static function is_wp_at_least($min_version)
{
return \version_compare(\get_bloginfo('version'), $min_version, '>=');
}
/**
* Are plugins loaded so we can check the version
*
* @return bool
*/
private function can_check_plugin_version()
{
return \did_action('plugins_loaded') > 0;
}
/**
* Checks if plugin is active and have designated version. Needs to be enabled in deferred way.
*
* @param string $min_version
*
* @return bool
*/
public static function is_wc_at_least($min_version)
{
return \defined('WC_VERSION') && \version_compare(\WC_VERSION, $min_version, '>=');
}
/**
* Checks if ssl version is valid
*
* @param int $required_version Version in hex. Version 9.6 is 0x000906000
*
* @return bool
* @see https://www.openssl.org/docs/man1.1.0/crypto/OPENSSL_VERSION_NUMBER.html
*
*/
public static function is_open_ssl_at_least($required_version)
{
return \defined('OPENSSL_VERSION_NUMBER') && \OPENSSL_VERSION_NUMBER > (int) $required_version;
}
/**
* @param $notices array
*
* @return array
*/
private function check_minimum_require_plugins_version_and_append_notices($notices)
{
$required_plugins = $this->retrieve_required_plugins_data();
if (\count($required_plugins) > 0) {
foreach ($required_plugins as $plugin) {
if (\version_compare($plugin['Version'], $plugin[self::PLUGIN_INFO_APPEND_PLUGIN_DATA], '<')) {
$notices[] = $this->prepare_notice_message(\sprintf(\__('The &#8220;%1$s&#8221; plugin requires at least %2$s version of %3$s to work correctly. Please update it to its latest release.', $this->get_text_domain()), \esc_html($this->plugin_name), $plugin[self::PLUGIN_INFO_APPEND_PLUGIN_DATA], $plugin['Name']));
}
}
}
return $notices;
}
/**
* Check the plugins directory and retrieve all plugin files with plugin data.
*
* @return array In format [ 'plugindir/pluginfile.php' => ['Name' => 'Plugin Name', 'Version' => '1.0.1', ...], ]
*/
private static function retrieve_plugins_data_in_transient()
{
static $never_executed = \true;
if ($never_executed) {
$never_executed = \false;
/** Required when WC starts later and these data should be in cache */
\add_filter('extra_plugin_headers', function ($headers = array()) {
$headers[] = 'WC tested up to';
$headers[] = 'WC requires at least';
$headers[] = 'Woo';
return \array_unique($headers);
});
}
$plugins = \get_transient(self::PLUGIN_INFO_TRANSIENT_NAME);
if ($plugins === \false) {
if (!\function_exists('get_plugins')) {
require_once \ABSPATH . '/wp-admin/includes/plugin.php';
}
$plugins = \function_exists('get_plugins') ? \get_plugins() : array();
\set_transient(self::PLUGIN_INFO_TRANSIENT_NAME, $plugins, self::PLUGIN_INFO_TRANSIENT_EXPIRATION_TIME);
}
return $plugins;
}
/**
* Check the plugins directory and retrieve all required plugin files with plugin data.
*
* @return array In format [ 'plugindir/pluginfile.php' => ['Name' => 'Plugin Name', 'Version' => '1.0.1', 'required_version' => '1.0.2']... ]
*/
private function retrieve_required_plugins_data()
{
$require_plugins = array();
$plugins = self::retrieve_plugins_data_in_transient();
if (\is_array($plugins)) {
if (\count($plugins) > 0) {
if (!empty($this->plugin_require)) {
foreach ($this->plugin_require as $plugin) {
$plugin_file_name = $plugin[self::PLUGIN_INFO_KEY_NAME];
$plugin_version = $plugin[self::PLUGIN_INFO_VERSION];
if (self::is_wp_plugin_active($plugin_file_name)) {
$require_plugins[$plugin_file_name] = $plugins[$plugin_file_name];
$require_plugins[$plugin_file_name][self::PLUGIN_INFO_APPEND_PLUGIN_DATA] = $plugin_version;
}
}
}
}
}
return $require_plugins;
}
/**
* @param array $notices
*
* @return array
*/
private function append_plugin_require_notices($notices)
{
if (\count($this->plugin_require) > 0) {
foreach ($this->plugin_require as $plugin_name => $plugin_info) {
$notice = null;
if (isset($plugin_info['repository_url'])) {
$notice = $this->prepare_plugin_repository_require_notice($plugin_info);
} elseif (!self::is_wp_plugin_active($plugin_name)) {
$notice = $this->prepare_notice_message(\sprintf(\__('The &#8220;%s&#8221; plugin cannot run without %s active. Please install and activate %s plugin.', $this->get_text_domain()), \esc_html($this->plugin_name), \esc_html(\basename($plugin_info[self::PLUGIN_INFO_KEY_NICE_NAME])), \esc_html(\basename($plugin_info[self::PLUGIN_INFO_KEY_NICE_NAME]))));
}
if ($notice !== null) {
$notices[] = $notice;
}
}
}
return $notices;
}
/**
* Prepares WP install url and injects info about plugin to the WP update engine.
*
* @param array $plugin_info
*
* @return string
*/
private function prepare_plugin_repository_install_url($plugin_info)
{
$slug = \basename($plugin_info[self::PLUGIN_INFO_KEY_NAME]);
$install_url = \self_admin_url('update.php?action=install-plugin&plugin=' . $slug);
if (\function_exists('wp_nonce_url') && \function_exists('wp_create_nonce')) {
$install_url = \wp_nonce_url($install_url, 'install-plugin_' . $slug);
}
\add_filter('plugins_api', function ($api, $action, $args) use($plugin_info, $slug) {
if ('plugin_information' !== $action || \false !== $api || !isset($args->slug) || $slug !== $args->slug) {
return $api;
}
$api = new \stdClass();
$api->name = $plugin_info['nice_name'];
// self in closures requires 5.4
$api->version = '';
$api->download_link = \esc_url($plugin_info['repository_url']);
// self in closures requires 5.4
return $api;
}, 10, 3);
return $install_url;
}
/**
* @param array $plugin_info Internal required plugin info data.
*
* @return string|null Return null if no notice is needed.
*/
private function prepare_plugin_repository_require_notice($plugin_info)
{
$name = $plugin_info[self::PLUGIN_INFO_KEY_NAME];
$nice_name = $plugin_info[self::PLUGIN_INFO_KEY_NICE_NAME];
if (!self::is_wp_plugin_active($name)) {
if (!self::is_wp_plugin_installed($name)) {
$install_url = $this->prepare_plugin_repository_install_url($plugin_info);
return $this->prepare_notice_message(\sprintf(\wp_kses(\__('The &#8220;%s&#8221; plugin requires free %s plugin. <a href="%s">Install %s →</a>', $this->get_text_domain()), array('a' => array('href' => array()))), $this->plugin_name, $nice_name, \esc_url($install_url), $nice_name));
}
$activate_url = 'plugins.php?action=activate&plugin=' . \urlencode($plugin_info[self::PLUGIN_INFO_KEY_NAME]) . '&plugin_status=all&paged=1&s';
if (\function_exists('wp_create_nonce')) {
$activate_url .= '&_wpnonce=' . \urlencode(\wp_create_nonce('activate-plugin_' . $name));
}
return $this->prepare_notice_message(\sprintf(\wp_kses(\__('The &#8220;%s&#8221; plugin requires activating %s plugin. <a href="%s">Activate %s →</a>', $this->get_text_domain()), array('a' => array('href' => array()))), $this->plugin_name, $nice_name, \esc_url(\admin_url($activate_url)), $nice_name));
}
return null;
}
/**
* Checks if plugin is active. Needs to be used in deferred way.
*
* @param string $plugin_file
*
* @return bool
*/
public static function is_wp_plugin_active($plugin_file)
{
$active_plugins = (array) \get_option('active_plugins', array());
if (\is_multisite()) {
$active_plugins = \array_merge($active_plugins, \get_site_option('active_sitewide_plugins', array()));
}
return \in_array($plugin_file, $active_plugins) || \array_key_exists($plugin_file, $active_plugins);
}
/**
* Checks if plugin is installed. Needs to be enabled in deferred way.
*
* @param string $plugin_file
*
* @return bool
*/
public static function is_wp_plugin_installed($plugin_file)
{
$plugins_data = self::retrieve_plugins_data_in_transient();
return \array_key_exists($plugin_file, (array) $plugins_data);
}
/**
* @param array $notices
*
* @return array
*/
private function append_module_require_notices($notices)
{
if (\count($this->module_require) > 0) {
foreach ($this->module_require as $module_name => $nice_module_name) {
if (!self::is_module_active($module_name)) {
$notices[] = $this->prepare_notice_message(\sprintf(\__('The &#8220;%s&#8221; plugin cannot run without %s PHP module installed. Please contact your host and ask them to install %s.', $this->get_text_domain()), \esc_html($this->plugin_name), \esc_html(\basename($nice_module_name)), \esc_html(\basename($nice_module_name))));
}
}
}
return $notices;
}
/**
* @param string $name
*
* @return bool
*/
public static function is_module_active($name)
{
return \extension_loaded($name);
}
/**
* @param array $notices
*
* @return array
*/
private function append_settings_require_notices($notices)
{
if (\count($this->setting_require) > 0) {
foreach ($this->setting_require as $setting => $value) {
if (!self::is_setting_set($setting, $value)) {
$notices[] = $this->prepare_notice_message(\sprintf(\__('The &#8220;%s&#8221; plugin cannot run without %s PHP setting set to %s. Please contact your host and ask them to set %s.', $this->get_text_domain()), \esc_html($this->plugin_name), \esc_html(\basename($setting)), \esc_html(\basename($value)), \esc_html(\basename($setting))));
}
}
}
return $notices;
}
/**
* @param string $name
* @param mixed $value
*
* @return bool
*/
public static function is_setting_set($name, $value)
{
return \ini_get($name) === (string) $value;
}
/**
* @return void
*
* @deprecated use render_notices or disable_plugin
*/
public function disable_plugin_render_notice()
{
\add_action(self::HOOK_ADMIN_NOTICES_ACTION, array($this, 'handle_render_notices_action'));
}
/**
* Renders requirement notices in admin panel
*
* @return void
*/
public function render_notices()
{
\add_action(self::HOOK_ADMIN_NOTICES_ACTION, array($this, 'handle_render_notices_action'));
}
/**
* Renders requirement notices in admin panel
*
* @return void
*/
public function disable_plugin()
{
\add_action(self::HOOK_ADMIN_NOTICES_ACTION, array($this, 'handle_deactivate_action'));
}
/**
* @return void
* @internal Do not use as public. Public only for wp action.
*
*/
public function handle_deactivate_action()
{
if (isset($this->plugin_file)) {
\deactivate_plugins(\plugin_basename($this->plugin_file));
\delete_transient(self::PLUGIN_INFO_TRANSIENT_NAME);
}
}
/**
* Triggers the transient delete after plugin deactivated
*
* @return void
*/
public function transient_delete_on_plugin_version_changed()
{
\add_action(self::HOOK_PLUGIN_DEACTIVATED_ACTION, array($this, 'handle_transient_delete_action'));
\add_action(self::HOOK_PLUGIN_ACTIVATED_ACTION, array($this, 'handle_transient_delete_action'));
}
/**
* Handles the transient delete
*
* @return void
*/
public function handle_transient_delete_action()
{
\delete_transient(self::PLUGIN_INFO_TRANSIENT_NAME);
}
/**
* Should be called as WordPress action
*
* @return void
* @internal Do not use as public. Public only for wp action.
*
*/
public function handle_render_notices_action()
{
foreach ($this->notices as $notice) {
echo $notice;
}
}
}
}

View File

@@ -0,0 +1,62 @@
<?php
namespace FcfVendor;
if (!\class_exists('FcfVendor\\Basic_Requirement_Checker')) {
require_once __DIR__ . '/Basic_Requirement_Checker.php';
}
if (!\class_exists('FcfVendor\\WPDesk_Basic_Requirement_Checker_With_Update_Disable')) {
require_once __DIR__ . '/Basic_Requirement_Checker_With_Update_Disable.php';
}
/**
* Falicitates createion of requirement checker
*/
class WPDesk_Basic_Requirement_Checker_Factory
{
const LIBRARY_TEXT_DOMAIN = 'flexible-checkout-fields';
/**
* Creates a simplest possible version of requirement checker.
*
* @param string $plugin_file
* @param string $plugin_name
* @param string|null $text_domain Text domain to use. If null try to use library text domain.
*
* @return WPDesk_Requirement_Checker
*/
public function create_requirement_checker($plugin_file, $plugin_name, $text_domain = null)
{
return new \FcfVendor\WPDesk_Basic_Requirement_Checker($plugin_file, $plugin_name, $text_domain, null, null);
}
/**
* Creates a requirement checker according to given requirements array info.
*
* @param string $plugin_file
* @param string $plugin_name
* @param string $text_domain Text domain to use. If null try to use library text domain.
* @param array $requirements Requirements array as given by plugin.
*
* @return WPDesk_Requirement_Checker
*/
public function create_from_requirement_array($plugin_file, $plugin_name, $requirements, $text_domain = null)
{
$requirements_checker = new \FcfVendor\WPDesk_Basic_Requirement_Checker_With_Update_Disable($plugin_file, $plugin_name, $text_domain, $requirements['php'], $requirements['wp']);
if (isset($requirements['plugins'])) {
foreach ($requirements['plugins'] as $requirement) {
$version = isset($requirement['version']) ? $requirement['version'] : null;
$requirements_checker->add_plugin_require($requirement['name'], $requirement['nice_name'], $version);
}
$requirements_checker->transient_delete_on_plugin_version_changed();
}
if (isset($requirements['repo_plugins'])) {
foreach ($requirements['repo_plugins'] as $requirement) {
$requirements_checker->add_plugin_repository_require($requirement['name'], $requirement['version'], $requirement['nice_name']);
}
}
if (isset($requirements['modules'])) {
foreach ($requirements['modules'] as $requirement) {
$requirements_checker->add_php_module_require($requirement['name'], $requirement['nice_name']);
}
}
return $requirements_checker;
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace FcfVendor;
if (!\class_exists('FcfVendor\\WPDesk_Basic_Requirement_Checker')) {
require_once __DIR__ . '/Basic_Requirement_Checker.php';
}
if (!\class_exists('FcfVendor\\WPDesk_Basic_Requirement_Checker_With_Update_Disable')) {
/**
* Checks requirements for plugin. When required plugin is updated right now, then say that requirements are not met temporary.
* have to be compatible with PHP 5.2.x
*/
class WPDesk_Basic_Requirement_Checker_With_Update_Disable extends \FcfVendor\WPDesk_Basic_Requirement_Checker
{
/**
* Returns true if are requirements are met.
*
* @return bool
*/
public function are_requirements_met()
{
$has_been_met = parent::are_requirements_met();
if (!$has_been_met) {
return $has_been_met;
}
foreach ($this->plugin_require as $name => $plugin_info) {
if ($this->is_currently_updated($name)) {
$nice_name = $plugin_info[self::PLUGIN_INFO_KEY_NICE_NAME];
$this->notices[] = $this->prepare_notice_message(\sprintf(\__('The &#8220;%s&#8221; plugin is temporarily disabled since the required %s plugin is being upgraded.', $this->get_text_domain()), $this->plugin_name, $nice_name, $nice_name));
}
}
return \count($this->notices) === 0;
}
/**
* Is plugin upgrading right now?
*
* @param string $name
*
* @return bool
*/
private function is_currently_updated($name)
{
return isset($_GET['action']) && $_GET['action'] === 'upgrade-plugin' && $_GET['plugin'] === $name;
}
}
}

View File

@@ -0,0 +1,74 @@
<?php
namespace FcfVendor;
/**
* Checks requirements for plugin
* have to be compatible with PHP 5.2.x
*/
interface WPDesk_Requirement_Checker
{
/**
* @param string $version
*
* @return $this
*/
public function set_min_php_require($version);
/**
* @param string $version
*
* @return $this
*/
public function set_min_wp_require($version);
/**
* @param string $version
*
* @return $this
*/
public function set_min_wc_require($version);
/**
* @param $version
*
* @return $this
*/
public function set_min_openssl_require($version);
/**
* @param string $plugin_name
* @param string $nice_plugin_name Nice plugin name for better looks in notice
*
* @return $this
*/
public function add_plugin_require($plugin_name, $nice_plugin_name = null);
/**
* @param string $module_name
* @param string $nice_name Nice module name for better looks in notice
*
* @return $this
*/
public function add_php_module_require($module_name, $nice_name = null);
/**
* @param string $setting
* @param mixed $value
*
* @return $this
*/
public function add_php_setting_require($setting, $value);
/**
* @return bool
*/
public function are_requirements_met();
/**
* @return void
*/
public function disable_plugin_render_notice();
/**
* @return void
*/
public function render_notices();
/**
* Renders requirement notices in admin panel
*
* @return void
*/
public function disable_plugin();
}

View File

@@ -0,0 +1,17 @@
<?php
namespace FcfVendor;
interface WPDesk_Requirement_Checker_Factory
{
/**
* @param $plugin_file
* @param $plugin_name
* @param $text_domain
* @param $php_version
* @param $wp_version
*
* @return WPDesk_Requirement_Checker
*/
public function create_requirement_checker($plugin_file, $plugin_name, $text_domain);
}

View File

@@ -0,0 +1,36 @@
{
"name": "wpdesk\/wp-builder",
"authors": [
{
"name": "Krzysiek",
"email": "krzysiek@wpdesk.pl"
}
],
"require": {
"php": ">=5.5"
},
"require-dev": {
"phpunit\/phpunit": "<7",
"wp-coding-standards\/wpcs": "^0.14.1",
"squizlabs\/php_codesniffer": "^3.0.2",
"mockery\/mockery": "*",
"10up\/wp_mock": "*",
"wimg\/php-compatibility": "^8"
},
"autoload": {
"psr-4": {
"FcfVendor\\WPDesk\\PluginBuilder\\": "src\/"
},
"classmap": [
"src\/Plugin\/WithoutNamespace"
]
},
"autoload-dev": {},
"scripts": {
"phpcs": "phpcs",
"phpunit-unit": "phpunit --configuration phpunit-unit.xml --coverage-text --colors=never",
"phpunit-unit-fast": "phpunit --configuration phpunit-unit.xml --no-coverage",
"phpunit-integration": "phpunit --configuration phpunit-integration.xml --coverage-text --colors=never",
"phpunit-integration-fast": "phpunit --configuration phpunit-integration.xml --no-coverage"
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace FcfVendor\WPDesk\PluginBuilder\BuildDirector;
use FcfVendor\WPDesk\PluginBuilder\Builder\AbstractBuilder;
use FcfVendor\WPDesk\PluginBuilder\Plugin\AbstractPlugin;
use FcfVendor\WPDesk\PluginBuilder\Storage\StorageFactory;
class LegacyBuildDirector
{
/** @var AbstractBuilder */
private $builder;
public function __construct(\FcfVendor\WPDesk\PluginBuilder\Builder\AbstractBuilder $builder)
{
$this->builder = $builder;
}
/**
* Builds plugin
*/
public function build_plugin()
{
$this->builder->build_plugin();
$this->builder->init_plugin();
$storage = new \FcfVendor\WPDesk\PluginBuilder\Storage\StorageFactory();
$this->builder->store_plugin($storage->create_storage());
}
/**
* Returns built plugin
*
* @return AbstractPlugin
*/
public function get_plugin()
{
return $this->builder->get_plugin();
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace FcfVendor\WPDesk\PluginBuilder\Builder;
use FcfVendor\WPDesk\PluginBuilder\Plugin\AbstractPlugin;
use FcfVendor\WPDesk\PluginBuilder\Storage\PluginStorage;
abstract class AbstractBuilder
{
/**
* Create plugin class
*/
public function build_plugin()
{
}
/**
* Store plugin class in some kind of storage
*/
public function store_plugin(\FcfVendor\WPDesk\PluginBuilder\Storage\PluginStorage $storage)
{
}
/**
* Init plugin internal structure
*/
public function init_plugin()
{
}
/**
* Return built plugin
* @return AbstractPlugin
*/
abstract function get_plugin();
/**
* Set settings class in plugin
*
* @param $settings
*/
public function set_settings($settings)
{
}
/**
* Set view class in plugin
*
* @param $view
*/
public function set_view($view)
{
}
/**
* Set tracker class in plugin
*
* @param $tracker
*/
public function set_tracker($tracker)
{
}
/**
* Set helper class in plugin
*
* @param $helper
*/
public function set_helper($helper)
{
}
}

View File

@@ -0,0 +1,70 @@
<?php
namespace FcfVendor\WPDesk\PluginBuilder\Builder;
use FcfVendor\WPDesk\PluginBuilder\Plugin\AbstractPlugin;
use FcfVendor\WPDesk\PluginBuilder\Plugin\ActivationAware;
use FcfVendor\WPDesk\PluginBuilder\Storage\PluginStorage;
/**
* Builder that have info about activations
*
* Warning: We can't extend InfoBuilder.php as some old plugins(without wp-flow) will load the old version od InfoBuilder class that have private plugin property.
*
* @package WPDesk\PluginBuilder\Builder
*/
class InfoActivationBuilder extends \FcfVendor\WPDesk\PluginBuilder\Builder\AbstractBuilder
{
const FILTER_PLUGIN_CLASS = 'wp_builder_plugin_class';
const HOOK_BEFORE_PLUGIN_INIT = 'wp_builder_before_plugin_init';
const HOOK_AFTER_PLUGIN_INIT = 'wp_builder_before_init';
/** @var AbstractPlugin */
private $plugin;
/** @var \WPDesk_Buildable */
private $info;
/** @var string */
protected $storage_id;
/**
* @var bool
*/
private $is_active;
/**
* @param \WPDesk_Buildable $info
* @param bool $is_active
*/
public function __construct(\FcfVendor\WPDesk_Buildable $info, $is_active)
{
$this->info = $info;
$this->storage_id = $info->get_class_name();
$this->is_active = $is_active;
}
/**
* Builds instance of plugin
*/
public function build_plugin()
{
$class_name = \apply_filters(self::FILTER_PLUGIN_CLASS, $this->info->get_class_name());
/** @var AbstractPlugin $plugin */
$this->plugin = new $class_name($this->info);
if ($this->plugin instanceof \FcfVendor\WPDesk\PluginBuilder\Plugin\ActivationAware && $this->is_active) {
$this->plugin->set_active();
}
return $this->plugin;
}
public function store_plugin(\FcfVendor\WPDesk\PluginBuilder\Storage\PluginStorage $storage)
{
$storage->add_to_storage($this->storage_id, $this->plugin);
}
public function init_plugin()
{
\do_action(self::HOOK_BEFORE_PLUGIN_INIT, $this->plugin);
$this->plugin->init();
\do_action(self::HOOK_AFTER_PLUGIN_INIT, $this->plugin);
}
/**
* @return AbstractPlugin
*/
public function get_plugin()
{
return $this->plugin;
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace FcfVendor\WPDesk\PluginBuilder\Builder;
use FcfVendor\WPDesk\PluginBuilder\Plugin\AbstractPlugin;
use FcfVendor\WPDesk\PluginBuilder\Storage\PluginStorage;
/**
* @deprecated Should not be used as some old plugins are using it and we can't touch this.
*
* @package WPDesk\PluginBuilder\Builder
*/
class InfoBuilder extends \FcfVendor\WPDesk\PluginBuilder\Builder\AbstractBuilder
{
const FILTER_PLUGIN_CLASS = 'wp_builder_plugin_class';
const HOOK_BEFORE_PLUGIN_INIT = 'wp_builder_before_plugin_init';
const HOOK_AFTER_PLUGIN_INIT = 'wp_builder_before_init';
/** @var AbstractPlugin */
private $plugin;
/** @var \WPDesk_Buildable */
private $info;
/** @var string */
protected $storage_id;
public function __construct(\FcfVendor\WPDesk_Buildable $info)
{
$this->info = $info;
$this->storage_id = $info->get_class_name();
}
/**
* Builds instance of plugin
*/
public function build_plugin()
{
$class_name = \apply_filters(self::FILTER_PLUGIN_CLASS, $this->info->get_class_name());
/** @var AbstractPlugin $plugin */
$this->plugin = new $class_name($this->info);
}
public function store_plugin(\FcfVendor\WPDesk\PluginBuilder\Storage\PluginStorage $storage)
{
$storage->add_to_storage($this->storage_id, $this->plugin);
}
public function init_plugin()
{
\do_action(self::HOOK_BEFORE_PLUGIN_INIT, $this->plugin);
$this->plugin->init();
\do_action(self::HOOK_AFTER_PLUGIN_INIT, $this->plugin);
}
/**
* @return AbstractPlugin
*/
public function get_plugin()
{
return $this->plugin;
}
}

View File

@@ -0,0 +1,203 @@
<?php
namespace FcfVendor\WPDesk\PluginBuilder\Plugin;
/**
* Base plugin with most basic functionalities used by every WPDesk plugin.
*
*
* Known issues:
*
* The class name is too generic but can't be changed as it would introduce a major incompatibility for most of the plugins.
* The $plugin_url, $docs_url and most other fields should be removed as they only litter the place but for compatibility reasons we can't do it right now.
* Hook methods should be moved to external classes but for compatibility reasons we can't do it right now.
*/
abstract class AbstractPlugin extends \FcfVendor\WPDesk\PluginBuilder\Plugin\SlimPlugin
{
/**
* Most info about plugin internals.
*
* @var \WPDesk_Plugin_Info
*/
protected $plugin_info;
/**
* Unique string for this plugin in [a-z_]+ format.
*
* @var string
*/
protected $plugin_namespace;
/**
* Absolute URL to the plugin dir.
*
* @var string
*/
protected $plugin_url;
/**
* Absolute URL to the plugin docs.
*
* @var string
*/
protected $docs_url;
/**
* Absolute URL to the plugin settings url.
*
* @var string
*/
protected $settings_url;
/**
* Support URL.
*
* @var string
*/
protected $support_url;
/**
* AbstractPlugin constructor.
*
* @param \WPDesk_Plugin_Info $plugin_info
*/
public function __construct($plugin_info)
{
$this->plugin_info = $plugin_info;
$this->plugin_namespace = \strtolower($plugin_info->get_plugin_dir());
$this->plugin_url = $this->plugin_info->get_plugin_url();
$this->init_base_variables();
}
/**
* Initialize internal state of the plugin.
*
* @return void
* @deprecated Just use __construct to initialize plugin internal state.
*
*/
public function init_base_variables()
{
}
/**
* Initializes plugin external state.
*
* The plugin internal state is initialized in the constructor and the plugin should be internally consistent after creation.
* The external state includes hooks execution, communication with other plugins, integration with WC etc.
*
* @return void
*/
public function init()
{
$this->hooks();
}
/**
* Returns absolute path to the plugin dir.
*
* @return string
*/
public function get_plugin_file_path()
{
return $this->plugin_info->get_plugin_file_name();
}
/**
* Returns plugin text domain.
*
* @return string
*/
public function get_text_domain()
{
return $this->plugin_info->get_text_domain();
}
/**
* Returns unique string for plugin in [a-z_]+ format. Can be used as plugin id in various places like plugin slug etc.
*
* @return string
*/
public function get_namespace()
{
return $this->plugin_namespace;
}
/**
* Returns plugin absolute URL.
*
* @return string
*/
public function get_plugin_url()
{
return \esc_url(\trailingslashit($this->plugin_url));
}
/**
* Returns plugin absolute URL to dir with front end assets.
*
* @return string
*/
public function get_plugin_assets_url()
{
return \esc_url(\trailingslashit($this->get_plugin_url() . 'assets'));
}
/**
* @return $this
* @deprecated For backward compatibility.
*
*/
public function get_plugin()
{
return $this;
}
/**
* Integrate with WordPress and with other plugins using action/filter system.
*
* @return void
*/
protected function hooks()
{
\add_action('admin_enqueue_scripts', [$this, 'admin_enqueue_scripts']);
\add_action('wp_enqueue_scripts', [$this, 'wp_enqueue_scripts']);
\add_action('plugins_loaded', [$this, 'load_plugin_text_domain']);
\add_filter('plugin_action_links_' . \plugin_basename($this->get_plugin_file_path()), [$this, 'links_filter']);
}
/**
* Initialize plugin test domain. This is a hook function. Do not execute directly.
*
* @return void
*/
public function load_plugin_text_domain()
{
\load_plugin_textdomain($this->get_text_domain(), \false, $this->get_namespace() . '/lang/');
}
/**
* Append JS scripts in the WordPress admin panel. This is a hook function. Do not execute directly.
*
* @return void
*/
public function admin_enqueue_scripts()
{
}
/**
* Append JS scripts in WordPress. This is a hook function. Do not execute directly.
*
* @return void
*/
public function wp_enqueue_scripts()
{
}
/**
* Initialize plugin admin links. This is a hook function. Do not execute directly.
*
* @param string[] $links
*
* @return string[]
*/
public function links_filter($links)
{
$support_link = \get_locale() === 'pl_PL' ? 'https://www.wpdesk.pl/support/' : 'https://www.wpdesk.net/support';
if ($this->support_url) {
$support_link = $this->support_url;
}
$plugin_links = ['<a target="_blank" href="' . $support_link . '">' . \__('Support', $this->get_text_domain()) . '</a>'];
$links = \array_merge($plugin_links, $links);
if ($this->docs_url) {
$plugin_links = ['<a target="_blank" href="' . $this->docs_url . '">' . \__('Docs', $this->get_text_domain()) . '</a>'];
$links = \array_merge($plugin_links, $links);
}
if ($this->settings_url) {
$plugin_links = ['<a href="' . $this->settings_url . '">' . \__('Settings', $this->get_text_domain()) . '</a>'];
$links = \array_merge($plugin_links, $links);
}
return $links;
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace FcfVendor\WPDesk\PluginBuilder\Plugin;
/**
* Tag the plugin with this ingterface to hook it to the WordPress activation hook.
*
* Note: works from plugin flow ^2.2.
*
* @package WPDesk\PluginBuilder\Plugin
*/
interface Activateable
{
/**
* Plugin activated in WordPress. Do not execute directly.
*
* @return void
*/
public function activate();
}

View File

@@ -0,0 +1,24 @@
<?php
namespace FcfVendor\WPDesk\PluginBuilder\Plugin;
/**
* It means that this class is should know about SUBSCRIPTION activation
*
* @package WPDesk\PluginBuilder\Plugin
*/
interface ActivationAware
{
/**
* Set the activation flag to true
*
* @return void
*/
public function set_active();
/**
* Is subscription active?
*
* @return bool
*/
public function is_active();
}

View File

@@ -0,0 +1,70 @@
<?php
namespace FcfVendor\WPDesk\PluginBuilder\Plugin;
/**
* @deprecated nobody uses it :) And also this library is not a place for this class
*
* @package WPDesk\PluginBuilder\Plugin
*/
class ActivationTracker
{
/**
* Namespace.
*
* @var string
*/
private $namespace;
/**
* ActivationTracker constructor.
*
* @param string $namespace Namespace for settings.
*/
public function __construct($namespace)
{
$this->namespace = $namespace;
}
/**
* Option name for date storage
*
* @return string
*/
private function get_option_name_activation_date()
{
return $this->namespace . '_activation';
}
/**
* Returns activation date and sets it if were not set before
*
* @return int unix timestamp for activation datetime
*/
public function get_activation_date()
{
$activation_date = \get_option($this->get_option_name_activation_date());
if (empty($activation_date)) {
return $this->touch_activation_date();
}
return \intval($activation_date);
}
/**
* Was activation more than two weeks before today
*
* @return bool
*/
public function is_activated_more_than_two_weeks()
{
$two_weeks = 60 * 60 * 24 * 7 * 2;
return $this->get_activation_date() + $two_weeks < \time();
}
/**
* Sets activatiion date for today
*
* @return int unit timestamp for now
*/
public function touch_activation_date()
{
$now = \time();
\update_option($this->get_option_name_activation_date(), $now);
return $now;
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace FcfVendor\WPDesk\PluginBuilder\Plugin;
/**
* Something that can be instantiated/hooked conditionally.
*
* @see https://github.com/mwpd/basic-scaffold/blob/master/src/Infrastructure/Conditional.php by Alain Schlesser
*
* @package WPDesk\PluginBuilder\Plugin
*/
interface Conditional
{
/**
* Check whether the conditional object is currently needed.
*
* @return bool Whether the conditional object is needed.
*/
public static function is_needed();
}

View File

@@ -0,0 +1,20 @@
<?php
namespace FcfVendor\WPDesk\PluginBuilder\Plugin;
/**
* Tag the plugin with this ingterface to hook it to the WordPress deactivation hook.
*
* Note: works from plugin flow ^2.2.
*
* @package WPDesk\PluginBuilder\Plugin
*/
interface Deactivateable
{
/**
* Plugin deactivate in WordPress. Do not execute directly.
*
* @return void
*/
public function deactivate();
}

View File

@@ -0,0 +1,13 @@
<?php
namespace FcfVendor\WPDesk\PluginBuilder\Plugin;
interface Hookable
{
/**
* Init hooks (actions and filters).
*
* @return void
*/
public function hooks();
}

View File

@@ -0,0 +1,21 @@
<?php
namespace FcfVendor\WPDesk\PluginBuilder\Plugin;
interface HookableCollection extends \FcfVendor\WPDesk\PluginBuilder\Plugin\Hookable
{
/**
* Add hookable object.
*
* @param Hookable|HookablePluginDependant $hookable_object Hookable object.
*/
public function add_hookable(\FcfVendor\WPDesk\PluginBuilder\Plugin\Hookable $hookable_object);
/**
* Get hookable instance.
*
* @param string $class_name Class name.
*
* @return false|Hookable
*/
public function get_hookable_instance_by_class_name($class_name);
}

View File

@@ -0,0 +1,57 @@
<?php
namespace FcfVendor\WPDesk\PluginBuilder\Plugin;
trait HookableParent
{
/**
* Hookable objects.
*
* @var array[Hookable]
*/
private $hookable_objects = array();
/**
* Add hookable object.
*
* @param Hookable|HookablePluginDependant $hookable_object Hookable object.
*/
public function add_hookable(\FcfVendor\WPDesk\PluginBuilder\Plugin\Hookable $hookable_object)
{
if ($hookable_object instanceof \FcfVendor\WPDesk\PluginBuilder\Plugin\HookablePluginDependant) {
$hookable_object->set_plugin($this);
}
$this->hookable_objects[] = $hookable_object;
}
/**
* Get hookable instance.
*
* @param string $class_name Class name.
*
* @return false|Hookable
*/
public function get_hookable_instance_by_class_name($class_name)
{
foreach ($this->hookable_objects as $hookable_object) {
if ($hookable_object instanceof $class_name) {
return $hookable_object;
}
}
return \false;
}
/**
* Run hooks method on all hookable objects.
*/
protected function hooks_on_hookable_objects()
{
/** @var Hookable $hookable_object $hookable_object */
foreach ($this->hookable_objects as $hookable_object) {
if ($hookable_object instanceof \FcfVendor\WPDesk\PluginBuilder\Plugin\Conditional) {
if ($hookable_object::is_needed()) {
$hookable_object->hooks();
}
} else {
$hookable_object->hooks();
}
}
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace FcfVendor\WPDesk\PluginBuilder\Plugin;
interface HookablePluginDependant extends \FcfVendor\WPDesk\PluginBuilder\Plugin\Hookable
{
/**
* Set Plugin.
*
* @param AbstractPlugin $plugin Plugin.
*
* @return null
*/
public function set_plugin(\FcfVendor\WPDesk\PluginBuilder\Plugin\AbstractPlugin $plugin);
/**
* Get plugin.
*
* @return AbstractPlugin.
*/
public function get_plugin();
}

View File

@@ -0,0 +1,34 @@
<?php
namespace FcfVendor\WPDesk\PluginBuilder\Plugin;
/**
* @package WPDesk\PluginBuilder\Plugin
*/
trait PluginAccess
{
/**
* Plugin.
*
* @var AbstractPlugin
*/
private $plugin;
/**
* Set plugin.
*
* @param AbstractPlugin $plugin Plugin.
*/
public function set_plugin(\FcfVendor\WPDesk\PluginBuilder\Plugin\AbstractPlugin $plugin)
{
$this->plugin = $plugin;
}
/**
* Get plugin.
*
* @return AbstractPlugin
*/
public function get_plugin()
{
return $this->plugin;
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace FcfVendor\WPDesk\PluginBuilder\Plugin;
/**
* Most clean plugin class with only most important details.
*/
abstract class SlimPlugin implements \FcfVendor\WPDesk_Translatable
{
/**
* Initializes plugin external state.
*
* The plugin internal state is initialized in the constructor and the plugin should be internally consistent after creation.
* The external state includes hooks execution, communication with other plugins, integration with WC etc.
*
* @return void
*/
public abstract function init();
}

View File

@@ -0,0 +1,63 @@
<?php
namespace FcfVendor\WPDesk\PluginBuilder\Plugin;
/**
* @deprecated Use wpdesk/wp-view
*
* @package WPDesk\PluginBuilder\Plugin
*/
trait TemplateLoad
{
/**
* Plugin path.
*
* @var string
*/
protected $plugin_path;
/**
* Template path.
*
* @var string
*/
protected $template_path;
/**
* Init base variables for plugin
*/
public function init_template_base_variables()
{
$this->plugin_path = $this->plugin_info->get_plugin_dir();
$this->template_path = $this->plugin_info->get_text_domain();
}
/**
* Renders end returns selected template
*
* @param string $name Name of the template.
* @param string $path Additional inner path to the template.
* @param array $args args Accessible from template.
*
* @return string
*/
public function load_template($name, $path = '', $args = array())
{
$plugin_template_path = \trailingslashit($this->plugin_path) . 'templates/';
// Look within passed path within the theme - this is priority.
$template = \locate_template(array(\trailingslashit($this->get_template_path()) . \trailingslashit($path) . $name . '.php'));
if (!$template) {
$template = $plugin_template_path . \trailingslashit($path) . $name . '.php';
}
\extract($args);
\ob_start();
include $template;
return \ob_get_clean();
}
/**
* Get template path.
*
* @return string
*/
public function get_template_path()
{
return \trailingslashit($this->template_path);
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace FcfVendor;
/**
* Have info about what class should be built by WPDesk_Builder
*
* have to be compatible with PHP 5.2.x
*/
interface WPDesk_Buildable
{
/** @return string */
public function get_class_name();
}

View File

@@ -0,0 +1,27 @@
<?php
namespace FcfVendor;
if (!\interface_exists('FcfVendor\\WPDesk_Translatable')) {
require_once __DIR__ . '/Translatable.php';
}
/**
* Have MUST HAVE info for plugin instantion
*
* have to be compatible with PHP 5.2.x
*/
interface WPDesk_Has_Plugin_Info extends \FcfVendor\WPDesk_Translatable
{
/**
* @return string
*/
public function get_plugin_file_name();
/**
* @return string
*/
public function get_plugin_dir();
/**
* @return string
*/
public function get_version();
}

View File

@@ -0,0 +1,165 @@
<?php
namespace FcfVendor;
if (!\interface_exists('FcfVendor\\WPDesk_Translatable')) {
require_once __DIR__ . '/Translatable.php';
}
if (!\interface_exists('FcfVendor\\WPDesk_Buildable')) {
require_once __DIR__ . '/Buildable.php';
}
if (!\interface_exists('FcfVendor\\WPDesk_Has_Plugin_Info')) {
require_once __DIR__ . '/Has_Plugin_Info.php';
}
/**
* Structure with core info about plugin
*
* have to be compatible with PHP 5.2.x
*/
class WPDesk_Plugin_Info implements \FcfVendor\WPDesk_Translatable, \FcfVendor\WPDesk_Buildable, \FcfVendor\WPDesk_Has_Plugin_Info
{
/** @var string */
private $plugin_file_name;
/** @var string */
private $plugin_dir;
/** @var string */
private $plugin_url;
/** @var string */
private $class_name;
/** @var string */
private $version;
/** @var string */
private $product_id;
/** @var string */
private $plugin_name;
/** @var \DateTimeInterface */
private $release_date;
/** string */
private $text_domain;
/**
* @return string
*/
public function get_plugin_file_name()
{
return $this->plugin_file_name;
}
/**
* @param string $plugin_name
*/
public function set_plugin_file_name($plugin_name)
{
$this->plugin_file_name = $plugin_name;
}
/**
* @return string
*/
public function get_plugin_dir()
{
return $this->plugin_dir;
}
/**
* @param string $plugin_dir
*/
public function set_plugin_dir($plugin_dir)
{
$this->plugin_dir = $plugin_dir;
}
/**
* @return string
*/
public function get_plugin_url()
{
return $this->plugin_url;
}
/**
* @param string $plugin_url
*/
public function set_plugin_url($plugin_url)
{
$this->plugin_url = $plugin_url;
}
/**
* @return string
*/
public function get_version()
{
return $this->version;
}
/**
* @param string $version
*/
public function set_version($version)
{
$this->version = $version;
}
/**
* @return string
*/
public function get_product_id()
{
return $this->product_id;
}
/**
* @param string $product_id
*/
public function set_product_id($product_id)
{
$this->product_id = $product_id;
}
/**
* @return string
*/
public function get_plugin_name()
{
return $this->plugin_name;
}
/**
* @param string $plugin_name
*/
public function set_plugin_name($plugin_name)
{
$this->plugin_name = $plugin_name;
}
/**
* @return DateTimeInterface
*/
public function get_release_date()
{
return $this->release_date;
}
/**
* @param \DateTimeInterface $release_date
*/
public function set_release_date($release_date)
{
$this->release_date = $release_date;
}
/**
* @return string
*/
public function get_class_name()
{
return $this->class_name;
}
/**
* @param string $class_name
*/
public function set_class_name($class_name)
{
$this->class_name = $class_name;
}
/**
* @return string
*/
public function get_text_domain()
{
return $this->text_domain;
}
/**
* @param $value
*/
public function set_text_domain($value)
{
$this->text_domain = $value;
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace FcfVendor;
/**
* @deprecated Have typo so better use WPDesk_Translatable
*/
interface WPDesk_Translable
{
/** @return string */
public function get_text_domain();
}

View File

@@ -0,0 +1,17 @@
<?php
namespace FcfVendor;
if (!\interface_exists('FcfVendor\\WPDesk_Translable')) {
require_once 'Translable.php';
}
/**
* Have info about textdomain - how to translate texts
*
* have to be compatible with PHP 5.2.x
*/
interface WPDesk_Translatable extends \FcfVendor\WPDesk_Translable
{
/** @return string */
public function get_text_domain();
}

View File

@@ -0,0 +1,7 @@
<?php
namespace FcfVendor\WPDesk\PluginBuilder\Storage\Exception;
class ClassAlreadyExists extends \RuntimeException
{
}

View File

@@ -0,0 +1,7 @@
<?php
namespace FcfVendor\WPDesk\PluginBuilder\Storage\Exception;
class ClassNotExists extends \RuntimeException
{
}

View File

@@ -0,0 +1,19 @@
<?php
namespace FcfVendor\WPDesk\PluginBuilder\Storage;
use FcfVendor\WPDesk\PluginBuilder\Plugin\AbstractPlugin;
interface PluginStorage
{
/**
* @param string $class
* @param AbstractPlugin $object
*/
public function add_to_storage($class, $object);
/**
* @param string $class
*
* @return AbstractPlugin
*/
public function get_from_storage($class);
}

View File

@@ -0,0 +1,37 @@
<?php
namespace FcfVendor\WPDesk\PluginBuilder\Storage;
use FcfVendor\WPDesk\PluginBuilder\Plugin\AbstractPlugin;
/**
* Can store plugin instances in static variable
*
* @package WPDesk\PluginBuilder\Storage
*/
class StaticStorage implements \FcfVendor\WPDesk\PluginBuilder\Storage\PluginStorage
{
protected static $instances = [];
/**
* @param string $class
* @param AbstractPlugin $object
*/
public function add_to_storage($class, $object)
{
if (isset(self::$instances[$class])) {
throw new \FcfVendor\WPDesk\PluginBuilder\Storage\Exception\ClassAlreadyExists("Class {$class} already exists");
}
self::$instances[$class] = $object;
}
/**
* @param string $class
*
* @return AbstractPlugin
*/
public function get_from_storage($class)
{
if (isset(self::$instances[$class])) {
return self::$instances[$class];
}
throw new \FcfVendor\WPDesk\PluginBuilder\Storage\Exception\ClassNotExists("Class {$class} not exists in storage");
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace FcfVendor\WPDesk\PluginBuilder\Storage;
class StorageFactory
{
/**
* @return PluginStorage
*/
public function create_storage()
{
return new \FcfVendor\WPDesk\PluginBuilder\Storage\WordpressFilterStorage();
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace FcfVendor\WPDesk\PluginBuilder\Storage;
use FcfVendor\WPDesk\PluginBuilder\Plugin\AbstractPlugin;
/**
* Can store plugin instances in WordPress filter system.
*
* @package WPDesk\PluginBuilder\Storage
*/
class WordpressFilterStorage implements \FcfVendor\WPDesk\PluginBuilder\Storage\PluginStorage
{
const STORAGE_FILTER_NAME = 'wpdesk_plugin_instances';
/**
* @param string $class
* @param AbstractPlugin $object
*/
public function add_to_storage($class, $object)
{
\add_filter(self::STORAGE_FILTER_NAME, static function ($plugins) use($class, $object) {
if (isset($plugins[$class])) {
throw new \FcfVendor\WPDesk\PluginBuilder\Storage\Exception\ClassAlreadyExists("Class {$class} already exists");
}
$plugins[$class] = $object;
return $plugins;
});
}
/**
* @param string $class
*
* @return AbstractPlugin
*/
public function get_from_storage($class)
{
$plugins = \apply_filters(self::STORAGE_FILTER_NAME, []);
if (isset($plugins[$class])) {
return $plugins[$class];
}
throw new \FcfVendor\WPDesk\PluginBuilder\Storage\Exception\ClassNotExists("Class {$class} not exists in storage");
}
}

View File

@@ -0,0 +1,42 @@
{
"name": "wpdesk\/wp-code-sniffer",
"description": "Library for WP Desk Coding Standards in plugins.",
"license": "MIT",
"keywords": [
"wordpress",
"code sniffer",
"admin",
"phpcs"
],
"homepage": "https:\/\/gitlab.com\/wpdesk\/wp-code-sniffer",
"type": "phpcodesniffer-standard",
"prefer-stable": true,
"version": "1.2.3",
"authors": [
{
"name": "grola",
"email": "grola@wpdesk.net"
}
],
"require": {
"php": ">=7.0",
"dealerdirect\/phpcodesniffer-composer-installer": "0.6|^0.7",
"squizlabs\/php_codesniffer": "^3.6",
"wp-coding-standards\/wpcs": "^2.3",
"phpcompatibility\/phpcompatibility-wp": "^2.1.1"
},
"require-dev": {
"roave\/security-advisories": "dev-latest"
},
"scripts": {
"install-codestandards": [
"Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin::run"
],
"pre-package-install": [
"@install-codestandards"
],
"post-package-update": [
"@install-codestandards"
]
}
}

View File

@@ -0,0 +1,67 @@
{
"name": "wpdesk\/wp-codeception",
"description": "Library for WP Desk Codeception tests.",
"license": "MIT",
"keywords": [
"wordpress",
"codeception",
"admin"
],
"homepage": "https:\/\/gitlab.com\/wpdesk\/wp-codeception",
"type": "composer-plugin",
"minimum-stability": "stable",
"authors": [
{
"name": "grola",
"email": "grola@wpdesk.net"
}
],
"config": {
"sort-packages": true,
"platform": {
"php": "7.0.8"
},
"allow-plugins": {
"kylekatarnls\/update-helper": true
}
},
"require": {
"php": ">=7.0.8",
"ext-json": "*",
"composer-plugin-api": "^1.1|^2",
"albertofem\/rsync-lib": "^1.0",
"bordoni\/phpass": "^0.3.5",
"codeception\/module-asserts": "^1.3",
"codeception\/module-cli": "^1.0",
"codeception\/module-db": "^1.0",
"codeception\/module-filesystem": "^1.0",
"codeception\/module-phpbrowser": "^1.0",
"codeception\/module-rest": "^1.2",
"codeception\/module-webdriver": "^1.0",
"codeception\/util-universalframework": "^1.0",
"lucatume\/codeception-steppify": "^1.0",
"lucatume\/wp-browser": "^2.4",
"phpunit\/php-code-coverage": "^5.3",
"symfony\/yaml": "^v3.4.47",
"vlucas\/phpdotenv": "^4.2.0",
"wp-cli\/admin-command": "^2.0",
"wp-cli\/wp-cli-bundle": "^2.4"
},
"require-dev": {
"10up\/wp_mock": "*"
},
"autoload": {
"psr-4": {
"FcfVendor\\WPDesk\\Codeception\\": "src\/WPDesk\/Codeception",
"FcfVendor\\Codeception\\Module\\": "src\/Codeception\/Module",
"FcfVendor\\WPDesk\\Composer\\Codeception\\": "src\/WPDesk\/Composer"
}
},
"autoload-dev": {},
"scripts": {
"phpcs": "phpcs"
},
"extra": {
"class": "WPDesk\\Composer\\Codeception\\Plugin"
}
}

View File

@@ -0,0 +1,63 @@
<?php
namespace FcfVendor\WPDesk\Codeception\Command;
use FcfVendor\Codeception\Lib\Generator\Test;
/**
* Class code for codeception example test for WP Desk plugin activation.
*
* @package WPDesk\Codeception\Command
*/
class AcceptanceTestGenerator extends \FcfVendor\Codeception\Lib\Generator\Test
{
protected $template = <<<EOF
<?php {{namespace}}
use WPDesk\\Codeception\\Tests\\Acceptance\\Cest\\AbstractCestForPluginActivation;
class {{name}} extends AbstractCestForPluginActivation {
\t/**
\t * Deactivate plugins before tests.
\t *
\t * @param AcceptanceTester \$i .
\t *
\t * @throws \\Codeception\\Exception\\ModuleException .
\t */
\tpublic function _before( \$i ) {
\t\t\$i->loginAsAdministrator();
\t\t\$i->amOnPluginsPage();
\t\t\$i->deactivatePlugin( \$this->getPluginSlug() );
\t\t\$i->amOnPluginsPage();
\t\t\$i->seePluginDeactivated( \$this->getPluginSlug() );
\t\t\$i->amOnPluginsPage();
\t\t\$i->deactivatePlugin( self::WOOCOMMERCE_PLUGIN_SLUG );
\t\t\$i->amOnPluginsPage();
\t\t\$i->seePluginDeactivated( self::WOOCOMMERCE_PLUGIN_SLUG );
\t}
\t/**
\t * Plugin activation.
\t *
\t * @param AcceptanceTester \$i .
\t *
\t * @throws \\Codeception\\Exception\\ModuleException .
\t */
\tpublic function pluginActivation( \$i ) {
\t\t\$i->loginAsAdministrator();
\t\t\$i->amOnPluginsPage();
\t\t\$i->seePluginDeactivated( \$this->getPluginSlug() );
\t\t// This is an example and you should change it to current plugin.
\t\t\$i->activateWPDeskPlugin(
\t\t\t\$this->getPluginSlug(),
\t\t\tarray( 'woocommerce' ),
\t\t\tarray( 'The “WooCommerce Fakturownia” plugin cannot run without WooCommerce active. Please install and activate WooCommerce plugin.' )
\t\t);
\t}
}
EOF;
}

View File

@@ -0,0 +1,70 @@
<?php
namespace FcfVendor\WPDesk\Codeception\Command;
use FcfVendor\Codeception\Command\GenerateTest;
use FcfVendor\Codeception\CustomCommandInterface;
use FcfVendor\Symfony\Component\Console\Input\InputInterface;
use FcfVendor\Symfony\Component\Console\Output\OutputInterface;
/**
* Generates codeception example test for WP Desk plugin activation.
*
* @package WPDesk\Codeception\Command
*/
class GeneratePluginActivation extends \FcfVendor\Codeception\Command\GenerateTest implements \FcfVendor\Codeception\CustomCommandInterface
{
/**
* Get codeception command description.
*
* @return string
*/
public function getDescription()
{
return 'Generates plugin activation tests.';
}
/**
* Returns the name of the command.
*
* @return string
*/
public static function getCommandName()
{
return 'generate:activation';
}
/**
* Get generator class.
*
* @param array $config .
* @param string $class .
* @return AcceptanceTestGenerator
*/
protected function getGenerator($config, $class)
{
return new \FcfVendor\WPDesk\Codeception\Command\AcceptanceTestGenerator($config, $class);
}
/**
* Execute command.
*
* @param InputInterface $input
* @param OutputInterface $output
*
* @return void
*/
public function execute(\FcfVendor\Symfony\Component\Console\Input\InputInterface $input, \FcfVendor\Symfony\Component\Console\Output\OutputInterface $output)
{
$suite = $input->getArgument('suite');
$class = $input->getArgument('class');
$config = $this->getSuiteConfig($suite);
$className = $this->getShortClassName($class);
$path = $this->createDirectoryFor($config['path'], $class);
$filename = $this->completeSuffix($className, 'Cest');
$filename = $path . $filename;
$gen = $this->getGenerator($config, $class);
$res = $this->createFile($filename, $gen->produce());
if (!$res) {
$output->writeln("<error>Test {$filename} already exists</error>");
return;
}
$output->writeln("<info>Test was created in {$filename}</info>");
}
}

View File

@@ -0,0 +1,70 @@
<?php
namespace FcfVendor\WPDesk\Codeception\Command;
use FcfVendor\Codeception\Command\GenerateTest;
use FcfVendor\Codeception\CustomCommandInterface;
use FcfVendor\Symfony\Component\Console\Input\InputInterface;
use FcfVendor\Symfony\Component\Console\Output\OutputInterface;
/**
* Generates codeception example test for WP Desk plugin activation.
*
* @package WPDesk\Codeception\Command
*/
class GenerateWooCommerce extends \FcfVendor\Codeception\Command\GenerateTest implements \FcfVendor\Codeception\CustomCommandInterface
{
/**
* Get codeception command description.
*
* @return string
*/
public function getDescription()
{
return 'Generates woocommerce tests.';
}
/**
* Returns the name of the command.
*
* @return string
*/
public static function getCommandName()
{
return 'generate:woocommerce';
}
/**
* Get generator class.
*
* @param array $config .
* @param string $class .
* @return WooCommerceTestGenerator
*/
protected function getGenerator($config, $class)
{
return new \FcfVendor\WPDesk\Codeception\Command\WooCommerceTestGenerator($config, $class);
}
/**
* Execute command.
*
* @param InputInterface $input
* @param OutputInterface $output
*
* @return void
*/
public function execute(\FcfVendor\Symfony\Component\Console\Input\InputInterface $input, \FcfVendor\Symfony\Component\Console\Output\OutputInterface $output)
{
$suite = $input->getArgument('suite');
$class = $input->getArgument('class');
$config = $this->getSuiteConfig($suite);
$className = $this->getShortClassName($class);
$path = $this->createDirectoryFor($config['path'], $class);
$filename = $this->completeSuffix($className, 'Cest');
$filename = $path . $filename;
$gen = $this->getGenerator($config, $class);
$res = $this->createFile($filename, $gen->produce());
if (!$res) {
$output->writeln("<error>Test {$filename} already exists</error>");
return;
}
$output->writeln("<info>Test was created in {$filename}</info>");
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace FcfVendor\WPDesk\Codeception\Command;
use FcfVendor\Codeception\Lib\Generator\Test;
/**
* Class code for codeception example test for WP Desk plugin activation.
*
* @package WPDesk\Codeception\Command
*/
class WooCommerceTestGenerator extends \FcfVendor\Codeception\Lib\Generator\Test
{
protected $template = <<<EOF
<?php {{namespace}}
use WPDesk\\Codeception\\Tests\\Acceptance\\Cest\\AbstractCestForWooCommerce;
/**
* Common WooCommerce tests.
*/
class {{name}} extends AbstractCestForWooCommerce {
}
EOF;
}

View File

@@ -0,0 +1,23 @@
<?php
namespace FcfVendor\WPDesk\Composer\Codeception;
use FcfVendor\WPDesk\Composer\Codeception\Commands\CreateCodeceptionTests;
use FcfVendor\WPDesk\Composer\Codeception\Commands\PrepareCodeceptionDb;
use FcfVendor\WPDesk\Composer\Codeception\Commands\PrepareLocalCodeceptionTests;
use FcfVendor\WPDesk\Composer\Codeception\Commands\PrepareLocalCodeceptionTestsWithCoverage;
use FcfVendor\WPDesk\Composer\Codeception\Commands\PrepareParallelCodeceptionTests;
use FcfVendor\WPDesk\Composer\Codeception\Commands\PrepareWordpressForCodeception;
use FcfVendor\WPDesk\Composer\Codeception\Commands\RunCodeceptionTests;
use FcfVendor\WPDesk\Composer\Codeception\Commands\RunLocalCodeceptionTests;
use FcfVendor\WPDesk\Composer\Codeception\Commands\RunLocalCodeceptionTestsWithCoverage;
/**
* Links plugin commands handlers to composer.
*/
class CommandProvider implements \FcfVendor\Composer\Plugin\Capability\CommandProvider
{
public function getCommands()
{
return [new \FcfVendor\WPDesk\Composer\Codeception\Commands\CreateCodeceptionTests(), new \FcfVendor\WPDesk\Composer\Codeception\Commands\RunCodeceptionTests(), new \FcfVendor\WPDesk\Composer\Codeception\Commands\RunLocalCodeceptionTests(), new \FcfVendor\WPDesk\Composer\Codeception\Commands\RunLocalCodeceptionTestsWithCoverage(), new \FcfVendor\WPDesk\Composer\Codeception\Commands\PrepareCodeceptionDb(), new \FcfVendor\WPDesk\Composer\Codeception\Commands\PrepareWordpressForCodeception(), new \FcfVendor\WPDesk\Composer\Codeception\Commands\PrepareLocalCodeceptionTests(), new \FcfVendor\WPDesk\Composer\Codeception\Commands\PrepareLocalCodeceptionTestsWithCoverage(), new \FcfVendor\WPDesk\Composer\Codeception\Commands\PrepareParallelCodeceptionTests()];
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace FcfVendor\WPDesk\Composer\Codeception\Commands;
use FcfVendor\Composer\Command\BaseCommand as CodeceptionBaseCommand;
use FcfVendor\Symfony\Component\Console\Output\OutputInterface;
/**
* Base for commands - declares common methods.
*
* @package WPDesk\Composer\Codeception\Commands
*/
abstract class BaseCommand extends \FcfVendor\Composer\Command\BaseCommand
{
/**
* @param string $command
* @param OutputInterface $output
*/
protected function execAndOutput($command, \FcfVendor\Symfony\Component\Console\Output\OutputInterface $output)
{
\passthru($command);
}
}

View File

@@ -0,0 +1,375 @@
<?php
/**
* Class Env
* @package WPDesk\Composer\Codeception\Commands
*/
namespace FcfVendor\WPDesk\Composer\Codeception\Commands;
use FcfVendor\Dotenv\Dotenv;
/**
* Env.
*/
class Configuration
{
const MYSQL_IP = 'MYSQL_IP';
const MYSQL_DBNAME = 'MYSQL_DBNAME';
const MYSQL_DBUSER = 'MYSQL_DBUSER';
const MYSQL_DBPASSWORD = 'MYSQL_DBPASSWORD';
const APACHE_DOCUMENT_ROOT = 'APACHE_DOCUMENT_ROOT';
const WOOTESTS_IP = 'WOOTESTS_IP';
const DEPENDENT_PLUGINS_DIR = 'DEPENDENT_PLUGINS_DIR';
const TEST_SITE_WP_URL = 'TEST_SITE_WP_URL';
/**
* @var string
*/
private $apache_document_root;
/**
* @var string
*/
private $wptests_ip;
/**
* @var string
*/
private $wptests_url;
/**
* @var string
*/
private $dbhost;
/**
* @var string
*/
private $dbname;
/**
* @var string
*/
private $dbuser;
/**
* @var string
*/
private $dbpassword;
/**
* @var string
*/
private $dependent_plugins_dir;
/**
* @var string
*/
private $plugin_slug;
/**
* @var string
*/
private $plugin_dir;
/**
* @var string
*/
private $plugin_file;
/**
* @var string
*/
private $plugin_title;
/**
* @var string
*/
private $plugin_product_id;
/**
* @var array
*/
private $repository_plugins;
/**
* @var array
*/
private $local_plugins;
/**
* @var array
*/
private $activate_plugins;
/**
* @var array
*/
private $prepare_database;
/**
* @var array
*/
private $theme_files;
/**
* Configuration constructor.
*
* @param $apache_document_root
* @param $wptests_ip
* @param $wptests_url
* @param $dbhost
* @param $dbname
* @param $dbuser
* @param $dbpassword
* @param $dependent_plugins_dir
* @param $plugin_slug
* @param $plugin_dir
* @param $plugin_file
* @param $plugin_title
* @param $plugin_product_id
* @param $repository_plugins
* @param $local_plugins
* @param $activate_plugins
* @param $prepare_database
* @param $theme_files
*/
public function __construct($apache_document_root, $wptests_ip, $wptests_url, $dbhost, $dbname, $dbuser, $dbpassword, $dependent_plugins_dir, $plugin_slug, $plugin_dir, $plugin_file, $plugin_title, $plugin_product_id, $repository_plugins, $local_plugins, $activate_plugins, $prepare_database, $theme_files)
{
$this->apache_document_root = $apache_document_root;
$this->wptests_ip = $wptests_ip;
$this->wptests_url = $wptests_url;
$this->dbhost = $dbhost;
$this->dbname = $dbname;
$this->dbuser = $dbuser;
$this->dbpassword = $dbpassword;
$this->dependent_plugins_dir = $dependent_plugins_dir;
$this->plugin_slug = $plugin_slug;
$this->plugin_dir = $plugin_dir;
$this->plugin_file = $plugin_file;
$this->plugin_title = $plugin_title;
$this->plugin_product_id = $plugin_product_id;
$this->repository_plugins = $repository_plugins;
$this->local_plugins = $local_plugins;
$this->activate_plugins = $activate_plugins;
$this->prepare_database = $prepare_database;
$this->theme_files = $theme_files;
}
/**
* @return string
*/
public function getApacheDocumentRoot()
{
return $this->apache_document_root;
}
/**
* @return string
*/
public function getWptestsIp()
{
return $this->wptests_ip;
}
/**
* @return string
*/
public function getWptestsUrl()
{
return $this->wptests_url;
}
/**
* @return string
*/
public function getDbhost()
{
return $this->dbhost;
}
/**
* @return string
*/
public function getDbname()
{
return $this->dbname;
}
/**
* @return string
*/
public function getDbuser()
{
return $this->dbuser;
}
/**
* @return string
*/
public function getDbpassword()
{
return $this->dbpassword;
}
/**
* @return string
*/
public function getDependentPluginsDir()
{
return $this->dependent_plugins_dir;
}
/**
* @return string
*/
public function getPluginSlug()
{
return $this->plugin_slug;
}
/**
* @return string
*/
public function getPluginDir()
{
return $this->plugin_dir;
}
/**
* @return string
*/
public function getPluginFile()
{
return $this->plugin_file;
}
/**
* @return string
*/
public function getPluginTitle()
{
return $this->plugin_title;
}
/**
* @return string
*/
public function getPluginProductId()
{
return $this->plugin_product_id;
}
/**
* @return array
*/
public function getRepositoryPlugins()
{
return $this->repository_plugins;
}
/**
* @return array
*/
public function getLocalPlugins()
{
return $this->local_plugins;
}
/**
* @return array
*/
public function getActivatePlugins()
{
return $this->activate_plugins;
}
/**
* @return array
*/
public function getPrepareDatabase()
{
return $this->prepare_database;
}
/**
* @return array
*/
public function getThemeFiles()
{
return $this->theme_files;
}
/**
* Set env variables from configuration.
*/
public function prepareEnvForConfiguration()
{
$this->putEnv('WPDESK_PLUGIN_SLUG', $this->getPluginSlug());
$this->putEnv('WPDESK_PLUGIN_FILE', $this->getPluginFile());
$this->putEnv('WPDESK_PLUGIN_TITLE', $this->getPluginTitle());
$this->putEnv('WPDESK_PLUGIN_PRODUCT_ID', $this->getPluginProductId());
}
/**
* @param string $env_variable
* @param string $value
*
* @return string
*/
private function putEnv($env_variable, $value)
{
\putenv($env_variable . '=' . $value);
}
/**
* @param array $configuration .
*
* @return Configuration
*/
public static function createFromEnvAndConfiguration(array $configuration)
{
$dotenv = \FcfVendor\Dotenv\Dotenv::createImmutable(\getcwd() . '/../');
$dotenv->safeLoad();
$apache_document_root = self::prepareFromEnv(self::APACHE_DOCUMENT_ROOT, self::prepareApacheDocumentRoot());
$wptests_ip = self::prepareFromEnv(self::WOOTESTS_IP, 'wptests.lh');
$dbhost = self::prepareFromEnv(self::MYSQL_IP, 'mysqltests');
$dbname = self::prepareFromEnv(self::MYSQL_DBNAME, 'wptest');
$dbuser = self::prepareFromEnv(self::MYSQL_DBUSER, 'mysql');
$dbpassword = self::prepareFromEnv(self::MYSQL_DBPASSWORD, 'mysql');
$wptest_url = self::prepareFromEnv(self::TEST_SITE_WP_URL, 'http://wptests.lh');
$dependent_plugins_dir = self::prepareFromEnv(self::DEPENDENT_PLUGINS_DIR, '../');
if (isset($configuration['plugin-slug'])) {
$plugin_slug = $configuration['plugin-slug'];
} else {
throw new \FcfVendor\WPDesk\Composer\Codeception\Commands\SettingsException('Missing plugin-slug setting!');
}
if (isset($configuration['plugin-file'])) {
$plugin_file = $configuration['plugin-file'];
} else {
throw new \FcfVendor\WPDesk\Composer\Codeception\Commands\SettingsException('Missing plugin-file setting!');
}
$plugin_file_exploded = \explode('/', $plugin_file);
$plugin_dir = $plugin_file_exploded[0];
if (isset($configuration['plugin-title'])) {
$plugin_title = $configuration['plugin-title'];
} else {
throw new \FcfVendor\WPDesk\Composer\Codeception\Commands\SettingsException('Missing plugin-title setting!');
}
if (isset($configuration['plugin-product-id'])) {
$plugin_product_id = $configuration['plugin-product-id'];
} else {
$plugin_product_id = '';
}
$prepare_database = array();
if (isset($configuration['prepare-database']) && \is_array($configuration['prepare-database'])) {
$prepare_database = $configuration['prepare-database'];
}
$theme_files = array();
if (isset($configuration['theme-files']) && \is_array($configuration['theme-files'])) {
$theme_files = $configuration['theme-files'];
}
$repository_plugins = self::getPluginsSettings($configuration, 'repository');
$local_plugins = self::getPluginsSettings($configuration, 'local');
$activate_plugins = self::getPluginsSettings($configuration, 'activate');
return new self($apache_document_root, $wptests_ip, $wptest_url, $dbhost, $dbname, $dbuser, $dbpassword, $dependent_plugins_dir, $plugin_slug, $plugin_dir, $plugin_file, $plugin_title, $plugin_product_id, $repository_plugins, $local_plugins, $activate_plugins, $prepare_database, $theme_files);
}
/**
* @param string $env_variable .
* @param string $default_value .
*
* @return string
*/
private static function prepareFromEnv($env_variable, $default_value)
{
$value = \getenv($env_variable);
$value = $value ? $value : $default_value;
return $value;
}
/**
* @return string
*/
private static function prepareApacheDocumentRoot()
{
return self::isWindows() ? 'c:\\xampp\\htdocs\\wptests' : '/tmp/wptests';
}
/**
* @return bool
*/
public static function isWindows()
{
return \false !== \stristr(\PHP_OS, 'WIN') && \false === \stristr(\PHP_OS, 'DARWIN');
}
/**
* @param array $configuration .
* @param string $plugins_section .
*
* @return array
*/
private static function getPluginsSettings(array $configuration, $plugins_section)
{
if (\is_array($configuration) && isset($configuration['plugins'], $configuration['plugins'][$plugins_section]) && \is_array($configuration['plugins'][$plugins_section])) {
return $configuration['plugins'][$plugins_section];
}
return array();
}
}

View File

@@ -0,0 +1,116 @@
<?php
namespace FcfVendor\WPDesk\Composer\Codeception\Commands;
use FcfVendor\Composer\Downloader\FilesystemException;
use FcfVendor\Symfony\Component\Console\Input\InputInterface;
use FcfVendor\Symfony\Component\Console\Output\OutputInterface;
/**
* Codeception tests creator command.
*
* @package WPDesk\Composer\Codeception\Commands
*/
class CreateCodeceptionTests extends \FcfVendor\WPDesk\Composer\Codeception\Commands\BaseCommand
{
use SedTrait;
/**
* Configure command.
*/
protected function configure()
{
parent::configure();
$this->setName('create-codeception-tests')->setDescription('Create codeception tests directories and files.');
}
/**
* Copy file.
*
* @param string $source
* @param string $dest
* @param string $exceptionMessage
* @throws FilesystemException
*/
private function copy($source, $dest, $exceptionMessage)
{
if (!\copy($source, $dest)) {
throw new \FcfVendor\Composer\Downloader\FilesystemException($exceptionMessage);
}
}
/**
* Copy configuration files.
*
* @param $codeceptionDir
* @param $testsDir
* @param $codeceptionYml
* @param $envConfig
* @param $acceptanceYml
* @param $bootstrapScript
* @return void
* @throws FilesystemException
*/
private function copyConfigurationFiles($codeceptionDir, $testsDir, $codeceptionYml, $envConfig, $acceptanceYml, $bootstrapScript)
{
if (!\file_exists('./' . $codeceptionYml)) {
$this->copy('./vendor/wpdesk/wp-codeception/configuration/' . $codeceptionYml, './' . $codeceptionYml, 'Error copying codeception configuration file!');
}
if (!\file_exists('./' . $envConfig)) {
$this->copy('./vendor/wpdesk/wp-codeception/configuration/' . $envConfig, './' . $envConfig, 'Error copying codeception env configuration file!');
}
if (\file_exists($testsDir . '/' . $acceptanceYml)) {
\unlink($testsDir . '/' . $acceptanceYml);
}
$this->copy('./vendor/wpdesk/wp-codeception/configuration/' . $acceptanceYml, $testsDir . '/' . $acceptanceYml, 'Error copying codeception acceptance configuration file!');
if (!\file_exists($codeceptionDir . '/' . $bootstrapScript)) {
$this->copy('./vendor/wpdesk/wp-codeception/scripts/' . $bootstrapScript, $codeceptionDir . '/' . $bootstrapScript, 'Error copying codeception bootstrap script file!');
}
if (!@\file_exists($testsDir . '/_output')) {
\mkdir($testsDir . '/_output', 0777, \true);
}
if (!\file_exists($testsDir . '/_output/.gitignore')) {
$this->copy('./vendor/wpdesk/wp-codeception/configuration/_output.gitignore', $testsDir . '/_output/.gitignore', 'Error copying codeception acceptance output .gitignore file!');
}
if (!@\file_exists($testsDir . '/_support/_generated')) {
\mkdir($testsDir . '/_support/_generated', 0777, \true);
}
if (!\file_exists($testsDir . '/_support/_generated/.gitignore')) {
$this->copy('./vendor/wpdesk/wp-codeception/configuration/_generated.gitignore', $testsDir . '/_support/_generated/.gitignore', 'Error copying codeception acceptance output .gitignore file!');
}
}
/**
* Inject traits into tester class.
*
* @param string $testsDir
* @return void
*/
private function injectTraitsIntoTesterClass($testsDir)
{
$file_pattern = $testsDir . '/_support/AcceptanceTester.php';
$pattern = "/use _generated\\\\AcceptanceTesterActions;/";
$replace = "use _generated\\AcceptanceTesterActions;\n" . "\n\tuse \\WPDesk\\Codeception\\Tests\\Acceptance\\Tester\\TesterWordpressActions;" . "\n\tuse \\WPDesk\\Codeception\\Tests\\Acceptance\\Tester\\TesterWooCommerceActions;" . "\n\tuse \\WPDesk\\Codeception\\Tests\\Acceptance\\Tester\\TesterWPDeskActions;";
$this->wpdeskSed($file_pattern, $pattern, $replace);
}
/**
* Execute command.
*
* @param InputInterface $input
* @param OutputInterface $output
* @return void
* @throws FilesystemException
*/
protected function execute(\FcfVendor\Symfony\Component\Console\Input\InputInterface $input, \FcfVendor\Symfony\Component\Console\Output\OutputInterface $output)
{
$codeceptionDir = './tests/codeception';
$testsDir = $codeceptionDir . '/tests';
$codeceptionYml = 'codeception.dist.yml';
$envConfig = '.env.testing';
$acceptanceYml = 'acceptance.suite.yml';
$bootstrapScript = 'bootstrap.sh';
if (!@\file_exists($testsDir)) {
\mkdir($testsDir, 0777, \true);
}
$this->copyConfigurationFiles($codeceptionDir, $testsDir, $codeceptionYml, $envConfig, $acceptanceYml, $bootstrapScript);
$this->execAndOutput('./vendor/bin/codecept bootstrap ' . $codeceptionDir, $output);
$this->execAndOutput('./vendor/bin/codecept generate:activation acceptance ActivationCest', $output);
$this->execAndOutput('./vendor/bin/codecept generate:woocommerce acceptance WooCommerceCest', $output);
$this->injectTraitsIntoTesterClass($testsDir);
}
}

View File

@@ -0,0 +1,273 @@
<?php
namespace FcfVendor\WPDesk\Composer\Codeception\Commands;
use FcfVendor\Composer\Downloader\FilesystemException;
use FcfVendor\Symfony\Component\Console\Input\InputInterface;
use FcfVendor\Symfony\Component\Console\Output\OutputInterface;
use FcfVendor\Symfony\Component\Yaml\Exception\ParseException;
use FcfVendor\Symfony\Component\Yaml\Yaml;
/**
* Common methods for local Codeception tests.
*/
trait LocalCodeceptionTrait
{
private function getWpDeskConfiguration()
{
try {
$wpdesk_configuration = \FcfVendor\Symfony\Component\Yaml\Yaml::parseFile(\getcwd() . '/tests/codeception/wpdesk.yml');
} catch (\FcfVendor\Symfony\Component\Yaml\Exception\ParseException $e) {
$wpdesk_configuration = array();
}
return \FcfVendor\WPDesk\Composer\Codeception\Commands\Configuration::createFromEnvAndConfiguration($wpdesk_configuration);
}
/**
* @param OutputInterface $output
* @param Configuration $configuration
*/
private function prepareWpConfig(\FcfVendor\Symfony\Component\Console\Output\OutputInterface $output, \FcfVendor\WPDesk\Composer\Codeception\Commands\Configuration $configuration)
{
$apache_document_root = $configuration->getApacheDocumentRoot();
$this->executeWpCliAndOutput('config set WP_DEBUG true --raw', $output, $apache_document_root);
$this->executeWpCliAndOutput('config set WP_DEBUG_LOG true --raw', $output, $apache_document_root);
$this->executeWpCliAndOutput('config set WP_DEBUG_DISPLAY false --raw', $output, $apache_document_root);
$this->executeWpCliAndOutput('config set WP_HOME ' . $configuration->getWptestsUrl(), $output, $apache_document_root);
$this->executeWpCliAndOutput('config set WP_SITEURL ' . $configuration->getWptestsUrl(), $output, $apache_document_root);
$this->executeWpCliAndOutput('config set WP_AUTO_UPDATE_CORE false --raw', $output, $apache_document_root);
$this->executeWpCliAndOutput('config set AUTOMATIC_UPDATER_DISABLED false --raw', $output, $apache_document_root);
$this->executeWpCliAndOutput('rewrite structure \'/%postname%/\'', $output, $apache_document_root);
$this->replace_in_file($apache_document_root . '/wp-config.php', 'if ( isset( $_SERVER[\'HTTP_X_FORWARDED_PROTO\'] ) && \'https\' === $_SERVER[\'HTTP_X_FORWARDED_PROTO\'] ) { $_SERVER[\'HTTPS\'] = \'on\'; }', '');
$this->replace_in_file($apache_document_root . '/wp-config.php', '<?php', '<?php if ( isset( $_SERVER[\'HTTP_X_FORWARDED_PROTO\'] ) && \'https\' === $_SERVER[\'HTTP_X_FORWARDED_PROTO\'] ) { $_SERVER[\'HTTPS\'] = \'on\'; }');
}
/**
* @param string $filename
* @param string $string_to_replace
* @param string $replace_with
*
* @return void
*/
private function replace_in_file($filename, $string_to_replace, $replace_with)
{
$content = \file_get_contents($filename);
$content_chunks = \explode($string_to_replace, $content);
$content = \implode($replace_with, $content_chunks);
\file_put_contents($filename, $content);
}
/**
* @param OutputInterface $output
* @param Configuration $configuration
*/
private function activatePlugins(\FcfVendor\Symfony\Component\Console\Output\OutputInterface $output, \FcfVendor\WPDesk\Composer\Codeception\Commands\Configuration $configuration)
{
$this->executeWpCliAndOutput('plugin deactivate --all', $output, $configuration->getApacheDocumentRoot());
$plugins = '';
foreach ($configuration->getRepositoryPlugins() as $plugin) {
$plugins .= ' ' . $plugin;
}
if ($plugins) {
$this->executeWpCliAndOutput('plugin install ' . $plugins, $output, $configuration->getApacheDocumentRoot());
}
foreach ($configuration->getLocalPlugins() as $plugin) {
$source = $this->preparePathForRsync($this->prepareLocalPluginDir($plugin, $configuration->getDependentPluginsDir()) . '/*', $configuration::isWindows());
$target = $this->preparePathForRsync($this->prepareTargetDir($plugin, $configuration) . '/', $configuration::isWindows());
$rsync = 'rsync -a ' . $source . ' ' . $target . ' --exclude=node_modules --exclude=.git --exclude=tests --exclude=.idea';
$output->writeln($rsync);
$this->execAndOutput($rsync, $output);
}
$this->executeWpCliAndOutput('plugin status', $output, $configuration->getApacheDocumentRoot());
$this->executeWpCliAndOutput('plugin list', $output, $configuration->getApacheDocumentRoot());
foreach ($configuration->getActivatePlugins() as $plugin) {
$this->executeWpCliAndOutput('plugin activate ' . $plugin, $output, $configuration->getApacheDocumentRoot());
}
$this->executeWpCliAndOutput('plugin activate ' . $configuration->getPluginDir(), $output, $configuration->getApacheDocumentRoot());
}
/**
* @param string $plugin .
*/
private function prepareLocalPluginDir($plugin, $local_plugins_dir = \false)
{
if (!$local_plugins_dir) {
$local_plugins_dir = \dirname(\getcwd());
}
return $this->trailingslashit($local_plugins_dir) . $plugin;
}
/**
* @param $string
*
* @return string
*/
private function trailingslashit($string)
{
return \rtrim($string, '/\\') . '/';
}
/**
* @param string $command
* @param OutputInterface $output
* @param string $apache_document_root
*/
private function executeWpCliAndOutput($command, \FcfVendor\Symfony\Component\Console\Output\OutputInterface $output, $apache_document_root)
{
$output->write("WPCLI: {$command}\n");
$wp = "wp";
$command = $wp . ' ' . $command . ' --allow-root --path=' . $apache_document_root;
$this->execAndOutput($command, $output);
}
/**
* @param string $plugin_dir
* @param OutputInterface $output
* @param Configuration $configuration
* @param bool $coverage
*/
private function installPlugin(string $plugin_dir, \FcfVendor\Symfony\Component\Console\Output\OutputInterface $output, \FcfVendor\WPDesk\Composer\Codeception\Commands\Configuration $configuration, bool $coverage = \true)
{
$source = $this->preparePathForRsync(\getcwd() . '/*', $configuration::isWindows());
$target = $this->preparePathForRsync($this->prepareTargetDir($plugin_dir, $configuration) . '/', $configuration::isWindows());
$rsync = 'rsync -av ' . $source . ' ' . $target . ' --exclude=node_modules --exclude=.git --exclude=.idea --exclude=vendor --exclude=vendor_prefixed --exclude=tests/wordpress ';
$this->execAndOutput($rsync, $output);
\copy(\getcwd() . '/.env.testing', $this->prepareTargetDir($plugin_dir, $configuration) . '/.env.testing');
if (!$coverage) {
$this->execAndOutput('composer install --working-dir=' . $configuration->getApacheDocumentRoot() . '/wp-content/plugins/' . $plugin_dir, $output);
$this->execAndOutput('composer install --no-dev --working-dir=' . $configuration->getApacheDocumentRoot() . '/wp-content/plugins/' . $plugin_dir, $output);
} else {
$this->execAndOutput('composer require --dev codeception/c3 --working-dir=' . $configuration->getApacheDocumentRoot() . '/wp-content/plugins/' . $plugin_dir, $output);
}
}
/**
* @param string $path
* @param bool $is_windows
*
* @return string
*/
private function preparePathForRsync($path, $is_windows)
{
if ($is_windows) {
$path = '/cygdrive/' . $path;
$path = \str_replace(':', '', $path);
$path = \str_replace('\\', '/', $path);
}
return $path;
}
/**
* @param string $plugin_slug
* @param Configuration $configuration
*
* @return string
*/
private function prepareTargetDir($plugin_slug, \FcfVendor\WPDesk\Composer\Codeception\Commands\Configuration $configuration)
{
return $configuration->getApacheDocumentRoot() . '/wp-content/plugins/' . $plugin_slug;
}
/**
* @param Configuration $configuration
* @param OutputInterface $output
*/
private function prepareCommonWpWcConfiguration(\FcfVendor\WPDesk\Composer\Codeception\Commands\Configuration $configuration, \FcfVendor\Symfony\Component\Console\Output\OutputInterface $output)
{
$this->executeWpCliAndOutput('db reset --yes', $output, $configuration->getApacheDocumentRoot());
$this->executeWpCliAndOutput('core install --url=' . $configuration->getWptestsIp() . ' --title=Woo-tests --admin_user=admin --admin_password=admin --admin_email=grola@seostudio.pl --skip-email', $output, $configuration->getApacheDocumentRoot());
$commands = array('theme activate storefront-wpdesk-tests', 'plugin activate woocommerce');
$commands = \array_merge($commands, $this->prepareWcOptionsCommands(), $this->prepareTaxes(), $this->prepareShippingMethods(), $this->prepareWooCommercePages(), $this->prepareCustomer(), $this->prepareDisableRESTApiPermissions(), $this->prepareCreateProductsCommands(), $configuration->getPrepareDatabase());
foreach ($commands as $command) {
$this->executeWpCliAndOutput($command, $output, $configuration->getApacheDocumentRoot());
}
}
/**
* @return array
*/
private function prepareWcOptionsCommands()
{
return array('option update woocommerce_admin_notices \'{}\'', 'option update storefront_nux_dismissed 1', 'option set woocommerce_store_address "al. Jana Pawła 12"', 'option set woocommerce_store_address_2 ""', 'option set woocommerce_store_city "Warszawa"', 'option set woocommerce_default_country "PL"', 'option set woocommerce_store_postalcode "22-100"', 'option set woocommerce_currency "PLN"', 'option set woocommerce_currency_pos "right_space"', 'option set woocommerce_product_type "physical"', 'option set woocommerce_allow_tracking "no"', 'option set --format=json woocommerce_stripe_settings \'{"enabled":"no","create_account":false,"email":false}\'', 'option set --format=json woocommerce_ppec_paypal_settings \'{"reroute_requests":false,"email":false}\'', 'option set --format=json woocommerce_cheque_settings \'{"enabled":"no"}\'', 'option set --format=json woocommerce_bacs_settings \'{"enabled":"no"}\'', 'option set --format=json woocommerce_cod_settings \'{"enabled":"yes"}\'', 'option set --format=json woocommerce_onboarding_profile \'{"skipped":true}\'', 'option set --format=json wc-admin-onboarding-profiler-reminder \'{"skipped":true}\'', 'option get woocommerce_onboarding_profile', 'option set woocommerce_task_list_hidden "yes"', 'option set woocommerce_task_list_hidden "yes"', 'option set woocommerce_show_marketplace_suggestions "no"');
}
/**
* @return string[]
*/
private function prepareTaxes()
{
return array('wc tax create --country="PL" --rate=23 --name=VAT --shipping=true --user=admin', 'option set woocommerce_calc_taxes "yes"');
}
/**
* @return string[]
*/
private function prepareShippingMethods()
{
return array('wc shipping_zone_method create 0 --method_id="flat_rate" --settings=\'{"title": "Flat rate", "cost":1, "tax_status": "taxable"}\' --enabled=true --user=admin');
}
/**
* @return string[]
*/
private function prepareWooCommercePages()
{
return array('wc --user=admin tool run install_pages');
}
/**
* @return string[]
*/
private function prepareCustomer()
{
return array('wc customer create --email=\'customer@woo.local\' --username="customer" --billing=\'{"first_name":"First","last_name":"Last","company":"WPDesk","address_1":"Street 1","city":"City","postcode": "53-030", "country": "PL", "phone": "012345678"}\' --password=\'customer\' --user=admin');
}
/**
* @return string[]
*/
private function prepareDisableRESTApiPermissions()
{
return array('option set wpdesk_rest_api_disable_permissions "1"');
}
/**
* @return array
*/
private function prepareCreateProductsCommands()
{
return array($this->prepareCreateProductCommand('100', '100'), $this->prepareCreateProductCommand('10', '10'), $this->prepareCreateProductCommand('9', '9'), $this->prepareCreateProductCommand('1', '1'), $this->prepareCreateProductCommand('09', '0.9'), $this->prepareCreateProductCommand('009', '0.09'), $this->prepareCreateProductCommand('01', '0.1'), $this->prepareCreateProductCommand('001', '0.01'), $this->prepareCreateProductCommand('0001', '0.001'), $this->prepareCreateProductCommand('00001', '0.0001'));
}
/**
* @param string $name
* @param string $price
* @param null|string $weight
* @param null|string $sku
* @param null|string $dimensions
*
* @return string
*/
private function prepareCreateProductCommand($name, $price, $weight = null, $sku = null, $dimensions = null)
{
$product_name = "Product {$name}";
$weight = $weight ? $weight : $price;
$sku = $sku ? $sku : 'product-' . $name;
$dimensions = $dimensions ? $dimensions : '{"width":"' . $price . '","length":"' . $price . '","height":"' . $price . '"}';
return "wc product create --name=\"{$product_name}\" --virtual=false --downloadable=false --type=simple --sku={$sku} --regular_price={$price} --weight={$weight} --dimensions='{$dimensions}' --user=admin";
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @param bool $coverage
*
* @throws \Composer\Downloader\FilesystemException
*/
private function prepareLocalCodeceptionTests(\FcfVendor\Symfony\Component\Console\Input\InputInterface $input, \FcfVendor\Symfony\Component\Console\Output\OutputInterface $output, bool $coverage = \false)
{
$configuration = $this->getWpDeskConfiguration();
$this->installPlugin($configuration->getPluginDir(), $output, $configuration, $coverage);
$this->activatePlugins($output, $configuration);
$this->prepareWpConfig($output, $configuration);
$this->copyThemeFiles($configuration->getThemeFiles(), $configuration->getApacheDocumentRoot() . '/wp-content/themes/storefront-wpdesk-tests');
$sep = \DIRECTORY_SEPARATOR;
$codecept = "vendor{$sep}bin{$sep}codecept";
$cleanOutput = $codecept . ' clean';
$this->execAndOutput($cleanOutput, $output);
}
/**
* @param array $theme_files
* @param $theme_folder
*
* @throws FilesystemException
*/
private function copyThemeFiles(array $theme_files, $theme_folder)
{
foreach ($theme_files as $theme_file) {
if (!\copy($theme_file, $this->trailingslashit($theme_folder) . \basename($theme_file))) {
throw new \FcfVendor\Composer\Downloader\FilesystemException('Error copying theme file: ' . $theme_file);
}
}
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace FcfVendor\WPDesk\Composer\Codeception\Commands;
use FcfVendor\Symfony\Component\Console\Input\InputArgument;
use FcfVendor\Symfony\Component\Console\Input\InputInterface;
use FcfVendor\Symfony\Component\Console\Output\OutputInterface;
use FcfVendor\Symfony\Component\Yaml\Exception\ParseException;
use FcfVendor\Symfony\Component\Yaml\Yaml;
/**
* Prepare Database for Codeception tests command.
*
* @package WPDesk\Composer\Codeception\Commands
*/
class PrepareCodeceptionDb extends \FcfVendor\WPDesk\Composer\Codeception\Commands\BaseCommand
{
use LocalCodeceptionTrait;
/**
* Configure command.
*/
protected function configure()
{
parent::configure();
$this->setName('prepare-codeception-db')->setDescription('Prepare codeception database.');
}
/**
* Execute command.
*
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int 0 if everything went fine, or an error code
*/
protected function execute(\FcfVendor\Symfony\Component\Console\Input\InputInterface $input, \FcfVendor\Symfony\Component\Console\Output\OutputInterface $output)
{
$configuration = $this->getWpDeskConfiguration();
$this->installPlugin($configuration->getPluginDir(), $output, $configuration);
$this->prepareCommonWpWcConfiguration($configuration, $output);
$this->prepareWpConfig($output, $configuration);
$this->activatePlugins($output, $configuration);
$this->executeWpCliAndOutput('db export ' . \getcwd() . '/tests/codeception/tests/_data/db.sql', $output, $configuration->getApacheDocumentRoot());
return 0;
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace FcfVendor\WPDesk\Composer\Codeception\Commands;
use FcfVendor\Composer\Downloader\FilesystemException;
use FcfVendor\Symfony\Component\Console\Input\InputArgument;
use FcfVendor\Symfony\Component\Console\Input\InputInterface;
use FcfVendor\Symfony\Component\Console\Output\OutputInterface;
use FcfVendor\Symfony\Component\Yaml\Exception\ParseException;
use FcfVendor\Symfony\Component\Yaml\Yaml;
/**
* Codeception tests run command.
*
* @package WPDesk\Composer\Codeception\Commands
*/
class PrepareLocalCodeceptionTests extends \FcfVendor\WPDesk\Composer\Codeception\Commands\RunCodeceptionTests
{
use LocalCodeceptionTrait;
/**
* Configure command.
*/
protected function configure()
{
parent::configure();
$this->setName('prepare-local-codeception-tests')->setDescription('Prepare local codeception tests.');
}
/**
* Execute command.
*
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int 0 if everything went fine, or an error code
*/
protected function execute(\FcfVendor\Symfony\Component\Console\Input\InputInterface $input, \FcfVendor\Symfony\Component\Console\Output\OutputInterface $output)
{
$this->prepareLocalCodeceptionTests($input, $output, \false);
return 0;
}
/**
* @param array $theme_files
* @param $theme_folder
*
* @throws FilesystemException
*/
private function copyThemeFiles(array $theme_files, $theme_folder)
{
foreach ($theme_files as $theme_file) {
if (!\copy($theme_file, $this->trailingslashit($theme_folder) . \basename($theme_file))) {
throw new \FcfVendor\Composer\Downloader\FilesystemException('Error copying theme file: ' . $theme_file);
}
}
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace FcfVendor\WPDesk\Composer\Codeception\Commands;
use FcfVendor\Composer\Downloader\FilesystemException;
use FcfVendor\Symfony\Component\Console\Input\InputArgument;
use FcfVendor\Symfony\Component\Console\Input\InputInterface;
use FcfVendor\Symfony\Component\Console\Output\OutputInterface;
use FcfVendor\Symfony\Component\Yaml\Exception\ParseException;
use FcfVendor\Symfony\Component\Yaml\Yaml;
/**
* Codeception tests run command.
*
* @package WPDesk\Composer\Codeception\Commands
*/
class PrepareLocalCodeceptionTestsWithCoverage extends \FcfVendor\WPDesk\Composer\Codeception\Commands\RunCodeceptionTests
{
use LocalCodeceptionTrait;
/**
* Configure command.
*/
protected function configure()
{
parent::configure();
$this->setName('prepare-local-codeception-tests-with-coverage')->setDescription('Prepare local codeception tests.');
}
/**
* Execute command.
*
* @param InputInterface $input
* @param OutputInterface $output
* @return void
*/
protected function execute(\FcfVendor\Symfony\Component\Console\Input\InputInterface $input, \FcfVendor\Symfony\Component\Console\Output\OutputInterface $output)
{
$this->prepareLocalCodeceptionTests($input, $output, \true);
$configuration = $this->getWpDeskConfiguration();
$plugin_file = $configuration->getApacheDocumentRoot() . '/wp-content/plugins/' . $configuration->getPluginFile();
\file_put_contents($plugin_file, "\ndefine('C3_CODECOVERAGE_ERROR_LOG_FILE', '/tmp/c3_error.log'); include __DIR__ . '/c3.php';", \FILE_APPEND);
}
}

View File

@@ -0,0 +1,57 @@
<?php
namespace FcfVendor\WPDesk\Composer\Codeception\Commands;
use FcfVendor\Symfony\Component\Console\Input\InputArgument;
use FcfVendor\Symfony\Component\Console\Input\InputInterface;
use FcfVendor\Symfony\Component\Console\Output\OutputInterface;
use FcfVendor\Symfony\Component\Yaml\Exception\ParseException;
use FcfVendor\Symfony\Component\Yaml\Yaml;
/**
* Split test to multiple directories for parallel running in CI.
*
* @package WPDesk\Composer\Codeception\Commands
*/
class PrepareParallelCodeceptionTests extends \FcfVendor\WPDesk\Composer\Codeception\Commands\BaseCommand
{
const NUMBER_OF_JOBS = 'number_of_jobs';
/**
* Configure command.
*/
protected function configure()
{
parent::configure();
$this->setName('prepare-parallel-codeception-tests')->setDescription('Prepare parallel codeception tests.')->setDefinition(array(new \FcfVendor\Symfony\Component\Console\Input\InputArgument(self::NUMBER_OF_JOBS, \FcfVendor\Symfony\Component\Console\Input\InputArgument::OPTIONAL, 'Number of jobs.', '4')));
}
/**
* Execute command.
*
* @param InputInterface $input
* @param OutputInterface $output
* @return void
*/
protected function execute(\FcfVendor\Symfony\Component\Console\Input\InputInterface $input, \FcfVendor\Symfony\Component\Console\Output\OutputInterface $output)
{
$numberOfJobs = (int) $input->getArgument(self::NUMBER_OF_JOBS);
$acceptanceTestsDir = \getcwd() . '/tests/codeception/tests/acceptance';
for ($i = 1; $i <= $numberOfJobs; $i++) {
$parallelDir = $acceptanceTestsDir . '/' . $i;
if (!\file_exists($parallelDir)) {
\mkdir($parallelDir);
}
}
$currentIndex = 1;
$files = \scandir($acceptanceTestsDir);
foreach ($files as $fileName) {
$fileFullPath = $acceptanceTestsDir . '/' . $fileName;
if (!\is_dir($fileFullPath)) {
$targetPath = $acceptanceTestsDir . '/' . $currentIndex . '/' . $fileName;
\copy($fileFullPath, $targetPath);
$currentIndex++;
if ($currentIndex > $numberOfJobs) {
$currentIndex = 1;
}
}
}
}
}

View File

@@ -0,0 +1,68 @@
<?php
namespace FcfVendor\WPDesk\Composer\Codeception\Commands;
use FcfVendor\Symfony\Component\Console\Input\InputArgument;
use FcfVendor\Symfony\Component\Console\Input\InputInterface;
use FcfVendor\Symfony\Component\Console\Output\OutputInterface;
use FcfVendor\Symfony\Component\Yaml\Exception\ParseException;
use FcfVendor\Symfony\Component\Yaml\Yaml;
/**
* Prepare Database for Codeception tests command.
*
* @package WPDesk\Composer\Codeception\Commands
*/
class PrepareWordpressForCodeception extends \FcfVendor\WPDesk\Composer\Codeception\Commands\BaseCommand
{
use LocalCodeceptionTrait;
/**
* Configure command.
*/
protected function configure()
{
parent::configure();
$this->setName('prepare-wordpress-for-codeception')->setDescription('Prepare wordpress installation for codeception tests.');
}
/**
* Execute command.
*
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int 0 if everything went fine, or an error code
*/
protected function execute(\FcfVendor\Symfony\Component\Console\Input\InputInterface $input, \FcfVendor\Symfony\Component\Console\Output\OutputInterface $output)
{
$configuration = $this->getWpDeskConfiguration();
$this->installWP($output, $configuration);
return 0;
}
/**
* @param OutputInterface $output
* @param Configuration $configuration
*/
private function installWP(\FcfVendor\Symfony\Component\Console\Output\OutputInterface $output, \FcfVendor\WPDesk\Composer\Codeception\Commands\Configuration $configuration)
{
if (!\file_exists($configuration->getApacheDocumentRoot())) {
\mkdir($configuration->getApacheDocumentRoot(), 0777, \true);
}
$dbhost = $configuration->getDbhost();
$dbname = $configuration->getDbname();
$dbuser = $configuration->getDbuser();
$dbpassword = $configuration->getDbpassword();
$url = $configuration->getWptestsIp();
$apache_document_root = $configuration->getApacheDocumentRoot();
$this->executeWpCliAndOutput('core download --force', $output, $apache_document_root);
$this->executeWpCliAndOutput("core config --dbhost={$dbhost} --dbname={$dbname} --dbuser={$dbuser} --dbpass={$dbpassword}", $output, $apache_document_root);
$this->executeWpCliAndOutput("core install --url={$url} --title=Woo-tests --admin_user=admin --admin_password=admin --admin_email=tests@wpdesk.dev --skip-email", $output, $apache_document_root);
\copy('./vendor/wpdesk/wp-codeception/wordpress/htaccess', $apache_document_root . '/.htaccess');
$this->executeWpCliAndOutput('rewrite structure \'/%postname%/\'', $output, $apache_document_root);
$this->executeWpCliAndOutput('plugin install woocommerce', $output, $apache_document_root);
$this->executeWpCliAndOutput('theme activate storefront', $output, $apache_document_root);
$this->executeWpCliAndOutput('theme delete storefront-wpdesk-tests', $output, $apache_document_root);
$this->executeWpCliAndOutput('theme install storefront', $output, $apache_document_root);
$this->executeWpCliAndOutput('theme install https://gitlab.com/wpdesk/storefront-wpdesk-tests/-/archive/master/storefront-wpdesk-tests-master.zip', $output, $apache_document_root);
\rename($apache_document_root . '/wp-content/themes/storefront-wpdesk-tests-master', $apache_document_root . '/wp-content/themes/storefront-wpdesk-tests');
$this->executeWpCliAndOutput('theme activate storefront-wpdesk-tests', $output, $apache_document_root);
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace FcfVendor\WPDesk\Composer\Codeception\Commands;
use FcfVendor\Symfony\Component\Console\Input\InputArgument;
use FcfVendor\Symfony\Component\Console\Input\InputInterface;
use FcfVendor\Symfony\Component\Console\Output\OutputInterface;
/**
* Codeception tests run command.
*
* @package WPDesk\Composer\Codeception\Commands
*/
class RunCodeceptionTests extends \FcfVendor\WPDesk\Composer\Codeception\Commands\BaseCommand
{
const SINGLE = 'single';
const FAST = 'fast';
const WOOCOMMERCE_VERSION = 'woo_version';
/**
* Configure command.
*/
protected function configure()
{
parent::configure();
$this->setName('run-codeception-tests')->setDescription('Run codeception tests.')->setDefinition(array(new \FcfVendor\Symfony\Component\Console\Input\InputArgument(self::SINGLE, \FcfVendor\Symfony\Component\Console\Input\InputArgument::OPTIONAL, 'Name of Single test to run.', 'all'), new \FcfVendor\Symfony\Component\Console\Input\InputArgument(self::FAST, \FcfVendor\Symfony\Component\Console\Input\InputArgument::OPTIONAL, 'Fast tests - do not shutdown docker-compose.', 'slow'), new \FcfVendor\Symfony\Component\Console\Input\InputArgument(self::WOOCOMMERCE_VERSION, \FcfVendor\Symfony\Component\Console\Input\InputArgument::OPTIONAL, 'WooCommerce version to install.', '')));
}
/**
* Execute command.
*
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int 0 if everything went fine, or an error code
*/
protected function execute(\FcfVendor\Symfony\Component\Console\Input\InputInterface $input, \FcfVendor\Symfony\Component\Console\Output\OutputInterface $output)
{
$dockerComposeYaml = 'vendor/wpdesk/wp-codeception/docker/docker-compose.yaml';
$singleTest = $input->getArgument(self::SINGLE);
$fastTest = $input->getArgument(self::FAST);
$wooVersion = $input->getArgument(self::WOOCOMMERCE_VERSION);
$cache_dir = \sys_get_temp_dir() . '/codeception_cache';
if (!\file_exists($cache_dir)) {
\mkdir($cache_dir, 0777, \true);
}
\putenv('TMP_CACHE_DIR=' . $cache_dir);
$codecept_param = ' --html --verbose -f ';
$additionalParameters = ' -e CODECEPT_PARAM="' . $codecept_param . '" ';
if (!empty($singleTest) && 'all' !== $singleTest) {
$additionalParameters .= ' -e CODECEPT_PARAM="' . $codecept_param . ' acceptance ' . $singleTest . '" ';
}
if (!empty($wooVersion)) {
$additionalParameters .= ' -e WOOCOMMERCE_VERSION="' . $wooVersion . '" ';
}
$runTestsCommand = 'docker-compose -f ' . $dockerComposeYaml . ' run ' . $additionalParameters . 'codecept';
$output->writeln('Codeception command: ' . $runTestsCommand);
$this->execAndOutput($runTestsCommand, $output);
if (empty($fastTest) || self::FAST !== $fastTest) {
$this->execAndOutput('docker-compose -f ' . $dockerComposeYaml . ' down -v', $output);
}
return 0;
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace FcfVendor\WPDesk\Composer\Codeception\Commands;
use FcfVendor\Symfony\Component\Console\Input\InputArgument;
use FcfVendor\Symfony\Component\Console\Input\InputInterface;
use FcfVendor\Symfony\Component\Console\Output\OutputInterface;
use FcfVendor\Symfony\Component\Yaml\Exception\ParseException;
use FcfVendor\Symfony\Component\Yaml\Yaml;
/**
* Codeception tests run command.
*
* @package WPDesk\Composer\Codeception\Commands
*/
class RunLocalCodeceptionTests extends \FcfVendor\WPDesk\Composer\Codeception\Commands\RunCodeceptionTests
{
use LocalCodeceptionTrait;
/**
* Configure command.
*/
protected function configure()
{
parent::configure();
$this->setName('run-local-codeception-tests')->setDescription('Run local codeception tests.')->setDefinition(array(new \FcfVendor\Symfony\Component\Console\Input\InputArgument(self::SINGLE, \FcfVendor\Symfony\Component\Console\Input\InputArgument::OPTIONAL, 'Name of Single test to run.', ' ')));
}
/**
* Execute command.
*
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int 0 if everything went fine, or an error code
*/
protected function execute(\FcfVendor\Symfony\Component\Console\Input\InputInterface $input, \FcfVendor\Symfony\Component\Console\Output\OutputInterface $output)
{
$configuration = $this->getWpDeskConfiguration();
$this->prepareWpConfig($output, $configuration);
$singleTest = $input->getArgument(self::SINGLE);
$sep = \DIRECTORY_SEPARATOR;
$codecept = "vendor{$sep}bin{$sep}codecept";
$cleanOutput = $codecept . ' clean';
$this->execAndOutput($cleanOutput, $output);
$runLocalTests = $codecept . ' run -f --steps --html --verbose acceptance ' . $singleTest;
$this->execAndOutput($runLocalTests, $output);
return 0;
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace FcfVendor\WPDesk\Composer\Codeception\Commands;
use FcfVendor\Symfony\Component\Console\Input\InputArgument;
use FcfVendor\Symfony\Component\Console\Input\InputInterface;
use FcfVendor\Symfony\Component\Console\Output\OutputInterface;
use FcfVendor\Symfony\Component\Yaml\Exception\ParseException;
use FcfVendor\Symfony\Component\Yaml\Yaml;
/**
* Codeception tests run command.
*
* @package WPDesk\Composer\Codeception\Commands
*/
class RunLocalCodeceptionTestsWithCoverage extends \FcfVendor\WPDesk\Composer\Codeception\Commands\RunCodeceptionTests
{
use LocalCodeceptionTrait;
/**
* Configure command.
*/
protected function configure()
{
parent::configure();
$this->setName('run-local-codeception-tests-with-coverage')->setDescription('Run local codeception tests.')->setDefinition(array(new \FcfVendor\Symfony\Component\Console\Input\InputArgument(self::SINGLE, \FcfVendor\Symfony\Component\Console\Input\InputArgument::OPTIONAL, 'Name of Single test to run.', ' ')));
}
/**
* Execute command.
*
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int 0 if everything went fine, or an error code
*/
protected function execute(\FcfVendor\Symfony\Component\Console\Input\InputInterface $input, \FcfVendor\Symfony\Component\Console\Output\OutputInterface $output)
{
$configuration = $this->getWpDeskConfiguration();
$this->prepareWpConfig($output, $configuration);
$singleTest = $input->getArgument(self::SINGLE);
$sep = \DIRECTORY_SEPARATOR;
$codecept = "vendor{$sep}bin{$sep}codecept";
$cleanOutput = $codecept . ' clean';
$this->execAndOutput($cleanOutput, $output);
$runLocalTests = $codecept . ' run -f --steps --html --coverage --coverage-xml --coverage-html --verbose acceptance ' . $singleTest;
$this->execAndOutput($runLocalTests, $output);
return 0;
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace FcfVendor\WPDesk\Composer\Codeception\Commands;
/**
* Trait with a sed like command
* @see https://pl.wikipedia.org/wiki/Sed_(program)
*
* @package WPDesk\Composer\GitPlugin\Command
*/
trait SedTrait
{
/**
* SED.
*
* @param string $file_pattern .
* @param string $pattern .
* @param string $replace .
*
* @return string[] array of changed files
*/
private function wpdeskSed($file_pattern, $pattern, $replace)
{
$changed_files = [];
foreach (\glob($file_pattern) as $filename) {
$input = \file_get_contents($filename);
$output = \preg_replace($pattern, $replace, $input);
if ($output !== $input) {
$changed_files[] = $filename;
\file_put_contents($filename, $output);
}
}
return $changed_files;
}
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* Class SettingsException
* @package WPDesk\Composer\Codeception\Commands
*/
namespace FcfVendor\WPDesk\Composer\Codeception\Commands;
/**
* Settings Exception.
*/
class SettingsException extends \RuntimeException
{
}

View File

@@ -0,0 +1,49 @@
<?php
namespace FcfVendor\WPDesk\Composer\Codeception;
use FcfVendor\Composer\Composer;
use FcfVendor\Composer\IO\IOInterface;
use FcfVendor\Composer\Plugin\Capable;
use FcfVendor\Composer\Plugin\PluginInterface;
/**
* Composer plugin.
*
* @package WPDesk\Composer\Codeception
*/
class Plugin implements \FcfVendor\Composer\Plugin\PluginInterface, \FcfVendor\Composer\Plugin\Capable
{
/**
* @var Composer
*/
private $composer;
/**
* @var IOInterface
*/
private $io;
public function activate(\FcfVendor\Composer\Composer $composer, \FcfVendor\Composer\IO\IOInterface $io)
{
$this->composer = $composer;
$this->io = $io;
}
/**
* @inheritDoc
*/
public function deactivate(\FcfVendor\Composer\Composer $composer, \FcfVendor\Composer\IO\IOInterface $io)
{
$this->composer = $composer;
$this->io = $io;
}
/**
* @inheritDoc
*/
public function uninstall(\FcfVendor\Composer\Composer $composer, \FcfVendor\Composer\IO\IOInterface $io)
{
$this->composer = $composer;
$this->io = $io;
}
public function getCapabilities()
{
return [\FcfVendor\Composer\Plugin\Capability\CommandProvider::class => \FcfVendor\WPDesk\Composer\Codeception\CommandProvider::class];
}
}

View File

@@ -0,0 +1,51 @@
{
"name": "wpdesk\/wp-logs",
"authors": [
{
"name": "Krzysiek",
"email": "krzysiek@wpdesk.pl"
}
],
"config": {
"platform": {
"php": "7.0.19"
},
"allow-plugins": {
"dealerdirect\/phpcodesniffer-composer-installer": true
}
},
"require": {
"php": ">=7.0|^8",
"psr\/log": "^1",
"monolog\/monolog": "^1.23",
"wpdesk\/wp-notice": "^3.0"
},
"require-dev": {
"phpunit\/phpunit": "^5",
"squizlabs\/php_codesniffer": "^3.0.2",
"wpdesk\/wp-code-sniffer": "^1.2.3"
},
"autoload": {
"psr-4": {
"FcfVendor\\WPDesk\\Logger\\": "src\/"
}
},
"scripts": {
"phpunit-unit": "phpunit --configuration phpunit-unit.xml --coverage-text --colors=never",
"phpunit-unit-fast": "phpunit --configuration phpunit-unit.xml --no-coverage",
"phpunit-integration": "phpunit --configuration phpunit-integration.xml --coverage-text --colors=never",
"phpunit-integration-fast": "phpunit --configuration phpunit-integration.xml --no-coverage"
},
"extra": {
"text-domain": "wp-logs",
"translations-folder": "lang",
"po-files": {
"pl_PL": "pl_PL.po",
"en_AU": "en_AU.po",
"en_CA": "en_CA.po",
"en_GB": "en_GB.po",
"es_ES": "es_ES.po",
"de_DE": "de_DE.po"
}
}
}

View File

@@ -0,0 +1,49 @@
<?php
namespace FcfVendor\WPDesk\Logger;
use FcfVendor\Monolog\Handler\HandlerInterface;
use FcfVendor\Monolog\Logger;
use FcfVendor\Monolog\Registry;
/**
* Manages and facilitates creation of logger
*
* @package WPDesk\Logger
*/
class BasicLoggerFactory implements \FcfVendor\WPDesk\Logger\LoggerFactory
{
/** @var string Last created logger name/channel */
private static $lastLoggerChannel;
/**
* Creates logger for plugin
*
* @param string $name The logging channel/name of logger
* @param HandlerInterface[] $handlers Optional stack of handlers, the first one in the array is called first, etc.
* @param callable[] $processors Optional array of processors
* @return Logger
*/
public function createLogger($name, $handlers = array(), array $processors = array())
{
if (\FcfVendor\Monolog\Registry::hasLogger($name)) {
return \FcfVendor\Monolog\Registry::getInstance($name);
}
self::$lastLoggerChannel = $name;
$logger = new \FcfVendor\Monolog\Logger($name, $handlers, $processors);
\FcfVendor\Monolog\Registry::addLogger($logger);
return $logger;
}
/**
* Returns created Logger by name or last created logger
*
* @param string $name Name of the logger
*
* @return Logger
*/
public function getLogger($name = null)
{
if ($name === null) {
$name = self::$lastLoggerChannel;
}
return \FcfVendor\Monolog\Registry::getInstance($name);
}
}

View File

@@ -0,0 +1,114 @@
<?php
namespace FcfVendor\WPDesk\Logger;
use FcfVendor\Monolog\Logger;
use Psr\Log\LogLevel;
use WP_Error;
use Exception;
/**
* Facilitates creation of logger with default WPDesk settings
*
* @deprecated Only for backward compatibility. Please use injected Logger compatible with PSR
*
* @package WPDesk\Logger
*/
class LoggerFacade
{
const BACKTRACE_FILENAME_KEY = 'file';
/** @var WPDeskLoggerFactory */
private static $factory;
/**
* Get logger by name. If not exists create one.
*
* @param string $name Name of the logger
* @return Logger
*/
public static function getLogger($name = \FcfVendor\WPDesk\Logger\WPDeskLoggerFactory::DEFAULT_LOGGER_CHANNEL_NAME)
{
if (self::$factory === null) {
self::$factory = new \FcfVendor\WPDesk\Logger\WPDeskLoggerFactory();
}
return self::$factory->createWPDeskLogger($name);
}
/**
* Snake case alias for getLogger
*
* @param string $name
*
* @return Logger
*/
public static function get_logger($name = \FcfVendor\WPDesk\Logger\WPDeskLoggerFactory::DEFAULT_LOGGER_CHANNEL_NAME)
{
return self::getLogger($name);
}
/**
* If set, logs are disabled
*
* @param string $name Name of the logger
*/
public static function set_disable_log($name = \FcfVendor\WPDesk\Logger\WPDeskLoggerFactory::DEFAULT_LOGGER_CHANNEL_NAME)
{
self::$factory->disableLog($name);
}
/**
* Log this exception into WPDesk logger
*
* @param WP_Error $e Error to log.
* @param array $backtrace Backtrace information with snapshot of error env.
* @param array $context Context to log
* @param string $level Level of error.
*
* @see http://php.net/manual/en/function.debug-backtrace.php
*/
public static function log_wp_error(\WP_Error $e, array $backtrace, array $context = array(), $level = \Psr\Log\LogLevel::ERROR)
{
$message = 'Error: ' . \get_class($e) . ' Code: ' . $e->get_error_code() . ' Message: ' . $e->get_error_message();
self::log_message_backtrace($message, $backtrace, $context, $level);
}
/**
* Log this exception into WPDesk logger
*
* @param Exception $e Exception to log.
* @param array $context Context to log
* @param string $level Level of error.
*/
public static function log_exception(\Exception $e, array $context = array(), $level = \Psr\Log\LogLevel::ERROR)
{
$message = 'Exception: ' . \get_class($e) . ' Code: ' . $e->getCode() . ' Message: ' . $e->getMessage() . ' Stack: ' . $e->getTraceAsString();
self::log_message($message, \array_merge($context, ['exception' => $e]), $e->getFile(), $level);
}
/**
* Log message into WPDesk logger
*
* @param string $message Message to log.
* @param array $context Context to log
* @param string $source Source of the message - can be file name, class name or whatever.
* @param string $level Level of error.
*/
public static function log_message($message, array $context = array(), $source = null, $level = \Psr\Log\LogLevel::DEBUG)
{
$logger = self::getLogger();
if ($source !== null) {
$context = \array_merge($context, ['source' => $source]);
}
$logger->log($level, $message, $context);
}
/**
* Log message into WPDesk logger
*
* @param string $message Message to log.
* @param array $backtrace Backtrace information with snapshot of error env.
* @param array $context Context to log
* @param string $level Level of error.
*/
public static function log_message_backtrace($message, array $backtrace, array $context = array(), $level = \Psr\Log\LogLevel::DEBUG)
{
$message .= ' Backtrace: ' . \json_encode($backtrace);
$source = null;
if (isset($backtrace[self::BACKTRACE_FILENAME_KEY])) {
$source = $backtrace[self::BACKTRACE_FILENAME_KEY];
}
self::log_message($message, $context, $source, $level);
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace FcfVendor\WPDesk\Logger;
use FcfVendor\Monolog\Logger;
/*
* @package WPDesk\Logger
*/
interface LoggerFactory
{
/**
* Returns created Logger
*
* @param string $name
*
* @return Logger
*/
public function getLogger($name);
}

View File

@@ -0,0 +1,14 @@
<?php
namespace FcfVendor\WPDesk\Logger;
use Psr\Log\LogLevel;
final class Settings
{
/** @var string */
public $level = \Psr\Log\LogLevel::DEBUG;
/** @var bool */
public $use_wc_log = \true;
/** @var bool */
public $use_wp_log = \true;
}

View File

@@ -0,0 +1,46 @@
<?php
declare (strict_types=1);
namespace FcfVendor\WPDesk\Logger;
use FcfVendor\Monolog\Handler\HandlerInterface;
use FcfVendor\Monolog\Handler\NullHandler;
use FcfVendor\Monolog\Logger;
use FcfVendor\Monolog\Handler\ErrorLogHandler;
use FcfVendor\WPDesk\Logger\WC\WooCommerceHandler;
final class SimpleLoggerFactory implements \FcfVendor\WPDesk\Logger\LoggerFactory
{
/** @var Settings */
private $options;
/** @var string */
private $channel;
/** @var Logger */
private $logger;
public function __construct(string $channel, \FcfVendor\WPDesk\Logger\Settings $options = null)
{
$this->channel = $channel;
$this->options = $options ?? new \FcfVendor\WPDesk\Logger\Settings();
}
public function getLogger($name = null) : \FcfVendor\Monolog\Logger
{
if ($this->logger) {
return $this->logger;
}
$logger = new \FcfVendor\Monolog\Logger($this->channel);
if ($this->options->use_wc_log && \function_exists('wc_get_logger')) {
$logger->pushHandler(new \FcfVendor\WPDesk\Logger\WC\WooCommerceHandler(\wc_get_logger(), $this->channel));
}
// Adding WooCommerce logger may have failed, if so add WP by default.
if ($this->options->use_wp_log || empty($logger->getHandlers())) {
$logger->pushHandler($this->get_wp_handler());
}
return $this->logger = $logger;
}
private function get_wp_handler() : \FcfVendor\Monolog\Handler\HandlerInterface
{
if (\defined('FcfVendor\\WP_DEBUG_LOG') && WP_DEBUG_LOG) {
return new \FcfVendor\Monolog\Handler\ErrorLogHandler(\FcfVendor\Monolog\Handler\ErrorLogHandler::OPERATING_SYSTEM, $this->options->level);
}
return new \FcfVendor\Monolog\Handler\NullHandler();
}
}

View File

@@ -0,0 +1,7 @@
<?php
namespace FcfVendor\WPDesk\Logger\WC\Exception;
class WCLoggerAlreadyCaptured extends \RuntimeException
{
}

View File

@@ -0,0 +1,132 @@
<?php
namespace FcfVendor\WPDesk\Logger\WC;
use FcfVendor\Monolog\Logger;
use FcfVendor\WPDesk\Logger\WC\Exception\WCLoggerAlreadyCaptured;
/**
* Can capture default WooCommerce logger
*
* @package WPDesk\Logger
*/
class WooCommerceCapture
{
const WOOCOMMERCE_LOGGER_FILTER = 'woocommerce_logging_class';
const WOOCOMMERCE_AFTER_IS_LOADED_ACTION = 'woocommerce_loaded';
/** @var string Minimal version of WooCommerce supported by logger capture */
const SUPPORTED_WC_VERSION = '3.5.0';
/**
* Is logger filter captured by library.
*
* @var bool
*/
private $isCaptured = \false;
/**
* Our monolog
*
* @var Logger
*/
private $monolog;
/**
* Original WC Logger
*
* @var \WC_Logger_Interface
*/
private $originalWCLogger;
/**
* WordPress hook function to return our logger
*
* @var ?callable
*/
private $captureHookFunction;
/**
* WordPress hook function to return original wc logger
*
* @var ?callable
*/
private $freeHookFunction;
public function __construct(\FcfVendor\Monolog\Logger $monolog)
{
$this->monolog = $monolog;
}
/**
* Prepares callable property captureHookFunction.
* For it to work WC have to be loaded
*/
private function prepareCaptureHookCallable()
{
$monolog = $this->monolog;
if ($this->captureHookFunction === null) {
$this->captureHookFunction = function () use($monolog) {
return new \FcfVendor\WPDesk\Logger\WC\WooCommerceMonologPlugin($monolog, $this->originalWCLogger);
};
$this->monolog->pushHandler(new \FcfVendor\WPDesk\Logger\WC\WooCommerceHandler($this->originalWCLogger));
}
}
/**
* Is this version of WooCommerce is supported by logger capture
*
* @return bool
*/
public static function isSupportedWCVersion()
{
return \class_exists(\WooCommerce::class) && \version_compare(\WooCommerce::instance()->version, self::SUPPORTED_WC_VERSION, '>=');
}
/**
* Capture WooCommerce logger and inject our decorated Logger
*/
public function captureWcLogger()
{
if (self::isSupportedWCVersion()) {
if ($this->isCaptured) {
throw new \FcfVendor\WPDesk\Logger\WC\Exception\WCLoggerAlreadyCaptured('Try to free wc logger first.');
}
if ($this->isWooCommerceLoggerAvailable()) {
$this->prepareFreeHookCallable();
$this->prepareCaptureHookCallable();
\remove_filter(self::WOOCOMMERCE_LOGGER_FILTER, $this->freeHookFunction);
\add_filter(self::WOOCOMMERCE_LOGGER_FILTER, $this->captureHookFunction);
$this->isCaptured = \true;
} elseif (\function_exists('add_action')) {
\add_action(self::WOOCOMMERCE_AFTER_IS_LOADED_ACTION, [$this, 'captureWcLogger']);
} else {
$this->monolog->alert('Cannot capture WC - WordPress is not available.');
}
} else {
$this->monolog->alert('Cannot capture WC - WooCommerce version is not supported.');
}
}
/**
* Can i fetch WC Logger?
*
* @return bool
*/
private function isWooCommerceLoggerAvailable()
{
return \function_exists('wc_get_logger');
}
/**
* Prepares callable property freeHookFunction.
* For it to work WC have to be loaded
*/
private function prepareFreeHookCallable()
{
if ($this->freeHookFunction === null) {
$this->originalWCLogger = $logger = \wc_get_logger();
$this->freeHookFunction = function () use($logger) {
return $logger;
};
}
}
/**
* Remove WooCommerce logger injection
*/
public function freeWcLogger()
{
if ($this->isCaptured) {
\remove_filter(self::WOOCOMMERCE_LOGGER_FILTER, $this->captureHookFunction);
\add_filter(self::WOOCOMMERCE_LOGGER_FILTER, $this->freeHookFunction);
$this->isCaptured = \false;
}
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace FcfVendor\WPDesk\Logger\WC;
use FcfVendor\Monolog\Handler\AbstractProcessingHandler;
use FcfVendor\Monolog\Logger;
/**
* Class WooCommerceFactory
*/
class WooCommerceHandler extends \FcfVendor\Monolog\Handler\AbstractProcessingHandler
{
const DEFAULT_WC_SOURCE = 'wpdesk-logger';
/** @var \WC_Logger_Interface */
private $wc_logger;
/** @var string */
private $channel;
/**
* Writes the record down to the log of the implementing handler
*
* @param array $record
* @return void
*/
protected function write(array $record)
{
$context = \array_merge(['source' => $this->channel], $record['extra'], $record['context']);
$this->wc_logger->log($this->convertMonologLevelToWC($record['level']), $record['message'], $context);
}
/**
* @param int $level
* @return string
*/
private function convertMonologLevelToWC($level)
{
return \FcfVendor\Monolog\Logger::getLevelName($level);
}
public function __construct(\WC_Logger_Interface $originalWcLogger, string $channel = self::DEFAULT_WC_SOURCE)
{
parent::__construct();
$this->wc_logger = $originalWcLogger;
$this->channel = $channel;
}
}

View File

@@ -0,0 +1,165 @@
<?php
namespace FcfVendor\WPDesk\Logger\WC;
use FcfVendor\Monolog\Logger;
use Psr\Log\LogLevel;
use WC_Log_Levels;
/**
* Can decorate monolog with WC_Logger_Interface
*
* @package WPDesk\Logger
*/
class WooCommerceMonologPlugin implements \WC_Logger_Interface
{
/** @var Logger */
private $monolog;
/** @var \WC_Logger */
private $originalWCLogger;
public function __construct(\FcfVendor\Monolog\Logger $monolog, \WC_Logger_Interface $originalLogger)
{
$this->monolog = $monolog;
$this->originalWCLogger = $originalLogger;
}
/**
* Method added for compatibility with \WC_Logger
*
* @param string $source
*/
public function clear($source = '')
{
$this->originalWCLogger->clear($source);
}
/**
* Method added for compatibility with \WC_Logger
*/
public function clear_expired_logs()
{
$this->originalWCLogger->clear_expired_logs();
}
/**
* Method for compatibility reason. Do not use.
*
* @param string $handle
* @param string $message
* @param string $level
* @return bool|void
*
* @deprecated
*/
public function add($handle, $message, $level = \WC_Log_Levels::NOTICE)
{
$this->log($message, $level);
}
/**
* System is unusable.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function emergency($message, $context = array())
{
$this->log(\Psr\Log\LogLevel::EMERGENCY, $message, $context);
}
public function log($level, $message, $context = [])
{
$this->monolog->log($level, $message, $context);
}
/**
* Action must be taken immediately.
*
* Example: Entire website down, database unavailable, etc. This should
* trigger the SMS alerts and wake you up.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function alert($message, $context = array())
{
$this->log(\Psr\Log\LogLevel::ALERT, $message, $context);
}
/**
* Critical conditions.
*
* Example: Application component unavailable, unexpected exception.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function critical($message, $context = array())
{
$this->log(\Psr\Log\LogLevel::CRITICAL, $message, $context);
}
/**
* Runtime errors that do not require immediate action but should typically
* be logged and monitored.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function error($message, $context = array())
{
$this->log(\Psr\Log\LogLevel::ERROR, $message, $context);
}
/**
* Exceptional occurrences that are not errors.
*
* Example: Use of deprecated APIs, poor use of an API, undesirable things
* that are not necessarily wrong.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function warning($message, $context = array())
{
$this->log(\Psr\Log\LogLevel::WARNING, $message, $context);
}
/**
* Normal but significant events.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function notice($message, $context = array())
{
$this->log(\Psr\Log\LogLevel::NOTICE, $message, $context);
}
/**
* Interesting events.
*
* Example: User logs in, SQL logs.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function info($message, $context = array())
{
$this->log(\Psr\Log\LogLevel::INFO, $message, $context);
}
/**
* Detailed debug information.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function debug($message, $context = array())
{
$this->log(\Psr\Log\LogLevel::DEBUG, $message, $context);
}
}

View File

@@ -0,0 +1,127 @@
<?php
namespace FcfVendor\WPDesk\Logger\WP;
class WPCapture
{
/** @var string */
private $filename;
const LOG_DIR = 'wpdesk-logs';
public function __construct($filename)
{
$this->filename = $filename;
}
/**
* Add notice for directory.
*
* @param string $dir Directory.
*/
private function add_notice_for_dir($dir)
{
new \FcfVendor\WPDesk\Notice\Notice(\sprintf(
// Translators: directory.
\__('Can not enable WP Desk Debug log! Cannot create directory %s or this directory is not writeable!', 'flexible-checkout-fields'),
$dir
), \FcfVendor\WPDesk\Notice\Notice::NOTICE_TYPE_ERROR);
}
/**
* Add notice for file.
*
* @param string $file File..
*/
private function add_notice_for_file($file)
{
new \FcfVendor\WPDesk\Notice\Notice(\sprintf(
// Translators: directory.
\__('Can not enable WP Desk Debug log! Cannot create file %s!', 'flexible-checkout-fields'),
$file
), \FcfVendor\WPDesk\Notice\Notice::NOTICE_TYPE_ERROR);
}
/**
* Is debug log writable.
*
* @return bool
*/
private function is_debug_log_writable_or_show_notice()
{
$log_dir = $this->get_log_dir();
$log_file = $this->get_log_file();
$index_file = $this->get_index_file();
if (!\file_exists($log_dir)) {
if (!\mkdir($log_dir, 0777, \true)) {
$this->add_notice_for_dir($log_dir);
return \false;
}
}
if (!\file_exists($index_file)) {
$index_html = \fopen($index_file, 'w');
if (\false === $index_html) {
$this->add_notice_for_file($index_file);
return \false;
} else {
\fclose($index_html);
}
}
if (!\file_exists($log_file)) {
$log = \fopen($log_file, 'w');
if (\false === $log) {
$this->add_notice_for_file($log_file);
return \false;
} else {
\fclose($log);
}
}
if (!\is_writable($log_file)) {
$this->add_notice_for_file($log_file);
return \false;
}
return \true;
}
/**
* Init debug log file.
*/
public function init_debug_log_file()
{
if ($this->is_debug_log_writable_or_show_notice()) {
\ini_set('log_errors', 1);
\ini_set('error_log', $this->get_log_file());
}
}
/**
* Get uploads dir.
*
* @return string
*/
private function get_uploads_dir()
{
$upload_dir = \wp_upload_dir();
return \untrailingslashit($upload_dir['basedir']);
}
/**
* Get log dir.
*
* @return string
*/
private function get_log_dir()
{
return \trailingslashit($this->get_uploads_dir()) . self::LOG_DIR;
}
/**
* Get log file.
*
* @return string
*/
public function get_log_file()
{
return \trailingslashit($this->get_log_dir()) . $this->filename;
}
/**
* Get log file.
*
* @return string
*/
private function get_index_file()
{
return \trailingslashit($this->get_log_dir()) . 'index.html';
}
}

View File

@@ -0,0 +1,209 @@
<?php
namespace FcfVendor\WPDesk\Logger;
use Exception;
use InvalidArgumentException;
use LogicException;
use FcfVendor\Monolog\Handler\NullHandler;
use FcfVendor\Monolog\Logger;
use FcfVendor\Monolog\Registry;
use FcfVendor\Monolog\Handler\StreamHandler;
use Psr\Log\LogLevel;
use FcfVendor\WPDesk\Logger\WC\WooCommerceCapture;
use FcfVendor\WPDesk\Logger\WP\WPCapture;
/**
* Manages and facilitates creation of logger
*
* @package WPDesk\Logger
*/
class WPDeskLoggerFactory extends \FcfVendor\WPDesk\Logger\BasicLoggerFactory
{
const DEFAULT_LOGGER_CHANNEL_NAME = 'wpdesk';
/** @var string Log to file when level is */
const LEVEL_WPDESK_FILE = \Psr\Log\LogLevel::DEBUG;
/** @var string Log to wc logger when level is */
const LEVEL_WC = \Psr\Log\LogLevel::ERROR;
/** @var bool Will factory return null logger or not */
public static $shouldLoggerBeActivated = \true;
/**
* Remove static instances. In general should be use only testing purposes.
*
* @param string $name Name of the logger
*/
public static function tearDown($name = self::DEFAULT_LOGGER_CHANNEL_NAME)
{
if (\FcfVendor\Monolog\Registry::hasLogger($name)) {
\FcfVendor\Monolog\Registry::removeLogger($name);
}
}
/**
* Disable logger. Still exists but logs won't be saved
*
* @param string $name Name of the logger
*/
public function disableLog($name = self::DEFAULT_LOGGER_CHANNEL_NAME)
{
if (!\FcfVendor\Monolog\Registry::hasLogger($name)) {
$this->createWPDeskLogger($name);
}
if (\FcfVendor\Monolog\Registry::hasLogger($name)) {
/** @var Logger $logger */
$logger = \FcfVendor\Monolog\Registry::getInstance($name);
$this->removeAllHandlers($logger);
}
}
/**
* Creates default WPDesk logger.
*
* Requirements:
* - get_option, add/remove_action, add/remove filter and WP_CONTENT_DIR should be available for logger.
*
* Assumptions:
* - logger is actively working when 'wpdesk_helper_options' has 'debug_log' set to '1';
* - fatal errors, exception and standard errors are recorded but in a transparent way;
* - WooCommerce logger is captured and returns this logger; That is true of default logger is instantiated.
* - logs are still correctly written to WooCommerce subsystem in a transparent way;
* - all recorded errors are written to WPDesk file.
*
* @param string $name Name of the logger
* @return Logger
*/
public function createWPDeskLogger($name = self::DEFAULT_LOGGER_CHANNEL_NAME)
{
if (!self::$shouldLoggerBeActivated) {
return new \FcfVendor\Monolog\Logger($name);
}
if (\FcfVendor\Monolog\Registry::hasLogger($name)) {
return \FcfVendor\Monolog\Registry::getInstance($name);
}
$logger = $this->createLogger($name);
if (self::isWPLogPermitted()) {
$this->appendMainLog($logger);
}
if ($name !== self::DEFAULT_LOGGER_CHANNEL_NAME) {
$this->appendFileLog($logger, $this->getFileName($name));
} else {
$this->captureWooCommerce($logger);
}
return $logger;
}
/**
* Is capturing the php log is permitted.
*
* @return bool
*/
public static function isWPLogPermitted()
{
return \apply_filters('wpdesk_is_wp_log_capture_permitted', \true);
}
/**
* @param $logger
*/
private function appendMainLog($logger)
{
$wpCapture = $this->captureWPLog();
if (\is_writable($wpCapture->get_log_file())) {
$this->appendFileLog($logger, $wpCapture->get_log_file());
}
}
/**
* @param Logger $logger
*/
private function appendFileLog($logger, $filename)
{
try {
$this->pushFileHandle($filename, $logger);
} catch (\InvalidArgumentException $e) {
$logger->emergency('Main log file could not be created - invalid filename.');
} catch (\Exception $e) {
$logger->emergency('Main log file could not be written.');
}
}
/**
* @return WPCapture
*/
private function captureWPLog()
{
static $wpCapture;
if (!$wpCapture) {
$wpCapture = new \FcfVendor\WPDesk\Logger\WP\WPCapture(\basename($this->getFileName()));
$wpCapture->init_debug_log_file();
}
return $wpCapture;
}
/**
* Capture WooCommerce and add handle
*
* @param Logger $logger
*/
private function captureWooCommerce(\FcfVendor\Monolog\Logger $logger)
{
if (!\defined('FcfVendor\\WC_LOG_THRESHOLD')) {
\define('FcfVendor\\WC_LOG_THRESHOLD', self::LEVEL_WC);
}
$wcIntegration = new \FcfVendor\WPDesk\Logger\WC\WooCommerceCapture($logger);
$wcIntegration->captureWcLogger();
}
/**
* Add WPDesk log file handle
*
* @param Logger $logger
* @param string $filename Name of file with path
*
* @throws Exception If a missing directory is not buildable
* @throws InvalidArgumentException If stream is not a resource or string
*/
private function pushFileHandle($filename, \FcfVendor\Monolog\Logger $logger)
{
$logger->pushHandler(new \FcfVendor\Monolog\Handler\StreamHandler($filename, self::LEVEL_WPDESK_FILE));
}
/**
* Get filename old way
*
* @deprecated not sure if can remove
*/
public function getWPDeskFileName()
{
return $this->getFileName();
}
/**
* Returns WPDesk filename.
*
* @param string $name Name of the logger
*
* @return string
*/
public function getFileName($name = self::DEFAULT_LOGGER_CHANNEL_NAME)
{
$upload_dir = \wp_upload_dir();
return \trailingslashit(\untrailingslashit($upload_dir['basedir'])) . \FcfVendor\WPDesk\Logger\WP\WPCapture::LOG_DIR . \DIRECTORY_SEPARATOR . $name . '_debug.log';
}
/**
* Removes all handlers from logger
*
* @param Logger $logger
*
* @return void
*/
private function removeAllHandlers(\FcfVendor\Monolog\Logger $logger)
{
try {
while (\true) {
$logger->popHandler();
}
} catch (\LogicException $e) {
$logger->pushHandler(new \FcfVendor\Monolog\Handler\NullHandler());
}
}
/**
* is WPDesk file log is working(writable, exists, connected).
* @param string $name Name of the logger
*
* @return bool
*/
public function isLogWorking($name = self::DEFAULT_LOGGER_CHANNEL_NAME)
{
return \FcfVendor\Monolog\Registry::hasLogger($name);
}
}

View File

@@ -0,0 +1,90 @@
<?php
namespace FcfVendor;
if (!\defined('ABSPATH')) {
exit;
}
if (!\class_exists('FcfVendor\\WPDesk_Logger_Factory')) {
/**
* @deprecated Only for backward compatibility. Please use injected Logger compatible with PSR
*/
class WPDesk_Logger_Factory
{
/**
* Static logger storage
*
* @var WPDesk_Logger
*/
private static $logger = null;
const BACKTRACE_FILENAME_KEY = 'file';
const WPDESK_LOG_ACTION_NAME = 'wpdesk_log';
/**
* Creates and returns a logger
*
* @return WPDesk_Logger
*/
public static function create_logger()
{
if (empty(self::$logger)) {
$logger = new \FcfVendor\WPDesk_Logger();
$logger->attach_hooks();
self::$logger = $logger;
}
return self::$logger;
}
/**
* Log this exception into wpdesk logger
*
* @param WP_Error $e Error to log.
* @param array $backtrace Backtrace information with snapshot of error env.
*
* @see http://php.net/manual/en/function.debug-backtrace.php
*/
public static function log_wp_error(\WP_Error $e, array $backtrace)
{
$message = 'Error: ' . \get_class($e) . ' Code: ' . $e->get_error_code() . ' Message: ' . $e->get_error_message();
self::log_message_backtrace($message, \FcfVendor\WPDesk_Logger::ERROR, $backtrace);
}
/**
* Log this exception into WPDesk logger
*
* @param Exception $e Exception to log.
*/
public static function log_exception(\Exception $e)
{
$message = 'Exception: ' . \get_class($e) . ' Code: ' . $e->getCode() . ' Message: ' . $e->getMessage() . ' Stack: ' . $e->getTraceAsString();
self::log_message($message, $e->getFile(), \FcfVendor\WPDesk_Logger::ERROR);
}
/**
* Log message into WPDesk logger
*
* @param string $message Message to log.
* @param string $source Source of the message - can be file name, class name or whatever.
* @param string $level Level of error.
*/
public static function log_message($message, $source = 'unknown', $level = \FcfVendor\WPDesk_Logger::DEBUG)
{
self::create_logger();
\do_action(self::WPDESK_LOG_ACTION_NAME, $level, $source, $message);
self::$logger->wpdesk_log($level, $source, $message);
}
/**
* Log message into WPDesk logger
*
* @param string $message Message to log.
* @param string $level Level of error.
* @param array $backtrace Backtrace information with snapshot of error env.
*/
public static function log_message_backtrace($message, $level = \FcfVendor\WPDesk_Logger::DEBUG, array $backtrace)
{
$message .= ' Backtrace: ' . \json_encode($backtrace);
if (isset($backtrace[self::BACKTRACE_FILENAME_KEY])) {
$filename = $backtrace[self::BACKTRACE_FILENAME_KEY];
} else {
$filename = 'unknown';
}
self::log_message($message, $filename, $level);
}
}
}

View File

@@ -0,0 +1,94 @@
<?php
namespace FcfVendor;
if (!\defined('ABSPATH')) {
exit;
}
if (!\class_exists('FcfVendor\\WPDesk_Logger')) {
/**
* @deprecated Only for backward compatibility. Please use injected Logger compatible with PSR
*/
class WPDesk_Logger
{
/** @var \Psr\Log\LoggerInterface */
static $logger;
const EMERGENCY = 'emergency';
const ALERT = 'alert';
const CRITICAL = 'critical';
const ERROR = 'error';
const WARNING = 'warning';
const NOTICE = 'notice';
const INFO = 'info';
const DEBUG = 'debug';
public function __construct()
{
if (!self::$logger) {
$loggerFactroy = new \FcfVendor\WPDesk\Logger\WPDeskLoggerFactory();
self::$logger = $loggerFactroy->createWPDeskLogger();
}
}
/**
* Level strings mapped to integer severity.
*
* @var array
*/
protected $level_to_severity = [self::EMERGENCY => 800, self::ALERT => 700, self::CRITICAL => 600, self::ERROR => 500, self::WARNING => 400, self::NOTICE => 300, self::INFO => 200, self::DEBUG => 100];
/**
* Attach hooks
*
* @return void
*/
public function attach_hooks()
{
\add_action('plugins_loaded', [$this, 'plugins_loaded']);
\add_filter('wpdesk_logger_level_options', [$this, 'wpdesk_logger_level_options']);
}
public function plugins_loaded()
{
if (\defined('WC_VERSION')) {
if (\version_compare(\WC_VERSION, '3.0', '<')) {
\add_action('wpdesk_log', [$this, 'wpdesk_log'], 10, 4);
} else {
\add_action('wpdesk_log', [$this, 'wpdesk_log_30'], 10, 4);
}
}
}
public function wpdesk_logger_level_options(array $options)
{
return ['disabled' => \__('Disabled', 'flexible-checkout-fields'), 'emergency' => \__('Emergency', 'flexible-checkout-fields'), 'alert' => \__('Alert', 'flexible-checkout-fields'), 'critical' => \__('Critical', 'flexible-checkout-fields'), 'error' => \__('Error', 'flexible-checkout-fields'), 'warning' => \__('Warning', 'flexible-checkout-fields'), 'notice' => \__('Notice', 'flexible-checkout-fields'), 'info' => \__('Info', 'flexible-checkout-fields'), 'debug' => \__('Debug', 'flexible-checkout-fields')];
}
/**
* @param string $level
* @param string $source
* @param string $message
* @param string $settings_level
*/
public function wpdesk_log($level, $source, $message, $settings_level = 'debug')
{
if (!isset($this->level_to_severity[$settings_level]) || !isset($this->level_to_severity[$level])) {
return;
}
if ($this->level_to_severity[$settings_level] > $this->level_to_severity[$level]) {
return;
}
if (\is_array($message) || \is_object($message)) {
$message = \print_r($message, \true);
}
self::$logger->log($level, $message, ['source' => $source]);
}
public function wpdesk_log_30($level, $source, $message, $settings_level = 'debug')
{
if (!isset($this->level_to_severity[$settings_level]) || !isset($this->level_to_severity[$level])) {
return;
}
if ($this->level_to_severity[$settings_level] > $this->level_to_severity[$level]) {
return;
}
if (\is_array($message) || \is_object($message)) {
$message = \print_r($message, \true);
}
self::$logger->log($level, $message, ['source' => $source]);
}
}
}

View File

@@ -0,0 +1,29 @@
jQuery( document ).ready(function() {
jQuery('.wpdesk-notice-gutenberg').each(function( index ) {
var classList = jQuery(this).attr('class').split(/\s+/);
var type = '';
jQuery.each(classList, function(index, item) {
if (item.startsWith('notice-')) {
type = item.replace('notice-','');
}
});
content = this.innerText;
actions = [];
jQuery.each(jQuery(this).find('a'), function(index, item) {
text = item.innerText;
actions.push({
url: item.href,
label: text.charAt(0).toUpperCase() + text.slice(1),
});
});
isDismiss = jQuery(this).hasClass('is-dismissible');
window.wp.data.dispatch( 'core/notices' ).createNotice(
type,
content,
{
isDismissible: isDismiss,
actions: actions
}
);
});
} );

View File

@@ -0,0 +1,22 @@
jQuery( document ).on( 'click', '.notice-dismiss', function() {
var notice_name = jQuery(this).closest('div.notice').data('notice-name');
var source = jQuery(this).closest('div.notice').data('source');
if ('' !== notice_name) {
jQuery.ajax({
url: ajaxurl,
type: 'post',
data: {
action: 'wpdesk_notice_dismiss',
notice_name: notice_name,
source: source,
},
success: function (response) {
}
});
}
});
jQuery( document ).on( 'click', '.notice-dismiss-link', function() {
jQuery(this).closest('div.notice').data('source',jQuery(this).data('source'));
jQuery(this).closest('div.notice').find('.notice-dismiss').click();
});

View File

@@ -0,0 +1 @@
jQuery(document).on("click",".notice-dismiss",function(){var a=jQuery(this).closest("div.notice").data("notice-name");var b=jQuery(this).closest("div.notice").data("source");if(""!==a){jQuery.ajax({url:ajaxurl,type:"post",data:{action:"wpdesk_notice_dismiss",notice_name:a,source:b},success:function(c){}})}});jQuery(document).on("click",".notice-dismiss-link",function(){jQuery(this).closest("div.notice").data("source",jQuery(this).data("source"));jQuery(this).closest("div.notice").find(".notice-dismiss").click()});

View File

@@ -0,0 +1,51 @@
{
"name": "wpdesk\/wp-notice",
"description": "Library for displaying Wordpress notices.",
"license": "MIT",
"keywords": [
"wordpress",
"notice",
"admin"
],
"homepage": "https:\/\/gitlab.com\/wpdesk\/wp-notice",
"minimum-stability": "stable",
"authors": [
{
"name": "grola",
"email": "grola@wpdesk.net"
}
],
"config": {
"platform": {
"php": "7.0"
}
},
"require": {
"php": ">=5.5",
"wpdesk\/wp-builder": "^1.0|^2.0"
},
"require-dev": {
"phpunit\/phpunit": "<7",
"wp-coding-standards\/wpcs": "^0.14.1",
"squizlabs\/php_codesniffer": "^3.0.2",
"mockery\/mockery": "*",
"10up\/wp_mock": "*",
"wimg\/php-compatibility": "^8"
},
"autoload": {
"psr-4": {
"FcfVendor\\WPDesk\\Notice\\": "src\/WPDesk\/Notice\/"
},
"files": [
"src\/WPDesk\/notice-functions.php"
]
},
"autoload-dev": {},
"scripts": {
"phpcs": "phpcs",
"phpunit-unit": "phpunit --configuration phpunit-unit.xml --coverage-text --colors=never",
"phpunit-unit-fast": "phpunit --configuration phpunit-unit.xml --no-coverage",
"phpunit-integration": "phpunit --configuration phpunit-integration.xml --coverage-text --colors=never",
"phpunit-integration-fast": "phpunit --configuration phpunit-integration.xml --no-coverage"
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace FcfVendor;
require_once __DIR__ . '/vendor/autoload.php';
if (!\class_exists('FcfVendor\\WPDesk\\Notice\\AjaxHandler')) {
require_once __DIR__ . '/src/WPDesk/Notice/AjaxHandler.php';
}
if (!\class_exists('FcfVendor\\WPDesk\\Notice\\Notice')) {
require_once __DIR__ . 'src/WPDesk/Notice/Notice.php';
}
if (!\class_exists('FcfVendor\\WPDesk\\Notice\\PermanentDismissibleNotice')) {
require_once __DIR__ . '/src/WPDesk/Notice/PermanentDismissibleNotice.php';
}
if (!\class_exists('FcfVendor\\WPDesk\\Notice\\Factory')) {
require_once __DIR__ . '/src/WPDesk/Notice/Factory.php';
}
require_once __DIR__ . '/src/WPDesk/notice-functions.php';

View File

@@ -0,0 +1,83 @@
<?php
namespace FcfVendor\WPDesk\Notice;
use FcfVendor\WPDesk\PluginBuilder\Plugin\HookablePluginDependant;
use FcfVendor\WPDesk\PluginBuilder\Plugin\PluginAccess;
/**
* Class AjaxHandler
*
* AjaxHandler for dismissible notices.
*
* @package WPDesk\Notice
*/
class AjaxHandler implements \FcfVendor\WPDesk\PluginBuilder\Plugin\HookablePluginDependant
{
use PluginAccess;
const POST_FIELD_NOTICE_NAME = 'notice_name';
const POST_FIELD_SOURCE = 'source';
const SCRIPTS_VERSION = '4';
const SCRIPT_HANDLE = 'wpdesk_notice';
/**
* @var string
*/
private $assetsURL;
/**
* AjaxHandler constructor.
*
* @param string|null $assetsURL Assets URL.
*/
public function __construct($assetsURL = null)
{
$this->assetsURL = $assetsURL;
}
/**
* Hooks.
*/
public function hooks()
{
if ($this->assetsURL) {
\add_action('admin_enqueue_scripts', [$this, 'enqueueAdminScripts']);
} else {
\add_action('admin_head', [$this, 'addScriptToAdminHead']);
}
\add_action('wp_ajax_wpdesk_notice_dismiss', [$this, 'processAjaxNoticeDismiss']);
}
/**
* Enqueue admin scripts.
*/
public function enqueueAdminScripts()
{
$suffix = \defined('SCRIPT_DEBUG') && \SCRIPT_DEBUG ? '' : '.min';
\wp_register_script(self::SCRIPT_HANDLE, \trailingslashit($this->assetsURL) . 'js/notice' . $suffix . '.js', array('jquery'), self::SCRIPTS_VERSION);
\wp_enqueue_script(self::SCRIPT_HANDLE);
}
/**
* Add Java Script to admin header.
*/
public function addScriptToAdminHead()
{
include __DIR__ . '/views/admin-head-js.php';
}
/**
* Process AJAX notice dismiss.
*
* Updates corresponded WordPress option and fires wpdesk_notice_dismissed_notice action with notice name.
*/
public function processAjaxNoticeDismiss()
{
if (isset($_POST[self::POST_FIELD_NOTICE_NAME])) {
$noticeName = \sanitize_text_field($_POST[self::POST_FIELD_NOTICE_NAME]);
if (isset($_POST[self::POST_FIELD_SOURCE])) {
$source = \sanitize_text_field($_POST[self::POST_FIELD_SOURCE]);
} else {
$source = null;
}
\update_option(\FcfVendor\WPDesk\Notice\PermanentDismissibleNotice::OPTION_NAME_PREFIX . $noticeName, \FcfVendor\WPDesk\Notice\PermanentDismissibleNotice::OPTION_VALUE_DISMISSED);
\do_action('wpdesk_notice_dismissed_notice', $noticeName, $source);
}
if (\defined('DOING_AJAX') && \DOING_AJAX) {
die;
}
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace FcfVendor\WPDesk\Notice;
/**
* Class Factory
*
* Factory for notices.
* @package WPDesk\Notice
*/
class Factory
{
/**
* Creates Notice object.
*
* @param string $noticeType Notice type.
* @param string $noticeContent Notice content.
* @param bool $isDismissible Is dismissible.
* @param int $priority Priority.
*
* @return Notice
*/
public static function notice($noticeContent = '', $noticeType = 'info', $isDismissible = \false, $priority = 10)
{
return new \FcfVendor\WPDesk\Notice\Notice($noticeContent, $noticeType, $isDismissible, $priority);
}
/**
* Creates PermanentDismissibleNotice object.
*
* @param string $noticeContent
* @param string $noticeType
* @param string $noticeName
* @param int $priority
*
* @return PermanentDismissibleNotice
*/
public static function permanentDismissibleNotice($noticeContent = '', $noticeName = '', $noticeType = '', $priority = 10)
{
return new \FcfVendor\WPDesk\Notice\PermanentDismissibleNotice($noticeContent, $noticeName, $noticeType, $priority);
}
}

View File

@@ -0,0 +1,251 @@
<?php
namespace FcfVendor\WPDesk\Notice;
/**
* Class Notice
*
* WordPress admin notice.
* @package WPDesk\Notice
*/
class Notice
{
const NOTICE_TYPE_ERROR = 'error';
const NOTICE_TYPE_WARNING = 'warning';
const NOTICE_TYPE_SUCCESS = 'success';
const NOTICE_TYPE_INFO = 'info';
const ADMIN_FOOTER_BASE_PRIORITY = 9999999;
/**
* Notice type.
*
* @var string
*/
protected $noticeType;
/**
* Notice content.
*
* @var string
*/
protected $noticeContent;
/**
* Is dismissible.
*
* @var bool
*/
protected $dismissible;
/**
* Notice hook priority.
* @var int;
*/
protected $priority;
/**
* Is action added?
* @var bool
*/
private $actionAdded = \false;
/**
* Attributes.
*
* @var string[]
*/
protected $attributes = array();
/**
* Show notice in gutenberg editor.
*
* @var bool
*/
protected $showInGutenberg = \false;
/**
* WPDesk_Flexible_Shipping_Notice constructor.
*
* @param string $noticeContent Notice content.
* @param string $noticeType Notice type.
* @param bool $dismissible Is dismissible.
* @param int $priority Notice priority.
* @param array $attributes Attributes.
* @param bool $showInGutenberg Show notice in gutenberg editor.
*/
public function __construct($noticeContent, $noticeType = 'info', $dismissible = \false, $priority = 10, $attributes = array(), $showInGutenberg = \false)
{
$this->noticeContent = $noticeContent;
$this->noticeType = $noticeType;
$this->dismissible = $dismissible;
$this->priority = $priority;
$this->attributes = $attributes;
$this->showInGutenberg = $showInGutenberg;
$this->addAction();
}
/**
* @return bool
*/
public function isBlockEditor()
{
if (!\function_exists('get_current_screen')) {
require_once \ABSPATH . '/wp-admin/includes/screen.php';
}
return \get_current_screen()->is_block_editor();
}
/**
* @return string
*/
public function getNoticeContent()
{
return $this->noticeContent;
}
/**
* @param string $noticeContent
*/
public function setNoticeContent($noticeContent)
{
$this->noticeContent = $noticeContent;
}
/**
* @return string
*/
public function getNoticeType()
{
return $this->noticeType;
}
/**
* @param string $noticeType
*/
public function setNoticeType($noticeType)
{
$this->noticeType = $noticeType;
}
/**
* @return bool
*/
public function isDismissible()
{
return $this->dismissible;
}
/**
* @param bool $dismissible
*/
public function setDismissible($dismissible)
{
$this->dismissible = $dismissible;
}
/**
* @return int
*/
public function getPriority()
{
return $this->priority;
}
/**
* @param int $priority
*/
public function setPriority($priority)
{
$this->priority = $priority;
if ($this->actionAdded) {
$this->removeAction();
$this->addAction();
}
}
/**
* Add notice action.
*/
protected function addAction()
{
if (!$this->actionAdded) {
\add_action('admin_notices', [$this, 'showNotice'], $this->priority);
\add_action('admin_footer', [$this, 'showNotice'], self::ADMIN_FOOTER_BASE_PRIORITY + \intval($this->priority));
\add_action('admin_head', [$this, 'addGutenbergScript']);
$this->actionAdded = \true;
}
}
/**
* Remove action.
*/
protected function removeAction()
{
if ($this->actionAdded) {
\remove_action('admin_notices', [$this, 'showNotice'], $this->priority);
\remove_action('admin_footer', [$this, 'showNotice'], self::ADMIN_FOOTER_BASE_PRIORITY + \intval($this->priority));
$this->actionAdded = \false;
}
}
/**
* Enqueue admin scripts.
*/
public function addGutenbergScript()
{
if ($this->isBlockEditor()) {
include_once __DIR__ . '/views/admin-head-js-gutenberg.php';
}
}
/**
* Add attribute.
*
* @param string $name Name
* @param string $value Value.
*/
public function addAttribute($name, $value)
{
$this->attributes[$name] = $value;
}
/**
* Get notice class.
*
* @return string
*/
protected function getNoticeClass()
{
$notice_classes = ['notice'];
if ('updated' === $this->noticeType) {
$notice_classes[] = $this->noticeType;
} else {
$notice_classes[] = 'notice-' . $this->noticeType;
}
if ($this->dismissible) {
$notice_classes[] = 'is-dismissible';
}
if (isset($this->attributes['class'])) {
$notice_classes[] = $this->attributes['class'];
}
if ($this->showInGutenberg) {
$notice_classes[] = 'wpdesk-notice-gutenberg';
}
return \implode(' ', $notice_classes);
}
/**
* Get attributes as string.
*
* @return string
*/
protected function getAttributesAsString()
{
$attribute_string = \sprintf('class="%1$s"', \esc_attr($this->getNoticeClass()));
foreach ($this->attributes as $attribute_name => $attribute_value) {
if ('class' !== $attribute_name) {
$attribute_string .= \sprintf(' %1$s="%2$s"', \esc_html($attribute_name), \esc_attr($attribute_value));
}
}
return $attribute_string;
}
private function addParagraphToContent()
{
if (0 === \strpos($this->noticeContent, '<p>')) {
return \false;
}
if (0 === \strpos($this->noticeContent, '<div>') || 0 === \strpos($this->noticeContent, '<div ')) {
return \false;
}
return \true;
}
/**
* Show notice;
*/
public function showNotice()
{
$this->removeAction();
$noticeFormat = '<div %1$s>%2$s</div>';
if ($this->addParagraphToContent()) {
$noticeFormat = '<div %1$s><p>%2$s</p></div>';
}
echo \sprintf($noticeFormat, $this->getAttributesAsString(), $this->noticeContent);
}
}

View File

@@ -0,0 +1,62 @@
<?php
namespace FcfVendor\WPDesk\Notice;
/**
* Class PermanentDismissibleNotice
*
* WordPress admin dismissible notice.
* @package WPDesk\Notice
*/
class PermanentDismissibleNotice extends \FcfVendor\WPDesk\Notice\Notice
{
const OPTION_NAME_PREFIX = 'wpdesk_notice_dismiss_';
const OPTION_VALUE_DISMISSED = '1';
/**
* @var string
*/
private $noticeName;
/**
* @var string
*/
private $noticeDismissOptionName;
/**
* WPDesk_Flexible_Shipping_Notice constructor.
*
* @param string $noticeContent Notice content.
* @param string $noticeName Notice dismiss option name.
* @param string $noticeType Notice type.
* @param int $priority Priority
* @param array $attributes Attributes.
* @param bool $showInGutenberg Show notice in gutenberg editor.
*/
public function __construct($noticeContent, $noticeName, $noticeType = 'info', $priority = 10, $attributes = array(), $showInGutenberg = \false)
{
parent::__construct($noticeContent, $noticeType, \true, $priority, $attributes, $showInGutenberg);
$this->noticeName = $noticeName;
$this->noticeDismissOptionName = static::OPTION_NAME_PREFIX . $noticeName;
if (self::OPTION_VALUE_DISMISSED === \get_option($this->noticeDismissOptionName, '')) {
$this->removeAction();
}
}
/**
* Undo dismiss notice.
*/
public function undoDismiss()
{
\delete_option($this->noticeDismissOptionName);
$this->addAction();
}
/**
* Get attributes as string.
*
* @return string
*/
protected function getAttributesAsString()
{
$attributesAsString = parent::getAttributesAsString();
$attributesAsString .= \sprintf(' data-notice-name="%1$s"', \esc_attr($this->noticeName));
$attributesAsString .= \sprintf(' id="wpdesk-notice-%1$s"', \esc_attr($this->noticeName));
return $attributesAsString;
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace FcfVendor;
if (!\defined('ABSPATH')) {
exit;
}
// Exit if accessed directly
?>
<script type="text/javascript">
<?php
include \dirname(__FILE__) . '/../../../../assets/js/gutenberg.js';
?>
</script>
<?php

View File

@@ -0,0 +1,15 @@
<?php
namespace FcfVendor;
if (!\defined('ABSPATH')) {
exit;
}
// Exit if accessed directly
?>
<script type="text/javascript">
<?php
include \dirname(__FILE__) . '/../../../../assets/js/notice.min.js';
?>
</script>
<?php

View File

@@ -0,0 +1,228 @@
<?php
namespace FcfVendor;
if (!\function_exists('FcfVendor\\WPDeskInitWpNoticeAjaxHandler')) {
/**
* Init notices AJAX Handler.
*
* @param string|null $assetsUrl
*
* @return \WPDesk\Notice\AjaxHandler
*/
function WPDeskInitWpNoticeAjaxHandler($assetsUrl = null)
{
$ajax_handler = new \FcfVendor\WPDesk\Notice\AjaxHandler($assetsUrl);
$ajax_handler->hooks();
return $ajax_handler;
}
}
if (!\function_exists('FcfVendor\\wpdesk_init_wp_notice_ajax_handler')) {
/**
* Alias for {@see WPDeskInitNoticeAjaxHandler()} function.
*
* @param null $assetsUrl
*
* @return \WPDesk\Notice\AjaxHandler
*/
function wpdesk_init_wp_notice_ajax_handler($assetsUrl = null)
{
return \FcfVendor\WPDeskInitWpNoticeAjaxHandler($assetsUrl);
}
}
if (!\function_exists('FcfVendor\\WPDeskWpNotice')) {
/**
* Creates Notice.
*
* @param string $noticeContent Notice content.
* @param string $noticeType Notice type.
* @param bool $dismissible Dismissible notice.
* @param int $priority Notice priority,
*
* @return \WPDesk\Notice\Notice
*/
function WPDeskWpNotice($noticeContent, $noticeType = 'info', $dismissible = \false, $priority = 10)
{
return \FcfVendor\WPDesk\Notice\Factory::notice($noticeContent, $noticeType, $dismissible, $priority);
}
}
if (!\function_exists('FcfVendor\\wpdesk_wp_notice')) {
/**
* Creates Notice.
*
* Alias for {@see WPDeskNotice()} function.
*
* @param string $noticeContent Notice content.
* @param string $noticeType Notice type.
* @param bool $dismissible Dismissible notice.
* @param int $priority Notice priority,
*
* @return \WPDesk\Notice\Notice
*/
function wpdesk_wp_notice($noticeContent, $noticeType = 'info', $dismissible = \false, $priority = 10)
{
return \FcfVendor\WPDeskWpNotice($noticeContent, $noticeType, $dismissible, $priority);
}
}
if (!\function_exists('FcfVendor\\WPDeskWpNoticeInfo')) {
/**
* Creates Notice Info.
*
* @param string $noticeContent Notice content.
* @param bool $dismissible Dismissible notice.
* @param int $priority Notice priority,
*
* @return \WPDesk\Notice\Notice
*/
function WPDeskWpNoticeInfo($noticeContent, $dismissible = \false, $priority = 10)
{
return \FcfVendor\WPDesk\Notice\Factory::notice($noticeContent, \FcfVendor\WPDesk\Notice\Notice::NOTICE_TYPE_INFO, $dismissible, $priority);
}
}
if (!\function_exists('FcfVendor\\wpdesk_wp_notice_info')) {
/**
* Creates Notice Info.
*
* Alias for {@see WPDeskNoticeInfo()} function.
*
* @param string $noticeContent Notice content.
* @param bool $dismissible Dismissible notice.
* @param int $priority Notice priority,
*
* @return \WPDesk\Notice\Notice
*/
function wpdesk_wp_notice_info($noticeContent, $dismissible = \false, $priority = 10)
{
return \FcfVendor\WPDeskWpNoticeInfo($noticeContent, $dismissible, $priority);
}
}
if (!\function_exists('FcfVendor\\WPDeskWpNoticeError')) {
/**
* Creates Notice Error.
*
* @param string $noticeContent Notice content.
* @param bool $dismissible Dismissible notice.
* @param int $priority Notice priority,
*
* @return \WPDesk\Notice\Notice
*/
function WPDeskWpNoticeError($noticeContent, $dismissible = \false, $priority = 10)
{
return \FcfVendor\WPDesk\Notice\Factory::notice($noticeContent, \FcfVendor\WPDesk\Notice\Notice::NOTICE_TYPE_ERROR, $dismissible, $priority);
}
}
if (!\function_exists('FcfVendor\\wpdesk_wp_notice_error')) {
/**
* Creates Notice Error.
*
* Alias for {@see WPDeskNoticeError()} function.
*
* @param string $noticeContent Notice content.
* @param bool $dismissible Dismissible notice.
* @param int $priority Notice priority,
*
* @return \WPDesk\Notice\Notice
*/
function wpdesk_wp_notice_error($noticeContent, $dismissible = \false, $priority = 10)
{
return \FcfVendor\WPDeskWpNoticeError($noticeContent, $dismissible, $priority);
}
}
if (!\function_exists('FcfVendor\\WPDeskWpNoticeWarning')) {
/**
* Creates Notice Warning.
*
* @param string $noticeContent Notice content.
* @param bool $dismissible Dismissible notice.
* @param int $priority Notice priority,
*
* @return \WPDesk\Notice\Notice
*/
function WPDeskWpNoticeWarning($noticeContent, $dismissible = \false, $priority = 10)
{
return \FcfVendor\WPDesk\Notice\Factory::notice($noticeContent, \FcfVendor\WPDesk\Notice\Notice::NOTICE_TYPE_WARNING, $dismissible, $priority);
}
}
if (!\function_exists('FcfVendor\\wpdesk_wp_notice_warning')) {
/**
* Creates Notice Warning.
*
* Alias for {@see WPDeskNoticeWarning()} function.
*
* @param string $noticeContent Notice content.
* @param bool $dismissible Dismissible notice.
* @param int $priority Notice priority,
*
* @return \WPDesk\Notice\Notice
*/
function wpdesk_wp_notice_warning($noticeContent, $dismissible = \false, $priority = 10)
{
return \FcfVendor\WPDeskWpNoticeWarning($noticeContent, $dismissible, $priority);
}
}
if (!\function_exists('FcfVendor\\WPDeskWpNoticeSuccess')) {
/**
* Creates Notice Success.
*
* @param string $noticeContent Notice content.
* @param bool $dismissible Dismissible notice.
* @param int $priority Notice priority,
*
* @return \WPDesk\Notice\Notice
*/
function WPDeskWpNoticeSuccess($noticeContent, $dismissible = \false, $priority = 10)
{
return \FcfVendor\WPDesk\Notice\Factory::notice($noticeContent, \FcfVendor\WPDesk\Notice\Notice::NOTICE_TYPE_SUCCESS, $dismissible, $priority);
}
}
if (!\function_exists('FcfVendor\\wpdesk_wp_notice_success')) {
/**
* Creates Notice Success.
*
* Alias for {@see WPDeskNoticeSuccess()} function.
*
* @param string $noticeContent Notice content.
* @param bool $dismissible Dismissible notice.
* @param int $priority Notice priority,
*
* @return \WPDesk\Notice\Notice
*/
function wpdesk_wp_notice_success($noticeContent, $dismissible = \false, $priority = 10)
{
return \FcfVendor\WPDeskWpNoticeSuccess($noticeContent, $dismissible, $priority);
}
}
if (!\function_exists('FcfVendor\\WPDeskPermanentDismissibleWpNotice')) {
/**
* Creates Permanent Dismissible Notice.
*
* @param string $noticeContent Notice content.
* @param string $noticeType Notice type.
* @param string $noticeName Notice name.
* @param int $priority Notice priority.
*
* @return \WPDesk\Notice\Notice
*/
function WPDeskPermanentDismissibleWpNotice($noticeContent, $noticeName, $noticeType = 'info', $priority = 10)
{
return \FcfVendor\WPDesk\Notice\Factory::permanentDismissibleNotice($noticeContent, $noticeName, $noticeType, $priority);
}
}
if (!\function_exists('FcfVendor\\wpdesk_permanent_dismissible_wp_notice')) {
/**
* Creates Permanent Dismissible Notice.
*
* Alias for {@see WPDeskPermanentDismissibleNotice()} function.
*
* @param string $noticeContent Notice content.
* @param string $noticeName Notice name.
* @param string $noticeType Notice type.
* @param int $priority Notice priority.
*
* @return \WPDesk\Notice\Notice
*/
function wpdesk_permanent_dismissible_wp_notice($noticeContent, $noticeName, $noticeType = 'info', $priority = 10)
{
return \FcfVendor\WPDeskPermanentDismissibleWpNotice($noticeContent, $noticeName, $noticeType, $priority);
}
}

View File

@@ -0,0 +1,48 @@
{
"name": "wpdesk\/wp-plugin-flow",
"authors": [
{
"name": "Krzysiek",
"email": "krzysiek@wpdesk.pl"
}
],
"require": {
"php": ">=5.6",
"wpdesk\/wp-basic-requirements": "^3.2.3",
"wpdesk\/wp-builder": "^1.4.4",
"wpdesk\/wp-wpdesk-license": "^2.10.1",
"wpdesk\/wp-wpdesk-helper": "^2.4",
"wpdesk\/wp-wpdesk-tracker": "^2.3.1"
},
"require-dev": {
"phpunit\/phpunit": "<7",
"wp-coding-standards\/wpcs": "^0.14.1",
"squizlabs\/php_codesniffer": "^3.4.2",
"10up\/wp_mock": "^0.2",
"wpdesk\/wp-wpdesk-composer": "^2.3"
},
"autoload": {
"classmap": [
"src"
]
},
"autoload-dev": {
"classmap": [
"vendor\/wpdesk\/wp-basic-requirements",
"tests\/Stub"
]
},
"extra": {
"text-domain": "wp-plugin-flow",
"translations-folder": "lang",
"po-files": {
"pl_PL": "pl_PL.po"
}
},
"scripts": {
"phpunit-unit": "phpunit --configuration phpunit-unit.xml --coverage-text --colors=never",
"phpunit-unit-fast": "phpunit --configuration phpunit-unit.xml --no-coverage",
"phpunit-integration": "phpunit --configuration phpunit-integration.xml --coverage-text --colors=never",
"phpunit-integration-fast": "phpunit --configuration phpunit-integration.xml --no-coverage"
}
}

View File

@@ -0,0 +1,69 @@
<?php
namespace FcfVendor\WPDesk\Plugin\Flow\Initialization;
use FcfVendor\WPDesk\PluginBuilder\Plugin\Activateable;
use FcfVendor\WPDesk\PluginBuilder\Plugin\Deactivateable;
use FcfVendor\WPDesk\PluginBuilder\Plugin\SlimPlugin;
use FcfVendor\WPDesk\PluginBuilder\Storage\StorageFactory;
/**
* Helps with plugin building concepts.
*
* @package WPDesk\Plugin\Flow\Initialization
*/
trait BuilderTrait
{
/**
* Build plugin from info.
*
* @param \WPDesk_Plugin_Info $plugin_info
*
* @return SlimPlugin
*/
private function build_plugin(\FcfVendor\WPDesk_Plugin_Info $plugin_info)
{
$class_name = \apply_filters('wp_builder_plugin_class', $plugin_info->get_class_name());
/** @var SlimPlugin $plugin */
$plugin = new $class_name($plugin_info);
return $plugin;
}
/**
* Initialize WP register hooks that have to be fire before any other.
*
* @param \WPDesk_Plugin_Info $plugin_info
* @param SlimPlugin $plugin
*
* @return SlimPlugin
*/
private function init_register_hooks(\FcfVendor\WPDesk_Plugin_Info $plugin_info, \FcfVendor\WPDesk\PluginBuilder\Plugin\SlimPlugin $plugin)
{
if ($plugin instanceof \FcfVendor\WPDesk\PluginBuilder\Plugin\Activateable) {
\register_activation_hook($plugin_info->get_plugin_file_name(), [$plugin, 'activate']);
}
if ($plugin instanceof \FcfVendor\WPDesk\PluginBuilder\Plugin\Deactivateable) {
\register_deactivation_hook($plugin_info->get_plugin_file_name(), [$plugin, 'deactivate']);
}
return $plugin;
}
/**
* Store plugin for others to use.
*
* @param SlimPlugin $plugin
*/
private function store_plugin(\FcfVendor\WPDesk\PluginBuilder\Plugin\SlimPlugin $plugin)
{
$storageFactory = new \FcfVendor\WPDesk\PluginBuilder\Storage\StorageFactory();
$storageFactory->create_storage()->add_to_storage(\get_class($plugin), $plugin);
}
/**
* Init integration layer of the plugin.
*
* @param SlimPlugin $plugin
*/
private function init_plugin(\FcfVendor\WPDesk\PluginBuilder\Plugin\SlimPlugin $plugin)
{
\do_action('wp_builder_before_plugin_init', $plugin);
$plugin->init();
\do_action('wp_builder_before_init', $plugin);
}
}

View File

@@ -0,0 +1,66 @@
<?php
namespace FcfVendor\WPDesk\Plugin\Flow\Initialization\Simple;
use FcfVendor\WPDesk\Helper\PrefixedHelperAsLibrary;
/**
* Trait helps with helper initialization
*
* @package WPDesk\Plugin\Flow\Initialization\Simple
*/
trait HelperInstanceAsFilterTrait
{
/** @var \WPDesk\Helper\PrefixedHelperAsLibrary */
private static $helper_instance;
/**
* Returns version of the helper. Inc when helper is changed and should be instantiated fist.
*
* @return int
*/
private function get_helper_version()
{
return 5;
}
/**
* Returns filter action name for helper instance
*
* @return string
*/
private function get_helper_action_name()
{
return 'wpdesk_helper_instance';
}
/**
* Instantiate helper and return it
*
* @return PrefixedHelperAsLibrary
*/
private function get_helper_instance()
{
return \apply_filters($this->get_helper_action_name(), null);
}
/**
* Prepare helper to be instantiated using wpdesk_helper_instance filter
*
* @return void|PrefixedHelperAsLibrary
*/
private function prepare_helper_action()
{
\class_exists(\WPDesk\Helper\HelperAsLibrary::class);
// autoload this class
\add_filter($this->get_helper_action_name(), function ($helper_instance) {
if (\is_object($helper_instance)) {
return $helper_instance;
}
if (\is_object(self::$helper_instance)) {
return self::$helper_instance;
}
if (\apply_filters('wpdesk_can_start_helper', \true, $this->plugin_info)) {
self::$helper_instance = new \FcfVendor\WPDesk\Helper\PrefixedHelperAsLibrary();
self::$helper_instance->hooks();
\do_action('wpdesk_helper_started', self::$helper_instance, $this->plugin_info);
return self::$helper_instance;
}
}, 10 - $this->get_helper_version());
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace FcfVendor\WPDesk\Plugin\Flow\Initialization;
/**
* Interface for factory of plugin initialization strategy
*/
interface InitializationFactory
{
/**
* @param \WPDesk_Plugin_Info $info
*
* @return InitializationStrategy
*/
public function create_initialization_strategy(\FcfVendor\WPDesk_Plugin_Info $info);
}

View File

@@ -0,0 +1,27 @@
<?php
namespace FcfVendor\WPDesk\Plugin\Flow\Initialization;
use FcfVendor\WPDesk\PluginBuilder\Plugin\SlimPlugin;
/**
* Interface for initialization strategy for plugin. How to initialize it?
*/
interface InitializationStrategy
{
/**
* Run tasks that prepares plugin to work. Have to run before plugin loaded.
*
* @param \WPDesk_Plugin_Info $plugin_info
*
* @return SlimPlugin
*/
public function run_before_init(\FcfVendor\WPDesk_Plugin_Info $plugin_info);
/**
* Run task that integrates plugin with other dependencies. Can be run in plugins_loaded.
*
* @param \WPDesk_Plugin_Info $plugin_info
*
* @return SlimPlugin
*/
public function run_init(\FcfVendor\WPDesk_Plugin_Info $plugin_info);
}

View File

@@ -0,0 +1,48 @@
<?php
namespace FcfVendor\WPDesk\Plugin\Flow\Initialization;
/**
* Can disable shared plugin before it's loaded using plugin filename
*/
class PluginDisablerByFileTrait
{
/** @var string */
private $plugin_file;
/**
* @param string $plugin_file
*/
public function __construct($plugin_file)
{
$this->plugin_file = $plugin_file;
}
/**
* @return void
*/
public function disable()
{
/**
* @param WPDesk_Loader[] $loaders
*
* @return array
*/
$false_for_helper = function ($loaders) {
return \array_filter($loaders, function ($loader) {
try {
// BIG HACK TO GET PRIVATE PROPERTY
$reflection = new \ReflectionClass($loader);
$property = $reflection->getProperty('loader_info');
$property->setAccessible(\true);
/** @var WPDesk_Composer_Loader_Info $inner_info */
$inner_info = $property->getValue($loader);
$plugin_info = $inner_info->get_plugin_info();
return \basename($plugin_info->get_plugin_file_name()) !== \basename($this->plugin_file);
} catch (\Exception $e) {
return \true;
}
});
};
\add_filter('wp_autoloader_loader_loaders_to_load', $false_for_helper);
\add_filter('wp_autoloader_loader_loaders_to_create', $false_for_helper);
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace FcfVendor\WPDesk\Plugin\Flow\Initialization\Simple;
use FcfVendor\WPDesk\Plugin\Flow\Initialization\InitializationFactory;
use FcfVendor\WPDesk\Plugin\Flow\Initialization\InitializationStrategy;
/**
* Can decide if strategy is for free plugin or paid plugin
*/
class SimpleFactory implements \FcfVendor\WPDesk\Plugin\Flow\Initialization\InitializationFactory
{
/** @var bool */
private $free;
/**
* @param bool $free True for free/repository plugin
*/
public function __construct($free = \false)
{
$this->free = $free;
}
/**
* Create strategy according to the given flag
*
* @param \WPDesk_Plugin_Info $info
*
* @return InitializationStrategy
*/
public function create_initialization_strategy(\FcfVendor\WPDesk_Plugin_Info $info)
{
if ($this->free) {
return new \FcfVendor\WPDesk\Plugin\Flow\Initialization\Simple\SimpleFreeStrategy($info);
}
return new \FcfVendor\WPDesk\Plugin\Flow\Initialization\Simple\SimplePaidStrategy($info);
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace FcfVendor\WPDesk\Plugin\Flow\Initialization\Simple;
use FcfVendor\WPDesk\Plugin\Flow\Initialization\ActivationTrait;
use FcfVendor\WPDesk\Plugin\Flow\Initialization\BuilderTrait;
use FcfVendor\WPDesk\Plugin\Flow\Initialization\InitializationStrategy;
use FcfVendor\WPDesk\PluginBuilder\Plugin\SlimPlugin;
/**
* Initialize free plugin
* - just build it already
*/
class SimpleFreeStrategy implements \FcfVendor\WPDesk\Plugin\Flow\Initialization\InitializationStrategy
{
use HelperInstanceAsFilterTrait;
use TrackerInstanceAsFilterTrait;
use BuilderTrait;
/** @var \WPDesk_Plugin_Info */
private $plugin_info;
/** @var SlimPlugin */
private $plugin;
public function __construct(\FcfVendor\WPDesk_Plugin_Info $plugin_info)
{
$this->plugin_info = $plugin_info;
}
/**
* Run tasks that prepares plugin to work. Have to run before plugin loaded.
*
* @param \WPDesk_Plugin_Info $plugin_info
*
* @return SlimPlugin
*/
public function run_before_init(\FcfVendor\WPDesk_Plugin_Info $plugin_info)
{
$this->plugin = $this->build_plugin($plugin_info);
$this->init_register_hooks($plugin_info, $this->plugin);
}
/**
* Run task that integrates plugin with other dependencies. Can be run in plugins_loaded.
*
* @param \WPDesk_Plugin_Info $plugin_info
*
* @return SlimPlugin
*/
public function run_init(\FcfVendor\WPDesk_Plugin_Info $plugin_info)
{
if (!$this->plugin) {
$this->plugin = $this->build_plugin($plugin_info);
}
$this->prepare_helper_action();
$this->prepare_tracker_action();
$this->store_plugin($this->plugin);
$this->init_plugin($this->plugin);
return $this->plugin;
}
}

View File

@@ -0,0 +1,135 @@
<?php
namespace FcfVendor\WPDesk\Plugin\Flow\Initialization\Simple;
use FcfVendor\WPDesk\Helper\HelperRemover;
use FcfVendor\WPDesk\Helper\PrefixedHelperAsLibrary;
use FcfVendor\WPDesk\License\PluginRegistrator;
use FcfVendor\WPDesk\Plugin\Flow\Initialization\ActivationTrait;
use FcfVendor\WPDesk\Plugin\Flow\Initialization\BuilderTrait;
use FcfVendor\WPDesk\Plugin\Flow\Initialization\PluginDisablerByFileTrait;
use FcfVendor\WPDesk\Plugin\Flow\Initialization\InitializationStrategy;
use FcfVendor\WPDesk\PluginBuilder\Plugin\ActivationAware;
use FcfVendor\WPDesk\PluginBuilder\Plugin\SlimPlugin;
/**
* Initialize standard paid plugin
* - register to helper
* - initialize helper
* - build with info about plugin active flag
*/
class SimplePaidStrategy implements \FcfVendor\WPDesk\Plugin\Flow\Initialization\InitializationStrategy
{
use HelperInstanceAsFilterTrait;
use TrackerInstanceAsFilterTrait;
use BuilderTrait;
/** @var \WPDesk_Plugin_Info */
private $plugin_info;
/** @var SlimPlugin */
private $plugin;
public function __construct(\FcfVendor\WPDesk_Plugin_Info $plugin_info)
{
$this->plugin_info = $plugin_info;
}
/**
* Run tasks that prepares plugin to work. Have to run before plugin loaded.
*
* @param \WPDesk_Plugin_Info $plugin_info
*
* @return SlimPlugin
*/
public function run_before_init(\FcfVendor\WPDesk_Plugin_Info $plugin_info)
{
$this->plugin = $this->build_plugin($plugin_info);
$this->init_register_hooks($plugin_info, $this->plugin);
}
/**
* Run task that integrates plugin with other dependencies. Can be run in plugins_loaded.
*
* @param \WPDesk_Plugin_Info $plugin_info
*
* @return SlimPlugin
*/
public function run_init(\FcfVendor\WPDesk_Plugin_Info $plugin_info)
{
if (!$this->plugin) {
$this->plugin = $this->build_plugin($plugin_info);
}
$this->prepare_tracker_action();
$registrator = $this->register_plugin();
\add_action('plugins_loaded', function () use($registrator) {
$this->init_helper();
$is_plugin_subscription_active = $registrator instanceof \FcfVendor\WPDesk\License\PluginRegistrator && $registrator->is_active();
if ($this->plugin instanceof \FcfVendor\WPDesk\PluginBuilder\Plugin\ActivationAware && $is_plugin_subscription_active) {
$this->plugin->set_active();
}
$this->store_plugin($this->plugin);
$this->init_plugin($this->plugin);
}, $priority_before_flow_2_5_after_2_6 = -45);
return $this->plugin;
}
/**
* Register plugin for subscriptions and updates
*
* @return PluginRegistrator
*
* @see init_helper note
*
*/
private function register_plugin()
{
if (\apply_filters('wpdesk_can_register_plugin', \true, $this->plugin_info)) {
$registrator = new \FcfVendor\WPDesk\License\PluginRegistrator($this->plugin_info);
$registrator->add_plugin_to_installed_plugins();
return $registrator;
}
}
/**
* Helper is a component that gives:
* - activation interface
* - automatic updates
* - logs
* - some other feats
*
* NOTE:
*
* It's possible for this method to not found classes embedded here.
* OTHER plugin in unlikely scenario that THIS plugin is disabled
* can use this class and do not have this library dependencies as
* these are loaded using composer.
*
* @return PrefixedHelperAsLibrary|null
*/
private function init_helper()
{
$this->prevent_older_helpers();
$this->prepare_helper_action();
return $this->get_helper_instance();
}
/**
* Try to disable all other types of helpers
*/
private function prevent_older_helpers()
{
if (\apply_filters('wpdesk_can_hack_shared_helper', \true, $this->plugin_info)) {
// hack to ensure that the class is loaded so other helpers are disabled
\class_exists(\WPDesk\Helper\HelperAsLibrary::class, \true);
}
if (\apply_filters('wpdesk_can_supress_original_helper', \true, $this->plugin_info)) {
$this->try_suppress_original_helper_load();
// start supression only once. Prevent doing it again
\add_filter('wpdesk_can_supress_original_helper', function () {
return \false;
});
}
if (\apply_filters('wpdesk_can_remove_old_helper_hooks', \true, $this->plugin_info)) {
(new \FcfVendor\WPDesk\Helper\HelperRemover())->hooks();
}
}
/**
* Tries to prevent original Helper from loading
*/
private function try_suppress_original_helper_load()
{
(new \FcfVendor\WPDesk\Plugin\Flow\Initialization\PluginDisablerByFileTrait('wpdesk-helper/wpdesk-helper.php'))->disable();
}
}

View File

@@ -0,0 +1,63 @@
<?php
namespace FcfVendor\WPDesk\Plugin\Flow\Initialization\Simple;
/**
* Trait helps with tracker initialization
*
* @package WPDesk\Plugin\Flow\Initialization\Simple\
*/
trait TrackerInstanceAsFilterTrait
{
/** @var \WPDesk_Tracker_Interface */
private static $tracker_instance;
/**
* Returns filter action name for tracker instance
*
* @return string
*/
private function get_tracker_action_name()
{
return 'wpdesk_tracker_instance';
}
/**
* Returns version of the tracker. Inc when trackker is changed and should be instantiated fist.
*
* @return int
*/
private function get_tracker_version()
{
return 2;
}
/**
* @return \WPDesk_Tracker_Interface
*/
private function get_tracker_instance()
{
return \apply_filters($this->get_tracker_action_name(), null);
}
/**
* Prepare tracker to be instantiated using wpdesk_tracker_instance filter
*
* @return void|\WPDesk_Tracker
*/
private function prepare_tracker_action()
{
\class_exists(\WPDesk_Tracker_Factory::class);
//autoload this class
\add_filter($this->get_tracker_action_name(), function ($tracker_instance) {
if (\is_object($tracker_instance)) {
return $tracker_instance;
}
if (\is_object(self::$tracker_instance)) {
return self::$tracker_instance;
}
if (\apply_filters('wpdesk_can_start_tracker', \true, $this->plugin_info)) {
$tracker_factory = new \FcfVendor\WPDesk_Tracker_Factory_Prefixed();
self::$tracker_instance = $tracker_factory->create_tracker(\basename($this->plugin_info->get_plugin_file_name()));
\do_action('wpdesk_tracker_started', self::$tracker_instance, $this->plugin_info);
return self::$tracker_instance;
}
}, 10 - $this->get_tracker_version());
}
}

View File

@@ -0,0 +1,145 @@
<?php
namespace FcfVendor\WPDesk\Plugin\Flow;
use FcfVendor\WPDesk\Plugin\Flow\Initialization\InitializationFactory;
/**
* Bootstrap plugin loading
* - check requirements
* - prepare plugin info
* - delegate plugin building to the initializator
*/
final class PluginBootstrap
{
const LIBRARY_TEXT_DOMAIN = 'flexible-checkout-fields';
const PRIORITY_BEFORE_FLOW_2_5 = -50;
/** @var string */
private $plugin_version;
/** @var string */
private $plugin_name;
/** @var string */
private $plugin_class_name;
/** @var string */
private $plugin_text_domain;
/** @var string */
private $plugin_dir;
/** @var string */
private $plugin_file;
/** @var array */
private $requirements;
/** @var string */
private $product_id;
/**
* Factory to build strategy how initialize that plugin
*
* @var InitializationFactory
*/
private $initialization_factory;
/**
* WPDesk_Plugin_Bootstrap constructor.
*
* @param string $plugin_version
* @param string $plugin_release_timestamp
* @param string $plugin_name
* @param string $plugin_class_name
* @param string $plugin_text_domain
* @param string $plugin_dir
* @param string $plugin_file
* @param array $requirements
* @param string $product_id
* @param InitializationFactory $build_factory
*/
public function __construct($plugin_version, $plugin_release_timestamp, $plugin_name, $plugin_class_name, $plugin_text_domain, $plugin_dir, $plugin_file, array $requirements, $product_id, \FcfVendor\WPDesk\Plugin\Flow\Initialization\InitializationFactory $build_factory)
{
$this->plugin_version = $plugin_version;
$this->plugin_name = $plugin_name;
$this->plugin_class_name = $plugin_class_name;
$this->plugin_text_domain = $plugin_text_domain;
$this->plugin_dir = $plugin_dir;
$this->plugin_file = $plugin_file;
$this->requirements = $requirements;
$this->product_id = $product_id;
$this->initialization_factory = $build_factory;
}
/**
* Run the plugin bootstrap
*/
public function run()
{
$plugin_info = $this->get_plugin_info();
$this->init_translations($plugin_info);
$strategy = $this->initialization_factory->create_initialization_strategy($plugin_info);
$requirements_checker = $this->create_requirements_checker();
if ($requirements_checker->are_requirements_met()) {
$strategy->run_before_init($plugin_info);
}
$this->add_activation_hook_for_save_activation_date();
\add_action('plugins_loaded', static function () use($strategy, $requirements_checker, $plugin_info) {
if ($requirements_checker->are_requirements_met()) {
$strategy->run_init($plugin_info);
} else {
$requirements_checker->render_notices();
}
}, self::PRIORITY_BEFORE_FLOW_2_5);
}
/**
* Initialize activated_plugin action.
* Action stores plugin activation date.
* Example option name: plugin_activation_flexible-shipping/flexible-shipping.php
*/
private function add_activation_hook_for_save_activation_date()
{
\add_action('activated_plugin', static function ($plugin_file, $network_wide = \false) {
if (!$network_wide) {
$option_name = 'plugin_activation_' . $plugin_file;
$activation_date = \get_option($option_name, '');
if ('' === $activation_date) {
$activation_date = \current_time('mysql');
\update_option($option_name, $activation_date);
}
}
});
}
/**
* Adds text domain used in a library
*/
private function init_translations(\FcfVendor\WPDesk_Plugin_Info $plugin_info)
{
$lang_dir = 'lang';
if (\method_exists($plugin_info, 'get_language_dir')) {
$lang_dir = $plugin_info->get_language_dir();
}
\load_plugin_textdomain($plugin_info->get_text_domain(), \false, \basename($plugin_info->get_plugin_dir()) . "/{$lang_dir}/");
}
/**
* Factory method creates requirement checker to run the checks
*
* @return \WPDesk_Requirement_Checker
*/
private function create_requirements_checker()
{
/** @var \WPDesk_Requirement_Checker_Factory $requirements_checker_factory */
$requirements_checker_factory = new \FcfVendor\WPDesk_Basic_Requirement_Checker_Factory();
return $requirements_checker_factory->create_from_requirement_array(__FILE__, $this->plugin_name, $this->requirements, $this->plugin_text_domain);
}
/**
* Factory method creates \WPDesk_Plugin_Info to bootstrap info about plugin in one place
*
* TODO: move to WPDesk_Plugin_Info factory
*
* @return \WPDesk_Plugin_Info
*/
private function get_plugin_info()
{
$plugin_info = new \FcfVendor\WPDesk_Plugin_Info();
$plugin_info->set_plugin_file_name(\plugin_basename($this->plugin_file));
$plugin_info->set_plugin_name($this->plugin_name);
$plugin_info->set_plugin_dir($this->plugin_dir);
$plugin_info->set_class_name($this->plugin_class_name);
$plugin_info->set_version($this->plugin_version);
$plugin_info->set_product_id($this->product_id);
$plugin_info->set_text_domain($this->plugin_text_domain);
$plugin_info->set_plugin_url(\plugins_url(\dirname(\plugin_basename($this->plugin_file))));
return $plugin_info;
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace FcfVendor;
/**
* @var string $plugin_version
* @var string $plugin_name
* @var string $plugin_class_name
* @var string $plugin_text_domain
* @var string $plugin_dir
* @var string $plugin_file
* @var array $requirements
* @var string $product_id
*/
if (!\defined('ABSPATH')) {
die;
}
// Code in PHP >= 5.3 but understandable by older parsers
if (\PHP_VERSION_ID > 50300) {
require_once $plugin_dir . '/vendor/autoload.php';
$plugin_init_factory = new \FcfVendor\WPDesk\Plugin\Flow\Initialization\Simple\SimpleFactory(\true);
}
require \dirname(__FILE__) . '/plugin-init-php52.php';

View File

@@ -0,0 +1,45 @@
<?php
namespace FcfVendor;
/**
* @var string $plugin_version
* @var string $plugin_name
* @var string $plugin_class_name
* @var string $plugin_text_domain
* @var string $plugin_dir
* @var string $plugin_file
* @var array $requirements
* @var string $product_id
* @var WPDesk\Plugin\Flow\Initialization\InitializationFactory|void $plugin_init_factory
*/
if (!\defined('ABSPATH')) {
die;
}
// Code in PHP >= 5.3 but understandable by older parsers
if (\PHP_VERSION_ID > 50300) {
require_once $plugin_dir . '/vendor/autoload.php';
if (!isset($plugin_init_factory)) {
$plugin_init_factory = new \FcfVendor\WPDesk\Plugin\Flow\Initialization\Simple\SimpleFactory();
}
$bootstrap = new \FcfVendor\WPDesk\Plugin\Flow\PluginBootstrap(
$plugin_version,
null,
// deprecated
$plugin_name,
$plugin_class_name,
$plugin_text_domain,
$plugin_dir,
$plugin_file,
$requirements,
$product_id,
$plugin_init_factory
);
$bootstrap->run();
// all optional vars must be cleared
unset($plugin_init_factory);
} else {
/** @noinspection PhpDeprecationInspection */
$php52_function = \create_function('', 'echo sprintf( __("<p><strong style=\'color: red;\'>PHP version is older than 5.3 so no WP Desk plugins will work. Please contact your host and ask them to upgrade. </strong></p>", \'wp-plugin-flow\') );');
\add_action('admin_notices', $php52_function);
}

View File

@@ -0,0 +1,33 @@
{
"name": "wpdesk\/wp-view",
"authors": [
{
"name": "Krzysiek",
"email": "krzysiek@wpdesk.pl"
}
],
"require": {
"php": ">=5.5"
},
"require-dev": {
"phpunit\/phpunit": "<7",
"wp-coding-standards\/wpcs": "^0.14.1",
"squizlabs\/php_codesniffer": "^3.0.2",
"mockery\/mockery": "*",
"10up\/wp_mock": "*",
"wimg\/php-compatibility": "^8"
},
"autoload": {
"psr-4": {
"FcfVendor\\WPDesk\\View\\": "src\/"
}
},
"autoload-dev": {},
"scripts": {
"phpcs": "phpcs",
"phpunit-unit": "phpunit --configuration phpunit-unit.xml --coverage-text --colors=never",
"phpunit-unit-fast": "phpunit --configuration phpunit-unit.xml --no-coverage",
"phpunit-integration": "phpunit --configuration phpunit-integration.xml --coverage-text --colors=never",
"phpunit-integration-fast": "phpunit --configuration phpunit-integration.xml --no-coverage"
}
}

View File

@@ -0,0 +1,68 @@
<?php
namespace FcfVendor\WPDesk\View;
use FcfVendor\WPDesk\View\Renderer\SimplePhpRenderer;
use FcfVendor\WPDesk\View\Resolver\ChainResolver;
use FcfVendor\WPDesk\View\Resolver\DirResolver;
use FcfVendor\WPDesk\View\Resolver\WPThemeResolver;
/**
* Facilitates building of the default plugin renderer.
*
* @package WPDesk\View
*/
class PluginViewBuilder
{
/** @var string */
private $plugin_dir;
/** @var string[] */
private $template_dirs;
/**
* @param string $plugin_dir Plugin directory path(absolute path)
* @param string|string[] $template_dir Directory or list of directories with templates to render
*/
public function __construct($plugin_dir, $template_dir = 'templates')
{
$this->plugin_dir = $plugin_dir;
if (!\is_array($template_dir)) {
$this->template_dirs = [$template_dir];
} else {
$this->template_dirs = $template_dir;
}
}
/**
* Creates simple renderer that search for the templates in plugin dir and in theme/child dir.
*
* For example if your plugin dir is /plugin, template dir is /templates, theme is /theme, and a child theme is /child
* the templates will be loaded from(order is important):
* - /child/plugin/*.php
* - /theme/plugin/*.php
* - /plugin/templates/*.php
*
* @return SimplePhpRenderer
*/
public function createSimpleRenderer()
{
$resolver = new \FcfVendor\WPDesk\View\Resolver\ChainResolver();
$resolver->appendResolver(new \FcfVendor\WPDesk\View\Resolver\WPThemeResolver(\basename($this->plugin_dir)));
foreach ($this->template_dirs as $dir) {
$dir = \trailingslashit($this->plugin_dir) . \trailingslashit($dir);
$resolver->appendResolver(new \FcfVendor\WPDesk\View\Resolver\DirResolver($dir));
}
return new \FcfVendor\WPDesk\View\Renderer\SimplePhpRenderer($resolver);
}
/**
* Load templates using simple renderer.
*
* @param string $name Name of the template
* @param string $path Additional path of the template ie. for path "path" the templates would be loaded from /plugin/templates/path/*.php
* @param array $args Arguments for templates to use
*
* @return string Rendered template.
*/
public function loadTemplate($name, $path = '.', $args = array())
{
$renderer = $this->createSimpleRenderer();
return $renderer->render(\trailingslashit($path) . $name, $args);
}
}

Some files were not shown because too many files have changed in this diff Show More