first commit

This commit is contained in:
2026-03-05 13:07:40 +01:00
commit 64ba0721ee
25709 changed files with 4691006 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
.wpdesk-rating-petition {
font-size: 15px;
width: 100%;
margin-bottom: 15px;
}
.wpdesk-rating-petition a {
text-decoration: none;
color: inherit;
font-weight: 600;
}
.wpdesk-rating-petition span.plugins-repository {
text-decoration: underline;
}
.wpdesk-rating-petition span.plugin-title {
font-weight: 600;
}
.wpdesk-rating-petition span.heart {
speak: none;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #dc3232;
font-size: 16px;
font-style: normal;
font-variant: normal;
font-weight: 400;
text-transform: none;
}
.wpdesk-rating-petition span.star {
speak: none;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #ffb900;
font-size: 16px;
font-style: normal;
font-variant: normal;
font-weight: 400;
text-transform: none;
text-decoration: none;
}

View File

@@ -0,0 +1,53 @@
{
"name": "wpdesk\/wp-wpdesk-rating-petition",
"license": "MIT",
"authors": [
{
"name": "Krzysiek",
"email": "krzysiek@wpdesk.pl"
}
],
"require": {
"php": ">=7.0",
"wpdesk\/wp-notice": "^3.1",
"wpdesk\/wp-plugin-flow-common": "^1"
},
"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": {
"FSVendor\\WPDesk\\RepositoryRating\\": "src\/"
}
},
"autoload-dev": {
"classmap": [
"tests\/"
]
},
"extra": {
"text-domain": "wpdesk-rating-petition",
"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": {
"test": "echo composer is alive",
"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,19 @@
<?php
namespace FSVendor\WPDesk\RepositoryRating\DisplayStrategy;
/**
* DisplayDecision - always display.
*/
class AlwaysDisplayDisplayDecision implements \FSVendor\WPDesk\RepositoryRating\DisplayStrategy\DisplayDecision
{
/**
* Should display?
*
* @return bool
*/
public function should_display() : bool
{
return \true;
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace FSVendor\WPDesk\RepositoryRating\DisplayStrategy;
/**
* ShouldDisplay interface.
*/
interface DisplayDecision
{
/**
* Returns true when element should be displayed.
*
* @return bool
*/
public function should_display() : bool;
}

View File

@@ -0,0 +1,43 @@
<?php
namespace FSVendor\WPDesk\RepositoryRating\DisplayStrategy;
/**
* DisplayDecision based on GET parameters.
*/
class GetParametersDisplayDecision implements \FSVendor\WPDesk\RepositoryRating\DisplayStrategy\DisplayDecision
{
/**
* Whether to show beacon on the page or not. Array of arrays with condition for _GET.
* Inner arrays mean AND, outer arrays mean OR conditions.
*
* ie. [ [ ... and ... and ... ] or [ ... and ... and ... ] or ... ]
*
* @var array
*/
private $conditions;
public function __construct(array $conditions)
{
$this->conditions = $conditions;
}
/**
* Should display?
*
* @return bool
*/
public function should_display() : bool
{
foreach ($this->conditions as $or_conditions) {
$display = \true;
foreach ($or_conditions as $parameter => $value) {
if (!isset($_GET[$parameter]) || $_GET[$parameter] !== $value) {
$display = \false;
}
}
if ($display) {
return $display;
}
}
return \false;
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace FSVendor\WPDesk\RepositoryRating\DisplayStrategy;
class ShippingMethodDisplayDecision implements \FSVendor\WPDesk\RepositoryRating\DisplayStrategy\DisplayDecision
{
/**
* @var \WC_Shipping_Zones
*/
private $shipping_zones;
/**
* @var string
*/
private $shipping_method_id;
/**
* @param \WC_Shipping_Zones $shipping_zones
* @param string $shipping_method_id
*/
public function __construct(\WC_Shipping_Zones $shipping_zones, string $shipping_method_id)
{
$this->shipping_zones = $shipping_zones;
$this->shipping_method_id = $shipping_method_id;
}
/**
* @inheritDoc
*/
public function should_display() : bool
{
if ($this->is_in_shipping_settings()) {
if ($this->is_get_parameter_with_value('section', $this->shipping_method_id)) {
return \true;
}
if (isset($_GET['instance_id'])) {
$shipping_method = $this->shipping_zones::get_shipping_method(\sanitize_key($_GET['instance_id']));
if ($shipping_method instanceof \WC_Shipping_Method) {
return $shipping_method->id === $this->shipping_method_id;
}
}
}
return \false;
}
private function is_in_shipping_settings() : bool
{
if ($this->is_get_parameter_with_value('page', 'wc-settings') && $this->is_get_parameter_with_value('tab', 'shipping')) {
return \true;
}
return \false;
}
private function is_get_parameter_with_value(string $parameter, string $value) : bool
{
return isset($_GET[$parameter]) && $_GET[$parameter] === $value;
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace FSVendor\WPDesk\RepositoryRating;
/**
* Petition text generator.
*/
interface PetitionText
{
/**
* Returns petition text.
*
* @return string
*/
public function get_petition_text() : string;
}

View File

@@ -0,0 +1,195 @@
<?php
/**
* Repository rating.
*
* @package Flexible Shipping Fedex
*/
namespace FSVendor\WPDesk\RepositoryRating;
use FSVendor\WPDesk\PluginBuilder\Plugin\Hookable;
/**
* Can display rating notices based on watcher time time.
*/
class RatingPetitionNotice implements \FSVendor\WPDesk\PluginBuilder\Plugin\Hookable
{
const CLOSE_TEMPORARY_NOTICE = 'close-temporary-notice';
const NOTICES_OFFSET = 1209600;
// Two weeks in seconds.
/**
* First notice start time.
*
* @var string
*/
private $first_notice_start_time;
/**
* Second notice start time.
*
* @var string
*/
private $second_notice_start_time;
/**
* Unique namespace for id/option generation
*
* @var string
*/
private $namespace;
/**
* Plugin name in notice content.
*
* @var string
*/
private $plugin_name;
/**
* Url to redirect when user wants to send a rate.
*
* @var string
*/
private $rate_url;
/**
* RatingPetitionNotice constructor.
*
* @param TimeWatcher $method_watcher Watcher to decide when should first notice be displayed.
* @param string $namespace Unique namespace for id/option generation.
* @param string $plugin_name Plugin name in notice content.
* @param string $rate_url Url to redirect when user wants to send a rate.
*/
public function __construct(\FSVendor\WPDesk\RepositoryRating\TimeWatcher $method_watcher, $namespace, $plugin_name, $rate_url = '')
{
$this->namespace = $namespace;
$this->plugin_name = $plugin_name;
$this->rate_url = $rate_url;
$this->second_notice_start_time = \get_option($this->prepare_notice_start_time_option_name(), '');
$this->first_notice_start_time = '';
if ('' === $this->second_notice_start_time && '' !== $method_watcher->get_creation_time()) {
$this->first_notice_start_time = \gmdate('Y-m-d H:i:s', \strtotime($method_watcher->get_creation_time()) + self::NOTICES_OFFSET);
}
}
/**
* Return option name for second notice timer.
*
* @return string
*/
private function prepare_notice_start_time_option_name()
{
return $this->namespace . '_second_notice_time';
}
/**
* Init hooks (actions and filters).
*/
public function hooks()
{
\add_action('admin_notices', array($this, 'maybe_show_first_notice'));
\add_action('admin_notices', array($this, 'maybe_show_second_notice'));
\add_action('wpdesk_notice_dismissed_notice', array($this, 'maybe_start_second_notice_on_dismiss_first_notice'), 10, 2);
}
/**
* Maybe reset counter.
*
* @param string $notice_name .
* @param string $source .
*/
public function maybe_start_second_notice_on_dismiss_first_notice($notice_name, $source = null)
{
if ($this->prepare_notice_name(1) === $notice_name && (empty($source) || self::CLOSE_TEMPORARY_NOTICE === $source)) {
\update_option($this->prepare_notice_start_time_option_name(), \gmdate('Y-m-d H:i:s', \intval(\current_time('timestamp')) + self::NOTICES_OFFSET));
}
}
/**
* Returns notice name for Notice class.
*
* @param int $notice_number Notice number as there are two notices.
*
* @return string
*/
private function prepare_notice_name($notice_number)
{
return $this->namespace . '_rating_' . $notice_number;
}
/**
* Maybe show first notice.
*/
public function maybe_show_first_notice()
{
if ($this->should_display_notice()) {
if ('' !== $this->first_notice_start_time && \current_time('mysql') > $this->first_notice_start_time) {
$this->show_notice($this->prepare_notice_name(1));
}
}
}
/**
* Should display notice.
*
* @return bool
*/
private function should_display_notice()
{
$current_screen = \get_current_screen();
$display_on_screens = ['shop_order', 'edit-shop_order', 'woocommerce_page_wc-settings'];
if (!empty($current_screen) && \in_array($current_screen->id, $display_on_screens, \true)) {
return \true;
}
return \false;
}
/**
* Show notice.
*
* @param string $notice_name .
*/
private function show_notice($notice_name)
{
new \FSVendor\WPDesk\Notice\PermanentDismissibleNotice($this->get_notice_content(), $notice_name, \FSVendor\WPDesk\Notice\Notice::NOTICE_TYPE_INFO);
}
/**
* Get notice content.
*
* @return string
*/
private function get_notice_content()
{
// Translators: plugin name.
$content = \sprintf(\__('Awesome, you\'ve been using %s for more than 2 weeks. Could you please do me a BIG favor and give it a 5-star rating on WordPress? ~ Peter', 'flexible-shipping'), $this->plugin_name);
$content .= '<br/>';
$content .= \implode(' | ', $this->action_links());
return $content;
}
/**
* Action links
*
* @return array
*/
protected function action_links()
{
$actions[] = \sprintf(
// phpcs:ignore
// Translators: link.
\__('%1$sOk, you deserved it%2$s', 'flexible-shipping'),
'<a class="fs-response-deserved" target="_blank" href="' . \esc_url($this->rate_url) . '">',
'</a>'
);
$actions[] = \sprintf(
// Translators: link.
\__('%1$sNope, maybe later%2$s', 'flexible-shipping'),
'<a class="fs-response-close-temporary-notice notice-dismiss-link" data-source="' . self::CLOSE_TEMPORARY_NOTICE . '" href="#">',
'</a>'
);
$actions[] = \sprintf(
// Translators: link.
\__('%1$sI already did%2$s', 'flexible-shipping'),
'<a class="fs-response-already-did notice-dismiss-link" data-source="already-did" href="#">',
'</a>'
);
return $actions;
}
/**
* Maybe show second notice.
*/
public function maybe_show_second_notice()
{
if ($this->should_display_notice()) {
if ('' !== $this->second_notice_start_time && \current_time('mysql') > $this->second_notice_start_time) {
$this->show_notice($this->prepare_notice_name(2));
}
}
}
}

View File

@@ -0,0 +1,49 @@
<?php
namespace FSVendor\WPDesk\RepositoryRating;
class RepositoryRatingPetitionText implements \FSVendor\WPDesk\RepositoryRating\PetitionText
{
/**
* @var string
*/
private $plugin_author;
/**
* @var string
*/
private $plugin_title;
/**
* @var string
*/
private $rating_url;
/**
* @var string
*/
private $text_align;
/**
* @param string $plugin_author
* @param string $plugin_title
* @param string $rating_url
* @param string $text_align
*/
public function __construct(string $plugin_author, string $plugin_title, string $rating_url, string $text_align)
{
$this->plugin_author = $plugin_author;
$this->plugin_title = $plugin_title;
$this->rating_url = $rating_url;
$this->text_align = $text_align;
}
/**
* @inheritDoc
*/
public function get_petition_text() : string
{
\ob_start();
$plugin_author = $this->plugin_author;
$plugin_title = $this->plugin_title;
$rating_url = $this->rating_url;
$text_align = $this->text_align;
include __DIR__ . '/views/html-text-petition.php';
return \ob_get_clean();
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace FSVendor\WPDesk\RepositoryRating;
use FSVendor\WPDesk\PluginBuilder\Plugin\Hookable;
use FSVendor\WPDesk\RepositoryRating\DisplayStrategy\DisplayDecision;
/**
* Can display text petition.
*/
class TextPetitionDisplayer implements \FSVendor\WPDesk\PluginBuilder\Plugin\Hookable
{
const SCRIPTS_VERSION = '2';
/**
* @var string
*/
private $display_on_action;
/**
* @var DisplayDecision
*/
private $display_decision;
/**
* @var PetitionText
*/
private $petition_text_generator;
/**
* @param string $display_on_action
* @param DisplayDecision $display_decision
* @param PetitionText $petition_text_generator
*/
public function __construct(string $display_on_action, \FSVendor\WPDesk\RepositoryRating\DisplayStrategy\DisplayDecision $display_decision, \FSVendor\WPDesk\RepositoryRating\PetitionText $petition_text_generator)
{
$this->display_on_action = $display_on_action;
$this->display_decision = $display_decision;
$this->petition_text_generator = $petition_text_generator;
}
public function hooks()
{
\add_action($this->display_on_action, [$this, 'display_petition_if_should']);
\add_action('admin_enqueue_scripts', [$this, 'enqueue_css_if_should']);
}
/**
* @internal
*/
public function enqueue_css_if_should()
{
if ($this->display_decision->should_display()) {
\wp_enqueue_style('flexible-shipping', \plugin_dir_url(__DIR__ . '/../assets/css/style.css') . 'style.css', array(), self::SCRIPTS_VERSION);
}
}
/**
* @internal
*/
public function display_petition_if_should()
{
if ($this->display_decision->should_display()) {
echo \wp_kses_post($this->petition_text_generator->get_petition_text());
}
}
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* TimeWatcher interface.
*
* @package WPDesk\RepositoryRating
*/
namespace FSVendor\WPDesk\RepositoryRating;
/**
* Simple interface for tracking creation time of certaing things.
*
* @package WPDesk\RepositoryRating
*/
interface TimeWatcher
{
/**
* Return creation time of watched item.
*
* @return string Returns date string. Can also return '' when no creation time is found.
*/
public function get_creation_time();
}

View File

@@ -0,0 +1,85 @@
<?php
/**
* Shipping method watcher.
*
* @package Flexible Shipping Fedex
*/
namespace FSVendor\WPDesk\RepositoryRating\TimeWatcher;
use FSVendor\WPDesk\PluginBuilder\Plugin\Hookable;
use FSVendor\WPDesk\RepositoryRating\TimeWatcher;
/**
* Can watch shipping method creation.
*/
class ShippingMethodGlobalSettingsWatcher implements \FSVendor\WPDesk\PluginBuilder\Plugin\Hookable, \FSVendor\WPDesk\RepositoryRating\TimeWatcher
{
const NO_CREATION_TIME = '';
/**
* Unique id of shipping method used in WC_Shipping_Method::id.
*
* @var string
*/
private $shipping_method_unique_id;
/**
* ShippingMethodGlobalSettingsWatcher constructor.
*
* @param string $shipping_method_unique_id Unique id of shipping method used in WC_Shipping_Method::id.
*/
public function __construct($shipping_method_unique_id)
{
$this->shipping_method_unique_id = $shipping_method_unique_id;
}
/**
* Init hooks (actions and filters).
*/
public function hooks()
{
\add_action("woocommerce_update_options_shipping_{$this->shipping_method_unique_id}", function () {
$this->watch_saved_settings();
});
}
/**
* Watch saved settings and save time when first saved.
*/
private function watch_saved_settings()
{
if (!$this->is_creation_time_saved()) {
$this->update_creation_time();
}
}
/**
* Is creation time saved or it's first time?.
*
* @return bool
*/
private function is_creation_time_saved()
{
return $this->get_creation_time() !== self::NO_CREATION_TIME;
}
/**
* Set ccreation time to now.
*/
private function update_creation_time()
{
\update_option($this->prepare_creation_time_option_name(), \current_time('mysql'));
}
/**
* Option name to save time in db.
*
* @return string
*/
private function prepare_creation_time_option_name()
{
return $this->shipping_method_unique_id . '_repository_creation_time';
}
/**
* Get first method creation time.
*
* @return string
*/
public function get_creation_time()
{
return \get_option($this->prepare_creation_time_option_name(), self::NO_CREATION_TIME);
}
}

View File

@@ -0,0 +1,169 @@
<?php
/**
* Shipping method watcher.
*
* @package Flexible Shipping Fedex
*/
namespace FSVendor\WPDesk\RepositoryRating\TimeWatcher;
use FSVendor\WPDesk\PluginBuilder\Plugin\Hookable;
use FSVendor\WPDesk\RepositoryRating\TimeWatcher;
/**
* Can watch shipping method creation.
*
* @todo This class is not tested yet. Should be used in UPS.
*/
class ShippingMethodInstanceWatcher implements \FSVendor\WPDesk\PluginBuilder\Plugin\Hookable, \FSVendor\WPDesk\RepositoryRating\TimeWatcher
{
/**
* First method added time.
*
* @var string
*/
private $first_method_creation_time = '';
/**
* First method watching.
*
* @var int
*/
private $first_method_watching = 0;
/**
* Unique namespace for id/option generation
*
* @var string
*/
private $namespace;
/**
* Name of shipping method ::class
*
* @var string
*/
private $shipping_method_name;
/**
* Name of WP option with plugin activation time
*
* @var string
*/
private $plugin_activation_time_option_name;
/**
* Date in string when functionality started and we can start watching
*
* @var string
*/
private $zero_date;
/**
* ShippingMethodInstanceWatcher constructor.
*
* @param string $namespace Unique namespace for id/option generation.
* @param string $plugin_activation_time_option_name Where in options is plugin activation time.
* @param string $zero_date When functionality started and we can start watching.
* @param string $shipping_method_name What method name is watched.
*/
public function __construct($namespace, $plugin_activation_time_option_name, $zero_date, $shipping_method_name)
{
$this->namespace = $namespace;
$this->plugin_activation_time_option_name = $plugin_activation_time_option_name;
$this->zero_date = $zero_date;
$this->shipping_method_name = $shipping_method_name;
}
/**
* Init hooks (actions and filters).
*/
public function hooks()
{
\add_action('admin_init', array($this, 'maybe_init_watching'), 10, 3);
\add_action('woocommerce_shipping_zone_method_added', array($this, 'watch_added_shipping_method'), 10, 3);
}
/**
* Init watching.
*/
public function maybe_init_watching()
{
$this->first_method_watching = \intval(\get_option($this->prepare_option_name_watching(), 0));
if (0 === $this->first_method_watching) {
$ups_free_activation_time = \get_option($this->plugin_activation_time_option_name, \current_time('mysql'));
if (\strtotime($ups_free_activation_time) < \strtotime($this->zero_date)) {
$this->init_watching_from_existing_shipping_methods();
}
\update_option($this->prepare_option_name_watching(), 1);
$this->first_method_watching = 1;
}
}
/**
* Returns options name for no idea what.
*
* @return string
*/
private function prepare_option_name_watching()
{
return $this->namespace . '_method_watching';
}
/**
* First time init watching.
* Sets first time to current time when Fedex shipping method already exists.
*/
private function init_watching_from_existing_shipping_methods()
{
$shipping_zones = \WC_Shipping_Zones::get_zones();
/** @var \WC_Shipping_Zone $shipping_zone */
// phpcs:ignore
foreach ($shipping_zones as $shipping_zone_data) {
$shipping_zone = \WC_Shipping_Zones::get_zone($shipping_zone_data['id']);
if ($shipping_zone && $shipping_zone instanceof \WC_Shipping_Zone) {
$shipping_methods = $shipping_zone->get_shipping_methods();
foreach ($shipping_methods as $shipping_method) {
if ($shipping_method instanceof $this->shipping_method_name) {
\update_option($this->prepare_option_name_method_created(), \current_time('mysql'));
}
}
}
}
}
/**
* Returns options name for info when shipping method was created.
*
* @return string
*/
private function prepare_option_name_method_created()
{
return $this->namespace . '_method_created';
}
/**
* Watch added shipping method.
* Set first time to current time when first UPS shipping method created.
*
* @param int $instance_id .
* @param string $type .
* @param int $zone_id .
*/
public function watch_added_shipping_method($instance_id, $type, $zone_id)
{
$this->first_method_creation_time = \get_option($this->prepare_option_name_method_created(), '');
if ($this->namespace === $type) {
if ('' === $this->first_method_creation_time) {
$this->first_method_creation_time = (string) \current_time('mysql');
\update_option($this->prepare_option_name_method_created(), $this->first_method_creation_time);
}
}
}
/**
* Get first method creation time.
*
* @return string
*/
public function get_creation_time()
{
$this->first_method_creation_time = \get_option($this->prepare_option_name_method_created(), '');
return $this->first_method_creation_time;
}
/**
* Returns date when functionality started and we can start watching.
*
* @return string
*/
private function get_watcher_zero_date()
{
return $this->zero_date;
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace FSVendor;
/**
* @var string $text_align
* @var string $plugin_author
* @var string $plugin_title
* @var string $rating_url
*/
?><div class="wpdesk-rating-petition" style="text-align: <?php
echo \esc_attr($text_align);
?>;">
<?php
echo \wp_kses_post(\sprintf(\__('Created with %1$s by %2$s - If you like %3$s you can %4$srate us %5$s in plugins repository &rarr;%6$s', 'flexible-shipping'), '<span class="heart">&hearts;</span>', $plugin_author, '<span class="plugin-title">' . $plugin_title . '</span>', '<a href="' . $rating_url . '" target="_blank">', '<span class="star">&#9733;&#9733;&#9733;&#9733;&#9733;</span><span class="plugins-repository">', '</span></a>'));
?>
</div><?php