Version Description
March 4, 2022 =
Security update. Fixes minor issues in the Freemius SDK (that handle licensing and plugin updates).
Download this release
Release Info
Developer | dgwyer |
Plugin | Simple Sitemap – Automatically Generate a Responsive Sitemap |
Version | 3.5.5 |
Comparing to | |
See all releases |
Code changes from version 3.5.4 to 3.5.5
- freemius/includes/class-freemius.php +26 -3
- freemius/includes/class-fs-api.php +663 -663
- freemius/includes/class-fs-options.php +430 -430
- freemius/includes/class-fs-plugin-updater.php +1561 -1561
- freemius/includes/class-fs-storage.php +531 -531
- freemius/includes/fs-core-functions.php +1415 -1415
- freemius/includes/fs-essential-functions.php +499 -499
- freemius/includes/fs-plugin-info-dialog.php +1644 -1644
- freemius/includes/i18n.php +605 -605
- freemius/includes/managers/class-fs-admin-notice-manager.php +7 -2
- freemius/includes/managers/class-fs-plugin-manager.php +219 -219
- freemius/includes/sdk/Exceptions/ArgumentNotExistException.php +5 -1
- freemius/includes/sdk/Exceptions/EmptyArgumentException.php +5 -1
- freemius/includes/sdk/Exceptions/Exception.php +5 -1
- freemius/includes/sdk/Exceptions/InvalidArgumentException.php +5 -1
- freemius/includes/sdk/Exceptions/OAuthException.php +5 -1
- freemius/includes/sdk/FreemiusBase.php +4 -0
- freemius/includes/sdk/FreemiusWordPress.php +4 -1
- freemius/languages/freemius-cs_CZ.mo +0 -0
- freemius/languages/freemius-da_DK.mo +0 -0
- freemius/languages/freemius-en.mo +0 -0
- freemius/languages/freemius-es_ES.mo +0 -0
- freemius/languages/freemius-fr_FR.mo +0 -0
- freemius/languages/freemius-he_IL.mo +0 -0
- freemius/languages/freemius-hu_HU.mo +0 -0
- freemius/languages/freemius-it_IT.mo +0 -0
- freemius/languages/freemius-ja.mo +0 -0
- freemius/languages/freemius-nl_NL.mo +0 -0
- freemius/languages/freemius-ru_RU.mo +0 -0
- freemius/languages/freemius-ta.mo +0 -0
- freemius/languages/freemius-zh_CN.mo +0 -0
- freemius/languages/freemius.pot +250 -258
- freemius/require.php +5 -1
- freemius/start.php +1 -1
- freemius/templates/account/partials/addon.php +6 -1
- freemius/templates/account/partials/site.php +351 -351
- freemius/templates/add-ons.php +501 -501
- freemius/templates/ajax-loader.php +6 -1
- freemius/templates/debug.php +8 -2
- freemius/templates/firewall-issues-js.php +10 -6
- freemius/templates/forms/data-debug-mode.php +212 -212
- freemius/templates/forms/optout.php +336 -336
- freemius/templates/forms/user-change.php +296 -296
- freemius/templates/partials/network-activation.php +6 -1
- freemius/templates/sticky-admin-notice-js.php +4 -2
- freemius/templates/tabs.php +189 -189
- lib/src/blocks/extend-blocks.js +2 -2
- readme.txt +5 -1
- simple-sitemap.php +1 -1
freemius/includes/class-freemius.php
CHANGED
@@ -3550,6 +3550,8 @@
|
|
3550 |
* @since 1.1.7.3
|
3551 |
*/
|
3552 |
static function _toggle_debug_mode() {
|
|
|
|
|
3553 |
if ( ! is_super_admin() ) {
|
3554 |
return;
|
3555 |
}
|
@@ -3571,10 +3573,19 @@
|
|
3571 |
* @since 1.2.1.6
|
3572 |
*/
|
3573 |
static function _get_debug_log() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3574 |
$logs = FS_Logger::load_db_logs(
|
3575 |
fs_request_get( 'filters', false, 'post' ),
|
3576 |
-
|
3577 |
-
|
3578 |
);
|
3579 |
|
3580 |
self::shoot_ajax_success( $logs );
|
@@ -4047,7 +4058,7 @@
|
|
4047 |
if ( empty( $unique_id ) || ! is_string( $unique_id ) ) {
|
4048 |
$key = fs_strip_url_protocol( get_site_url( $blog_id ) );
|
4049 |
|
4050 |
-
$secure_auth = SECURE_AUTH_KEY;
|
4051 |
if ( empty( $secure_auth ) ||
|
4052 |
false !== strpos( $secure_auth, ' ' ) ||
|
4053 |
'put your unique phrase here' === $secure_auth
|
@@ -4447,6 +4458,12 @@
|
|
4447 |
* @since 1.0.9
|
4448 |
*/
|
4449 |
function _email_about_firewall_issue() {
|
|
|
|
|
|
|
|
|
|
|
|
|
4450 |
$this->_admin_notices->remove_sticky( 'failed_connect_api' );
|
4451 |
|
4452 |
$pong = $this->ping();
|
@@ -4521,6 +4538,12 @@
|
|
4521 |
* @since 1.1.7.4
|
4522 |
*/
|
4523 |
function _retry_connectivity_test() {
|
|
|
|
|
|
|
|
|
|
|
|
|
4524 |
$this->_admin_notices->remove_sticky( 'failed_connect_api_first' );
|
4525 |
|
4526 |
$pong = $this->ping();
|
3550 |
* @since 1.1.7.3
|
3551 |
*/
|
3552 |
static function _toggle_debug_mode() {
|
3553 |
+
check_admin_referer( 'fs_toggle_debug_mode' );
|
3554 |
+
|
3555 |
if ( ! is_super_admin() ) {
|
3556 |
return;
|
3557 |
}
|
3573 |
* @since 1.2.1.6
|
3574 |
*/
|
3575 |
static function _get_debug_log() {
|
3576 |
+
check_admin_referer( 'fs_get_debug_log' );
|
3577 |
+
|
3578 |
+
if ( ! is_super_admin() ) {
|
3579 |
+
return;
|
3580 |
+
}
|
3581 |
+
|
3582 |
+
$limit = min( ! empty( $_POST['limit'] ) ? absint( $_POST['limit'] ) : 200, 200 );
|
3583 |
+
$offset = min( ! empty( $_POST['offset'] ) ? absint( $_POST['offset'] ) : 200, 200 );
|
3584 |
+
|
3585 |
$logs = FS_Logger::load_db_logs(
|
3586 |
fs_request_get( 'filters', false, 'post' ),
|
3587 |
+
$limit,
|
3588 |
+
$offset
|
3589 |
);
|
3590 |
|
3591 |
self::shoot_ajax_success( $logs );
|
4058 |
if ( empty( $unique_id ) || ! is_string( $unique_id ) ) {
|
4059 |
$key = fs_strip_url_protocol( get_site_url( $blog_id ) );
|
4060 |
|
4061 |
+
$secure_auth = defined( 'SECURE_AUTH_KEY' ) ? SECURE_AUTH_KEY : '';
|
4062 |
if ( empty( $secure_auth ) ||
|
4063 |
false !== strpos( $secure_auth, ' ' ) ||
|
4064 |
'put your unique phrase here' === $secure_auth
|
4458 |
* @since 1.0.9
|
4459 |
*/
|
4460 |
function _email_about_firewall_issue() {
|
4461 |
+
check_admin_referer( 'fs_resolve_firewall_issues' );
|
4462 |
+
|
4463 |
+
if ( ! current_user_can( is_multisite() ? 'manage_options' : 'activate_plugins' ) ) {
|
4464 |
+
return;
|
4465 |
+
}
|
4466 |
+
|
4467 |
$this->_admin_notices->remove_sticky( 'failed_connect_api' );
|
4468 |
|
4469 |
$pong = $this->ping();
|
4538 |
* @since 1.1.7.4
|
4539 |
*/
|
4540 |
function _retry_connectivity_test() {
|
4541 |
+
check_admin_referer( 'fs_retry_connectivity_test' );
|
4542 |
+
|
4543 |
+
if ( ! current_user_can( is_multisite() ? 'manage_options' : 'activate_plugins' ) ) {
|
4544 |
+
return;
|
4545 |
+
}
|
4546 |
+
|
4547 |
$this->_admin_notices->remove_sticky( 'failed_connect_api_first' );
|
4548 |
|
4549 |
$pong = $this->ping();
|
freemius/includes/class-fs-api.php
CHANGED
@@ -1,664 +1,664 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* @package Freemius
|
4 |
-
* @copyright Copyright (c) 2015, Freemius, Inc.
|
5 |
-
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
|
6 |
-
* @since 1.0.4
|
7 |
-
*/
|
8 |
-
|
9 |
-
if ( ! defined( 'ABSPATH' ) ) {
|
10 |
-
exit;
|
11 |
-
}
|
12 |
-
|
13 |
-
/**
|
14 |
-
* Class FS_Api
|
15 |
-
*
|
16 |
-
* Wraps Freemius API SDK to handle:
|
17 |
-
* 1. Clock sync.
|
18 |
-
* 2. Fallback to HTTP when HTTPS fails.
|
19 |
-
* 3. Adds caching layer to GET requests.
|
20 |
-
* 4. Adds consistency for failed requests by using last cached version.
|
21 |
-
*/
|
22 |
-
class FS_Api {
|
23 |
-
/**
|
24 |
-
* @var FS_Api[]
|
25 |
-
*/
|
26 |
-
private static $_instances = array();
|
27 |
-
|
28 |
-
/**
|
29 |
-
* @var FS_Option_Manager Freemius options, options-manager.
|
30 |
-
*/
|
31 |
-
private static $_options;
|
32 |
-
|
33 |
-
/**
|
34 |
-
* @var FS_Cache_Manager API Caching layer
|
35 |
-
*/
|
36 |
-
private static $_cache;
|
37 |
-
|
38 |
-
/**
|
39 |
-
* @var int Clock diff in seconds between current server to API server.
|
40 |
-
*/
|
41 |
-
private static $_clock_diff;
|
42 |
-
|
43 |
-
/**
|
44 |
-
* @var Freemius_Api_WordPress
|
45 |
-
*/
|
46 |
-
private $_api;
|
47 |
-
|
48 |
-
/**
|
49 |
-
* @var string
|
50 |
-
*/
|
51 |
-
private $_slug;
|
52 |
-
|
53 |
-
/**
|
54 |
-
* @var FS_Logger
|
55 |
-
* @since 1.0.4
|
56 |
-
*/
|
57 |
-
private $_logger;
|
58 |
-
|
59 |
-
/**
|
60 |
-
* @author Leo Fajardo (@leorw)
|
61 |
-
* @since 2.3.0
|
62 |
-
*
|
63 |
-
* @var string
|
64 |
-
*/
|
65 |
-
private $_sdk_version;
|
66 |
-
|
67 |
-
/**
|
68 |
-
* @param string $slug
|
69 |
-
* @param string $scope 'app', 'developer', 'user' or 'install'.
|
70 |
-
* @param number $id Element's id.
|
71 |
-
* @param string $public_key Public key.
|
72 |
-
* @param bool $is_sandbox
|
73 |
-
* @param bool|string $secret_key Element's secret key.
|
74 |
-
* @param null|string $sdk_version
|
75 |
-
*
|
76 |
-
* @return FS_Api
|
77 |
-
*/
|
78 |
-
static function instance(
|
79 |
-
$slug,
|
80 |
-
$scope,
|
81 |
-
$id,
|
82 |
-
$public_key,
|
83 |
-
$is_sandbox,
|
84 |
-
$secret_key = false,
|
85 |
-
$sdk_version = null
|
86 |
-
) {
|
87 |
-
$identifier = md5( $slug . $scope . $id . $public_key . ( is_string( $secret_key ) ? $secret_key : '' ) . json_encode( $is_sandbox ) );
|
88 |
-
|
89 |
-
if ( ! isset( self::$_instances[ $identifier ] ) ) {
|
90 |
-
self::_init();
|
91 |
-
|
92 |
-
self::$_instances[ $identifier ] = new FS_Api( $slug, $scope, $id, $public_key, $secret_key, $is_sandbox, $sdk_version );
|
93 |
-
}
|
94 |
-
|
95 |
-
return self::$_instances[ $identifier ];
|
96 |
-
}
|
97 |
-
|
98 |
-
private static function _init() {
|
99 |
-
if ( isset( self::$_options ) ) {
|
100 |
-
return;
|
101 |
-
}
|
102 |
-
|
103 |
-
if ( ! class_exists( 'Freemius_Api_WordPress' ) ) {
|
104 |
-
require_once WP_FS__DIR_SDK . '/FreemiusWordPress.php';
|
105 |
-
}
|
106 |
-
|
107 |
-
self::$_options = FS_Option_Manager::get_manager( WP_FS__OPTIONS_OPTION_NAME, true, true );
|
108 |
-
self::$_cache = FS_Cache_Manager::get_manager( WP_FS__API_CACHE_OPTION_NAME );
|
109 |
-
|
110 |
-
self::$_clock_diff = self::$_options->get_option( 'api_clock_diff', 0 );
|
111 |
-
Freemius_Api_WordPress::SetClockDiff( self::$_clock_diff );
|
112 |
-
|
113 |
-
if ( self::$_options->get_option( 'api_force_http', false ) ) {
|
114 |
-
Freemius_Api_WordPress::SetHttp();
|
115 |
-
}
|
116 |
-
}
|
117 |
-
|
118 |
-
/**
|
119 |
-
* @param string $slug
|
120 |
-
* @param string $scope 'app', 'developer', 'user' or 'install'.
|
121 |
-
* @param number $id Element's id.
|
122 |
-
* @param string $public_key Public key.
|
123 |
-
* @param bool|string $secret_key Element's secret key.
|
124 |
-
* @param bool $is_sandbox
|
125 |
-
* @param null|string $sdk_version
|
126 |
-
*/
|
127 |
-
private function __construct(
|
128 |
-
$slug,
|
129 |
-
$scope,
|
130 |
-
$id,
|
131 |
-
$public_key,
|
132 |
-
$secret_key,
|
133 |
-
$is_sandbox,
|
134 |
-
$sdk_version
|
135 |
-
) {
|
136 |
-
$this->_api = new Freemius_Api_WordPress( $scope, $id, $public_key, $secret_key, $is_sandbox );
|
137 |
-
|
138 |
-
$this->_slug = $slug;
|
139 |
-
$this->_sdk_version = $sdk_version;
|
140 |
-
$this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $slug . '_api', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
|
141 |
-
}
|
142 |
-
|
143 |
-
/**
|
144 |
-
* Find clock diff between server and API server, and store the diff locally.
|
145 |
-
*
|
146 |
-
* @param bool|int $diff
|
147 |
-
*
|
148 |
-
* @return bool|int False if clock diff didn't change, otherwise returns the clock diff in seconds.
|
149 |
-
*/
|
150 |
-
private function _sync_clock_diff( $diff = false ) {
|
151 |
-
$this->_logger->entrance();
|
152 |
-
|
153 |
-
// Sync clock and store.
|
154 |
-
$new_clock_diff = ( false === $diff ) ?
|
155 |
-
Freemius_Api_WordPress::FindClockDiff() :
|
156 |
-
$diff;
|
157 |
-
|
158 |
-
if ( $new_clock_diff === self::$_clock_diff ) {
|
159 |
-
return false;
|
160 |
-
}
|
161 |
-
|
162 |
-
self::$_clock_diff = $new_clock_diff;
|
163 |
-
|
164 |
-
// Update API clock's diff.
|
165 |
-
Freemius_Api_WordPress::SetClockDiff( self::$_clock_diff );
|
166 |
-
|
167 |
-
// Store new clock diff in storage.
|
168 |
-
self::$_options->set_option( 'api_clock_diff', self::$_clock_diff, true );
|
169 |
-
|
170 |
-
return $new_clock_diff;
|
171 |
-
}
|
172 |
-
|
173 |
-
/**
|
174 |
-
* Override API call to enable retry with servers' clock auto sync method.
|
175 |
-
*
|
176 |
-
* @param string $path
|
177 |
-
* @param string $method
|
178 |
-
* @param array $params
|
179 |
-
* @param bool $retry Is in retry or first call attempt.
|
180 |
-
*
|
181 |
-
* @return array|mixed|string|void
|
182 |
-
*/
|
183 |
-
private function _call( $path, $method = 'GET', $params = array(), $retry = false ) {
|
184 |
-
$this->_logger->entrance( $method . ':' . $path );
|
185 |
-
|
186 |
-
if ( self::is_temporary_down() ) {
|
187 |
-
$result = $this->get_temporary_unavailable_error();
|
188 |
-
} else {
|
189 |
-
/**
|
190 |
-
* @since 2.3.0 Include the SDK version with all API requests that going through the API manager. IMPORTANT: Only pass the SDK version if the caller didn't include it yet.
|
191 |
-
*/
|
192 |
-
if ( ! empty( $this->_sdk_version ) ) {
|
193 |
-
if ( false === strpos( $path, 'sdk_version=' ) &&
|
194 |
-
! isset( $params['sdk_version'] )
|
195 |
-
) {
|
196 |
-
// Always add the sdk_version param in the querystring. DO NOT INCLUDE IT IN THE BODY PARAMS, OTHERWISE, IT MAY LEAD TO AN UNEXPECTED PARAMS PARSING IN CASES WHERE THE $params IS A REGULAR NON-ASSOCIATIVE ARRAY.
|
197 |
-
$path = add_query_arg( 'sdk_version', $this->_sdk_version, $path );
|
198 |
-
}
|
199 |
-
}
|
200 |
-
|
201 |
-
$result = $this->_api->Api( $path, $method, $params );
|
202 |
-
|
203 |
-
if ( null !== $result &&
|
204 |
-
isset( $result->error ) &&
|
205 |
-
isset( $result->error->code ) &&
|
206 |
-
'request_expired' === $result->error->code
|
207 |
-
) {
|
208 |
-
if ( ! $retry ) {
|
209 |
-
$diff = isset( $result->error->timestamp ) ?
|
210 |
-
( time() - strtotime( $result->error->timestamp ) ) :
|
211 |
-
false;
|
212 |
-
|
213 |
-
// Try to sync clock diff.
|
214 |
-
if ( false !== $this->_sync_clock_diff( $diff ) ) {
|
215 |
-
// Retry call with new synced clock.
|
216 |
-
return $this->_call( $path, $method, $params, true );
|
217 |
-
}
|
218 |
-
}
|
219 |
-
}
|
220 |
-
}
|
221 |
-
|
222 |
-
if ( $this->_logger->is_on() && self::is_api_error( $result ) ) {
|
223 |
-
// Log API errors.
|
224 |
-
$this->_logger->api_error( $result );
|
225 |
-
}
|
226 |
-
|
227 |
-
return $result;
|
228 |
-
}
|
229 |
-
|
230 |
-
/**
|
231 |
-
* Override API call to wrap it in servers' clock sync method.
|
232 |
-
*
|
233 |
-
* @param string $path
|
234 |
-
* @param string $method
|
235 |
-
* @param array $params
|
236 |
-
*
|
237 |
-
* @return array|mixed|string|void
|
238 |
-
* @throws Freemius_Exception
|
239 |
-
*/
|
240 |
-
function call( $path, $method = 'GET', $params = array() ) {
|
241 |
-
return $this->_call( $path, $method, $params );
|
242 |
-
}
|
243 |
-
|
244 |
-
/**
|
245 |
-
* Get API request URL signed via query string.
|
246 |
-
*
|
247 |
-
* @param string $path
|
248 |
-
*
|
249 |
-
* @return string
|
250 |
-
*/
|
251 |
-
function get_signed_url( $path ) {
|
252 |
-
return $this->_api->GetSignedUrl( $path );
|
253 |
-
}
|
254 |
-
|
255 |
-
/**
|
256 |
-
* @param string $path
|
257 |
-
* @param bool $flush
|
258 |
-
* @param int $expiration (optional) Time until expiration in seconds from now, defaults to 24 hours
|
259 |
-
*
|
260 |
-
* @return stdClass|mixed
|
261 |
-
*/
|
262 |
-
function get( $path = '/', $flush = false, $expiration = WP_FS__TIME_24_HOURS_IN_SEC ) {
|
263 |
-
$this->_logger->entrance( $path );
|
264 |
-
|
265 |
-
$cache_key = $this->get_cache_key( $path );
|
266 |
-
|
267 |
-
// Always flush during development.
|
268 |
-
if ( WP_FS__DEV_MODE || $this->_api->IsSandbox() ) {
|
269 |
-
$flush = true;
|
270 |
-
}
|
271 |
-
|
272 |
-
$cached_result = self::$_cache->get( $cache_key );
|
273 |
-
|
274 |
-
if ( $flush || ! self::$_cache->has_valid( $cache_key, $expiration ) ) {
|
275 |
-
$result = $this->call( $path );
|
276 |
-
|
277 |
-
if ( ! is_object( $result ) || isset( $result->error ) ) {
|
278 |
-
// Api returned an error.
|
279 |
-
if ( is_object( $cached_result ) &&
|
280 |
-
! isset( $cached_result->error )
|
281 |
-
) {
|
282 |
-
// If there was an error during a newer data fetch,
|
283 |
-
// fallback to older data version.
|
284 |
-
$result = $cached_result;
|
285 |
-
|
286 |
-
if ( $this->_logger->is_on() ) {
|
287 |
-
$this->_logger->warn( 'Fallback to cached API result: ' . var_export( $cached_result, true ) );
|
288 |
-
}
|
289 |
-
} else {
|
290 |
-
if ( is_object( $result ) && isset( $result->error->http ) && 404 == $result->error->http ) {
|
291 |
-
/**
|
292 |
-
* If the response code is 404, cache the result for half of the `$expiration`.
|
293 |
-
*
|
294 |
-
* @author Leo Fajardo (@leorw)
|
295 |
-
* @since 2.2.4
|
296 |
-
*/
|
297 |
-
$expiration /= 2;
|
298 |
-
} else {
|
299 |
-
// If no older data version and the response code is not 404, return result without
|
300 |
-
// caching the error.
|
301 |
-
return $result;
|
302 |
-
}
|
303 |
-
}
|
304 |
-
}
|
305 |
-
|
306 |
-
self::$_cache->set( $cache_key, $result, $expiration );
|
307 |
-
|
308 |
-
$cached_result = $result;
|
309 |
-
} else {
|
310 |
-
$this->_logger->log( 'Using cached API result.' );
|
311 |
-
}
|
312 |
-
|
313 |
-
return $cached_result;
|
314 |
-
}
|
315 |
-
|
316 |
-
/**
|
317 |
-
* Check if there's a cached version of the API request.
|
318 |
-
*
|
319 |
-
* @author Vova Feldman (@svovaf)
|
320 |
-
* @since 1.2.1
|
321 |
-
*
|
322 |
-
* @param string $path
|
323 |
-
* @param string $method
|
324 |
-
* @param array $params
|
325 |
-
*
|
326 |
-
* @return bool
|
327 |
-
*/
|
328 |
-
function is_cached( $path, $method = 'GET', $params = array() ) {
|
329 |
-
$cache_key = $this->get_cache_key( $path, $method, $params );
|
330 |
-
|
331 |
-
return self::$_cache->has_valid( $cache_key );
|
332 |
-
}
|
333 |
-
|
334 |
-
/**
|
335 |
-
* Invalidate a cached version of the API request.
|
336 |
-
*
|
337 |
-
* @author Vova Feldman (@svovaf)
|
338 |
-
* @since 1.2.1.5
|
339 |
-
*
|
340 |
-
* @param string $path
|
341 |
-
* @param string $method
|
342 |
-
* @param array $params
|
343 |
-
*/
|
344 |
-
function purge_cache( $path, $method = 'GET', $params = array() ) {
|
345 |
-
$this->_logger->entrance( "{$method}:{$path}" );
|
346 |
-
|
347 |
-
$cache_key = $this->get_cache_key( $path, $method, $params );
|
348 |
-
|
349 |
-
self::$_cache->purge( $cache_key );
|
350 |
-
}
|
351 |
-
|
352 |
-
/**
|
353 |
-
* Invalidate a cached version of the API request.
|
354 |
-
*
|
355 |
-
* @author Vova Feldman (@svovaf)
|
356 |
-
* @since 2.0.0
|
357 |
-
*
|
358 |
-
* @param string $path
|
359 |
-
* @param int $expiration
|
360 |
-
* @param string $method
|
361 |
-
* @param array $params
|
362 |
-
*/
|
363 |
-
function update_cache_expiration( $path, $expiration = WP_FS__TIME_24_HOURS_IN_SEC, $method = 'GET', $params = array() ) {
|
364 |
-
$this->_logger->entrance( "{$method}:{$path}:{$expiration}" );
|
365 |
-
|
366 |
-
$cache_key = $this->get_cache_key( $path, $method, $params );
|
367 |
-
|
368 |
-
self::$_cache->update_expiration( $cache_key, $expiration );
|
369 |
-
}
|
370 |
-
|
371 |
-
/**
|
372 |
-
* @param string $path
|
373 |
-
* @param string $method
|
374 |
-
* @param array $params
|
375 |
-
*
|
376 |
-
* @return string
|
377 |
-
* @throws \Freemius_Exception
|
378 |
-
*/
|
379 |
-
private function get_cache_key( $path, $method = 'GET', $params = array() ) {
|
380 |
-
$canonized = $this->_api->CanonizePath( $path );
|
381 |
-
// $exploded = explode('/', $canonized);
|
382 |
-
// return $method . '_' . array_pop($exploded) . '_' . md5($canonized . json_encode($params));
|
383 |
-
return strtolower( $method . ':' . $canonized ) . ( ! empty( $params ) ? '#' . md5( json_encode( $params ) ) : '' );
|
384 |
-
}
|
385 |
-
|
386 |
-
/**
|
387 |
-
* Test API connectivity.
|
388 |
-
*
|
389 |
-
* @author Vova Feldman (@svovaf)
|
390 |
-
* @since 1.0.9 If fails, try to fallback to HTTP.
|
391 |
-
* @since 1.1.6 Added a 5-min caching mechanism, to prevent from overloading the server if the API if
|
392 |
-
* temporary down.
|
393 |
-
*
|
394 |
-
* @return bool True if successful connectivity to the API.
|
395 |
-
*/
|
396 |
-
static function test() {
|
397 |
-
self::_init();
|
398 |
-
|
399 |
-
$cache_key = 'ping_test';
|
400 |
-
|
401 |
-
$test = self::$_cache->get_valid( $cache_key, null );
|
402 |
-
|
403 |
-
if ( is_null( $test ) ) {
|
404 |
-
$test = Freemius_Api_WordPress::Test();
|
405 |
-
|
406 |
-
if ( false === $test && Freemius_Api_WordPress::IsHttps() ) {
|
407 |
-
// Fallback to HTTP, since HTTPS fails.
|
408 |
-
Freemius_Api_WordPress::SetHttp();
|
409 |
-
|
410 |
-
self::$_options->set_option( 'api_force_http', true, true );
|
411 |
-
|
412 |
-
$test = Freemius_Api_WordPress::Test();
|
413 |
-
|
414 |
-
if ( false === $test ) {
|
415 |
-
/**
|
416 |
-
* API connectivity test fail also in HTTP request, therefore,
|
417 |
-
* fallback to HTTPS to keep connection secure.
|
418 |
-
*
|
419 |
-
* @since 1.1.6
|
420 |
-
*/
|
421 |
-
self::$_options->set_option( 'api_force_http', false, true );
|
422 |
-
}
|
423 |
-
}
|
424 |
-
|
425 |
-
self::$_cache->set( $cache_key, $test, WP_FS__TIME_5_MIN_IN_SEC );
|
426 |
-
}
|
427 |
-
|
428 |
-
return $test;
|
429 |
-
}
|
430 |
-
|
431 |
-
/**
|
432 |
-
* Check if API is temporary down.
|
433 |
-
*
|
434 |
-
* @author Vova Feldman (@svovaf)
|
435 |
-
* @since 1.1.6
|
436 |
-
*
|
437 |
-
* @return bool
|
438 |
-
*/
|
439 |
-
static function is_temporary_down() {
|
440 |
-
self::_init();
|
441 |
-
|
442 |
-
$test = self::$_cache->get_valid( 'ping_test', null );
|
443 |
-
|
444 |
-
return ( false === $test );
|
445 |
-
}
|
446 |
-
|
447 |
-
/**
|
448 |
-
* @author Vova Feldman (@svovaf)
|
449 |
-
* @since 1.1.6
|
450 |
-
*
|
451 |
-
* @return object
|
452 |
-
*/
|
453 |
-
private function get_temporary_unavailable_error() {
|
454 |
-
return (object) array(
|
455 |
-
'error' => (object) array(
|
456 |
-
'type' => 'TemporaryUnavailable',
|
457 |
-
'message' => 'API is temporary unavailable, please retry in ' . ( self::$_cache->get_record_expiration( 'ping_test' ) - WP_FS__SCRIPT_START_TIME ) . ' sec.',
|
458 |
-
'code' => 'temporary_unavailable',
|
459 |
-
'http' => 503
|
460 |
-
)
|
461 |
-
);
|
462 |
-
}
|
463 |
-
|
464 |
-
/**
|
465 |
-
* Ping API for connectivity test, and return result object.
|
466 |
-
*
|
467 |
-
* @author Vova Feldman (@svovaf)
|
468 |
-
* @since 1.0.9
|
469 |
-
*
|
470 |
-
* @param null|string $unique_anonymous_id
|
471 |
-
* @param array $params
|
472 |
-
*
|
473 |
-
* @return object
|
474 |
-
*/
|
475 |
-
function ping( $unique_anonymous_id = null, $params = array() ) {
|
476 |
-
$this->_logger->entrance();
|
477 |
-
|
478 |
-
if ( self::is_temporary_down() ) {
|
479 |
-
return $this->get_temporary_unavailable_error();
|
480 |
-
}
|
481 |
-
|
482 |
-
$pong = is_null( $unique_anonymous_id ) ?
|
483 |
-
Freemius_Api_WordPress::Ping() :
|
484 |
-
$this->_call( 'ping.json?' . http_build_query( array_merge(
|
485 |
-
array( 'uid' => $unique_anonymous_id ),
|
486 |
-
$params
|
487 |
-
) ) );
|
488 |
-
|
489 |
-
if ( $this->is_valid_ping( $pong ) ) {
|
490 |
-
return $pong;
|
491 |
-
}
|
492 |
-
|
493 |
-
if ( self::should_try_with_http( $pong ) ) {
|
494 |
-
// Fallback to HTTP, since HTTPS fails.
|
495 |
-
Freemius_Api_WordPress::SetHttp();
|
496 |
-
|
497 |
-
self::$_options->set_option( 'api_force_http', true, true );
|
498 |
-
|
499 |
-
$pong = is_null( $unique_anonymous_id ) ?
|
500 |
-
Freemius_Api_WordPress::Ping() :
|
501 |
-
$this->_call( 'ping.json?' . http_build_query( array_merge(
|
502 |
-
array( 'uid' => $unique_anonymous_id ),
|
503 |
-
$params
|
504 |
-
) ) );
|
505 |
-
|
506 |
-
if ( ! $this->is_valid_ping( $pong ) ) {
|
507 |
-
self::$_options->set_option( 'api_force_http', false, true );
|
508 |
-
}
|
509 |
-
}
|
510 |
-
|
511 |
-
return $pong;
|
512 |
-
}
|
513 |
-
|
514 |
-
/**
|
515 |
-
* Check if based on the API result we should try
|
516 |
-
* to re-run the same request with HTTP instead of HTTPS.
|
517 |
-
*
|
518 |
-
* @author Vova Feldman (@svovaf)
|
519 |
-
* @since 1.1.6
|
520 |
-
*
|
521 |
-
* @param $result
|
522 |
-
*
|
523 |
-
* @return bool
|
524 |
-
*/
|
525 |
-
private static function should_try_with_http( $result ) {
|
526 |
-
if ( ! Freemius_Api_WordPress::IsHttps() ) {
|
527 |
-
return false;
|
528 |
-
}
|
529 |
-
|
530 |
-
return ( ! is_object( $result ) ||
|
531 |
-
! isset( $result->error ) ||
|
532 |
-
! isset( $result->error->code ) ||
|
533 |
-
! in_array( $result->error->code, array(
|
534 |
-
'curl_missing',
|
535 |
-
'cloudflare_ddos_protection',
|
536 |
-
'maintenance_mode',
|
537 |
-
'squid_cache_block',
|
538 |
-
'too_many_requests',
|
539 |
-
) ) );
|
540 |
-
|
541 |
-
}
|
542 |
-
|
543 |
-
/**
|
544 |
-
* Check if valid ping request result.
|
545 |
-
*
|
546 |
-
* @author Vova Feldman (@svovaf)
|
547 |
-
* @since 1.1.1
|
548 |
-
*
|
549 |
-
* @param mixed $pong
|
550 |
-
*
|
551 |
-
* @return bool
|
552 |
-
*/
|
553 |
-
function is_valid_ping( $pong ) {
|
554 |
-
return Freemius_Api_WordPress::Test( $pong );
|
555 |
-
}
|
556 |
-
|
557 |
-
function get_url( $path = '' ) {
|
558 |
-
return Freemius_Api_WordPress::GetUrl( $path, $this->_api->IsSandbox() );
|
559 |
-
}
|
560 |
-
|
561 |
-
/**
|
562 |
-
* Clear API cache.
|
563 |
-
*
|
564 |
-
* @author Vova Feldman (@svovaf)
|
565 |
-
* @since 1.0.9
|
566 |
-
*/
|
567 |
-
static function clear_cache() {
|
568 |
-
self::_init();
|
569 |
-
|
570 |
-
self::$_cache = FS_Cache_Manager::get_manager( WP_FS__API_CACHE_OPTION_NAME );
|
571 |
-
self::$_cache->clear();
|
572 |
-
}
|
573 |
-
|
574 |
-
#----------------------------------------------------------------------------------
|
575 |
-
#region Error Handling
|
576 |
-
#----------------------------------------------------------------------------------
|
577 |
-
|
578 |
-
/**
|
579 |
-
* @author Vova Feldman (@svovaf)
|
580 |
-
* @since 1.2.1.5
|
581 |
-
*
|
582 |
-
* @param mixed $result
|
583 |
-
*
|
584 |
-
* @return bool Is API result contains an error.
|
585 |
-
*/
|
586 |
-
static function is_api_error( $result ) {
|
587 |
-
return ( is_object( $result ) && isset( $result->error ) ) ||
|
588 |
-
is_string( $result );
|
589 |
-
}
|
590 |
-
|
591 |
-
/**
|
592 |
-
* @author Vova Feldman (@svovaf)
|
593 |
-
* @since 2.0.0
|
594 |
-
*
|
595 |
-
* @param mixed $result
|
596 |
-
*
|
597 |
-
* @return bool Is API result contains an error.
|
598 |
-
*/
|
599 |
-
static function is_api_error_object( $result ) {
|
600 |
-
return (
|
601 |
-
is_object( $result ) &&
|
602 |
-
isset( $result->error ) &&
|
603 |
-
isset( $result->error->message )
|
604 |
-
);
|
605 |
-
}
|
606 |
-
|
607 |
-
/**
|
608 |
-
* Checks if given API result is a non-empty and not an error object.
|
609 |
-
*
|
610 |
-
* @author Vova Feldman (@svovaf)
|
611 |
-
* @since 1.2.1.5
|
612 |
-
*
|
613 |
-
* @param mixed $result
|
614 |
-
* @param string|null $required_property Optional property we want to verify that is set.
|
615 |
-
*
|
616 |
-
* @return bool
|
617 |
-
*/
|
618 |
-
static function is_api_result_object( $result, $required_property = null ) {
|
619 |
-
return (
|
620 |
-
is_object( $result ) &&
|
621 |
-
! isset( $result->error ) &&
|
622 |
-
( empty( $required_property ) || isset( $result->{$required_property} ) )
|
623 |
-
);
|
624 |
-
}
|
625 |
-
|
626 |
-
/**
|
627 |
-
* Checks if given API result is a non-empty entity object with non-empty ID.
|
628 |
-
*
|
629 |
-
* @author Vova Feldman (@svovaf)
|
630 |
-
* @since 1.2.1.5
|
631 |
-
*
|
632 |
-
* @param mixed $result
|
633 |
-
*
|
634 |
-
* @return bool
|
635 |
-
*/
|
636 |
-
static function is_api_result_entity( $result ) {
|
637 |
-
return self::is_api_result_object( $result, 'id' ) &&
|
638 |
-
FS_Entity::is_valid_id( $result->id );
|
639 |
-
}
|
640 |
-
|
641 |
-
/**
|
642 |
-
* Get API result error code. If failed to get code, returns an empty string.
|
643 |
-
*
|
644 |
-
* @author Vova Feldman (@svovaf)
|
645 |
-
* @since 2.0.0
|
646 |
-
*
|
647 |
-
* @param mixed $result
|
648 |
-
*
|
649 |
-
* @return string
|
650 |
-
*/
|
651 |
-
static function get_error_code( $result ) {
|
652 |
-
if ( is_object( $result ) &&
|
653 |
-
isset( $result->error ) &&
|
654 |
-
is_object( $result->error ) &&
|
655 |
-
! empty( $result->error->code )
|
656 |
-
) {
|
657 |
-
return $result->error->code;
|
658 |
-
}
|
659 |
-
|
660 |
-
return '';
|
661 |
-
}
|
662 |
-
|
663 |
-
#endregion
|
664 |
}
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package Freemius
|
4 |
+
* @copyright Copyright (c) 2015, Freemius, Inc.
|
5 |
+
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
|
6 |
+
* @since 1.0.4
|
7 |
+
*/
|
8 |
+
|
9 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
10 |
+
exit;
|
11 |
+
}
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Class FS_Api
|
15 |
+
*
|
16 |
+
* Wraps Freemius API SDK to handle:
|
17 |
+
* 1. Clock sync.
|
18 |
+
* 2. Fallback to HTTP when HTTPS fails.
|
19 |
+
* 3. Adds caching layer to GET requests.
|
20 |
+
* 4. Adds consistency for failed requests by using last cached version.
|
21 |
+
*/
|
22 |
+
class FS_Api {
|
23 |
+
/**
|
24 |
+
* @var FS_Api[]
|
25 |
+
*/
|
26 |
+
private static $_instances = array();
|
27 |
+
|
28 |
+
/**
|
29 |
+
* @var FS_Option_Manager Freemius options, options-manager.
|
30 |
+
*/
|
31 |
+
private static $_options;
|
32 |
+
|
33 |
+
/**
|
34 |
+
* @var FS_Cache_Manager API Caching layer
|
35 |
+
*/
|
36 |
+
private static $_cache;
|
37 |
+
|
38 |
+
/**
|
39 |
+
* @var int Clock diff in seconds between current server to API server.
|
40 |
+
*/
|
41 |
+
private static $_clock_diff;
|
42 |
+
|
43 |
+
/**
|
44 |
+
* @var Freemius_Api_WordPress
|
45 |
+
*/
|
46 |
+
private $_api;
|
47 |
+
|
48 |
+
/**
|
49 |
+
* @var string
|
50 |
+
*/
|
51 |
+
private $_slug;
|
52 |
+
|
53 |
+
/**
|
54 |
+
* @var FS_Logger
|
55 |
+
* @since 1.0.4
|
56 |
+
*/
|
57 |
+
private $_logger;
|
58 |
+
|
59 |
+
/**
|
60 |
+
* @author Leo Fajardo (@leorw)
|
61 |
+
* @since 2.3.0
|
62 |
+
*
|
63 |
+
* @var string
|
64 |
+
*/
|
65 |
+
private $_sdk_version;
|
66 |
+
|
67 |
+
/**
|
68 |
+
* @param string $slug
|
69 |
+
* @param string $scope 'app', 'developer', 'user' or 'install'.
|
70 |
+
* @param number $id Element's id.
|
71 |
+
* @param string $public_key Public key.
|
72 |
+
* @param bool $is_sandbox
|
73 |
+
* @param bool|string $secret_key Element's secret key.
|
74 |
+
* @param null|string $sdk_version
|
75 |
+
*
|
76 |
+
* @return FS_Api
|
77 |
+
*/
|
78 |
+
static function instance(
|
79 |
+
$slug,
|
80 |
+
$scope,
|
81 |
+
$id,
|
82 |
+
$public_key,
|
83 |
+
$is_sandbox,
|
84 |
+
$secret_key = false,
|
85 |
+
$sdk_version = null
|
86 |
+
) {
|
87 |
+
$identifier = md5( $slug . $scope . $id . $public_key . ( is_string( $secret_key ) ? $secret_key : '' ) . json_encode( $is_sandbox ) );
|
88 |
+
|
89 |
+
if ( ! isset( self::$_instances[ $identifier ] ) ) {
|
90 |
+
self::_init();
|
91 |
+
|
92 |
+
self::$_instances[ $identifier ] = new FS_Api( $slug, $scope, $id, $public_key, $secret_key, $is_sandbox, $sdk_version );
|
93 |
+
}
|
94 |
+
|
95 |
+
return self::$_instances[ $identifier ];
|
96 |
+
}
|
97 |
+
|
98 |
+
private static function _init() {
|
99 |
+
if ( isset( self::$_options ) ) {
|
100 |
+
return;
|
101 |
+
}
|
102 |
+
|
103 |
+
if ( ! class_exists( 'Freemius_Api_WordPress' ) ) {
|
104 |
+
require_once WP_FS__DIR_SDK . '/FreemiusWordPress.php';
|
105 |
+
}
|
106 |
+
|
107 |
+
self::$_options = FS_Option_Manager::get_manager( WP_FS__OPTIONS_OPTION_NAME, true, true );
|
108 |
+
self::$_cache = FS_Cache_Manager::get_manager( WP_FS__API_CACHE_OPTION_NAME );
|
109 |
+
|
110 |
+
self::$_clock_diff = self::$_options->get_option( 'api_clock_diff', 0 );
|
111 |
+
Freemius_Api_WordPress::SetClockDiff( self::$_clock_diff );
|
112 |
+
|
113 |
+
if ( self::$_options->get_option( 'api_force_http', false ) ) {
|
114 |
+
Freemius_Api_WordPress::SetHttp();
|
115 |
+
}
|
116 |
+
}
|
117 |
+
|
118 |
+
/**
|
119 |
+
* @param string $slug
|
120 |
+
* @param string $scope 'app', 'developer', 'user' or 'install'.
|
121 |
+
* @param number $id Element's id.
|
122 |
+
* @param string $public_key Public key.
|
123 |
+
* @param bool|string $secret_key Element's secret key.
|
124 |
+
* @param bool $is_sandbox
|
125 |
+
* @param null|string $sdk_version
|
126 |
+
*/
|
127 |
+
private function __construct(
|
128 |
+
$slug,
|
129 |
+
$scope,
|
130 |
+
$id,
|
131 |
+
$public_key,
|
132 |
+
$secret_key,
|
133 |
+
$is_sandbox,
|
134 |
+
$sdk_version
|
135 |
+
) {
|
136 |
+
$this->_api = new Freemius_Api_WordPress( $scope, $id, $public_key, $secret_key, $is_sandbox );
|
137 |
+
|
138 |
+
$this->_slug = $slug;
|
139 |
+
$this->_sdk_version = $sdk_version;
|
140 |
+
$this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $slug . '_api', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
|
141 |
+
}
|
142 |
+
|
143 |
+
/**
|
144 |
+
* Find clock diff between server and API server, and store the diff locally.
|
145 |
+
*
|
146 |
+
* @param bool|int $diff
|
147 |
+
*
|
148 |
+
* @return bool|int False if clock diff didn't change, otherwise returns the clock diff in seconds.
|
149 |
+
*/
|
150 |
+
private function _sync_clock_diff( $diff = false ) {
|
151 |
+
$this->_logger->entrance();
|
152 |
+
|
153 |
+
// Sync clock and store.
|
154 |
+
$new_clock_diff = ( false === $diff ) ?
|
155 |
+
Freemius_Api_WordPress::FindClockDiff() :
|
156 |
+
$diff;
|
157 |
+
|
158 |
+
if ( $new_clock_diff === self::$_clock_diff ) {
|
159 |
+
return false;
|
160 |
+
}
|
161 |
+
|
162 |
+
self::$_clock_diff = $new_clock_diff;
|
163 |
+
|
164 |
+
// Update API clock's diff.
|
165 |
+
Freemius_Api_WordPress::SetClockDiff( self::$_clock_diff );
|
166 |
+
|
167 |
+
// Store new clock diff in storage.
|
168 |
+
self::$_options->set_option( 'api_clock_diff', self::$_clock_diff, true );
|
169 |
+
|
170 |
+
return $new_clock_diff;
|
171 |
+
}
|
172 |
+
|
173 |
+
/**
|
174 |
+
* Override API call to enable retry with servers' clock auto sync method.
|
175 |
+
*
|
176 |
+
* @param string $path
|
177 |
+
* @param string $method
|
178 |
+
* @param array $params
|
179 |
+
* @param bool $retry Is in retry or first call attempt.
|
180 |
+
*
|
181 |
+
* @return array|mixed|string|void
|
182 |
+
*/
|
183 |
+
private function _call( $path, $method = 'GET', $params = array(), $retry = false ) {
|
184 |
+
$this->_logger->entrance( $method . ':' . $path );
|
185 |
+
|
186 |
+
if ( self::is_temporary_down() ) {
|
187 |
+
$result = $this->get_temporary_unavailable_error();
|
188 |
+
} else {
|
189 |
+
/**
|
190 |
+
* @since 2.3.0 Include the SDK version with all API requests that going through the API manager. IMPORTANT: Only pass the SDK version if the caller didn't include it yet.
|
191 |
+
*/
|
192 |
+
if ( ! empty( $this->_sdk_version ) ) {
|
193 |
+
if ( false === strpos( $path, 'sdk_version=' ) &&
|
194 |
+
! isset( $params['sdk_version'] )
|
195 |
+
) {
|
196 |
+
// Always add the sdk_version param in the querystring. DO NOT INCLUDE IT IN THE BODY PARAMS, OTHERWISE, IT MAY LEAD TO AN UNEXPECTED PARAMS PARSING IN CASES WHERE THE $params IS A REGULAR NON-ASSOCIATIVE ARRAY.
|
197 |
+
$path = add_query_arg( 'sdk_version', $this->_sdk_version, $path );
|
198 |
+
}
|
199 |
+
}
|
200 |
+
|
201 |
+
$result = $this->_api->Api( $path, $method, $params );
|
202 |
+
|
203 |
+
if ( null !== $result &&
|
204 |
+
isset( $result->error ) &&
|
205 |
+
isset( $result->error->code ) &&
|
206 |
+
'request_expired' === $result->error->code
|
207 |
+
) {
|
208 |
+
if ( ! $retry ) {
|
209 |
+
$diff = isset( $result->error->timestamp ) ?
|
210 |
+
( time() - strtotime( $result->error->timestamp ) ) :
|
211 |
+
false;
|
212 |
+
|
213 |
+
// Try to sync clock diff.
|
214 |
+
if ( false !== $this->_sync_clock_diff( $diff ) ) {
|
215 |
+
// Retry call with new synced clock.
|
216 |
+
return $this->_call( $path, $method, $params, true );
|
217 |
+
}
|
218 |
+
}
|
219 |
+
}
|
220 |
+
}
|
221 |
+
|
222 |
+
if ( $this->_logger->is_on() && self::is_api_error( $result ) ) {
|
223 |
+
// Log API errors.
|
224 |
+
$this->_logger->api_error( $result );
|
225 |
+
}
|
226 |
+
|
227 |
+
return $result;
|
228 |
+
}
|
229 |
+
|
230 |
+
/**
|
231 |
+
* Override API call to wrap it in servers' clock sync method.
|
232 |
+
*
|
233 |
+
* @param string $path
|
234 |
+
* @param string $method
|
235 |
+
* @param array $params
|
236 |
+
*
|
237 |
+
* @return array|mixed|string|void
|
238 |
+
* @throws Freemius_Exception
|
239 |
+
*/
|
240 |
+
function call( $path, $method = 'GET', $params = array() ) {
|
241 |
+
return $this->_call( $path, $method, $params );
|
242 |
+
}
|
243 |
+
|
244 |
+
/**
|
245 |
+
* Get API request URL signed via query string.
|
246 |
+
*
|
247 |
+
* @param string $path
|
248 |
+
*
|
249 |
+
* @return string
|
250 |
+
*/
|
251 |
+
function get_signed_url( $path ) {
|
252 |
+
return $this->_api->GetSignedUrl( $path );
|
253 |
+
}
|
254 |
+
|
255 |
+
/**
|
256 |
+
* @param string $path
|
257 |
+
* @param bool $flush
|
258 |
+
* @param int $expiration (optional) Time until expiration in seconds from now, defaults to 24 hours
|
259 |
+
*
|
260 |
+
* @return stdClass|mixed
|
261 |
+
*/
|
262 |
+
function get( $path = '/', $flush = false, $expiration = WP_FS__TIME_24_HOURS_IN_SEC ) {
|
263 |
+
$this->_logger->entrance( $path );
|
264 |
+
|
265 |
+
$cache_key = $this->get_cache_key( $path );
|
266 |
+
|
267 |
+
// Always flush during development.
|
268 |
+
if ( WP_FS__DEV_MODE || $this->_api->IsSandbox() ) {
|
269 |
+
$flush = true;
|
270 |
+
}
|
271 |
+
|
272 |
+
$cached_result = self::$_cache->get( $cache_key );
|
273 |
+
|
274 |
+
if ( $flush || ! self::$_cache->has_valid( $cache_key, $expiration ) ) {
|
275 |
+
$result = $this->call( $path );
|
276 |
+
|
277 |
+
if ( ! is_object( $result ) || isset( $result->error ) ) {
|
278 |
+
// Api returned an error.
|
279 |
+
if ( is_object( $cached_result ) &&
|
280 |
+
! isset( $cached_result->error )
|
281 |
+
) {
|
282 |
+
// If there was an error during a newer data fetch,
|
283 |
+
// fallback to older data version.
|
284 |
+
$result = $cached_result;
|
285 |
+
|
286 |
+
if ( $this->_logger->is_on() ) {
|
287 |
+
$this->_logger->warn( 'Fallback to cached API result: ' . var_export( $cached_result, true ) );
|
288 |
+
}
|
289 |
+
} else {
|
290 |
+
if ( is_object( $result ) && isset( $result->error->http ) && 404 == $result->error->http ) {
|
291 |
+
/**
|
292 |
+
* If the response code is 404, cache the result for half of the `$expiration`.
|
293 |
+
*
|
294 |
+
* @author Leo Fajardo (@leorw)
|
295 |
+
* @since 2.2.4
|
296 |
+
*/
|
297 |
+
$expiration /= 2;
|
298 |
+
} else {
|
299 |
+
// If no older data version and the response code is not 404, return result without
|
300 |
+
// caching the error.
|
301 |
+
return $result;
|
302 |
+
}
|
303 |
+
}
|
304 |
+
}
|
305 |
+
|
306 |
+
self::$_cache->set( $cache_key, $result, $expiration );
|
307 |
+
|
308 |
+
$cached_result = $result;
|
309 |
+
} else {
|
310 |
+
$this->_logger->log( 'Using cached API result.' );
|
311 |
+
}
|
312 |
+
|
313 |
+
return $cached_result;
|
314 |
+
}
|
315 |
+
|
316 |
+
/**
|
317 |
+
* Check if there's a cached version of the API request.
|
318 |
+
*
|
319 |
+
* @author Vova Feldman (@svovaf)
|
320 |
+
* @since 1.2.1
|
321 |
+
*
|
322 |
+
* @param string $path
|
323 |
+
* @param string $method
|
324 |
+
* @param array $params
|
325 |
+
*
|
326 |
+
* @return bool
|
327 |
+
*/
|
328 |
+
function is_cached( $path, $method = 'GET', $params = array() ) {
|
329 |
+
$cache_key = $this->get_cache_key( $path, $method, $params );
|
330 |
+
|
331 |
+
return self::$_cache->has_valid( $cache_key );
|
332 |
+
}
|
333 |
+
|
334 |
+
/**
|
335 |
+
* Invalidate a cached version of the API request.
|
336 |
+
*
|
337 |
+
* @author Vova Feldman (@svovaf)
|
338 |
+
* @since 1.2.1.5
|
339 |
+
*
|
340 |
+
* @param string $path
|
341 |
+
* @param string $method
|
342 |
+
* @param array $params
|
343 |
+
*/
|
344 |
+
function purge_cache( $path, $method = 'GET', $params = array() ) {
|
345 |
+
$this->_logger->entrance( "{$method}:{$path}" );
|
346 |
+
|
347 |
+
$cache_key = $this->get_cache_key( $path, $method, $params );
|
348 |
+
|
349 |
+
self::$_cache->purge( $cache_key );
|
350 |
+
}
|
351 |
+
|
352 |
+
/**
|
353 |
+
* Invalidate a cached version of the API request.
|
354 |
+
*
|
355 |
+
* @author Vova Feldman (@svovaf)
|
356 |
+
* @since 2.0.0
|
357 |
+
*
|
358 |
+
* @param string $path
|
359 |
+
* @param int $expiration
|
360 |
+
* @param string $method
|
361 |
+
* @param array $params
|
362 |
+
*/
|
363 |
+
function update_cache_expiration( $path, $expiration = WP_FS__TIME_24_HOURS_IN_SEC, $method = 'GET', $params = array() ) {
|
364 |
+
$this->_logger->entrance( "{$method}:{$path}:{$expiration}" );
|
365 |
+
|
366 |
+
$cache_key = $this->get_cache_key( $path, $method, $params );
|
367 |
+
|
368 |
+
self::$_cache->update_expiration( $cache_key, $expiration );
|
369 |
+
}
|
370 |
+
|
371 |
+
/**
|
372 |
+
* @param string $path
|
373 |
+
* @param string $method
|
374 |
+
* @param array $params
|
375 |
+
*
|
376 |
+
* @return string
|
377 |
+
* @throws \Freemius_Exception
|
378 |
+
*/
|
379 |
+
private function get_cache_key( $path, $method = 'GET', $params = array() ) {
|
380 |
+
$canonized = $this->_api->CanonizePath( $path );
|
381 |
+
// $exploded = explode('/', $canonized);
|
382 |
+
// return $method . '_' . array_pop($exploded) . '_' . md5($canonized . json_encode($params));
|
383 |
+
return strtolower( $method . ':' . $canonized ) . ( ! empty( $params ) ? '#' . md5( json_encode( $params ) ) : '' );
|
384 |
+
}
|
385 |
+
|
386 |
+
/**
|
387 |
+
* Test API connectivity.
|
388 |
+
*
|
389 |
+
* @author Vova Feldman (@svovaf)
|
390 |
+
* @since 1.0.9 If fails, try to fallback to HTTP.
|
391 |
+
* @since 1.1.6 Added a 5-min caching mechanism, to prevent from overloading the server if the API if
|
392 |
+
* temporary down.
|
393 |
+
*
|
394 |
+
* @return bool True if successful connectivity to the API.
|
395 |
+
*/
|
396 |
+
static function test() {
|
397 |
+
self::_init();
|
398 |
+
|
399 |
+
$cache_key = 'ping_test';
|
400 |
+
|
401 |
+
$test = self::$_cache->get_valid( $cache_key, null );
|
402 |
+
|
403 |
+
if ( is_null( $test ) ) {
|
404 |
+
$test = Freemius_Api_WordPress::Test();
|
405 |
+
|
406 |
+
if ( false === $test && Freemius_Api_WordPress::IsHttps() ) {
|
407 |
+
// Fallback to HTTP, since HTTPS fails.
|
408 |
+
Freemius_Api_WordPress::SetHttp();
|
409 |
+
|
410 |
+
self::$_options->set_option( 'api_force_http', true, true );
|
411 |
+
|
412 |
+
$test = Freemius_Api_WordPress::Test();
|
413 |
+
|
414 |
+
if ( false === $test ) {
|
415 |
+
/**
|
416 |
+
* API connectivity test fail also in HTTP request, therefore,
|
417 |
+
* fallback to HTTPS to keep connection secure.
|
418 |
+
*
|
419 |
+
* @since 1.1.6
|
420 |
+
*/
|
421 |
+
self::$_options->set_option( 'api_force_http', false, true );
|
422 |
+
}
|
423 |
+
}
|
424 |
+
|
425 |
+
self::$_cache->set( $cache_key, $test, WP_FS__TIME_5_MIN_IN_SEC );
|
426 |
+
}
|
427 |
+
|
428 |
+
return $test;
|
429 |
+
}
|
430 |
+
|
431 |
+
/**
|
432 |
+
* Check if API is temporary down.
|
433 |
+
*
|
434 |
+
* @author Vova Feldman (@svovaf)
|
435 |
+
* @since 1.1.6
|
436 |
+
*
|
437 |
+
* @return bool
|
438 |
+
*/
|
439 |
+
static function is_temporary_down() {
|
440 |
+
self::_init();
|
441 |
+
|
442 |
+
$test = self::$_cache->get_valid( 'ping_test', null );
|
443 |
+
|
444 |
+
return ( false === $test );
|
445 |
+
}
|
446 |
+
|
447 |
+
/**
|
448 |
+
* @author Vova Feldman (@svovaf)
|
449 |
+
* @since 1.1.6
|
450 |
+
*
|
451 |
+
* @return object
|
452 |
+
*/
|
453 |
+
private function get_temporary_unavailable_error() {
|
454 |
+
return (object) array(
|
455 |
+
'error' => (object) array(
|
456 |
+
'type' => 'TemporaryUnavailable',
|
457 |
+
'message' => 'API is temporary unavailable, please retry in ' . ( self::$_cache->get_record_expiration( 'ping_test' ) - WP_FS__SCRIPT_START_TIME ) . ' sec.',
|
458 |
+
'code' => 'temporary_unavailable',
|
459 |
+
'http' => 503
|
460 |
+
)
|
461 |
+
);
|
462 |
+
}
|
463 |
+
|
464 |
+
/**
|
465 |
+
* Ping API for connectivity test, and return result object.
|
466 |
+
*
|
467 |
+
* @author Vova Feldman (@svovaf)
|
468 |
+
* @since 1.0.9
|
469 |
+
*
|
470 |
+
* @param null|string $unique_anonymous_id
|
471 |
+
* @param array $params
|
472 |
+
*
|
473 |
+
* @return object
|
474 |
+
*/
|
475 |
+
function ping( $unique_anonymous_id = null, $params = array() ) {
|
476 |
+
$this->_logger->entrance();
|
477 |
+
|
478 |
+
if ( self::is_temporary_down() ) {
|
479 |
+
return $this->get_temporary_unavailable_error();
|
480 |
+
}
|
481 |
+
|
482 |
+
$pong = is_null( $unique_anonymous_id ) ?
|
483 |
+
Freemius_Api_WordPress::Ping() :
|
484 |
+
$this->_call( 'ping.json?' . http_build_query( array_merge(
|
485 |
+
array( 'uid' => $unique_anonymous_id ),
|
486 |
+
$params
|
487 |
+
) ) );
|
488 |
+
|
489 |
+
if ( $this->is_valid_ping( $pong ) ) {
|
490 |
+
return $pong;
|
491 |
+
}
|
492 |
+
|
493 |
+
if ( self::should_try_with_http( $pong ) ) {
|
494 |
+
// Fallback to HTTP, since HTTPS fails.
|
495 |
+
Freemius_Api_WordPress::SetHttp();
|
496 |
+
|
497 |
+
self::$_options->set_option( 'api_force_http', true, true );
|
498 |
+
|
499 |
+
$pong = is_null( $unique_anonymous_id ) ?
|
500 |
+
Freemius_Api_WordPress::Ping() :
|
501 |
+
$this->_call( 'ping.json?' . http_build_query( array_merge(
|
502 |
+
array( 'uid' => $unique_anonymous_id ),
|
503 |
+
$params
|
504 |
+
) ) );
|
505 |
+
|
506 |
+
if ( ! $this->is_valid_ping( $pong ) ) {
|
507 |
+
self::$_options->set_option( 'api_force_http', false, true );
|
508 |
+
}
|
509 |
+
}
|
510 |
+
|
511 |
+
return $pong;
|
512 |
+
}
|
513 |
+
|
514 |
+
/**
|
515 |
+
* Check if based on the API result we should try
|
516 |
+
* to re-run the same request with HTTP instead of HTTPS.
|
517 |
+
*
|
518 |
+
* @author Vova Feldman (@svovaf)
|
519 |
+
* @since 1.1.6
|
520 |
+
*
|
521 |
+
* @param $result
|
522 |
+
*
|
523 |
+
* @return bool
|
524 |
+
*/
|
525 |
+
private static function should_try_with_http( $result ) {
|
526 |
+
if ( ! Freemius_Api_WordPress::IsHttps() ) {
|
527 |
+
return false;
|
528 |
+
}
|
529 |
+
|
530 |
+
return ( ! is_object( $result ) ||
|
531 |
+
! isset( $result->error ) ||
|
532 |
+
! isset( $result->error->code ) ||
|
533 |
+
! in_array( $result->error->code, array(
|
534 |
+
'curl_missing',
|
535 |
+
'cloudflare_ddos_protection',
|
536 |
+
'maintenance_mode',
|
537 |
+
'squid_cache_block',
|
538 |
+
'too_many_requests',
|
539 |
+
) ) );
|
540 |
+
|
541 |
+
}
|
542 |
+
|
543 |
+
/**
|
544 |
+
* Check if valid ping request result.
|
545 |
+
*
|
546 |
+
* @author Vova Feldman (@svovaf)
|
547 |
+
* @since 1.1.1
|
548 |
+
*
|
549 |
+
* @param mixed $pong
|
550 |
+
*
|
551 |
+
* @return bool
|
552 |
+
*/
|
553 |
+
function is_valid_ping( $pong ) {
|
554 |
+
return Freemius_Api_WordPress::Test( $pong );
|
555 |
+
}
|
556 |
+
|
557 |
+
function get_url( $path = '' ) {
|
558 |
+
return Freemius_Api_WordPress::GetUrl( $path, $this->_api->IsSandbox() );
|
559 |
+
}
|
560 |
+
|
561 |
+
/**
|
562 |
+
* Clear API cache.
|
563 |
+
*
|
564 |
+
* @author Vova Feldman (@svovaf)
|
565 |
+
* @since 1.0.9
|
566 |
+
*/
|
567 |
+
static function clear_cache() {
|
568 |
+
self::_init();
|
569 |
+
|
570 |
+
self::$_cache = FS_Cache_Manager::get_manager( WP_FS__API_CACHE_OPTION_NAME );
|
571 |
+
self::$_cache->clear();
|
572 |
+
}
|
573 |
+
|
574 |
+
#----------------------------------------------------------------------------------
|
575 |
+
#region Error Handling
|
576 |
+
#----------------------------------------------------------------------------------
|
577 |
+
|
578 |
+
/**
|
579 |
+
* @author Vova Feldman (@svovaf)
|
580 |
+
* @since 1.2.1.5
|
581 |
+
*
|
582 |
+
* @param mixed $result
|
583 |
+
*
|
584 |
+
* @return bool Is API result contains an error.
|
585 |
+
*/
|
586 |
+
static function is_api_error( $result ) {
|
587 |
+
return ( is_object( $result ) && isset( $result->error ) ) ||
|
588 |
+
is_string( $result );
|
589 |
+
}
|
590 |
+
|
591 |
+
/**
|
592 |
+
* @author Vova Feldman (@svovaf)
|
593 |
+
* @since 2.0.0
|
594 |
+
*
|
595 |
+
* @param mixed $result
|
596 |
+
*
|
597 |
+
* @return bool Is API result contains an error.
|
598 |
+
*/
|
599 |
+
static function is_api_error_object( $result ) {
|
600 |
+
return (
|
601 |
+
is_object( $result ) &&
|
602 |
+
isset( $result->error ) &&
|
603 |
+
isset( $result->error->message )
|
604 |
+
);
|
605 |
+
}
|
606 |
+
|
607 |
+
/**
|
608 |
+
* Checks if given API result is a non-empty and not an error object.
|
609 |
+
*
|
610 |
+
* @author Vova Feldman (@svovaf)
|
611 |
+
* @since 1.2.1.5
|
612 |
+
*
|
613 |
+
* @param mixed $result
|
614 |
+
* @param string|null $required_property Optional property we want to verify that is set.
|
615 |
+
*
|
616 |
+
* @return bool
|
617 |
+
*/
|
618 |
+
static function is_api_result_object( $result, $required_property = null ) {
|
619 |
+
return (
|
620 |
+
is_object( $result ) &&
|
621 |
+
! isset( $result->error ) &&
|
622 |
+
( empty( $required_property ) || isset( $result->{$required_property} ) )
|
623 |
+
);
|
624 |
+
}
|
625 |
+
|
626 |
+
/**
|
627 |
+
* Checks if given API result is a non-empty entity object with non-empty ID.
|
628 |
+
*
|
629 |
+
* @author Vova Feldman (@svovaf)
|
630 |
+
* @since 1.2.1.5
|
631 |
+
*
|
632 |
+
* @param mixed $result
|
633 |
+
*
|
634 |
+
* @return bool
|
635 |
+
*/
|
636 |
+
static function is_api_result_entity( $result ) {
|
637 |
+
return self::is_api_result_object( $result, 'id' ) &&
|
638 |
+
FS_Entity::is_valid_id( $result->id );
|
639 |
+
}
|
640 |
+
|
641 |
+
/**
|
642 |
+
* Get API result error code. If failed to get code, returns an empty string.
|
643 |
+
*
|
644 |
+
* @author Vova Feldman (@svovaf)
|
645 |
+
* @since 2.0.0
|
646 |
+
*
|
647 |
+
* @param mixed $result
|
648 |
+
*
|
649 |
+
* @return string
|
650 |
+
*/
|
651 |
+
static function get_error_code( $result ) {
|
652 |
+
if ( is_object( $result ) &&
|
653 |
+
isset( $result->error ) &&
|
654 |
+
is_object( $result->error ) &&
|
655 |
+
! empty( $result->error->code )
|
656 |
+
) {
|
657 |
+
return $result->error->code;
|
658 |
+
}
|
659 |
+
|
660 |
+
return '';
|
661 |
+
}
|
662 |
+
|
663 |
+
#endregion
|
664 |
}
|
freemius/includes/class-fs-options.php
CHANGED
@@ -1,431 +1,431 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* @package Freemius
|
4 |
-
* @copyright Copyright (c) 2015, Freemius, Inc.
|
5 |
-
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
|
6 |
-
* @since 1.2.3
|
7 |
-
*/
|
8 |
-
|
9 |
-
if ( ! defined( 'ABSPATH' ) ) {
|
10 |
-
exit;
|
11 |
-
}
|
12 |
-
|
13 |
-
/**
|
14 |
-
* Class FS_Options
|
15 |
-
*
|
16 |
-
* A wrapper class for handling network level and single site level options.
|
17 |
-
*/
|
18 |
-
class FS_Options {
|
19 |
-
/**
|
20 |
-
* @var string
|
21 |
-
*/
|
22 |
-
private $_id;
|
23 |
-
|
24 |
-
/**
|
25 |
-
* @var array[string]FS_Options {
|
26 |
-
* @key string
|
27 |
-
* @value FS_Options
|
28 |
-
* }
|
29 |
-
*/
|
30 |
-
private static $_instances;
|
31 |
-
|
32 |
-
/**
|
33 |
-
* @var FS_Option_Manager Site level options.
|
34 |
-
*/
|
35 |
-
private $_options;
|
36 |
-
|
37 |
-
/**
|
38 |
-
* @var FS_Option_Manager Network level options.
|
39 |
-
*/
|
40 |
-
private $_network_options;
|
41 |
-
|
42 |
-
/**
|
43 |
-
* @var int The ID of the blog that is associated with the current site level options.
|
44 |
-
*/
|
45 |
-
private $_blog_id = 0;
|
46 |
-
|
47 |
-
/**
|
48 |
-
* @var bool
|
49 |
-
*/
|
50 |
-
private $_is_multisite;
|
51 |
-
|
52 |
-
/**
|
53 |
-
* @var string[] Lazy collection of params on the site level.
|
54 |
-
*/
|
55 |
-
private static $_SITE_OPTIONS_MAP;
|
56 |
-
|
57 |
-
/**
|
58 |
-
* @author Leo Fajardo (@leorw)
|
59 |
-
* @since 2.0.0
|
60 |
-
*
|
61 |
-
* @param string $id
|
62 |
-
* @param bool $load
|
63 |
-
*
|
64 |
-
* @return FS_Options
|
65 |
-
*/
|
66 |
-
static function instance( $id, $load = false ) {
|
67 |
-
if ( ! isset( self::$_instances[ $id ] ) ) {
|
68 |
-
self::$_instances[ $id ] = new FS_Options( $id, $load );
|
69 |
-
}
|
70 |
-
|
71 |
-
return self::$_instances[ $id ];
|
72 |
-
}
|
73 |
-
|
74 |
-
/**
|
75 |
-
* @author Leo Fajardo (@leorw)
|
76 |
-
* @since 2.0.0
|
77 |
-
*
|
78 |
-
* @param string $id
|
79 |
-
* @param bool $load
|
80 |
-
*/
|
81 |
-
private function __construct( $id, $load = false ) {
|
82 |
-
$this->_id = $id;
|
83 |
-
$this->_is_multisite = is_multisite();
|
84 |
-
|
85 |
-
if ( $this->_is_multisite ) {
|
86 |
-
$this->_blog_id = get_current_blog_id();
|
87 |
-
$this->_network_options = FS_Option_Manager::get_manager( $id, $load, true );
|
88 |
-
}
|
89 |
-
|
90 |
-
$this->_options = FS_Option_Manager::get_manager( $id, $load, $this->_blog_id );
|
91 |
-
}
|
92 |
-
|
93 |
-
/**
|
94 |
-
* Switch the context of the site level options manager.
|
95 |
-
*
|
96 |
-
* @author Vova Feldman (@svovaf)
|
97 |
-
* @since 2.0.0
|
98 |
-
*
|
99 |
-
* @param $blog_id
|
100 |
-
*/
|
101 |
-
function set_site_blog_context( $blog_id ) {
|
102 |
-
$this->_blog_id = $blog_id;
|
103 |
-
|
104 |
-
$this->_options = FS_Option_Manager::get_manager( $this->_id, false, $this->_blog_id );
|
105 |
-
}
|
106 |
-
|
107 |
-
/**
|
108 |
-
* @author Leo Fajardo (@leorw)
|
109 |
-
*
|
110 |
-
* @param string $option
|
111 |
-
* @param mixed $default
|
112 |
-
* @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_SITE_LEVEL_PARAMS).
|
113 |
-
*
|
114 |
-
* @return mixed
|
115 |
-
*/
|
116 |
-
function get_option( $option, $default = null, $network_level_or_blog_id = null ) {
|
117 |
-
if ( $this->should_use_network_storage( $option, $network_level_or_blog_id ) ) {
|
118 |
-
return $this->_network_options->get_option( $option, $default );
|
119 |
-
}
|
120 |
-
|
121 |
-
$site_options = $this->get_site_options( $network_level_or_blog_id );
|
122 |
-
|
123 |
-
return $site_options->get_option( $option, $default );
|
124 |
-
}
|
125 |
-
|
126 |
-
/**
|
127 |
-
* @author Leo Fajardo (@leorw)
|
128 |
-
* @since 2.0.0
|
129 |
-
*
|
130 |
-
* @param string $option
|
131 |
-
* @param mixed $value
|
132 |
-
* @param bool $flush
|
133 |
-
* @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_SITE_LEVEL_PARAMS).
|
134 |
-
*/
|
135 |
-
function set_option( $option, $value, $flush = false, $network_level_or_blog_id = null ) {
|
136 |
-
if ( $this->should_use_network_storage( $option, $network_level_or_blog_id ) ) {
|
137 |
-
$this->_network_options->set_option( $option, $value, $flush );
|
138 |
-
} else {
|
139 |
-
$site_options = $this->get_site_options( $network_level_or_blog_id );
|
140 |
-
$site_options->set_option( $option, $value, $flush );
|
141 |
-
}
|
142 |
-
}
|
143 |
-
|
144 |
-
/**
|
145 |
-
* @author Vova Feldman (@svovaf)
|
146 |
-
* @since 2.0.0
|
147 |
-
*
|
148 |
-
* @param string $option
|
149 |
-
* @param bool $flush
|
150 |
-
* @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_SITE_LEVEL_PARAMS).
|
151 |
-
*/
|
152 |
-
function unset_option( $option, $flush = false, $network_level_or_blog_id = null ) {
|
153 |
-
if ( $this->should_use_network_storage( $option, $network_level_or_blog_id ) ) {
|
154 |
-
$this->_network_options->unset_option( $option, $flush );
|
155 |
-
} else {
|
156 |
-
$site_options = $this->get_site_options( $network_level_or_blog_id );
|
157 |
-
$site_options->unset_option( $option, $flush );
|
158 |
-
}
|
159 |
-
}
|
160 |
-
|
161 |
-
/**
|
162 |
-
* @author Leo Fajardo (@leorw)
|
163 |
-
* @since 2.0.0
|
164 |
-
*
|
165 |
-
* @param bool $flush
|
166 |
-
* @param bool $network_level
|
167 |
-
*/
|
168 |
-
function load( $flush = false, $network_level = true ) {
|
169 |
-
if ( $this->_is_multisite && $network_level ) {
|
170 |
-
$this->_network_options->load( $flush );
|
171 |
-
} else {
|
172 |
-
$this->_options->load( $flush );
|
173 |
-
}
|
174 |
-
}
|
175 |
-
|
176 |
-
/**
|
177 |
-
* @author Leo Fajardo (@leorw)
|
178 |
-
* @since 2.0.0
|
179 |
-
*
|
180 |
-
* @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, store both network storage and the current context blog storage.
|
181 |
-
*/
|
182 |
-
function store( $network_level_or_blog_id = null ) {
|
183 |
-
if ( ! $this->_is_multisite ||
|
184 |
-
false === $network_level_or_blog_id ||
|
185 |
-
0 == $network_level_or_blog_id ||
|
186 |
-
is_null( $network_level_or_blog_id )
|
187 |
-
) {
|
188 |
-
$site_options = $this->get_site_options( $network_level_or_blog_id );
|
189 |
-
$site_options->store();
|
190 |
-
}
|
191 |
-
|
192 |
-
if ( $this->_is_multisite &&
|
193 |
-
( is_null( $network_level_or_blog_id ) || true === $network_level_or_blog_id )
|
194 |
-
) {
|
195 |
-
$this->_network_options->store();
|
196 |
-
}
|
197 |
-
}
|
198 |
-
|
199 |
-
/**
|
200 |
-
* @author Vova Feldman (@svovaf)
|
201 |
-
* @since 2.0.0
|
202 |
-
*
|
203 |
-
* @param int|null|bool $network_level_or_blog_id
|
204 |
-
* @param bool $flush
|
205 |
-
*/
|
206 |
-
function clear( $network_level_or_blog_id = null, $flush = false ) {
|
207 |
-
if ( ! $this->_is_multisite ||
|
208 |
-
false === $network_level_or_blog_id ||
|
209 |
-
is_null( $network_level_or_blog_id ) ||
|
210 |
-
is_numeric( $network_level_or_blog_id )
|
211 |
-
) {
|
212 |
-
$site_options = $this->get_site_options( $network_level_or_blog_id );
|
213 |
-
$site_options->clear( $flush );
|
214 |
-
}
|
215 |
-
|
216 |
-
if ( $this->_is_multisite &&
|
217 |
-
( true === $network_level_or_blog_id || is_null( $network_level_or_blog_id ) )
|
218 |
-
) {
|
219 |
-
$this->_network_options->clear( $flush );
|
220 |
-
}
|
221 |
-
}
|
222 |
-
|
223 |
-
/**
|
224 |
-
* Migration script to the new storage data structure that is network compatible.
|
225 |
-
*
|
226 |
-
* IMPORTANT:
|
227 |
-
* This method should be executed only after it is determined if this is a network
|
228 |
-
* level compatible product activation.
|
229 |
-
*
|
230 |
-
* @author Vova Feldman (@svovaf)
|
231 |
-
* @since 2.0.0
|
232 |
-
*
|
233 |
-
* @param int $blog_id
|
234 |
-
*/
|
235 |
-
function migrate_to_network( $blog_id = 0 ) {
|
236 |
-
if ( ! $this->_is_multisite ) {
|
237 |
-
return;
|
238 |
-
}
|
239 |
-
|
240 |
-
$updated = false;
|
241 |
-
|
242 |
-
$site_options = $this->get_site_options( $blog_id );
|
243 |
-
|
244 |
-
$keys = $site_options->get_options_keys();
|
245 |
-
|
246 |
-
foreach ( $keys as $option ) {
|
247 |
-
if ( $this->is_site_option( $option ) ||
|
248 |
-
// Don't move admin notices to the network storage.
|
249 |
-
in_array($option, array(
|
250 |
-
// Don't move admin notices to the network storage.
|
251 |
-
'admin_notices',
|
252 |
-
// Don't migrate the module specific data, it will be migrated by the FS_Storage.
|
253 |
-
'plugin_data',
|
254 |
-
'theme_data',
|
255 |
-
))
|
256 |
-
) {
|
257 |
-
continue;
|
258 |
-
}
|
259 |
-
|
260 |
-
$option_updated = false;
|
261 |
-
|
262 |
-
// Migrate option to the network storage.
|
263 |
-
$site_option = $site_options->get_option( $option );
|
264 |
-
|
265 |
-
if ( ! $this->_network_options->has_option( $option ) ) {
|
266 |
-
// Option not set on the network level, so just set it.
|
267 |
-
$this->_network_options->set_option( $option, $site_option, false );
|
268 |
-
|
269 |
-
$option_updated = true;
|
270 |
-
} else {
|
271 |
-
// Option already set on the network level, so we need to merge it inelegantly.
|
272 |
-
$network_option = $this->_network_options->get_option( $option );
|
273 |
-
|
274 |
-
if ( is_array( $network_option ) && is_array( $site_option ) ) {
|
275 |
-
// Option is an array.
|
276 |
-
foreach ( $site_option as $key => $value ) {
|
277 |
-
if ( ! isset( $network_option[ $key ] ) ) {
|
278 |
-
$network_option[ $key ] = $value;
|
279 |
-
|
280 |
-
$option_updated = true;
|
281 |
-
} else if ( is_array( $network_option[ $key ] ) && is_array( $value ) ) {
|
282 |
-
if ( empty( $network_option[ $key ] ) ) {
|
283 |
-
$network_option[ $key ] = $value;
|
284 |
-
|
285 |
-
$option_updated = true;
|
286 |
-
} else if ( empty( $value ) ) {
|
287 |
-
// Do nothing.
|
288 |
-
} else {
|
289 |
-
reset($value);
|
290 |
-
$first_key = key($value);
|
291 |
-
if ( $value[$first_key] instanceof FS_Entity ) {
|
292 |
-
// Merge entities by IDs.
|
293 |
-
$network_entities_ids = array();
|
294 |
-
foreach ( $network_option[ $key ] as $entity ) {
|
295 |
-
$network_entities_ids[ $entity->id ] = true;
|
296 |
-
}
|
297 |
-
|
298 |
-
foreach ( $value as $entity ) {
|
299 |
-
if ( ! isset( $network_entities_ids[ $entity->id ] ) ) {
|
300 |
-
$network_option[ $key ][] = $entity;
|
301 |
-
|
302 |
-
$option_updated = true;
|
303 |
-
}
|
304 |
-
}
|
305 |
-
}
|
306 |
-
}
|
307 |
-
}
|
308 |
-
}
|
309 |
-
}
|
310 |
-
|
311 |
-
if ( $option_updated ) {
|
312 |
-
$this->_network_options->set_option( $option, $network_option, false );
|
313 |
-
}
|
314 |
-
}
|
315 |
-
|
316 |
-
/**
|
317 |
-
* Remove the option from site level storage.
|
318 |
-
*
|
319 |
-
* IMPORTANT:
|
320 |
-
* The line below is intentionally commented since we want to preserve the option
|
321 |
-
* on the site storage level for "downgrade compatibility". Basically, if the user
|
322 |
-
* will downgrade to an older version of the plugin with the prev storage structure,
|
323 |
-
* it will continue working.
|
324 |
-
*
|
325 |
-
* @todo After a few releases we can remove this.
|
326 |
-
*/
|
327 |
-
// $site_options->unset_option($option, false);
|
328 |
-
|
329 |
-
if ( $option_updated ) {
|
330 |
-
$updated = true;
|
331 |
-
}
|
332 |
-
}
|
333 |
-
|
334 |
-
if ( ! $updated ) {
|
335 |
-
return;
|
336 |
-
}
|
337 |
-
|
338 |
-
// Update network level storage.
|
339 |
-
$this->_network_options->store();
|
340 |
-
// $site_options->store();
|
341 |
-
}
|
342 |
-
|
343 |
-
|
344 |
-
#--------------------------------------------------------------------------------
|
345 |
-
#region Helper Methods
|
346 |
-
#--------------------------------------------------------------------------------
|
347 |
-
|
348 |
-
/**
|
349 |
-
* We don't want to load the map right away since it's not even needed in a non-MS environment.
|
350 |
-
*
|
351 |
-
* @author Vova Feldman (@svovaf)
|
352 |
-
* @since 2.0.0
|
353 |
-
*/
|
354 |
-
private static function load_site_options_map() {
|
355 |
-
self::$_SITE_OPTIONS_MAP = array(
|
356 |
-
'sites' => true,
|
357 |
-
'theme_sites' => true,
|
358 |
-
'unique_id' => true,
|
359 |
-
'active_plugins' => true,
|
360 |
-
);
|
361 |
-
}
|
362 |
-
|
363 |
-
/**
|
364 |
-
* @author Vova Feldman (@svovaf)
|
365 |
-
* @since 2.0.0
|
366 |
-
*
|
367 |
-
* @param string $option
|
368 |
-
*
|
369 |
-
* @return bool
|
370 |
-
*/
|
371 |
-
private function is_site_option( $option ) {
|
372 |
-
if ( WP_FS__ACCOUNTS_OPTION_NAME != $this->_id ) {
|
373 |
-
return false;
|
374 |
-
}
|
375 |
-
|
376 |
-
if ( ! isset( self::$_SITE_OPTIONS_MAP ) ) {
|
377 |
-
self::load_site_options_map();
|
378 |
-
}
|
379 |
-
|
380 |
-
return isset( self::$_SITE_OPTIONS_MAP[ $option ] );
|
381 |
-
}
|
382 |
-
|
383 |
-
/**
|
384 |
-
* @author Vova Feldman (@svovaf)
|
385 |
-
* @since 2.0.0
|
386 |
-
*
|
387 |
-
* @param int $blog_id
|
388 |
-
*
|
389 |
-
* @return FS_Option_Manager
|
390 |
-
*/
|
391 |
-
private function get_site_options( $blog_id = 0 ) {
|
392 |
-
if ( 0 == $blog_id || $blog_id == $this->_blog_id ) {
|
393 |
-
return $this->_options;
|
394 |
-
}
|
395 |
-
|
396 |
-
return FS_Option_Manager::get_manager( $this->_id, true, $blog_id );
|
397 |
-
}
|
398 |
-
|
399 |
-
/**
|
400 |
-
* Check if an option should be stored on the MS network storage.
|
401 |
-
*
|
402 |
-
* @author Vova Feldman (@svovaf)
|
403 |
-
* @since 2.0.0
|
404 |
-
*
|
405 |
-
* @param string $option
|
406 |
-
* @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_SITE_LEVEL_PARAMS).
|
407 |
-
*
|
408 |
-
* @return bool
|
409 |
-
*/
|
410 |
-
private function should_use_network_storage( $option, $network_level_or_blog_id = null ) {
|
411 |
-
if ( ! $this->_is_multisite ) {
|
412 |
-
// Not a multisite environment.
|
413 |
-
return false;
|
414 |
-
}
|
415 |
-
|
416 |
-
if ( is_numeric( $network_level_or_blog_id ) ) {
|
417 |
-
// Explicitly asked to use a specified blog storage.
|
418 |
-
return false;
|
419 |
-
}
|
420 |
-
|
421 |
-
if ( is_bool( $network_level_or_blog_id ) ) {
|
422 |
-
// Explicitly specified whether should use the network or blog level storage.
|
423 |
-
return $network_level_or_blog_id;
|
424 |
-
}
|
425 |
-
|
426 |
-
// Determine which storage to use based on the option.
|
427 |
-
return ! $this->is_site_option( $option );
|
428 |
-
}
|
429 |
-
|
430 |
-
#endregion
|
431 |
}
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package Freemius
|
4 |
+
* @copyright Copyright (c) 2015, Freemius, Inc.
|
5 |
+
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
|
6 |
+
* @since 1.2.3
|
7 |
+
*/
|
8 |
+
|
9 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
10 |
+
exit;
|
11 |
+
}
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Class FS_Options
|
15 |
+
*
|
16 |
+
* A wrapper class for handling network level and single site level options.
|
17 |
+
*/
|
18 |
+
class FS_Options {
|
19 |
+
/**
|
20 |
+
* @var string
|
21 |
+
*/
|
22 |
+
private $_id;
|
23 |
+
|
24 |
+
/**
|
25 |
+
* @var array[string]FS_Options {
|
26 |
+
* @key string
|
27 |
+
* @value FS_Options
|
28 |
+
* }
|
29 |
+
*/
|
30 |
+
private static $_instances;
|
31 |
+
|
32 |
+
/**
|
33 |
+
* @var FS_Option_Manager Site level options.
|
34 |
+
*/
|
35 |
+
private $_options;
|
36 |
+
|
37 |
+
/**
|
38 |
+
* @var FS_Option_Manager Network level options.
|
39 |
+
*/
|
40 |
+
private $_network_options;
|
41 |
+
|
42 |
+
/**
|
43 |
+
* @var int The ID of the blog that is associated with the current site level options.
|
44 |
+
*/
|
45 |
+
private $_blog_id = 0;
|
46 |
+
|
47 |
+
/**
|
48 |
+
* @var bool
|
49 |
+
*/
|
50 |
+
private $_is_multisite;
|
51 |
+
|
52 |
+
/**
|
53 |
+
* @var string[] Lazy collection of params on the site level.
|
54 |
+
*/
|
55 |
+
private static $_SITE_OPTIONS_MAP;
|
56 |
+
|
57 |
+
/**
|
58 |
+
* @author Leo Fajardo (@leorw)
|
59 |
+
* @since 2.0.0
|
60 |
+
*
|
61 |
+
* @param string $id
|
62 |
+
* @param bool $load
|
63 |
+
*
|
64 |
+
* @return FS_Options
|
65 |
+
*/
|
66 |
+
static function instance( $id, $load = false ) {
|
67 |
+
if ( ! isset( self::$_instances[ $id ] ) ) {
|
68 |
+
self::$_instances[ $id ] = new FS_Options( $id, $load );
|
69 |
+
}
|
70 |
+
|
71 |
+
return self::$_instances[ $id ];
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* @author Leo Fajardo (@leorw)
|
76 |
+
* @since 2.0.0
|
77 |
+
*
|
78 |
+
* @param string $id
|
79 |
+
* @param bool $load
|
80 |
+
*/
|
81 |
+
private function __construct( $id, $load = false ) {
|
82 |
+
$this->_id = $id;
|
83 |
+
$this->_is_multisite = is_multisite();
|
84 |
+
|
85 |
+
if ( $this->_is_multisite ) {
|
86 |
+
$this->_blog_id = get_current_blog_id();
|
87 |
+
$this->_network_options = FS_Option_Manager::get_manager( $id, $load, true );
|
88 |
+
}
|
89 |
+
|
90 |
+
$this->_options = FS_Option_Manager::get_manager( $id, $load, $this->_blog_id );
|
91 |
+
}
|
92 |
+
|
93 |
+
/**
|
94 |
+
* Switch the context of the site level options manager.
|
95 |
+
*
|
96 |
+
* @author Vova Feldman (@svovaf)
|
97 |
+
* @since 2.0.0
|
98 |
+
*
|
99 |
+
* @param $blog_id
|
100 |
+
*/
|
101 |
+
function set_site_blog_context( $blog_id ) {
|
102 |
+
$this->_blog_id = $blog_id;
|
103 |
+
|
104 |
+
$this->_options = FS_Option_Manager::get_manager( $this->_id, false, $this->_blog_id );
|
105 |
+
}
|
106 |
+
|
107 |
+
/**
|
108 |
+
* @author Leo Fajardo (@leorw)
|
109 |
+
*
|
110 |
+
* @param string $option
|
111 |
+
* @param mixed $default
|
112 |
+
* @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_SITE_LEVEL_PARAMS).
|
113 |
+
*
|
114 |
+
* @return mixed
|
115 |
+
*/
|
116 |
+
function get_option( $option, $default = null, $network_level_or_blog_id = null ) {
|
117 |
+
if ( $this->should_use_network_storage( $option, $network_level_or_blog_id ) ) {
|
118 |
+
return $this->_network_options->get_option( $option, $default );
|
119 |
+
}
|
120 |
+
|
121 |
+
$site_options = $this->get_site_options( $network_level_or_blog_id );
|
122 |
+
|
123 |
+
return $site_options->get_option( $option, $default );
|
124 |
+
}
|
125 |
+
|
126 |
+
/**
|
127 |
+
* @author Leo Fajardo (@leorw)
|
128 |
+
* @since 2.0.0
|
129 |
+
*
|
130 |
+
* @param string $option
|
131 |
+
* @param mixed $value
|
132 |
+
* @param bool $flush
|
133 |
+
* @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_SITE_LEVEL_PARAMS).
|
134 |
+
*/
|
135 |
+
function set_option( $option, $value, $flush = false, $network_level_or_blog_id = null ) {
|
136 |
+
if ( $this->should_use_network_storage( $option, $network_level_or_blog_id ) ) {
|
137 |
+
$this->_network_options->set_option( $option, $value, $flush );
|
138 |
+
} else {
|
139 |
+
$site_options = $this->get_site_options( $network_level_or_blog_id );
|
140 |
+
$site_options->set_option( $option, $value, $flush );
|
141 |
+
}
|
142 |
+
}
|
143 |
+
|
144 |
+
/**
|
145 |
+
* @author Vova Feldman (@svovaf)
|
146 |
+
* @since 2.0.0
|
147 |
+
*
|
148 |
+
* @param string $option
|
149 |
+
* @param bool $flush
|
150 |
+
* @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_SITE_LEVEL_PARAMS).
|
151 |
+
*/
|
152 |
+
function unset_option( $option, $flush = false, $network_level_or_blog_id = null ) {
|
153 |
+
if ( $this->should_use_network_storage( $option, $network_level_or_blog_id ) ) {
|
154 |
+
$this->_network_options->unset_option( $option, $flush );
|
155 |
+
} else {
|
156 |
+
$site_options = $this->get_site_options( $network_level_or_blog_id );
|
157 |
+
$site_options->unset_option( $option, $flush );
|
158 |
+
}
|
159 |
+
}
|
160 |
+
|
161 |
+
/**
|
162 |
+
* @author Leo Fajardo (@leorw)
|
163 |
+
* @since 2.0.0
|
164 |
+
*
|
165 |
+
* @param bool $flush
|
166 |
+
* @param bool $network_level
|
167 |
+
*/
|
168 |
+
function load( $flush = false, $network_level = true ) {
|
169 |
+
if ( $this->_is_multisite && $network_level ) {
|
170 |
+
$this->_network_options->load( $flush );
|
171 |
+
} else {
|
172 |
+
$this->_options->load( $flush );
|
173 |
+
}
|
174 |
+
}
|
175 |
+
|
176 |
+
/**
|
177 |
+
* @author Leo Fajardo (@leorw)
|
178 |
+
* @since 2.0.0
|
179 |
+
*
|
180 |
+
* @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, store both network storage and the current context blog storage.
|
181 |
+
*/
|
182 |
+
function store( $network_level_or_blog_id = null ) {
|
183 |
+
if ( ! $this->_is_multisite ||
|
184 |
+
false === $network_level_or_blog_id ||
|
185 |
+
0 == $network_level_or_blog_id ||
|
186 |
+
is_null( $network_level_or_blog_id )
|
187 |
+
) {
|
188 |
+
$site_options = $this->get_site_options( $network_level_or_blog_id );
|
189 |
+
$site_options->store();
|
190 |
+
}
|
191 |
+
|
192 |
+
if ( $this->_is_multisite &&
|
193 |
+
( is_null( $network_level_or_blog_id ) || true === $network_level_or_blog_id )
|
194 |
+
) {
|
195 |
+
$this->_network_options->store();
|
196 |
+
}
|
197 |
+
}
|
198 |
+
|
199 |
+
/**
|
200 |
+
* @author Vova Feldman (@svovaf)
|
201 |
+
* @since 2.0.0
|
202 |
+
*
|
203 |
+
* @param int|null|bool $network_level_or_blog_id
|
204 |
+
* @param bool $flush
|
205 |
+
*/
|
206 |
+
function clear( $network_level_or_blog_id = null, $flush = false ) {
|
207 |
+
if ( ! $this->_is_multisite ||
|
208 |
+
false === $network_level_or_blog_id ||
|
209 |
+
is_null( $network_level_or_blog_id ) ||
|
210 |
+
is_numeric( $network_level_or_blog_id )
|
211 |
+
) {
|
212 |
+
$site_options = $this->get_site_options( $network_level_or_blog_id );
|
213 |
+
$site_options->clear( $flush );
|
214 |
+
}
|
215 |
+
|
216 |
+
if ( $this->_is_multisite &&
|
217 |
+
( true === $network_level_or_blog_id || is_null( $network_level_or_blog_id ) )
|
218 |
+
) {
|
219 |
+
$this->_network_options->clear( $flush );
|
220 |
+
}
|
221 |
+
}
|
222 |
+
|
223 |
+
/**
|
224 |
+
* Migration script to the new storage data structure that is network compatible.
|
225 |
+
*
|
226 |
+
* IMPORTANT:
|
227 |
+
* This method should be executed only after it is determined if this is a network
|
228 |
+
* level compatible product activation.
|
229 |
+
*
|
230 |
+
* @author Vova Feldman (@svovaf)
|
231 |
+
* @since 2.0.0
|
232 |
+
*
|
233 |
+
* @param int $blog_id
|
234 |
+
*/
|
235 |
+
function migrate_to_network( $blog_id = 0 ) {
|
236 |
+
if ( ! $this->_is_multisite ) {
|
237 |
+
return;
|
238 |
+
}
|
239 |
+
|
240 |
+
$updated = false;
|
241 |
+
|
242 |
+
$site_options = $this->get_site_options( $blog_id );
|
243 |
+
|
244 |
+
$keys = $site_options->get_options_keys();
|
245 |
+
|
246 |
+
foreach ( $keys as $option ) {
|
247 |
+
if ( $this->is_site_option( $option ) ||
|
248 |
+
// Don't move admin notices to the network storage.
|
249 |
+
in_array($option, array(
|
250 |
+
// Don't move admin notices to the network storage.
|
251 |
+
'admin_notices',
|
252 |
+
// Don't migrate the module specific data, it will be migrated by the FS_Storage.
|
253 |
+
'plugin_data',
|
254 |
+
'theme_data',
|
255 |
+
))
|
256 |
+
) {
|
257 |
+
continue;
|
258 |
+
}
|
259 |
+
|
260 |
+
$option_updated = false;
|
261 |
+
|
262 |
+
// Migrate option to the network storage.
|
263 |
+
$site_option = $site_options->get_option( $option );
|
264 |
+
|
265 |
+
if ( ! $this->_network_options->has_option( $option ) ) {
|
266 |
+
// Option not set on the network level, so just set it.
|
267 |
+
$this->_network_options->set_option( $option, $site_option, false );
|
268 |
+
|
269 |
+
$option_updated = true;
|
270 |
+
} else {
|
271 |
+
// Option already set on the network level, so we need to merge it inelegantly.
|
272 |
+
$network_option = $this->_network_options->get_option( $option );
|
273 |
+
|
274 |
+
if ( is_array( $network_option ) && is_array( $site_option ) ) {
|
275 |
+
// Option is an array.
|
276 |
+
foreach ( $site_option as $key => $value ) {
|
277 |
+
if ( ! isset( $network_option[ $key ] ) ) {
|
278 |
+
$network_option[ $key ] = $value;
|
279 |
+
|
280 |
+
$option_updated = true;
|
281 |
+
} else if ( is_array( $network_option[ $key ] ) && is_array( $value ) ) {
|
282 |
+
if ( empty( $network_option[ $key ] ) ) {
|
283 |
+
$network_option[ $key ] = $value;
|
284 |
+
|
285 |
+
$option_updated = true;
|
286 |
+
} else if ( empty( $value ) ) {
|
287 |
+
// Do nothing.
|
288 |
+
} else {
|
289 |
+
reset($value);
|
290 |
+
$first_key = key($value);
|
291 |
+
if ( $value[$first_key] instanceof FS_Entity ) {
|
292 |
+
// Merge entities by IDs.
|
293 |
+
$network_entities_ids = array();
|
294 |
+
foreach ( $network_option[ $key ] as $entity ) {
|
295 |
+
$network_entities_ids[ $entity->id ] = true;
|
296 |
+
}
|
297 |
+
|
298 |
+
foreach ( $value as $entity ) {
|
299 |
+
if ( ! isset( $network_entities_ids[ $entity->id ] ) ) {
|
300 |
+
$network_option[ $key ][] = $entity;
|
301 |
+
|
302 |
+
$option_updated = true;
|
303 |
+
}
|
304 |
+
}
|
305 |
+
}
|
306 |
+
}
|
307 |
+
}
|
308 |
+
}
|
309 |
+
}
|
310 |
+
|
311 |
+
if ( $option_updated ) {
|
312 |
+
$this->_network_options->set_option( $option, $network_option, false );
|
313 |
+
}
|
314 |
+
}
|
315 |
+
|
316 |
+
/**
|
317 |
+
* Remove the option from site level storage.
|
318 |
+
*
|
319 |
+
* IMPORTANT:
|
320 |
+
* The line below is intentionally commented since we want to preserve the option
|
321 |
+
* on the site storage level for "downgrade compatibility". Basically, if the user
|
322 |
+
* will downgrade to an older version of the plugin with the prev storage structure,
|
323 |
+
* it will continue working.
|
324 |
+
*
|
325 |
+
* @todo After a few releases we can remove this.
|
326 |
+
*/
|
327 |
+
// $site_options->unset_option($option, false);
|
328 |
+
|
329 |
+
if ( $option_updated ) {
|
330 |
+
$updated = true;
|
331 |
+
}
|
332 |
+
}
|
333 |
+
|
334 |
+
if ( ! $updated ) {
|
335 |
+
return;
|
336 |
+
}
|
337 |
+
|
338 |
+
// Update network level storage.
|
339 |
+
$this->_network_options->store();
|
340 |
+
// $site_options->store();
|
341 |
+
}
|
342 |
+
|
343 |
+
|
344 |
+
#--------------------------------------------------------------------------------
|
345 |
+
#region Helper Methods
|
346 |
+
#--------------------------------------------------------------------------------
|
347 |
+
|
348 |
+
/**
|
349 |
+
* We don't want to load the map right away since it's not even needed in a non-MS environment.
|
350 |
+
*
|
351 |
+
* @author Vova Feldman (@svovaf)
|
352 |
+
* @since 2.0.0
|
353 |
+
*/
|
354 |
+
private static function load_site_options_map() {
|
355 |
+
self::$_SITE_OPTIONS_MAP = array(
|
356 |
+
'sites' => true,
|
357 |
+
'theme_sites' => true,
|
358 |
+
'unique_id' => true,
|
359 |
+
'active_plugins' => true,
|
360 |
+
);
|
361 |
+
}
|
362 |
+
|
363 |
+
/**
|
364 |
+
* @author Vova Feldman (@svovaf)
|
365 |
+
* @since 2.0.0
|
366 |
+
*
|
367 |
+
* @param string $option
|
368 |
+
*
|
369 |
+
* @return bool
|
370 |
+
*/
|
371 |
+
private function is_site_option( $option ) {
|
372 |
+
if ( WP_FS__ACCOUNTS_OPTION_NAME != $this->_id ) {
|
373 |
+
return false;
|
374 |
+
}
|
375 |
+
|
376 |
+
if ( ! isset( self::$_SITE_OPTIONS_MAP ) ) {
|
377 |
+
self::load_site_options_map();
|
378 |
+
}
|
379 |
+
|
380 |
+
return isset( self::$_SITE_OPTIONS_MAP[ $option ] );
|
381 |
+
}
|
382 |
+
|
383 |
+
/**
|
384 |
+
* @author Vova Feldman (@svovaf)
|
385 |
+
* @since 2.0.0
|
386 |
+
*
|
387 |
+
* @param int $blog_id
|
388 |
+
*
|
389 |
+
* @return FS_Option_Manager
|
390 |
+
*/
|
391 |
+
private function get_site_options( $blog_id = 0 ) {
|
392 |
+
if ( 0 == $blog_id || $blog_id == $this->_blog_id ) {
|
393 |
+
return $this->_options;
|
394 |
+
}
|
395 |
+
|
396 |
+
return FS_Option_Manager::get_manager( $this->_id, true, $blog_id );
|
397 |
+
}
|
398 |
+
|
399 |
+
/**
|
400 |
+
* Check if an option should be stored on the MS network storage.
|
401 |
+
*
|
402 |
+
* @author Vova Feldman (@svovaf)
|
403 |
+
* @since 2.0.0
|
404 |
+
*
|
405 |
+
* @param string $option
|
406 |
+
* @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_SITE_LEVEL_PARAMS).
|
407 |
+
*
|
408 |
+
* @return bool
|
409 |
+
*/
|
410 |
+
private function should_use_network_storage( $option, $network_level_or_blog_id = null ) {
|
411 |
+
if ( ! $this->_is_multisite ) {
|
412 |
+
// Not a multisite environment.
|
413 |
+
return false;
|
414 |
+
}
|
415 |
+
|
416 |
+
if ( is_numeric( $network_level_or_blog_id ) ) {
|
417 |
+
// Explicitly asked to use a specified blog storage.
|
418 |
+
return false;
|
419 |
+
}
|
420 |
+
|
421 |
+
if ( is_bool( $network_level_or_blog_id ) ) {
|
422 |
+
// Explicitly specified whether should use the network or blog level storage.
|
423 |
+
return $network_level_or_blog_id;
|
424 |
+
}
|
425 |
+
|
426 |
+
// Determine which storage to use based on the option.
|
427 |
+
return ! $this->is_site_option( $option );
|
428 |
+
}
|
429 |
+
|
430 |
+
#endregion
|
431 |
}
|
freemius/includes/class-fs-plugin-updater.php
CHANGED
@@ -1,1561 +1,1561 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* @package Freemius
|
4 |
-
* @copyright Copyright (c) 2015, Freemius, Inc.
|
5 |
-
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
|
6 |
-
* @since 1.0.4
|
7 |
-
*
|
8 |
-
* @link https://github.com/easydigitaldownloads/EDD-License-handler/blob/master/EDD_SL_Plugin_Updater.php
|
9 |
-
*/
|
10 |
-
|
11 |
-
if ( ! defined( 'ABSPATH' ) ) {
|
12 |
-
exit;
|
13 |
-
}
|
14 |
-
|
15 |
-
class FS_Plugin_Updater {
|
16 |
-
|
17 |
-
/**
|
18 |
-
* @var Freemius
|
19 |
-
* @since 1.0.4
|
20 |
-
*/
|
21 |
-
private $_fs;
|
22 |
-
/**
|
23 |
-
* @var FS_Logger
|
24 |
-
* @since 1.0.4
|
25 |
-
*/
|
26 |
-
private $_logger;
|
27 |
-
/**
|
28 |
-
* @var object
|
29 |
-
* @since 1.1.8.1
|
30 |
-
*/
|
31 |
-
private $_update_details;
|
32 |
-
/**
|
33 |
-
* @var array
|
34 |
-
* @since 2.1.2
|
35 |
-
*/
|
36 |
-
private $_translation_updates;
|
37 |
-
|
38 |
-
private static $_upgrade_basename = null;
|
39 |
-
|
40 |
-
#--------------------------------------------------------------------------------
|
41 |
-
#region Singleton
|
42 |
-
#--------------------------------------------------------------------------------
|
43 |
-
|
44 |
-
/**
|
45 |
-
* @var FS_Plugin_Updater[]
|
46 |
-
* @since 2.0.0
|
47 |
-
*/
|
48 |
-
private static $_INSTANCES = array();
|
49 |
-
|
50 |
-
/**
|
51 |
-
* @param Freemius $freemius
|
52 |
-
*
|
53 |
-
* @return FS_Plugin_Updater
|
54 |
-
*/
|
55 |
-
static function instance( Freemius $freemius ) {
|
56 |
-
$key = $freemius->get_id();
|
57 |
-
|
58 |
-
if ( ! isset( self::$_INSTANCES[ $key ] ) ) {
|
59 |
-
self::$_INSTANCES[ $key ] = new self( $freemius );
|
60 |
-
}
|
61 |
-
|
62 |
-
return self::$_INSTANCES[ $key ];
|
63 |
-
}
|
64 |
-
|
65 |
-
#endregion
|
66 |
-
|
67 |
-
private function __construct( Freemius $freemius ) {
|
68 |
-
$this->_fs = $freemius;
|
69 |
-
|
70 |
-
$this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $freemius->get_slug() . '_updater', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
|
71 |
-
|
72 |
-
$this->filters();
|
73 |
-
}
|
74 |
-
|
75 |
-
/**
|
76 |
-
* Initiate required filters.
|
77 |
-
*
|
78 |
-
* @author Vova Feldman (@svovaf)
|
79 |
-
* @since 1.0.4
|
80 |
-
*/
|
81 |
-
private function filters() {
|
82 |
-
// Override request for plugin information
|
83 |
-
add_filter( 'plugins_api', array( &$this, 'plugins_api_filter' ), 10, 3 );
|
84 |
-
|
85 |
-
$this->add_transient_filters();
|
86 |
-
|
87 |
-
/**
|
88 |
-
* If user has the premium plugin's code but do NOT have an active license,
|
89 |
-
* encourage him to upgrade by showing that there's a new release, but instead
|
90 |
-
* of showing an update link, show upgrade link to the pricing page.
|
91 |
-
*
|
92 |
-
* @since 1.1.6
|
93 |
-
*
|
94 |
-
*/
|
95 |
-
// WP 2.9+
|
96 |
-
add_action( "after_plugin_row_{$this->_fs->get_plugin_basename()}", array(
|
97 |
-
&$this,
|
98 |
-
'catch_plugin_update_row'
|
99 |
-
), 9 );
|
100 |
-
add_action( "after_plugin_row_{$this->_fs->get_plugin_basename()}", array(
|
101 |
-
&$this,
|
102 |
-
'edit_and_echo_plugin_update_row'
|
103 |
-
), 11, 2 );
|
104 |
-
|
105 |
-
if ( ! $this->_fs->has_any_active_valid_license() ) {
|
106 |
-
add_action( 'admin_head', array( &$this, 'catch_plugin_information_dialog_contents' ) );
|
107 |
-
}
|
108 |
-
|
109 |
-
if ( ! WP_FS__IS_PRODUCTION_MODE ) {
|
110 |
-
add_filter( 'http_request_host_is_external', array(
|
111 |
-
$this,
|
112 |
-
'http_request_host_is_external_filter'
|
113 |
-
), 10, 3 );
|
114 |
-
}
|
115 |
-
|
116 |
-
if ( $this->_fs->is_premium() ) {
|
117 |
-
if ( ! $this->is_correct_folder_name() ) {
|
118 |
-
add_filter( 'upgrader_post_install', array( &$this, '_maybe_update_folder_name' ), 10, 3 );
|
119 |
-
}
|
120 |
-
|
121 |
-
add_filter( 'upgrader_pre_install', array( 'FS_Plugin_Updater', '_store_basename_for_source_adjustment' ), 1, 2 );
|
122 |
-
add_filter( 'upgrader_source_selection', array( 'FS_Plugin_Updater', '_maybe_adjust_source_dir' ), 1, 3 );
|
123 |
-
|
124 |
-
if ( ! $this->_fs->has_any_active_valid_license() ) {
|
125 |
-
add_filter( 'wp_prepare_themes_for_js', array( &$this, 'change_theme_update_info_html' ), 10, 1 );
|
126 |
-
}
|
127 |
-
}
|
128 |
-
}
|
129 |
-
|
130 |
-
/**
|
131 |
-
* @author Leo Fajardo (@leorw)
|
132 |
-
* @since 2.1.4
|
133 |
-
*/
|
134 |
-
function catch_plugin_information_dialog_contents() {
|
135 |
-
if (
|
136 |
-
'plugin-information' !== fs_request_get( 'tab', false ) ||
|
137 |
-
$this->_fs->get_slug() !== fs_request_get( 'plugin', false )
|
138 |
-
) {
|
139 |
-
return;
|
140 |
-
}
|
141 |
-
|
142 |
-
add_action( 'admin_footer', array( &$this, 'edit_and_echo_plugin_information_dialog_contents' ), 0, 1 );
|
143 |
-
|
144 |
-
ob_start();
|
145 |
-
}
|
146 |
-
|
147 |
-
/**
|
148 |
-
* @author Leo Fajardo (@leorw)
|
149 |
-
* @since 2.1.4
|
150 |
-
*
|
151 |
-
* @param string $hook_suffix
|
152 |
-
*/
|
153 |
-
function edit_and_echo_plugin_information_dialog_contents( $hook_suffix ) {
|
154 |
-
if (
|
155 |
-
'plugin-information' !== fs_request_get( 'tab', false ) ||
|
156 |
-
$this->_fs->get_slug() !== fs_request_get( 'plugin', false )
|
157 |
-
) {
|
158 |
-
return;
|
159 |
-
}
|
160 |
-
|
161 |
-
$license = $this->_fs->_get_license();
|
162 |
-
|
163 |
-
$subscription = ( is_object( $license ) && ! $license->is_lifetime() ) ?
|
164 |
-
$this->_fs->_get_subscription( $license->id ) :
|
165 |
-
null;
|
166 |
-
|
167 |
-
$contents = ob_get_clean();
|
168 |
-
|
169 |
-
$update_button_id_attribute_pos = strpos( $contents, 'id="plugin_update_from_iframe"' );
|
170 |
-
|
171 |
-
if ( false !== $update_button_id_attribute_pos ) {
|
172 |
-
$update_button_start_pos = strrpos(
|
173 |
-
substr( $contents, 0, $update_button_id_attribute_pos ),
|
174 |
-
'<a'
|
175 |
-
);
|
176 |
-
|
177 |
-
$update_button_end_pos = ( strpos( $contents, '</a>', $update_button_id_attribute_pos ) + strlen( '</a>' ) );
|
178 |
-
|
179 |
-
/**
|
180 |
-
* The part of the contents without the update button.
|
181 |
-
*
|
182 |
-
* @author Leo Fajardo (@leorw)
|
183 |
-
* @since 2.2.5
|
184 |
-
*/
|
185 |
-
$modified_contents = substr( $contents, 0, $update_button_start_pos );
|
186 |
-
|
187 |
-
$update_button = substr( $contents, $update_button_start_pos, ( $update_button_end_pos - $update_button_start_pos ) );
|
188 |
-
|
189 |
-
/**
|
190 |
-
* Replace the plugin information dialog's "Install Update Now" button's text and URL. If there's a license,
|
191 |
-
* the text will be "Renew license" and will link to the checkout page with the license's billing cycle
|
192 |
-
* and quota. If there's no license, the text will be "Buy license" and will link to the pricing page.
|
193 |
-
*/
|
194 |
-
$update_button = preg_replace(
|
195 |
-
'/(\<a.+)(id="plugin_update_from_iframe")(.+href=")([^\s]+)(".*\>)(.+)(\<\/a>)/is',
|
196 |
-
is_object( $license ) ?
|
197 |
-
sprintf(
|
198 |
-
'$1$3%s$5%s$7',
|
199 |
-
$this->_fs->checkout_url(
|
200 |
-
is_object( $subscription ) ?
|
201 |
-
( 1 == $subscription->billing_cycle ? WP_FS__PERIOD_MONTHLY : WP_FS__PERIOD_ANNUALLY ) :
|
202 |
-
WP_FS__PERIOD_LIFETIME,
|
203 |
-
false,
|
204 |
-
array( 'licenses' => $license->quota )
|
205 |
-
),
|
206 |
-
fs_text_inline( 'Renew license', 'renew-license', $this->_fs->get_slug() )
|
207 |
-
) :
|
208 |
-
sprintf(
|
209 |
-
'$1$3%s$5%s$7',
|
210 |
-
$this->_fs->pricing_url(),
|
211 |
-
fs_text_inline( 'Buy license', 'buy-license', $this->_fs->get_slug() )
|
212 |
-
),
|
213 |
-
$update_button
|
214 |
-
);
|
215 |
-
|
216 |
-
/**
|
217 |
-
* Append the modified button.
|
218 |
-
*
|
219 |
-
* @author Leo Fajardo (@leorw)
|
220 |
-
* @since 2.2.5
|
221 |
-
*/
|
222 |
-
$modified_contents .= $update_button;
|
223 |
-
|
224 |
-
/**
|
225 |
-
* Append the remaining part of the contents after the update button.
|
226 |
-
*
|
227 |
-
* @author Leo Fajardo (@leorw)
|
228 |
-
* @since 2.2.5
|
229 |
-
*/
|
230 |
-
$modified_contents .= substr( $contents, $update_button_end_pos );
|
231 |
-
|
232 |
-
$contents = $modified_contents;
|
233 |
-
}
|
234 |
-
|
235 |
-
echo $contents;
|
236 |
-
}
|
237 |
-
|
238 |
-
/**
|
239 |
-
* @author Vova Feldman (@svovaf)
|
240 |
-
* @since 2.0.0
|
241 |
-
*/
|
242 |
-
private function add_transient_filters() {
|
243 |
-
if ( $this->_fs->is_premium() && ! $this->_fs->is_tracking_allowed() ) {
|
244 |
-
$this->_logger->log( 'Opted out sites cannot receive automatic software updates.' );
|
245 |
-
|
246 |
-
return;
|
247 |
-
}
|
248 |
-
|
249 |
-
add_filter( 'pre_set_site_transient_update_plugins', array(
|
250 |
-
&$this,
|
251 |
-
'pre_set_site_transient_update_plugins_filter'
|
252 |
-
) );
|
253 |
-
|
254 |
-
add_filter( 'pre_set_site_transient_update_themes', array(
|
255 |
-
&$this,
|
256 |
-
'pre_set_site_transient_update_plugins_filter'
|
257 |
-
) );
|
258 |
-
}
|
259 |
-
|
260 |
-
/**
|
261 |
-
* @author Vova Feldman (@svovaf)
|
262 |
-
* @since 2.0.0
|
263 |
-
*/
|
264 |
-
private function remove_transient_filters() {
|
265 |
-
remove_filter( 'pre_set_site_transient_update_plugins', array(
|
266 |
-
&$this,
|
267 |
-
'pre_set_site_transient_update_plugins_filter'
|
268 |
-
) );
|
269 |
-
|
270 |
-
remove_filter( 'pre_set_site_transient_update_themes', array(
|
271 |
-
&$this,
|
272 |
-
'pre_set_site_transient_update_plugins_filter'
|
273 |
-
) );
|
274 |
-
}
|
275 |
-
|
276 |
-
/**
|
277 |
-
* Capture plugin update row by turning output buffering.
|
278 |
-
*
|
279 |
-
* @author Vova Feldman (@svovaf)
|
280 |
-
* @since 1.1.6
|
281 |
-
*/
|
282 |
-
function catch_plugin_update_row() {
|
283 |
-
ob_start();
|
284 |
-
}
|
285 |
-
|
286 |
-
/**
|
287 |
-
* Overrides default update message format with "renew your license" message.
|
288 |
-
*
|
289 |
-
* @author Vova Feldman (@svovaf)
|
290 |
-
* @since 1.1.6
|
291 |
-
*
|
292 |
-
* @param string $file
|
293 |
-
* @param array $plugin_data
|
294 |
-
*/
|
295 |
-
function edit_and_echo_plugin_update_row( $file, $plugin_data ) {
|
296 |
-
$plugin_update_row = ob_get_clean();
|
297 |
-
|
298 |
-
$current = get_site_transient( 'update_plugins' );
|
299 |
-
if ( ! isset( $current->response[ $file ] ) ) {
|
300 |
-
echo $plugin_update_row;
|
301 |
-
|
302 |
-
return;
|
303 |
-
}
|
304 |
-
|
305 |
-
$r = $current->response[ $file ];
|
306 |
-
|
307 |
-
$has_beta_update = $this->_fs->has_beta_update();
|
308 |
-
|
309 |
-
if ( $this->_fs->has_any_active_valid_license() ) {
|
310 |
-
if ( $has_beta_update ) {
|
311 |
-
/**
|
312 |
-
* Turn the "new version" text into "new Beta version".
|
313 |
-
*
|
314 |
-
* Sample input:
|
315 |
-
* There is a new version of Awesome Plugin available. <a href="...>View version x.y.z details</a> or <a href="...>update now</a>.
|
316 |
-
* Output:
|
317 |
-
* There is a new Beta version of Awesome Plugin available. <a href="...>View version x.y.z details</a> or <a href="...>update now</a>.
|
318 |
-
*
|
319 |
-
* @author Leo Fajardo (@leorw)
|
320 |
-
* @since 2.3.0
|
321 |
-
*/
|
322 |
-
$plugin_update_row = preg_replace(
|
323 |
-
'/(\<div.+>)(.+)(\<a.+href="([^\s]+)"([^\<]+)\>.+\<a.+)(\<\/div\>)/is',
|
324 |
-
(
|
325 |
-
'$1' .
|
326 |
-
sprintf(
|
327 |
-
fs_text_inline( 'There is a %s of %s available.', 'new-version-available', $this->_fs->get_slug() ),
|
328 |
-
$has_beta_update ?
|
329 |
-
fs_text_inline( 'new Beta version', 'new-beta-version', $this->_fs->get_slug() ) :
|
330 |
-
fs_text_inline( 'new version', 'new-version', $this->_fs->get_slug() ),
|
331 |
-
$this->_fs->get_plugin_title()
|
332 |
-
) .
|
333 |
-
' ' .
|
334 |
-
'$3' .
|
335 |
-
'$6'
|
336 |
-
),
|
337 |
-
$plugin_update_row
|
338 |
-
);
|
339 |
-
}
|
340 |
-
} else {
|
341 |
-
/**
|
342 |
-
* Turn the "new version" text into a link that opens the plugin information dialog when clicked and
|
343 |
-
* make the "View version x details" text link to the checkout page instead of opening the plugin
|
344 |
-
* information dialog when clicked.
|
345 |
-
*
|
346 |
-
* Sample input:
|
347 |
-
* There is a new version of Awesome Plugin available. <a href="...>View version x.y.z details</a> or <a href="...>update now</a>.
|
348 |
-
* Output:
|
349 |
-
* There is a <a href="...>new version</a> of Awesome Plugin available. <a href="...>Buy a license now</a> to access version x.y.z security & feature updates, and support.
|
350 |
-
* OR
|
351 |
-
* There is a <a href="...>new Beta version</a> of Awesome Plugin available. <a href="...>Buy a license now</a> to access version x.y.z security & feature updates, and support.
|
352 |
-
*
|
353 |
-
* @author Leo Fajardo (@leorw)
|
354 |
-
*/
|
355 |
-
$plugin_update_row = preg_replace(
|
356 |
-
'/(\<div.+>)(.+)(\<a.+href="([^\s]+)"([^\<]+)\>.+\<a.+)(\<\/div\>)/is',
|
357 |
-
(
|
358 |
-
'$1' .
|
359 |
-
sprintf(
|
360 |
-
fs_text_inline( 'There is a %s of %s available.', 'new-version-available', $this->_fs->get_slug() ),
|
361 |
-
sprintf(
|
362 |
-
'<a href="$4"%s>%s</a>',
|
363 |
-
'$5',
|
364 |
-
$has_beta_update ?
|
365 |
-
fs_text_inline( 'new Beta version', 'new-beta-version', $this->_fs->get_slug() ) :
|
366 |
-
fs_text_inline( 'new version', 'new-version', $this->_fs->get_slug() )
|
367 |
-
),
|
368 |
-
$this->_fs->get_plugin_title()
|
369 |
-
) .
|
370 |
-
' ' .
|
371 |
-
$this->_fs->version_upgrade_checkout_link( $r->new_version ) .
|
372 |
-
'$6'
|
373 |
-
),
|
374 |
-
$plugin_update_row
|
375 |
-
);
|
376 |
-
}
|
377 |
-
|
378 |
-
if (
|
379 |
-
$this->_fs->is_plugin() &&
|
380 |
-
isset( $r->upgrade_notice ) &&
|
381 |
-
strlen( trim( $r->upgrade_notice ) ) > 0
|
382 |
-
) {
|
383 |
-
$slug = $this->_fs->get_slug();
|
384 |
-
|
385 |
-
$upgrade_notice_html = sprintf(
|
386 |
-
'<p class="notice fs-upgrade-notice fs-slug-%1$s fs-type-%2$s" data-slug="%1$s" data-type="%2$s"><strong>%3$s</strong> %4$s</p>',
|
387 |
-
$slug,
|
388 |
-
$this->_fs->get_module_type(),
|
389 |
-
fs_text_inline( 'Important Upgrade Notice:', 'upgrade_notice', $slug ),
|
390 |
-
esc_html( $r->upgrade_notice )
|
391 |
-
);
|
392 |
-
|
393 |
-
$plugin_update_row = str_replace( '</div>', '</div>' . $upgrade_notice_html, $plugin_update_row );
|
394 |
-
}
|
395 |
-
|
396 |
-
echo $plugin_update_row;
|
397 |
-
}
|
398 |
-
|
399 |
-
/**
|
400 |
-
* @author Leo Fajardo (@leorw)
|
401 |
-
* @since 2.0.2
|
402 |
-
*
|
403 |
-
* @param array $prepared_themes
|
404 |
-
*
|
405 |
-
* @return array
|
406 |
-
*/
|
407 |
-
function change_theme_update_info_html( $prepared_themes ) {
|
408 |
-
$theme_basename = $this->_fs->get_plugin_basename();
|
409 |
-
|
410 |
-
if ( ! isset( $prepared_themes[ $theme_basename ] ) ) {
|
411 |
-
return $prepared_themes;
|
412 |
-
}
|
413 |
-
|
414 |
-
$themes_update = get_site_transient( 'update_themes' );
|
415 |
-
if ( ! isset( $themes_update->response[ $theme_basename ] ) ||
|
416 |
-
empty( $themes_update->response[ $theme_basename ]['package'] )
|
417 |
-
) {
|
418 |
-
return $prepared_themes;
|
419 |
-
}
|
420 |
-
|
421 |
-
$prepared_themes[ $theme_basename ]['update'] = preg_replace(
|
422 |
-
'/(\<p.+>)(.+)(\<a.+\<a.+)\.(.+\<\/p\>)/is',
|
423 |
-
'$1 $2 ' . $this->_fs->version_upgrade_checkout_link( $themes_update->response[ $theme_basename ]['new_version'] ) .
|
424 |
-
'$4',
|
425 |
-
$prepared_themes[ $theme_basename ]['update']
|
426 |
-
);
|
427 |
-
|
428 |
-
// Set to false to prevent the "Update now" link for the context theme from being shown on the "Themes" page.
|
429 |
-
$prepared_themes[ $theme_basename ]['hasPackage'] = false;
|
430 |
-
|
431 |
-
return $prepared_themes;
|
432 |
-
}
|
433 |
-
|
434 |
-
/**
|
435 |
-
* Since WP version 3.6, a new security feature was added that denies access to repository with a local ip.
|
436 |
-
* During development mode we want to be able updating plugin versions via our localhost repository. This
|
437 |
-
* filter white-list all domains including "api.freemius".
|
438 |
-
*
|
439 |
-
* @link http://www.emanueletessore.com/wordpress-download-failed-valid-url-provided/
|
440 |
-
*
|
441 |
-
* @author Vova Feldman (@svovaf)
|
442 |
-
* @since 1.0.4
|
443 |
-
*
|
444 |
-
* @param bool $allow
|
445 |
-
* @param string $host
|
446 |
-
* @param string $url
|
447 |
-
*
|
448 |
-
* @return bool
|
449 |
-
*/
|
450 |
-
function http_request_host_is_external_filter( $allow, $host, $url ) {
|
451 |
-
return ( false !== strpos( $host, 'freemius' ) ) ? true : $allow;
|
452 |
-
}
|
453 |
-
|
454 |
-
/**
|
455 |
-
* Check for Updates at the defined API endpoint and modify the update array.
|
456 |
-
*
|
457 |
-
* This function dives into the update api just when WordPress creates its update array,
|
458 |
-
* then adds a custom API call and injects the custom plugin data retrieved from the API.
|
459 |
-
* It is reassembled from parts of the native WordPress plugin update code.
|
460 |
-
* See wp-includes/update.php line 121 for the original wp_update_plugins() function.
|
461 |
-
*
|
462 |
-
* @author Vova Feldman (@svovaf)
|
463 |
-
* @since 1.0.4
|
464 |
-
*
|
465 |
-
* @uses FS_Api
|
466 |
-
*
|
467 |
-
* @param object $transient_data Update array build by WordPress.
|
468 |
-
*
|
469 |
-
* @return object Modified update array with custom plugin data.
|
470 |
-
*/
|
471 |
-
function pre_set_site_transient_update_plugins_filter( $transient_data ) {
|
472 |
-
$this->_logger->entrance();
|
473 |
-
|
474 |
-
/**
|
475 |
-
* "plugins" or "themes".
|
476 |
-
*
|
477 |
-
* @author Leo Fajardo (@leorw)
|
478 |
-
* @since 1.2.2
|
479 |
-
*/
|
480 |
-
$module_type = $this->_fs->get_module_type() . 's';
|
481 |
-
|
482 |
-
/**
|
483 |
-
* Ensure that we don't mix plugins update info with themes update info.
|
484 |
-
*
|
485 |
-
* @author Leo Fajardo (@leorw)
|
486 |
-
* @since 1.2.2
|
487 |
-
*/
|
488 |
-
if ( "pre_set_site_transient_update_{$module_type}" !== current_filter() ) {
|
489 |
-
return $transient_data;
|
490 |
-
}
|
491 |
-
|
492 |
-
if ( empty( $transient_data ) ||
|
493 |
-
defined( 'WP_FS__UNINSTALL_MODE' )
|
494 |
-
) {
|
495 |
-
return $transient_data;
|
496 |
-
}
|
497 |
-
|
498 |
-
global $wp_current_filter;
|
499 |
-
|
500 |
-
$current_plugin_version = $this->_fs->get_plugin_version();
|
501 |
-
|
502 |
-
if ( ! empty( $wp_current_filter ) && 'upgrader_process_complete' === $wp_current_filter[0] ) {
|
503 |
-
if (
|
504 |
-
is_null( $this->_update_details ) ||
|
505 |
-
( is_object( $this->_update_details ) && $this->_update_details->new_version !== $current_plugin_version )
|
506 |
-
) {
|
507 |
-
/**
|
508 |
-
* After an update, clear the stored update details and reparse the plugin's main file in order to get
|
509 |
-
* the updated version's information and prevent the previous update information from showing up on the
|
510 |
-
* updates page.
|
511 |
-
*
|
512 |
-
* @author Leo Fajardo (@leorw)
|
513 |
-
* @since 2.3.1
|
514 |
-
*/
|
515 |
-
$this->_update_details = null;
|
516 |
-
$current_plugin_version = $this->_fs->get_plugin_version( true );
|
517 |
-
}
|
518 |
-
}
|
519 |
-
|
520 |
-
if ( ! isset( $this->_update_details ) ) {
|
521 |
-
// Get plugin's newest update.
|
522 |
-
$new_version = $this->_fs->get_update(
|
523 |
-
false,
|
524 |
-
fs_request_get_bool( 'force-check' ),
|
525 |
-
WP_FS__TIME_24_HOURS_IN_SEC / 24,
|
526 |
-
$current_plugin_version
|
527 |
-
);
|
528 |
-
|
529 |
-
$this->_update_details = false;
|
530 |
-
|
531 |
-
if ( is_object( $new_version ) && $this->is_new_version_premium( $new_version ) ) {
|
532 |
-
$this->_logger->log( 'Found newer plugin version ' . $new_version->version );
|
533 |
-
|
534 |
-
/**
|
535 |
-
* Cache plugin details locally since set_site_transient( 'update_plugins' )
|
536 |
-
* called multiple times and the non wp.org plugins are filtered after the
|
537 |
-
* call to .org.
|
538 |
-
*
|
539 |
-
* @since 1.1.8.1
|
540 |
-
*/
|
541 |
-
$this->_update_details = $this->get_update_details( $new_version );
|
542 |
-
}
|
543 |
-
}
|
544 |
-
|
545 |
-
// Alias.
|
546 |
-
$basename = $this->_fs->premium_plugin_basename();
|
547 |
-
|
548 |
-
if ( is_object( $this->_update_details ) ) {
|
549 |
-
if ( isset( $transient_data->no_update ) ) {
|
550 |
-
unset( $transient_data->no_update[ $basename ] );
|
551 |
-
}
|
552 |
-
|
553 |
-
if ( ! isset( $transient_data->response ) ) {
|
554 |
-
$transient_data->response = array();
|
555 |
-
}
|
556 |
-
|
557 |
-
// Add plugin to transient data.
|
558 |
-
$transient_data->response[ $basename ] = $this->_fs->is_plugin() ?
|
559 |
-
$this->_update_details :
|
560 |
-
(array) $this->_update_details;
|
561 |
-
} else {
|
562 |
-
if ( isset( $transient_data->response ) ) {
|
563 |
-
/**
|
564 |
-
* Ensure that there's no update data for the plugin to prevent upgrading the premium version to the latest free version.
|
565 |
-
*
|
566 |
-
* @author Leo Fajardo (@leorw)
|
567 |
-
* @since 2.3.0
|
568 |
-
*/
|
569 |
-
unset( $transient_data->response[ $basename ] );
|
570 |
-
}
|
571 |
-
|
572 |
-
if ( ! isset( $transient_data->no_update ) ) {
|
573 |
-
$transient_data->no_update = array();
|
574 |
-
}
|
575 |
-
|
576 |
-
/**
|
577 |
-
* Add product to no_update transient data to properly integrate with WP 5.5 auto-updates UI.
|
578 |
-
*
|
579 |
-
* @since 2.4.1
|
580 |
-
* @link https://make.wordpress.org/core/2020/07/30/recommended-usage-of-the-updates-api-to-support-the-auto-updates-ui-for-plugins-and-themes-in-wordpress-5-5/
|
581 |
-
*/
|
582 |
-
$transient_data->no_update[ $basename ] = $this->_fs->is_plugin() ?
|
583 |
-
(object) array(
|
584 |
-
'id' => $basename,
|
585 |
-
'slug' => $this->_fs->get_slug(),
|
586 |
-
'plugin' => $basename,
|
587 |
-
'new_version' => $this->_fs->get_plugin_version(),
|
588 |
-
'url' => '',
|
589 |
-
'package' => '',
|
590 |
-
'icons' => array(),
|
591 |
-
'banners' => array(),
|
592 |
-
'banners_rtl' => array(),
|
593 |
-
'tested' => '',
|
594 |
-
'requires_php' => '',
|
595 |
-
'compatibility' => new stdClass(),
|
596 |
-
) :
|
597 |
-
array(
|
598 |
-
'theme' => $basename,
|
599 |
-
'new_version' => $this->_fs->get_plugin_version(),
|
600 |
-
'url' => '',
|
601 |
-
'package' => '',
|
602 |
-
'requires' => '',
|
603 |
-
'requires_php' => '',
|
604 |
-
);
|
605 |
-
}
|
606 |
-
|
607 |
-
$slug = $this->_fs->get_slug();
|
608 |
-
|
609 |
-
if ( $this->_fs->is_org_repo_compliant() && $this->_fs->is_freemium() ) {
|
610 |
-
if ( ! isset( $this->_translation_updates ) ) {
|
611 |
-
$this->_translation_updates = array();
|
612 |
-
|
613 |
-
if ( current_user_can( 'update_languages' ) ) {
|
614 |
-
$translation_updates = $this->fetch_wp_org_module_translation_updates( $module_type, $slug );
|
615 |
-
if ( ! empty( $translation_updates ) ) {
|
616 |
-
$this->_translation_updates = $translation_updates;
|
617 |
-
}
|
618 |
-
}
|
619 |
-
}
|
620 |
-
|
621 |
-
if ( ! empty( $this->_translation_updates ) ) {
|
622 |
-
$all_translation_updates = ( isset( $transient_data->translations ) && is_array( $transient_data->translations ) ) ?
|
623 |
-
$transient_data->translations :
|
624 |
-
array();
|
625 |
-
|
626 |
-
$current_plugin_translation_updates_map = array();
|
627 |
-
foreach ( $all_translation_updates as $key => $translation_update ) {
|
628 |
-
if ( $module_type === ( $translation_update['type'] . 's' ) && $slug === $translation_update['slug'] ) {
|
629 |
-
$current_plugin_translation_updates_map[ $translation_update['language'] ] = $translation_update;
|
630 |
-
unset( $all_translation_updates[ $key ] );
|
631 |
-
}
|
632 |
-
}
|
633 |
-
|
634 |
-
foreach ( $this->_translation_updates as $translation_update ) {
|
635 |
-
$lang = $translation_update['language'];
|
636 |
-
if ( ! isset( $current_plugin_translation_updates_map[ $lang ] ) ||
|
637 |
-
version_compare( $translation_update['version'], $current_plugin_translation_updates_map[ $lang ]['version'], '>' )
|
638 |
-
) {
|
639 |
-
$current_plugin_translation_updates_map[ $lang ] = $translation_update;
|
640 |
-
}
|
641 |
-
}
|
642 |
-
|
643 |
-
$transient_data->translations = array_merge( $all_translation_updates, array_values( $current_plugin_translation_updates_map ) );
|
644 |
-
}
|
645 |
-
}
|
646 |
-
|
647 |
-
return $transient_data;
|
648 |
-
}
|
649 |
-
|
650 |
-
/**
|
651 |
-
* Get module's required data for the updates mechanism.
|
652 |
-
*
|
653 |
-
* @author Vova Feldman (@svovaf)
|
654 |
-
* @since 2.0.0
|
655 |
-
*
|
656 |
-
* @param \FS_Plugin_Tag $new_version
|
657 |
-
*
|
658 |
-
* @return object
|
659 |
-
*/
|
660 |
-
function get_update_details( FS_Plugin_Tag $new_version ) {
|
661 |
-
$update = new stdClass();
|
662 |
-
$update->slug = $this->_fs->get_slug();
|
663 |
-
$update->new_version = $new_version->version;
|
664 |
-
$update->url = WP_FS__ADDRESS;
|
665 |
-
$update->package = $new_version->url;
|
666 |
-
$update->tested = $new_version->tested_up_to_version;
|
667 |
-
$update->requires = $new_version->requires_platform_version;
|
668 |
-
|
669 |
-
$icon = $this->_fs->get_local_icon_url();
|
670 |
-
|
671 |
-
if ( ! empty( $icon ) ) {
|
672 |
-
$update->icons = array(
|
673 |
-
// '1x' => $icon,
|
674 |
-
// '2x' => $icon,
|
675 |
-
'default' => $icon,
|
676 |
-
);
|
677 |
-
}
|
678 |
-
|
679 |
-
if ( $this->_fs->is_premium() ) {
|
680 |
-
$latest_tag = $this->_fs->_fetch_latest_version( $this->_fs->get_id(), false );
|
681 |
-
|
682 |
-
if (
|
683 |
-
isset( $latest_tag->readme ) &&
|
684 |
-
isset( $latest_tag->readme->upgrade_notice ) &&
|
685 |
-
! empty( $latest_tag->readme->upgrade_notice )
|
686 |
-
) {
|
687 |
-
$update->upgrade_notice = $latest_tag->readme->upgrade_notice;
|
688 |
-
}
|
689 |
-
}
|
690 |
-
|
691 |
-
$update->{$this->_fs->get_module_type()} = $this->_fs->get_plugin_basename();
|
692 |
-
|
693 |
-
return $update;
|
694 |
-
}
|
695 |
-
|
696 |
-
/**
|
697 |
-
* @author Leo Fajardo (@leorw)
|
698 |
-
* @since 2.3.0
|
699 |
-
*
|
700 |
-
* @param FS_Plugin_Tag $new_version
|
701 |
-
*
|
702 |
-
* @return bool
|
703 |
-
*/
|
704 |
-
private function is_new_version_premium( FS_Plugin_Tag $new_version ) {
|
705 |
-
$query_str = parse_url( $new_version->url, PHP_URL_QUERY );
|
706 |
-
if ( empty( $query_str ) ) {
|
707 |
-
return false;
|
708 |
-
}
|
709 |
-
|
710 |
-
parse_str( $query_str, $params );
|
711 |
-
|
712 |
-
return ( isset( $params['is_premium'] ) && 'true' == $params['is_premium'] );
|
713 |
-
}
|
714 |
-
|
715 |
-
/**
|
716 |
-
* Update the updates transient with the module's update information.
|
717 |
-
*
|
718 |
-
* This method is required for multisite environment.
|
719 |
-
* If a module is site activated (not network) and not on the main site,
|
720 |
-
* the module will NOT be executed on the network level, therefore, the
|
721 |
-
* custom updates logic will not be executed as well, so unless we force
|
722 |
-
* the injection of the update into the updates transient, premium updates
|
723 |
-
* will not work.
|
724 |
-
*
|
725 |
-
* @author Vova Feldman (@svovaf)
|
726 |
-
* @since 2.0.0
|
727 |
-
*
|
728 |
-
* @param \FS_Plugin_Tag $new_version
|
729 |
-
*/
|
730 |
-
function set_update_data( FS_Plugin_Tag $new_version ) {
|
731 |
-
$this->_logger->entrance();
|
732 |
-
|
733 |
-
if ( ! $this->is_new_version_premium( $new_version ) ) {
|
734 |
-
return;
|
735 |
-
}
|
736 |
-
|
737 |
-
$transient_key = "update_{$this->_fs->get_module_type()}s";
|
738 |
-
|
739 |
-
$transient_data = get_site_transient( $transient_key );
|
740 |
-
|
741 |
-
$transient_data = is_object( $transient_data ) ?
|
742 |
-
$transient_data :
|
743 |
-
new stdClass();
|
744 |
-
|
745 |
-
// Alias.
|
746 |
-
$basename = $this->_fs->get_plugin_basename();
|
747 |
-
$is_plugin = $this->_fs->is_plugin();
|
748 |
-
|
749 |
-
if ( ! isset( $transient_data->response ) ||
|
750 |
-
! is_array( $transient_data->response )
|
751 |
-
) {
|
752 |
-
$transient_data->response = array();
|
753 |
-
} else if ( ! empty( $transient_data->response[ $basename ] ) ) {
|
754 |
-
$version = $is_plugin ?
|
755 |
-
( ! empty( $transient_data->response[ $basename ]->new_version ) ?
|
756 |
-
$transient_data->response[ $basename ]->new_version :
|
757 |
-
null
|
758 |
-
) : ( ! empty( $transient_data->response[ $basename ]['new_version'] ) ?
|
759 |
-
$transient_data->response[ $basename ]['new_version'] :
|
760 |
-
null
|
761 |
-
);
|
762 |
-
|
763 |
-
if ( $version == $new_version->version ) {
|
764 |
-
// The update data is already set.
|
765 |
-
return;
|
766 |
-
}
|
767 |
-
}
|
768 |
-
|
769 |
-
// Remove the added filters.
|
770 |
-
$this->remove_transient_filters();
|
771 |
-
|
772 |
-
$this->_update_details = $this->get_update_details( $new_version );
|
773 |
-
|
774 |
-
// Set update data in transient.
|
775 |
-
$transient_data->response[ $basename ] = $is_plugin ?
|
776 |
-
$this->_update_details :
|
777 |
-
(array) $this->_update_details;
|
778 |
-
|
779 |
-
if ( ! isset( $transient_data->checked ) ||
|
780 |
-
! is_array( $transient_data->checked )
|
781 |
-
) {
|
782 |
-
$transient_data->checked = array();
|
783 |
-
}
|
784 |
-
|
785 |
-
// Flag the module as if it was already checked.
|
786 |
-
$transient_data->checked[ $basename ] = $this->_fs->get_plugin_version();
|
787 |
-
$transient_data->last_checked = time();
|
788 |
-
|
789 |
-
set_site_transient( $transient_key, $transient_data );
|
790 |
-
|
791 |
-
$this->add_transient_filters();
|
792 |
-
}
|
793 |
-
|
794 |
-
/**
|
795 |
-
* @author Leo Fajardo (@leorw)
|
796 |
-
* @since 2.0.2
|
797 |
-
*/
|
798 |
-
function delete_update_data() {
|
799 |
-
$this->_logger->entrance();
|
800 |
-
|
801 |
-
$transient_key = "update_{$this->_fs->get_module_type()}s";
|
802 |
-
|
803 |
-
$transient_data = get_site_transient( $transient_key );
|
804 |
-
|
805 |
-
// Alias
|
806 |
-
$basename = $this->_fs->get_plugin_basename();
|
807 |
-
|
808 |
-
if ( ! is_object( $transient_data ) ||
|
809 |
-
! isset( $transient_data->response ) ||
|
810 |
-
! is_array( $transient_data->response ) ||
|
811 |
-
empty( $transient_data->response[ $basename ] )
|
812 |
-
) {
|
813 |
-
return;
|
814 |
-
}
|
815 |
-
|
816 |
-
unset( $transient_data->response[ $basename ] );
|
817 |
-
|
818 |
-
// Remove the added filters.
|
819 |
-
$this->remove_transient_filters();
|
820 |
-
|
821 |
-
set_site_transient( $transient_key, $transient_data );
|
822 |
-
|
823 |
-
$this->add_transient_filters();
|
824 |
-
}
|
825 |
-
|
826 |
-
/**
|
827 |
-
* Try to fetch plugin's info from .org repository.
|
828 |
-
*
|
829 |
-
* @author Vova Feldman (@svovaf)
|
830 |
-
* @since 1.0.5
|
831 |
-
*
|
832 |
-
* @param string $action
|
833 |
-
* @param object $args
|
834 |
-
*
|
835 |
-
* @return bool|mixed
|
836 |
-
*/
|
837 |
-
static function _fetch_plugin_info_from_repository( $action, $args ) {
|
838 |
-
$url = $http_url = 'http://api.wordpress.org/plugins/info/1.0/';
|
839 |
-
if ( $ssl = wp_http_supports( array( 'ssl' ) ) ) {
|
840 |
-
$url = set_url_scheme( $url, 'https' );
|
841 |
-
}
|
842 |
-
|
843 |
-
$args = array(
|
844 |
-
'timeout' => 15,
|
845 |
-
'body' => array(
|
846 |
-
'action' => $action,
|
847 |
-
'request' => serialize( $args )
|
848 |
-
)
|
849 |
-
);
|
850 |
-
|
851 |
-
$request = wp_remote_post( $url, $args );
|
852 |
-
|
853 |
-
if ( is_wp_error( $request ) ) {
|
854 |
-
return false;
|
855 |
-
}
|
856 |
-
|
857 |
-
$res = maybe_unserialize( wp_remote_retrieve_body( $request ) );
|
858 |
-
|
859 |
-
if ( ! is_object( $res ) && ! is_array( $res ) ) {
|
860 |
-
return false;
|
861 |
-
}
|
862 |
-
|
863 |
-
return $res;
|
864 |
-
}
|
865 |
-
|
866 |
-
/**
|
867 |
-
* Fetches module translation updates from wordpress.org.
|
868 |
-
*
|
869 |
-
* @author Leo Fajardo (@leorw)
|
870 |
-
* @since 2.1.2
|
871 |
-
*
|
872 |
-
* @param string $module_type
|
873 |
-
* @param string $slug
|
874 |
-
*
|
875 |
-
* @return array|null
|
876 |
-
*/
|
877 |
-
private function fetch_wp_org_module_translation_updates( $module_type, $slug ) {
|
878 |
-
$plugin_data = $this->_fs->get_plugin_data();
|
879 |
-
|
880 |
-
$locales = array_values( get_available_languages() );
|
881 |
-
$locales = apply_filters( "{$module_type}_update_check_locales", $locales );
|
882 |
-
$locales = array_unique( $locales );
|
883 |
-
|
884 |
-
$plugin_basename = $this->_fs->get_plugin_basename();
|
885 |
-
if ( 'themes' === $module_type ) {
|
886 |
-
$plugin_basename = $slug;
|
887 |
-
}
|
888 |
-
|
889 |
-
global $wp_version;
|
890 |
-
|
891 |
-
$request_args = array(
|
892 |
-
'timeout' => 15,
|
893 |
-
'body' => array(
|
894 |
-
"{$module_type}" => json_encode(
|
895 |
-
array(
|
896 |
-
"{$module_type}" => array(
|
897 |
-
$plugin_basename => array(
|
898 |
-
'Name' => trim( str_replace( $this->_fs->get_plugin()->premium_suffix, '', $plugin_data['Name'] ) ),
|
899 |
-
'Author' => $plugin_data['Author'],
|
900 |
-
)
|
901 |
-
)
|
902 |
-
)
|
903 |
-
),
|
904 |
-
'translations' => json_encode( $this->get_installed_translations( $module_type, $slug ) ),
|
905 |
-
'locale' => json_encode( $locales )
|
906 |
-
),
|
907 |
-
'user-agent' => ( 'WordPress/' . $wp_version . '; ' . home_url( '/' ) )
|
908 |
-
);
|
909 |
-
|
910 |
-
$url = "http://api.wordpress.org/{$module_type}/update-check/1.1/";
|
911 |
-
if ( $ssl = wp_http_supports( array( 'ssl' ) ) ) {
|
912 |
-
$url = set_url_scheme( $url, 'https' );
|
913 |
-
}
|
914 |
-
|
915 |
-
$raw_response = Freemius::safe_remote_post(
|
916 |
-
$url,
|
917 |
-
$request_args,
|
918 |
-
WP_FS__TIME_24_HOURS_IN_SEC,
|
919 |
-
WP_FS__TIME_12_HOURS_IN_SEC,
|
920 |
-
false
|
921 |
-
);
|
922 |
-
|
923 |
-
if ( is_wp_error( $raw_response ) ) {
|
924 |
-
return null;
|
925 |
-
}
|
926 |
-
|
927 |
-
$response = json_decode( wp_remote_retrieve_body( $raw_response ), true );
|
928 |
-
|
929 |
-
if ( ! is_array( $response ) ) {
|
930 |
-
return null;
|
931 |
-
}
|
932 |
-
|
933 |
-
if ( ! isset( $response['translations'] ) || empty( $response['translations'] ) ) {
|
934 |
-
return null;
|
935 |
-
}
|
936 |
-
|
937 |
-
return $response['translations'];
|
938 |
-
}
|
939 |
-
|
940 |
-
/**
|
941 |
-
* @author Leo Fajardo (@leorw)
|
942 |
-
* @since 2.1.2
|
943 |
-
*
|
944 |
-
* @param string $module_type
|
945 |
-
* @param string $slug
|
946 |
-
*
|
947 |
-
* @return array
|
948 |
-
*/
|
949 |
-
private function get_installed_translations( $module_type, $slug ) {
|
950 |
-
if ( function_exists( 'wp_get_installed_translations' ) ) {
|
951 |
-
return wp_get_installed_translations( $module_type );
|
952 |
-
}
|
953 |
-
|
954 |
-
$dir = "/{$module_type}";
|
955 |
-
|
956 |
-
if ( ! is_dir( WP_LANG_DIR . $dir ) )
|
957 |
-
return array();
|
958 |
-
|
959 |
-
$files = scandir( WP_LANG_DIR . $dir );
|
960 |
-
if ( ! $files )
|
961 |
-
return array();
|
962 |
-
|
963 |
-
$language_data = array();
|
964 |
-
|
965 |
-
foreach ( $files as $file ) {
|
966 |
-
if ( 0 !== strpos( $file, $slug ) ) {
|
967 |
-
continue;
|
968 |
-
}
|
969 |
-
|
970 |
-
if ( '.' === $file[0] || is_dir( WP_LANG_DIR . "{$dir}/{$file}" ) ) {
|
971 |
-
continue;
|
972 |
-
}
|
973 |
-
|
974 |
-
if ( substr( $file, -3 ) !== '.po' ) {
|
975 |
-
continue;
|
976 |
-
}
|
977 |
-
|
978 |
-
if ( ! preg_match( '/(?:(.+)-)?([a-z]{2,3}(?:_[A-Z]{2})?(?:_[a-z0-9]+)?).po/', $file, $match ) ) {
|
979 |
-
continue;
|
980 |
-
}
|
981 |
-
|
982 |
-
if ( ! in_array( substr( $file, 0, -3 ) . '.mo', $files ) ) {
|
983 |
-
continue;
|
984 |
-
}
|
985 |
-
|
986 |
-
list( , $textdomain, $language ) = $match;
|
987 |
-
|
988 |
-
if ( '' === $textdomain ) {
|
989 |
-
$textdomain = 'default';
|
990 |
-
}
|
991 |
-
|
992 |
-
$language_data[ $textdomain ][ $language ] = wp_get_pomo_file_data( WP_LANG_DIR . "{$dir}/{$file}" );
|
993 |
-
}
|
994 |
-
|
995 |
-
return $language_data;
|
996 |
-
}
|
997 |
-
|
998 |
-
/**
|
999 |
-
* Updates information on the "View version x.x details" page with custom data.
|
1000 |
-
*
|
1001 |
-
* @author Vova Feldman (@svovaf)
|
1002 |
-
* @since 1.0.4
|
1003 |
-
*
|
1004 |
-
* @uses FS_Api
|
1005 |
-
*
|
1006 |
-
* @param object $data
|
1007 |
-
* @param string $action
|
1008 |
-
* @param mixed $args
|
1009 |
-
*
|
1010 |
-
* @return object
|
1011 |
-
*/
|
1012 |
-
function plugins_api_filter( $data, $action = '', $args = null ) {
|
1013 |
-
$this->_logger->entrance();
|
1014 |
-
|
1015 |
-
if ( ( 'plugin_information' !== $action ) ||
|
1016 |
-
! isset( $args->slug )
|
1017 |
-
) {
|
1018 |
-
return $data;
|
1019 |
-
}
|
1020 |
-
|
1021 |
-
$addon = false;
|
1022 |
-
$is_addon = false;
|
1023 |
-
$addon_version = false;
|
1024 |
-
|
1025 |
-
if ( $this->_fs->get_slug() !== $args->slug ) {
|
1026 |
-
$addon = $this->_fs->get_addon_by_slug( $args->slug );
|
1027 |
-
|
1028 |
-
if ( ! is_object( $addon ) ) {
|
1029 |
-
return $data;
|
1030 |
-
}
|
1031 |
-
|
1032 |
-
if ( $this->_fs->is_addon_activated( $addon->id ) ) {
|
1033 |
-
$addon_version = $this->_fs->get_addon_instance( $addon->id )->get_plugin_version();
|
1034 |
-
} else if ( $this->_fs->is_addon_installed( $addon->id ) ) {
|
1035 |
-
$addon_plugin_data = get_plugin_data(
|
1036 |
-
( WP_PLUGIN_DIR . '/' . $this->_fs->get_addon_basename( $addon->id ) ),
|
1037 |
-
false,
|
1038 |
-
false
|
1039 |
-
);
|
1040 |
-
|
1041 |
-
if ( ! empty( $addon_plugin_data ) ) {
|
1042 |
-
$addon_version = $addon_plugin_data['Version'];
|
1043 |
-
}
|
1044 |
-
}
|
1045 |
-
|
1046 |
-
$is_addon = true;
|
1047 |
-
}
|
1048 |
-
|
1049 |
-
$plugin_in_repo = false;
|
1050 |
-
if ( ! $is_addon ) {
|
1051 |
-
// Try to fetch info from .org repository.
|
1052 |
-
$data = self::_fetch_plugin_info_from_repository( $action, $args );
|
1053 |
-
|
1054 |
-
$plugin_in_repo = ( false !== $data );
|
1055 |
-
}
|
1056 |
-
|
1057 |
-
if ( ! $plugin_in_repo ) {
|
1058 |
-
$data = $args;
|
1059 |
-
|
1060 |
-
// Fetch as much as possible info from local files.
|
1061 |
-
$plugin_local_data = $this->_fs->get_plugin_data();
|
1062 |
-
$data->name = $plugin_local_data['Name'];
|
1063 |
-
$data->author = $plugin_local_data['Author'];
|
1064 |
-
$data->sections = array(
|
1065 |
-
'description' => 'Upgrade ' . $plugin_local_data['Name'] . ' to latest.',
|
1066 |
-
);
|
1067 |
-
|
1068 |
-
// @todo Store extra plugin info on Freemius or parse readme.txt markup.
|
1069 |
-
/*$info = $this->_fs->get_api_site_scope()->call('/information.json');
|
1070 |
-
|
1071 |
-
if ( !isset($info->error) ) {
|
1072 |
-
$data = $info;
|
1073 |
-
}*/
|
1074 |
-
}
|
1075 |
-
|
1076 |
-
$plugin_version = $is_addon ?
|
1077 |
-
$addon_version :
|
1078 |
-
$this->_fs->get_plugin_version();
|
1079 |
-
|
1080 |
-
// Get plugin's newest update.
|
1081 |
-
$new_version = $this->get_latest_download_details( $is_addon ? $addon->id : false, $plugin_version );
|
1082 |
-
|
1083 |
-
if ( ! is_object( $new_version ) || empty( $new_version->version ) ) {
|
1084 |
-
$data->version = $plugin_version;
|
1085 |
-
} else {
|
1086 |
-
if ( $is_addon ) {
|
1087 |
-
$data->name = $addon->title . ' ' . $this->_fs->get_text_inline( 'Add-On', 'addon' );
|
1088 |
-
$data->slug = $addon->slug;
|
1089 |
-
$data->url = WP_FS__ADDRESS;
|
1090 |
-
$data->package = $new_version->url;
|
1091 |
-
}
|
1092 |
-
|
1093 |
-
if ( ! $plugin_in_repo ) {
|
1094 |
-
$data->last_updated = ! is_null( $new_version->updated ) ? $new_version->updated : $new_version->created;
|
1095 |
-
$data->requires = $new_version->requires_platform_version;
|
1096 |
-
$data->tested = $new_version->tested_up_to_version;
|
1097 |
-
}
|
1098 |
-
|
1099 |
-
$data->version = $new_version->version;
|
1100 |
-
$data->download_link = $new_version->url;
|
1101 |
-
|
1102 |
-
if ( isset( $new_version->readme ) && is_object( $new_version->readme ) ) {
|
1103 |
-
$new_version_readme_data = $new_version->readme;
|
1104 |
-
if ( isset( $new_version_readme_data->sections ) ) {
|
1105 |
-
$new_version_readme_data->sections = (array) $new_version_readme_data->sections;
|
1106 |
-
} else {
|
1107 |
-
$new_version_readme_data->sections = array();
|
1108 |
-
}
|
1109 |
-
|
1110 |
-
if ( isset( $data->sections ) ) {
|
1111 |
-
if ( isset( $data->sections['screenshots'] ) ) {
|
1112 |
-
$new_version_readme_data->sections['screenshots'] = $data->sections['screenshots'];
|
1113 |
-
}
|
1114 |
-
|
1115 |
-
if ( isset( $data->sections['reviews'] ) ) {
|
1116 |
-
$new_version_readme_data->sections['reviews'] = $data->sections['reviews'];
|
1117 |
-
}
|
1118 |
-
}
|
1119 |
-
|
1120 |
-
if ( isset( $new_version_readme_data->banners ) ) {
|
1121 |
-
$new_version_readme_data->banners = (array) $new_version_readme_data->banners;
|
1122 |
-
} else if ( isset( $data->banners ) ) {
|
1123 |
-
$new_version_readme_data->banners = $data->banners;
|
1124 |
-
}
|
1125 |
-
|
1126 |
-
$wp_org_sections = array(
|
1127 |
-
'author',
|
1128 |
-
'author_profile',
|
1129 |
-
'rating',
|
1130 |
-
'ratings',
|
1131 |
-
'num_ratings',
|
1132 |
-
'support_threads',
|
1133 |
-
'support_threads_resolved',
|
1134 |
-
'active_installs',
|
1135 |
-
'added',
|
1136 |
-
'homepage'
|
1137 |
-
);
|
1138 |
-
|
1139 |
-
foreach ( $wp_org_sections as $wp_org_section ) {
|
1140 |
-
if ( isset( $data->{$wp_org_section} ) ) {
|
1141 |
-
$new_version_readme_data->{$wp_org_section} = $data->{$wp_org_section};
|
1142 |
-
}
|
1143 |
-
}
|
1144 |
-
|
1145 |
-
$data = $new_version_readme_data;
|
1146 |
-
}
|
1147 |
-
}
|
1148 |
-
|
1149 |
-
return $data;
|
1150 |
-
}
|
1151 |
-
|
1152 |
-
/**
|
1153 |
-
* @author Vova Feldman (@svovaf)
|
1154 |
-
* @since 1.2.1.7
|
1155 |
-
*
|
1156 |
-
* @param number|bool $addon_id
|
1157 |
-
* @param bool|string $newer_than Since 2.2.1
|
1158 |
-
* @param bool|string $fetch_readme Since 2.2.1
|
1159 |
-
*
|
1160 |
-
* @return object
|
1161 |
-
*/
|
1162 |
-
private function get_latest_download_details( $addon_id = false, $newer_than = false, $fetch_readme = true ) {
|
1163 |
-
return $this->_fs->_fetch_latest_version( $addon_id, true, WP_FS__TIME_24_HOURS_IN_SEC, $newer_than, $fetch_readme );
|
1164 |
-
}
|
1165 |
-
|
1166 |
-
/**
|
1167 |
-
* Checks if a given basename has a matching folder name
|
1168 |
-
* with the current context plugin.
|
1169 |
-
*
|
1170 |
-
* @author Vova Feldman (@svovaf)
|
1171 |
-
* @since 1.2.1.6
|
1172 |
-
*
|
1173 |
-
* @return bool
|
1174 |
-
*/
|
1175 |
-
private function is_correct_folder_name() {
|
1176 |
-
return ( $this->_fs->get_target_folder_name() == trim( dirname( $this->_fs->get_plugin_basename() ), '/\\' ) );
|
1177 |
-
}
|
1178 |
-
|
1179 |
-
/**
|
1180 |
-
* This is a special after upgrade handler for migrating modules
|
1181 |
-
* that didn't use the '-premium' suffix folder structure before
|
1182 |
-
* the migration.
|
1183 |
-
*
|
1184 |
-
* @author Vova Feldman (@svovaf)
|
1185 |
-
* @since 1.2.1.6
|
1186 |
-
*
|
1187 |
-
* @param bool $response Install response.
|
1188 |
-
* @param array $hook_extra Extra arguments passed to hooked filters.
|
1189 |
-
* @param array $result Installation result data.
|
1190 |
-
*
|
1191 |
-
* @return bool
|
1192 |
-
*/
|
1193 |
-
function _maybe_update_folder_name( $response, $hook_extra, $result ) {
|
1194 |
-
$basename = $this->_fs->get_plugin_basename();
|
1195 |
-
|
1196 |
-
if ( true !== $response ||
|
1197 |
-
empty( $hook_extra ) ||
|
1198 |
-
empty( $hook_extra['plugin'] ) ||
|
1199 |
-
$basename !== $hook_extra['plugin']
|
1200 |
-
) {
|
1201 |
-
return $response;
|
1202 |
-
}
|
1203 |
-
|
1204 |
-
$active_plugins_basenames = get_option( 'active_plugins' );
|
1205 |
-
|
1206 |
-
foreach ( $active_plugins_basenames as $key => $active_plugin_basename ) {
|
1207 |
-
if ( $basename === $active_plugin_basename ) {
|
1208 |
-
// Get filename including extension.
|
1209 |
-
$filename = basename( $basename );
|
1210 |
-
|
1211 |
-
$new_basename = plugin_basename(
|
1212 |
-
trailingslashit( $this->_fs->is_premium() ? $this->_fs->get_premium_slug() : $this->_fs->get_slug() ) .
|
1213 |
-
$filename
|
1214 |
-
);
|
1215 |
-
|
1216 |
-
// Verify that the expected correct path exists.
|
1217 |
-
if ( file_exists( fs_normalize_path( WP_PLUGIN_DIR . '/' . $new_basename ) ) ) {
|
1218 |
-
// Override active plugin name.
|
1219 |
-
$active_plugins_basenames[ $key ] = $new_basename;
|
1220 |
-
update_option( 'active_plugins', $active_plugins_basenames );
|
1221 |
-
}
|
1222 |
-
|
1223 |
-
break;
|
1224 |
-
}
|
1225 |
-
}
|
1226 |
-
|
1227 |
-
return $response;
|
1228 |
-
}
|
1229 |
-
|
1230 |
-
#----------------------------------------------------------------------------------
|
1231 |
-
#region Auto Activation
|
1232 |
-
#----------------------------------------------------------------------------------
|
1233 |
-
|
1234 |
-
/**
|
1235 |
-
* Installs and active a plugin when explicitly requested that from a 3rd party service.
|
1236 |
-
*
|
1237 |
-
* This logic was inspired by the TGMPA GPL licensed library by Thomas Griffin.
|
1238 |
-
*
|
1239 |
-
* @link http://tgmpluginactivation.com/
|
1240 |
-
*
|
1241 |
-
* @author Vova Feldman
|
1242 |
-
* @since 1.2.1.7
|
1243 |
-
*
|
1244 |
-
* @link https://make.wordpress.org/plugins/2017/03/16/clarification-of-guideline-8-executable-code-and-installs/
|
1245 |
-
*
|
1246 |
-
* @uses WP_Filesystem
|
1247 |
-
* @uses WP_Error
|
1248 |
-
* @uses WP_Upgrader
|
1249 |
-
* @uses Plugin_Upgrader
|
1250 |
-
* @uses Plugin_Installer_Skin
|
1251 |
-
* @uses Plugin_Upgrader_Skin
|
1252 |
-
*
|
1253 |
-
* @param number|bool $plugin_id
|
1254 |
-
*
|
1255 |
-
* @return array
|
1256 |
-
*/
|
1257 |
-
function install_and_activate_plugin( $plugin_id = false ) {
|
1258 |
-
if ( ! empty( $plugin_id ) && ! FS_Plugin::is_valid_id( $plugin_id ) ) {
|
1259 |
-
// Invalid plugin ID.
|
1260 |
-
return array(
|
1261 |
-
'message' => $this->_fs->get_text_inline( 'Invalid module ID.', 'auto-install-error-invalid-id' ),
|
1262 |
-
'code' => 'invalid_module_id',
|
1263 |
-
);
|
1264 |
-
}
|
1265 |
-
|
1266 |
-
$is_addon = false;
|
1267 |
-
if ( FS_Plugin::is_valid_id( $plugin_id ) &&
|
1268 |
-
$plugin_id != $this->_fs->get_id()
|
1269 |
-
) {
|
1270 |
-
$addon = $this->_fs->get_addon( $plugin_id );
|
1271 |
-
|
1272 |
-
if ( ! is_object( $addon ) ) {
|
1273 |
-
// Invalid add-on ID.
|
1274 |
-
return array(
|
1275 |
-
'message' => $this->_fs->get_text_inline( 'Invalid module ID.', 'auto-install-error-invalid-id' ),
|
1276 |
-
'code' => 'invalid_module_id',
|
1277 |
-
);
|
1278 |
-
}
|
1279 |
-
|
1280 |
-
$slug = $addon->slug;
|
1281 |
-
$premium_slug = $addon->premium_slug;
|
1282 |
-
$title = $addon->title . ' ' . $this->_fs->get_text_inline( 'Add-On', 'addon' );
|
1283 |
-
|
1284 |
-
$is_addon = true;
|
1285 |
-
} else {
|
1286 |
-
$slug = $this->_fs->get_slug();
|
1287 |
-
$premium_slug = $this->_fs->get_premium_slug();
|
1288 |
-
$title = $this->_fs->get_plugin_title() .
|
1289 |
-
( $this->_fs->is_addon() ? ' ' . $this->_fs->get_text_inline( 'Add-On', 'addon' ) : '' );
|
1290 |
-
}
|
1291 |
-
|
1292 |
-
if ( $this->is_premium_plugin_active( $plugin_id ) ) {
|
1293 |
-
// Premium version already activated.
|
1294 |
-
return array(
|
1295 |
-
'message' => $is_addon ?
|
1296 |
-
$this->_fs->get_text_inline( 'Premium add-on version already installed.', 'auto-install-error-premium-addon-activated' ) :
|
1297 |
-
$this->_fs->get_text_inline( 'Premium version already active.', 'auto-install-error-premium-activated' ),
|
1298 |
-
'code' => 'premium_installed',
|
1299 |
-
);
|
1300 |
-
}
|
1301 |
-
|
1302 |
-
$latest_version = $this->get_latest_download_details( $plugin_id, false, false );
|
1303 |
-
$target_folder = $premium_slug;
|
1304 |
-
|
1305 |
-
// Prep variables for Plugin_Installer_Skin class.
|
1306 |
-
$extra = array();
|
1307 |
-
$extra['slug'] = $target_folder;
|
1308 |
-
$source = $latest_version->url;
|
1309 |
-
$api = null;
|
1310 |
-
|
1311 |
-
$install_url = add_query_arg(
|
1312 |
-
array(
|
1313 |
-
'action' => 'install-plugin',
|
1314 |
-
'plugin' => urlencode( $slug ),
|
1315 |
-
),
|
1316 |
-
'update.php'
|
1317 |
-
);
|
1318 |
-
|
1319 |
-
if ( ! class_exists( 'Plugin_Upgrader', false ) ) {
|
1320 |
-
// Include required resources for the installation.
|
1321 |
-
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
|
1322 |
-
}
|
1323 |
-
|
1324 |
-
$skin_args = array(
|
1325 |
-
'type' => 'web',
|
1326 |
-
'title' => sprintf( $this->_fs->get_text_inline( 'Installing plugin: %s', 'installing-plugin-x' ), $title ),
|
1327 |
-
'url' => esc_url_raw( $install_url ),
|
1328 |
-
'nonce' => 'install-plugin_' . $slug,
|
1329 |
-
'plugin' => '',
|
1330 |
-
'api' => $api,
|
1331 |
-
'extra' => $extra,
|
1332 |
-
);
|
1333 |
-
|
1334 |
-
// $skin = new Automatic_Upgrader_Skin( $skin_args );
|
1335 |
-
// $skin = new Plugin_Installer_Skin( $skin_args );
|
1336 |
-
$skin = new WP_Ajax_Upgrader_Skin( $skin_args );
|
1337 |
-
|
1338 |
-
// Create a new instance of Plugin_Upgrader.
|
1339 |
-
$upgrader = new Plugin_Upgrader( $skin );
|
1340 |
-
|
1341 |
-
// Perform the action and install the plugin from the $source urldecode().
|
1342 |
-
add_filter( 'upgrader_source_selection', array( 'FS_Plugin_Updater', '_maybe_adjust_source_dir' ), 1, 3 );
|
1343 |
-
|
1344 |
-
$install_result = $upgrader->install( $source );
|
1345 |
-
|
1346 |
-
remove_filter( 'upgrader_source_selection', array( 'FS_Plugin_Updater', '_maybe_adjust_source_dir' ), 1 );
|
1347 |
-
|
1348 |
-
if ( is_wp_error( $install_result ) ) {
|
1349 |
-
return array(
|
1350 |
-
'message' => $install_result->get_error_message(),
|
1351 |
-
'code' => $install_result->get_error_code(),
|
1352 |
-
);
|
1353 |
-
} elseif ( is_wp_error( $skin->result ) ) {
|
1354 |
-
return array(
|
1355 |
-
'message' => $skin->result->get_error_message(),
|
1356 |
-
'code' => $skin->result->get_error_code(),
|
1357 |
-
);
|
1358 |
-
} elseif ( $skin->get_errors()->get_error_code() ) {
|
1359 |
-
return array(
|
1360 |
-
'message' => $skin->get_error_messages(),
|
1361 |
-
'code' => 'unknown',
|
1362 |
-
);
|
1363 |
-
} elseif ( is_null( $install_result ) ) {
|
1364 |
-
global $wp_filesystem;
|
1365 |
-
|
1366 |
-
$error_code = 'unable_to_connect_to_filesystem';
|
1367 |
-
$error_message = $this->_fs->get_text_inline( 'Unable to connect to the filesystem. Please confirm your credentials.' );
|
1368 |
-
|
1369 |
-
// Pass through the error from WP_Filesystem if one was raised.
|
1370 |
-
if ( $wp_filesystem instanceof WP_Filesystem_Base &&
|
1371 |
-
is_wp_error( $wp_filesystem->errors ) &&
|
1372 |
-
$wp_filesystem->errors->get_error_code()
|
1373 |
-
) {
|
1374 |
-
$error_message = $wp_filesystem->errors->get_error_message();
|
1375 |
-
}
|
1376 |
-
|
1377 |
-
return array(
|
1378 |
-
'message' => $error_message,
|
1379 |
-
'code' => $error_code,
|
1380 |
-
);
|
1381 |
-
}
|
1382 |
-
|
1383 |
-
// Grab the full path to the main plugin's file.
|
1384 |
-
$plugin_activate = $upgrader->plugin_info();
|
1385 |
-
|
1386 |
-
// Try to activate the plugin.
|
1387 |
-
$activation_result = $this->try_activate_plugin( $plugin_activate );
|
1388 |
-
|
1389 |
-
if ( is_wp_error( $activation_result ) ) {
|
1390 |
-
return array(
|
1391 |
-
'message' => $activation_result->get_error_message(),
|
1392 |
-
'code' => $activation_result->get_error_code(),
|
1393 |
-
);
|
1394 |
-
}
|
1395 |
-
|
1396 |
-
return $skin->get_upgrade_messages();
|
1397 |
-
}
|
1398 |
-
|
1399 |
-
/**
|
1400 |
-
* Tries to activate a plugin. If fails, returns the error.
|
1401 |
-
*
|
1402 |
-
* @author Vova Feldman
|
1403 |
-
* @since 1.2.1.7
|
1404 |
-
*
|
1405 |
-
* @param string $file_path Path within wp-plugins/ to main plugin file.
|
1406 |
-
* This determines the styling of the output messages.
|
1407 |
-
*
|
1408 |
-
* @return bool|WP_Error
|
1409 |
-
*/
|
1410 |
-
protected function try_activate_plugin( $file_path ) {
|
1411 |
-
$activate = activate_plugin( $file_path, '', $this->_fs->is_network_active() );
|
1412 |
-
|
1413 |
-
return is_wp_error( $activate ) ?
|
1414 |
-
$activate :
|
1415 |
-
true;
|
1416 |
-
}
|
1417 |
-
|
1418 |
-
/**
|
1419 |
-
* Check if a premium module version is already active.
|
1420 |
-
*
|
1421 |
-
* @author Vova Feldman
|
1422 |
-
* @since 1.2.1.7
|
1423 |
-
*
|
1424 |
-
* @param number|bool $plugin_id
|
1425 |
-
*
|
1426 |
-
* @return bool
|
1427 |
-
*/
|
1428 |
-
private function is_premium_plugin_active( $plugin_id = false ) {
|
1429 |
-
if ( $plugin_id != $this->_fs->get_id() ) {
|
1430 |
-
return $this->_fs->is_addon_activated( $plugin_id, true );
|
1431 |
-
}
|
1432 |
-
|
1433 |
-
return is_plugin_active( $this->_fs->premium_plugin_basename() );
|
1434 |
-
}
|
1435 |
-
|
1436 |
-
/**
|
1437 |
-
* Store the basename since it's not always available in the `_maybe_adjust_source_dir` method below.
|
1438 |
-
*
|
1439 |
-
* @author Leo Fajardo (@leorw)
|
1440 |
-
* @since 2.2.1
|
1441 |
-
*
|
1442 |
-
* @param bool|WP_Error $response Response.
|
1443 |
-
* @param array $hook_extra Extra arguments passed to hooked filters.
|
1444 |
-
*
|
1445 |
-
* @return bool|WP_Error
|
1446 |
-
*/
|
1447 |
-
static function _store_basename_for_source_adjustment( $response, $hook_extra ) {
|
1448 |
-
if ( isset( $hook_extra['plugin'] ) ) {
|
1449 |
-
self::$_upgrade_basename = $hook_extra['plugin'];
|
1450 |
-
} else if ( isset( $hook_extra['theme'] ) ) {
|
1451 |
-
self::$_upgrade_basename = $hook_extra['theme'];
|
1452 |
-
} else {
|
1453 |
-
self::$_upgrade_basename = null;
|
1454 |
-
}
|
1455 |
-
|
1456 |
-
return $response;
|
1457 |
-
}
|
1458 |
-
|
1459 |
-
/**
|
1460 |
-
* Adjust the plugin directory name if necessary.
|
1461 |
-
* Assumes plugin has a folder (not a single file plugin).
|
1462 |
-
*
|
1463 |
-
* The final destination directory of a plugin is based on the subdirectory name found in the
|
1464 |
-
* (un)zipped source. In some cases this subdirectory name is not the same as the expected
|
1465 |
-
* slug and the plugin will not be recognized as installed. This is fixed by adjusting
|
1466 |
-
* the temporary unzipped source subdirectory name to the expected plugin slug.
|
1467 |
-
*
|
1468 |
-
* @author Vova Feldman
|
1469 |
-
* @since 1.2.1.7
|
1470 |
-
* @since 2.2.1 The method was converted to static since when the admin update bulk products via the Updates section, the logic applies the `upgrader_source_selection` filter for every product that is being updated.
|
1471 |
-
*
|
1472 |
-
* @param string $source Path to upgrade/zip-file-name.tmp/subdirectory/.
|
1473 |
-
* @param string $remote_source Path to upgrade/zip-file-name.tmp.
|
1474 |
-
* @param \WP_Upgrader $upgrader Instance of the upgrader which installs the plugin.
|
1475 |
-
*
|
1476 |
-
* @return string|WP_Error
|
1477 |
-
*/
|
1478 |
-
static function _maybe_adjust_source_dir( $source, $remote_source, $upgrader ) {
|
1479 |
-
if ( ! is_object( $GLOBALS['wp_filesystem'] ) ) {
|
1480 |
-
return $source;
|
1481 |
-
}
|
1482 |
-
|
1483 |
-
$basename = self::$_upgrade_basename;
|
1484 |
-
$is_theme = false;
|
1485 |
-
|
1486 |
-
// Figure out what the slug is supposed to be.
|
1487 |
-
if ( isset( $upgrader->skin->options['extra'] ) ) {
|
1488 |
-
// Set by the auto-install logic.
|
1489 |
-
$desired_slug = $upgrader->skin->options['extra']['slug'];
|
1490 |
-
} else if ( ! empty( $basename ) ) {
|
1491 |
-
/**
|
1492 |
-
* If it doesn't end with ".php", it's a theme.
|
1493 |
-
*
|
1494 |
-
* @author Leo Fajardo (@leorw)
|
1495 |
-
* @since 2.2.1
|
1496 |
-
*/
|
1497 |
-
$is_theme = ( ! fs_ends_with( $basename, '.php' ) );
|
1498 |
-
|
1499 |
-
$desired_slug = ( ! $is_theme ) ?
|
1500 |
-
dirname( $basename ) :
|
1501 |
-
// Theme slug
|
1502 |
-
$basename;
|
1503 |
-
} else {
|
1504 |
-
// Can't figure out the desired slug, stop the execution.
|
1505 |
-
return $source;
|
1506 |
-
}
|
1507 |
-
|
1508 |
-
if ( is_multisite() ) {
|
1509 |
-
/**
|
1510 |
-
* If we are running in a multisite environment and the product is not network activated,
|
1511 |
-
* the instance will not exist anyway. Therefore, try to update the source if necessary
|
1512 |
-
* regardless if the Freemius instance of the product exists or not.
|
1513 |
-
*
|
1514 |
-
* @author Vova Feldman
|
1515 |
-
*/
|
1516 |
-
} else if ( ! empty( $basename ) ) {
|
1517 |
-
$fs = Freemius::get_instance_by_file(
|
1518 |
-
$basename,
|
1519 |
-
$is_theme ?
|
1520 |
-
WP_FS__MODULE_TYPE_THEME :
|
1521 |
-
WP_FS__MODULE_TYPE_PLUGIN
|
1522 |
-
);
|
1523 |
-
|
1524 |
-
if ( ! is_object( $fs ) ) {
|
1525 |
-
/**
|
1526 |
-
* If the Freemius instance does not exist on a non-multisite network environment, it means that:
|
1527 |
-
* 1. The product is not powered by Freemius; OR
|
1528 |
-
* 2. The product is not activated, therefore, we don't mind if after the update the folder name will change.
|
1529 |
-
*
|
1530 |
-
* @author Leo Fajardo (@leorw)
|
1531 |
-
* @since 2.2.1
|
1532 |
-
*/
|
1533 |
-
return $source;
|
1534 |
-
}
|
1535 |
-
}
|
1536 |
-
|
1537 |
-
$subdir_name = untrailingslashit( str_replace( trailingslashit( $remote_source ), '', $source ) );
|
1538 |
-
|
1539 |
-
if ( ! empty( $subdir_name ) && $subdir_name !== $desired_slug ) {
|
1540 |
-
$from_path = untrailingslashit( $source );
|
1541 |
-
$to_path = trailingslashit( $remote_source ) . $desired_slug;
|
1542 |
-
|
1543 |
-
if ( true === $GLOBALS['wp_filesystem']->move( $from_path, $to_path ) ) {
|
1544 |
-
return trailingslashit( $to_path );
|
1545 |
-
}
|
1546 |
-
|
1547 |
-
return new WP_Error(
|
1548 |
-
'rename_failed',
|
1549 |
-
fs_text_inline( 'The remote plugin package does not contain a folder with the desired slug and renaming did not work.', 'module-package-rename-failure' ),
|
1550 |
-
array(
|
1551 |
-
'found' => $subdir_name,
|
1552 |
-
'expected' => $desired_slug
|
1553 |
-
)
|
1554 |
-
);
|
1555 |
-
}
|
1556 |
-
|
1557 |
-
return $source;
|
1558 |
-
}
|
1559 |
-
|
1560 |
-
#endregion
|
1561 |
-
}
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package Freemius
|
4 |
+
* @copyright Copyright (c) 2015, Freemius, Inc.
|
5 |
+
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
|
6 |
+
* @since 1.0.4
|
7 |
+
*
|
8 |
+
* @link https://github.com/easydigitaldownloads/EDD-License-handler/blob/master/EDD_SL_Plugin_Updater.php
|
9 |
+
*/
|
10 |
+
|
11 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
12 |
+
exit;
|
13 |
+
}
|
14 |
+
|
15 |
+
class FS_Plugin_Updater {
|
16 |
+
|
17 |
+
/**
|
18 |
+
* @var Freemius
|
19 |
+
* @since 1.0.4
|
20 |
+
*/
|
21 |
+
private $_fs;
|
22 |
+
/**
|
23 |
+
* @var FS_Logger
|
24 |
+
* @since 1.0.4
|
25 |
+
*/
|
26 |
+
private $_logger;
|
27 |
+
/**
|
28 |
+
* @var object
|
29 |
+
* @since 1.1.8.1
|
30 |
+
*/
|
31 |
+
private $_update_details;
|
32 |
+
/**
|
33 |
+
* @var array
|
34 |
+
* @since 2.1.2
|
35 |
+
*/
|
36 |
+
private $_translation_updates;
|
37 |
+
|
38 |
+
private static $_upgrade_basename = null;
|
39 |
+
|
40 |
+
#--------------------------------------------------------------------------------
|
41 |
+
#region Singleton
|
42 |
+
#--------------------------------------------------------------------------------
|
43 |
+
|
44 |
+
/**
|
45 |
+
* @var FS_Plugin_Updater[]
|
46 |
+
* @since 2.0.0
|
47 |
+
*/
|
48 |
+
private static $_INSTANCES = array();
|
49 |
+
|
50 |
+
/**
|
51 |
+
* @param Freemius $freemius
|
52 |
+
*
|
53 |
+
* @return FS_Plugin_Updater
|
54 |
+
*/
|
55 |
+
static function instance( Freemius $freemius ) {
|
56 |
+
$key = $freemius->get_id();
|
57 |
+
|
58 |
+
if ( ! isset( self::$_INSTANCES[ $key ] ) ) {
|
59 |
+
self::$_INSTANCES[ $key ] = new self( $freemius );
|
60 |
+
}
|
61 |
+
|
62 |
+
return self::$_INSTANCES[ $key ];
|
63 |
+
}
|
64 |
+
|
65 |
+
#endregion
|
66 |
+
|
67 |
+
private function __construct( Freemius $freemius ) {
|
68 |
+
$this->_fs = $freemius;
|
69 |
+
|
70 |
+
$this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $freemius->get_slug() . '_updater', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
|
71 |
+
|
72 |
+
$this->filters();
|
73 |
+
}
|
74 |
+
|
75 |
+
/**
|
76 |
+
* Initiate required filters.
|
77 |
+
*
|
78 |
+
* @author Vova Feldman (@svovaf)
|
79 |
+
* @since 1.0.4
|
80 |
+
*/
|
81 |
+
private function filters() {
|
82 |
+
// Override request for plugin information
|
83 |
+
add_filter( 'plugins_api', array( &$this, 'plugins_api_filter' ), 10, 3 );
|
84 |
+
|
85 |
+
$this->add_transient_filters();
|
86 |
+
|
87 |
+
/**
|
88 |
+
* If user has the premium plugin's code but do NOT have an active license,
|
89 |
+
* encourage him to upgrade by showing that there's a new release, but instead
|
90 |
+
* of showing an update link, show upgrade link to the pricing page.
|
91 |
+
*
|
92 |
+
* @since 1.1.6
|
93 |
+
*
|
94 |
+
*/
|
95 |
+
// WP 2.9+
|
96 |
+
add_action( "after_plugin_row_{$this->_fs->get_plugin_basename()}", array(
|
97 |
+
&$this,
|
98 |
+
'catch_plugin_update_row'
|
99 |
+
), 9 );
|
100 |
+
add_action( "after_plugin_row_{$this->_fs->get_plugin_basename()}", array(
|
101 |
+
&$this,
|
102 |
+
'edit_and_echo_plugin_update_row'
|
103 |
+
), 11, 2 );
|
104 |
+
|
105 |
+
if ( ! $this->_fs->has_any_active_valid_license() ) {
|
106 |
+
add_action( 'admin_head', array( &$this, 'catch_plugin_information_dialog_contents' ) );
|
107 |
+
}
|
108 |
+
|
109 |
+
if ( ! WP_FS__IS_PRODUCTION_MODE ) {
|
110 |
+
add_filter( 'http_request_host_is_external', array(
|
111 |
+
$this,
|
112 |
+
'http_request_host_is_external_filter'
|
113 |
+
), 10, 3 );
|
114 |
+
}
|
115 |
+
|
116 |
+
if ( $this->_fs->is_premium() ) {
|
117 |
+
if ( ! $this->is_correct_folder_name() ) {
|
118 |
+
add_filter( 'upgrader_post_install', array( &$this, '_maybe_update_folder_name' ), 10, 3 );
|
119 |
+
}
|
120 |
+
|
121 |
+
add_filter( 'upgrader_pre_install', array( 'FS_Plugin_Updater', '_store_basename_for_source_adjustment' ), 1, 2 );
|
122 |
+
add_filter( 'upgrader_source_selection', array( 'FS_Plugin_Updater', '_maybe_adjust_source_dir' ), 1, 3 );
|
123 |
+
|
124 |
+
if ( ! $this->_fs->has_any_active_valid_license() ) {
|
125 |
+
add_filter( 'wp_prepare_themes_for_js', array( &$this, 'change_theme_update_info_html' ), 10, 1 );
|
126 |
+
}
|
127 |
+
}
|
128 |
+
}
|
129 |
+
|
130 |
+
/**
|
131 |
+
* @author Leo Fajardo (@leorw)
|
132 |
+
* @since 2.1.4
|
133 |
+
*/
|
134 |
+
function catch_plugin_information_dialog_contents() {
|
135 |
+
if (
|
136 |
+
'plugin-information' !== fs_request_get( 'tab', false ) ||
|
137 |
+
$this->_fs->get_slug() !== fs_request_get( 'plugin', false )
|
138 |
+
) {
|
139 |
+
return;
|
140 |
+
}
|
141 |
+
|
142 |
+
add_action( 'admin_footer', array( &$this, 'edit_and_echo_plugin_information_dialog_contents' ), 0, 1 );
|
143 |
+
|
144 |
+
ob_start();
|
145 |
+
}
|
146 |
+
|
147 |
+
/**
|
148 |
+
* @author Leo Fajardo (@leorw)
|
149 |
+
* @since 2.1.4
|
150 |
+
*
|
151 |
+
* @param string $hook_suffix
|
152 |
+
*/
|
153 |
+
function edit_and_echo_plugin_information_dialog_contents( $hook_suffix ) {
|
154 |
+
if (
|
155 |
+
'plugin-information' !== fs_request_get( 'tab', false ) ||
|
156 |
+
$this->_fs->get_slug() !== fs_request_get( 'plugin', false )
|
157 |
+
) {
|
158 |
+
return;
|
159 |
+
}
|
160 |
+
|
161 |
+
$license = $this->_fs->_get_license();
|
162 |
+
|
163 |
+
$subscription = ( is_object( $license ) && ! $license->is_lifetime() ) ?
|
164 |
+
$this->_fs->_get_subscription( $license->id ) :
|
165 |
+
null;
|
166 |
+
|
167 |
+
$contents = ob_get_clean();
|
168 |
+
|
169 |
+
$update_button_id_attribute_pos = strpos( $contents, 'id="plugin_update_from_iframe"' );
|
170 |
+
|
171 |
+
if ( false !== $update_button_id_attribute_pos ) {
|
172 |
+
$update_button_start_pos = strrpos(
|
173 |
+
substr( $contents, 0, $update_button_id_attribute_pos ),
|
174 |
+
'<a'
|
175 |
+
);
|
176 |
+
|
177 |
+
$update_button_end_pos = ( strpos( $contents, '</a>', $update_button_id_attribute_pos ) + strlen( '</a>' ) );
|
178 |
+
|
179 |
+
/**
|
180 |
+
* The part of the contents without the update button.
|
181 |
+
*
|
182 |
+
* @author Leo Fajardo (@leorw)
|
183 |
+
* @since 2.2.5
|
184 |
+
*/
|
185 |
+
$modified_contents = substr( $contents, 0, $update_button_start_pos );
|
186 |
+
|
187 |
+
$update_button = substr( $contents, $update_button_start_pos, ( $update_button_end_pos - $update_button_start_pos ) );
|
188 |
+
|
189 |
+
/**
|
190 |
+
* Replace the plugin information dialog's "Install Update Now" button's text and URL. If there's a license,
|
191 |
+
* the text will be "Renew license" and will link to the checkout page with the license's billing cycle
|
192 |
+
* and quota. If there's no license, the text will be "Buy license" and will link to the pricing page.
|
193 |
+
*/
|
194 |
+
$update_button = preg_replace(
|
195 |
+
'/(\<a.+)(id="plugin_update_from_iframe")(.+href=")([^\s]+)(".*\>)(.+)(\<\/a>)/is',
|
196 |
+
is_object( $license ) ?
|
197 |
+
sprintf(
|
198 |
+
'$1$3%s$5%s$7',
|
199 |
+
$this->_fs->checkout_url(
|
200 |
+
is_object( $subscription ) ?
|
201 |
+
( 1 == $subscription->billing_cycle ? WP_FS__PERIOD_MONTHLY : WP_FS__PERIOD_ANNUALLY ) :
|
202 |
+
WP_FS__PERIOD_LIFETIME,
|
203 |
+
false,
|
204 |
+
array( 'licenses' => $license->quota )
|
205 |
+
),
|
206 |
+
fs_text_inline( 'Renew license', 'renew-license', $this->_fs->get_slug() )
|
207 |
+
) :
|
208 |
+
sprintf(
|
209 |
+
'$1$3%s$5%s$7',
|
210 |
+
$this->_fs->pricing_url(),
|
211 |
+
fs_text_inline( 'Buy license', 'buy-license', $this->_fs->get_slug() )
|
212 |
+
),
|
213 |
+
$update_button
|
214 |
+
);
|
215 |
+
|
216 |
+
/**
|
217 |
+
* Append the modified button.
|
218 |
+
*
|
219 |
+
* @author Leo Fajardo (@leorw)
|
220 |
+
* @since 2.2.5
|
221 |
+
*/
|
222 |
+
$modified_contents .= $update_button;
|
223 |
+
|
224 |
+
/**
|
225 |
+
* Append the remaining part of the contents after the update button.
|
226 |
+
*
|
227 |
+
* @author Leo Fajardo (@leorw)
|
228 |
+
* @since 2.2.5
|
229 |
+
*/
|
230 |
+
$modified_contents .= substr( $contents, $update_button_end_pos );
|
231 |
+
|
232 |
+
$contents = $modified_contents;
|
233 |
+
}
|
234 |
+
|
235 |
+
echo $contents;
|
236 |
+
}
|
237 |
+
|
238 |
+
/**
|
239 |
+
* @author Vova Feldman (@svovaf)
|
240 |
+
* @since 2.0.0
|
241 |
+
*/
|
242 |
+
private function add_transient_filters() {
|
243 |
+
if ( $this->_fs->is_premium() && ! $this->_fs->is_tracking_allowed() ) {
|
244 |
+
$this->_logger->log( 'Opted out sites cannot receive automatic software updates.' );
|
245 |
+
|
246 |
+
return;
|
247 |
+
}
|
248 |
+
|
249 |
+
add_filter( 'pre_set_site_transient_update_plugins', array(
|
250 |
+
&$this,
|
251 |
+
'pre_set_site_transient_update_plugins_filter'
|
252 |
+
) );
|
253 |
+
|
254 |
+
add_filter( 'pre_set_site_transient_update_themes', array(
|
255 |
+
&$this,
|
256 |
+
'pre_set_site_transient_update_plugins_filter'
|
257 |
+
) );
|
258 |
+
}
|
259 |
+
|
260 |
+
/**
|
261 |
+
* @author Vova Feldman (@svovaf)
|
262 |
+
* @since 2.0.0
|
263 |
+
*/
|
264 |
+
private function remove_transient_filters() {
|
265 |
+
remove_filter( 'pre_set_site_transient_update_plugins', array(
|
266 |
+
&$this,
|
267 |
+
'pre_set_site_transient_update_plugins_filter'
|
268 |
+
) );
|
269 |
+
|
270 |
+
remove_filter( 'pre_set_site_transient_update_themes', array(
|
271 |
+
&$this,
|
272 |
+
'pre_set_site_transient_update_plugins_filter'
|
273 |
+
) );
|
274 |
+
}
|
275 |
+
|
276 |
+
/**
|
277 |
+
* Capture plugin update row by turning output buffering.
|
278 |
+
*
|
279 |
+
* @author Vova Feldman (@svovaf)
|
280 |
+
* @since 1.1.6
|
281 |
+
*/
|
282 |
+
function catch_plugin_update_row() {
|
283 |
+
ob_start();
|
284 |
+
}
|
285 |
+
|
286 |
+
/**
|
287 |
+
* Overrides default update message format with "renew your license" message.
|
288 |
+
*
|
289 |
+
* @author Vova Feldman (@svovaf)
|
290 |
+
* @since 1.1.6
|
291 |
+
*
|
292 |
+
* @param string $file
|
293 |
+
* @param array $plugin_data
|
294 |
+
*/
|
295 |
+
function edit_and_echo_plugin_update_row( $file, $plugin_data ) {
|
296 |
+
$plugin_update_row = ob_get_clean();
|
297 |
+
|
298 |
+
$current = get_site_transient( 'update_plugins' );
|
299 |
+
if ( ! isset( $current->response[ $file ] ) ) {
|
300 |
+
echo $plugin_update_row;
|
301 |
+
|
302 |
+
return;
|
303 |
+
}
|
304 |
+
|
305 |
+
$r = $current->response[ $file ];
|
306 |
+
|
307 |
+
$has_beta_update = $this->_fs->has_beta_update();
|
308 |
+
|
309 |
+
if ( $this->_fs->has_any_active_valid_license() ) {
|
310 |
+
if ( $has_beta_update ) {
|
311 |
+
/**
|
312 |
+
* Turn the "new version" text into "new Beta version".
|
313 |
+
*
|
314 |
+
* Sample input:
|
315 |
+
* There is a new version of Awesome Plugin available. <a href="...>View version x.y.z details</a> or <a href="...>update now</a>.
|
316 |
+
* Output:
|
317 |
+
* There is a new Beta version of Awesome Plugin available. <a href="...>View version x.y.z details</a> or <a href="...>update now</a>.
|
318 |
+
*
|
319 |
+
* @author Leo Fajardo (@leorw)
|
320 |
+
* @since 2.3.0
|
321 |
+
*/
|
322 |
+
$plugin_update_row = preg_replace(
|
323 |
+
'/(\<div.+>)(.+)(\<a.+href="([^\s]+)"([^\<]+)\>.+\<a.+)(\<\/div\>)/is',
|
324 |
+
(
|
325 |
+
'$1' .
|
326 |
+
sprintf(
|
327 |
+
fs_text_inline( 'There is a %s of %s available.', 'new-version-available', $this->_fs->get_slug() ),
|
328 |
+
$has_beta_update ?
|
329 |
+
fs_text_inline( 'new Beta version', 'new-beta-version', $this->_fs->get_slug() ) :
|
330 |
+
fs_text_inline( 'new version', 'new-version', $this->_fs->get_slug() ),
|
331 |
+
$this->_fs->get_plugin_title()
|
332 |
+
) .
|
333 |
+
' ' .
|
334 |
+
'$3' .
|
335 |
+
'$6'
|
336 |
+
),
|
337 |
+
$plugin_update_row
|
338 |
+
);
|
339 |
+
}
|
340 |
+
} else {
|
341 |
+
/**
|
342 |
+
* Turn the "new version" text into a link that opens the plugin information dialog when clicked and
|
343 |
+
* make the "View version x details" text link to the checkout page instead of opening the plugin
|
344 |
+
* information dialog when clicked.
|
345 |
+
*
|
346 |
+
* Sample input:
|
347 |
+
* There is a new version of Awesome Plugin available. <a href="...>View version x.y.z details</a> or <a href="...>update now</a>.
|
348 |
+
* Output:
|
349 |
+
* There is a <a href="...>new version</a> of Awesome Plugin available. <a href="...>Buy a license now</a> to access version x.y.z security & feature updates, and support.
|
350 |
+
* OR
|
351 |
+
* There is a <a href="...>new Beta version</a> of Awesome Plugin available. <a href="...>Buy a license now</a> to access version x.y.z security & feature updates, and support.
|
352 |
+
*
|
353 |
+
* @author Leo Fajardo (@leorw)
|
354 |
+
*/
|
355 |
+
$plugin_update_row = preg_replace(
|
356 |
+
'/(\<div.+>)(.+)(\<a.+href="([^\s]+)"([^\<]+)\>.+\<a.+)(\<\/div\>)/is',
|
357 |
+
(
|
358 |
+
'$1' .
|
359 |
+
sprintf(
|
360 |
+
fs_text_inline( 'There is a %s of %s available.', 'new-version-available', $this->_fs->get_slug() ),
|
361 |
+
sprintf(
|
362 |
+
'<a href="$4"%s>%s</a>',
|
363 |
+
'$5',
|
364 |
+
$has_beta_update ?
|
365 |
+
fs_text_inline( 'new Beta version', 'new-beta-version', $this->_fs->get_slug() ) :
|
366 |
+
fs_text_inline( 'new version', 'new-version', $this->_fs->get_slug() )
|
367 |
+
),
|
368 |
+
$this->_fs->get_plugin_title()
|
369 |
+
) .
|
370 |
+
' ' .
|
371 |
+
$this->_fs->version_upgrade_checkout_link( $r->new_version ) .
|
372 |
+
'$6'
|
373 |
+
),
|
374 |
+
$plugin_update_row
|
375 |
+
);
|
376 |
+
}
|
377 |
+
|
378 |
+
if (
|
379 |
+
$this->_fs->is_plugin() &&
|
380 |
+
isset( $r->upgrade_notice ) &&
|
381 |
+
strlen( trim( $r->upgrade_notice ) ) > 0
|
382 |
+
) {
|
383 |
+
$slug = $this->_fs->get_slug();
|
384 |
+
|
385 |
+
$upgrade_notice_html = sprintf(
|
386 |
+
'<p class="notice fs-upgrade-notice fs-slug-%1$s fs-type-%2$s" data-slug="%1$s" data-type="%2$s"><strong>%3$s</strong> %4$s</p>',
|
387 |
+
$slug,
|
388 |
+
$this->_fs->get_module_type(),
|
389 |
+
fs_text_inline( 'Important Upgrade Notice:', 'upgrade_notice', $slug ),
|
390 |
+
esc_html( $r->upgrade_notice )
|
391 |
+
);
|
392 |
+
|
393 |
+
$plugin_update_row = str_replace( '</div>', '</div>' . $upgrade_notice_html, $plugin_update_row );
|
394 |
+
}
|
395 |
+
|
396 |
+
echo $plugin_update_row;
|
397 |
+
}
|
398 |
+
|
399 |
+
/**
|
400 |
+
* @author Leo Fajardo (@leorw)
|
401 |
+
* @since 2.0.2
|
402 |
+
*
|
403 |
+
* @param array $prepared_themes
|
404 |
+
*
|
405 |
+
* @return array
|
406 |
+
*/
|
407 |
+
function change_theme_update_info_html( $prepared_themes ) {
|
408 |
+
$theme_basename = $this->_fs->get_plugin_basename();
|
409 |
+
|
410 |
+
if ( ! isset( $prepared_themes[ $theme_basename ] ) ) {
|
411 |
+
return $prepared_themes;
|
412 |
+
}
|
413 |
+
|
414 |
+
$themes_update = get_site_transient( 'update_themes' );
|
415 |
+
if ( ! isset( $themes_update->response[ $theme_basename ] ) ||
|
416 |
+
empty( $themes_update->response[ $theme_basename ]['package'] )
|
417 |
+
) {
|
418 |
+
return $prepared_themes;
|
419 |
+
}
|
420 |
+
|
421 |
+
$prepared_themes[ $theme_basename ]['update'] = preg_replace(
|
422 |
+
'/(\<p.+>)(.+)(\<a.+\<a.+)\.(.+\<\/p\>)/is',
|
423 |
+
'$1 $2 ' . $this->_fs->version_upgrade_checkout_link( $themes_update->response[ $theme_basename ]['new_version'] ) .
|
424 |
+
'$4',
|
425 |
+
$prepared_themes[ $theme_basename ]['update']
|
426 |
+
);
|
427 |
+
|
428 |
+
// Set to false to prevent the "Update now" link for the context theme from being shown on the "Themes" page.
|
429 |
+
$prepared_themes[ $theme_basename ]['hasPackage'] = false;
|
430 |
+
|
431 |
+
return $prepared_themes;
|
432 |
+
}
|
433 |
+
|
434 |
+
/**
|
435 |
+
* Since WP version 3.6, a new security feature was added that denies access to repository with a local ip.
|
436 |
+
* During development mode we want to be able updating plugin versions via our localhost repository. This
|
437 |
+
* filter white-list all domains including "api.freemius".
|
438 |
+
*
|
439 |
+
* @link http://www.emanueletessore.com/wordpress-download-failed-valid-url-provided/
|
440 |
+
*
|
441 |
+
* @author Vova Feldman (@svovaf)
|
442 |
+
* @since 1.0.4
|
443 |
+
*
|
444 |
+
* @param bool $allow
|
445 |
+
* @param string $host
|
446 |
+
* @param string $url
|
447 |
+
*
|
448 |
+
* @return bool
|
449 |
+
*/
|
450 |
+
function http_request_host_is_external_filter( $allow, $host, $url ) {
|
451 |
+
return ( false !== strpos( $host, 'freemius' ) ) ? true : $allow;
|
452 |
+
}
|
453 |
+
|
454 |
+
/**
|
455 |
+
* Check for Updates at the defined API endpoint and modify the update array.
|
456 |
+
*
|
457 |
+
* This function dives into the update api just when WordPress creates its update array,
|
458 |
+
* then adds a custom API call and injects the custom plugin data retrieved from the API.
|
459 |
+
* It is reassembled from parts of the native WordPress plugin update code.
|
460 |
+
* See wp-includes/update.php line 121 for the original wp_update_plugins() function.
|
461 |
+
*
|
462 |
+
* @author Vova Feldman (@svovaf)
|
463 |
+
* @since 1.0.4
|
464 |
+
*
|
465 |
+
* @uses FS_Api
|
466 |
+
*
|
467 |
+
* @param object $transient_data Update array build by WordPress.
|
468 |
+
*
|
469 |
+
* @return object Modified update array with custom plugin data.
|
470 |
+
*/
|
471 |
+
function pre_set_site_transient_update_plugins_filter( $transient_data ) {
|
472 |
+
$this->_logger->entrance();
|
473 |
+
|
474 |
+
/**
|
475 |
+
* "plugins" or "themes".
|
476 |
+
*
|
477 |
+
* @author Leo Fajardo (@leorw)
|
478 |
+
* @since 1.2.2
|
479 |
+
*/
|
480 |
+
$module_type = $this->_fs->get_module_type() . 's';
|
481 |
+
|
482 |
+
/**
|
483 |
+
* Ensure that we don't mix plugins update info with themes update info.
|
484 |
+
*
|
485 |
+
* @author Leo Fajardo (@leorw)
|
486 |
+
* @since 1.2.2
|
487 |
+
*/
|
488 |
+
if ( "pre_set_site_transient_update_{$module_type}" !== current_filter() ) {
|
489 |
+
return $transient_data;
|
490 |
+
}
|
491 |
+
|
492 |
+
if ( empty( $transient_data ) ||
|
493 |
+
defined( 'WP_FS__UNINSTALL_MODE' )
|
494 |
+
) {
|
495 |
+
return $transient_data;
|
496 |
+
}
|
497 |
+
|
498 |
+
global $wp_current_filter;
|
499 |
+
|
500 |
+
$current_plugin_version = $this->_fs->get_plugin_version();
|
501 |
+
|
502 |
+
if ( ! empty( $wp_current_filter ) && 'upgrader_process_complete' === $wp_current_filter[0] ) {
|
503 |
+
if (
|
504 |
+
is_null( $this->_update_details ) ||
|
505 |
+
( is_object( $this->_update_details ) && $this->_update_details->new_version !== $current_plugin_version )
|
506 |
+
) {
|
507 |
+
/**
|
508 |
+
* After an update, clear the stored update details and reparse the plugin's main file in order to get
|
509 |
+
* the updated version's information and prevent the previous update information from showing up on the
|
510 |
+
* updates page.
|
511 |
+
*
|
512 |
+
* @author Leo Fajardo (@leorw)
|
513 |
+
* @since 2.3.1
|
514 |
+
*/
|
515 |
+
$this->_update_details = null;
|
516 |
+
$current_plugin_version = $this->_fs->get_plugin_version( true );
|
517 |
+
}
|
518 |
+
}
|
519 |
+
|
520 |
+
if ( ! isset( $this->_update_details ) ) {
|
521 |
+
// Get plugin's newest update.
|
522 |
+
$new_version = $this->_fs->get_update(
|
523 |
+
false,
|
524 |
+
fs_request_get_bool( 'force-check' ),
|
525 |
+
WP_FS__TIME_24_HOURS_IN_SEC / 24,
|
526 |
+
$current_plugin_version
|
527 |
+
);
|
528 |
+
|
529 |
+
$this->_update_details = false;
|
530 |
+
|
531 |
+
if ( is_object( $new_version ) && $this->is_new_version_premium( $new_version ) ) {
|
532 |
+
$this->_logger->log( 'Found newer plugin version ' . $new_version->version );
|
533 |
+
|
534 |
+
/**
|
535 |
+
* Cache plugin details locally since set_site_transient( 'update_plugins' )
|
536 |
+
* called multiple times and the non wp.org plugins are filtered after the
|
537 |
+
* call to .org.
|
538 |
+
*
|
539 |
+
* @since 1.1.8.1
|
540 |
+
*/
|
541 |
+
$this->_update_details = $this->get_update_details( $new_version );
|
542 |
+
}
|
543 |
+
}
|
544 |
+
|
545 |
+
// Alias.
|
546 |
+
$basename = $this->_fs->premium_plugin_basename();
|
547 |
+
|
548 |
+
if ( is_object( $this->_update_details ) ) {
|
549 |
+
if ( isset( $transient_data->no_update ) ) {
|
550 |
+
unset( $transient_data->no_update[ $basename ] );
|
551 |
+
}
|
552 |
+
|
553 |
+
if ( ! isset( $transient_data->response ) ) {
|
554 |
+
$transient_data->response = array();
|
555 |
+
}
|
556 |
+
|
557 |
+
// Add plugin to transient data.
|
558 |
+
$transient_data->response[ $basename ] = $this->_fs->is_plugin() ?
|
559 |
+
$this->_update_details :
|
560 |
+
(array) $this->_update_details;
|
561 |
+
} else {
|
562 |
+
if ( isset( $transient_data->response ) ) {
|
563 |
+
/**
|
564 |
+
* Ensure that there's no update data for the plugin to prevent upgrading the premium version to the latest free version.
|
565 |
+
*
|
566 |
+
* @author Leo Fajardo (@leorw)
|
567 |
+
* @since 2.3.0
|
568 |
+
*/
|
569 |
+
unset( $transient_data->response[ $basename ] );
|
570 |
+
}
|
571 |
+
|
572 |
+
if ( ! isset( $transient_data->no_update ) ) {
|
573 |
+
$transient_data->no_update = array();
|
574 |
+
}
|
575 |
+
|
576 |
+
/**
|
577 |
+
* Add product to no_update transient data to properly integrate with WP 5.5 auto-updates UI.
|
578 |
+
*
|
579 |
+
* @since 2.4.1
|
580 |
+
* @link https://make.wordpress.org/core/2020/07/30/recommended-usage-of-the-updates-api-to-support-the-auto-updates-ui-for-plugins-and-themes-in-wordpress-5-5/
|
581 |
+
*/
|
582 |
+
$transient_data->no_update[ $basename ] = $this->_fs->is_plugin() ?
|
583 |
+
(object) array(
|
584 |
+
'id' => $basename,
|
585 |
+
'slug' => $this->_fs->get_slug(),
|
586 |
+
'plugin' => $basename,
|
587 |
+
'new_version' => $this->_fs->get_plugin_version(),
|
588 |
+
'url' => '',
|
589 |
+
'package' => '',
|
590 |
+
'icons' => array(),
|
591 |
+
'banners' => array(),
|
592 |
+
'banners_rtl' => array(),
|
593 |
+
'tested' => '',
|
594 |
+
'requires_php' => '',
|
595 |
+
'compatibility' => new stdClass(),
|
596 |
+
) :
|
597 |
+
array(
|
598 |
+
'theme' => $basename,
|
599 |
+
'new_version' => $this->_fs->get_plugin_version(),
|
600 |
+
'url' => '',
|
601 |
+
'package' => '',
|
602 |
+
'requires' => '',
|
603 |
+
'requires_php' => '',
|
604 |
+
);
|
605 |
+
}
|
606 |
+
|
607 |
+
$slug = $this->_fs->get_slug();
|
608 |
+
|
609 |
+
if ( $this->_fs->is_org_repo_compliant() && $this->_fs->is_freemium() ) {
|
610 |
+
if ( ! isset( $this->_translation_updates ) ) {
|
611 |
+
$this->_translation_updates = array();
|
612 |
+
|
613 |
+
if ( current_user_can( 'update_languages' ) ) {
|
614 |
+
$translation_updates = $this->fetch_wp_org_module_translation_updates( $module_type, $slug );
|
615 |
+
if ( ! empty( $translation_updates ) ) {
|
616 |
+
$this->_translation_updates = $translation_updates;
|
617 |
+
}
|
618 |
+
}
|
619 |
+
}
|
620 |
+
|
621 |
+
if ( ! empty( $this->_translation_updates ) ) {
|
622 |
+
$all_translation_updates = ( isset( $transient_data->translations ) && is_array( $transient_data->translations ) ) ?
|
623 |
+
$transient_data->translations :
|
624 |
+
array();
|
625 |
+
|
626 |
+
$current_plugin_translation_updates_map = array();
|
627 |
+
foreach ( $all_translation_updates as $key => $translation_update ) {
|
628 |
+
if ( $module_type === ( $translation_update['type'] . 's' ) && $slug === $translation_update['slug'] ) {
|
629 |
+
$current_plugin_translation_updates_map[ $translation_update['language'] ] = $translation_update;
|
630 |
+
unset( $all_translation_updates[ $key ] );
|
631 |
+
}
|
632 |
+
}
|
633 |
+
|
634 |
+
foreach ( $this->_translation_updates as $translation_update ) {
|
635 |
+
$lang = $translation_update['language'];
|
636 |
+
if ( ! isset( $current_plugin_translation_updates_map[ $lang ] ) ||
|
637 |
+
version_compare( $translation_update['version'], $current_plugin_translation_updates_map[ $lang ]['version'], '>' )
|
638 |
+
) {
|
639 |
+
$current_plugin_translation_updates_map[ $lang ] = $translation_update;
|
640 |
+
}
|
641 |
+
}
|
642 |
+
|
643 |
+
$transient_data->translations = array_merge( $all_translation_updates, array_values( $current_plugin_translation_updates_map ) );
|
644 |
+
}
|
645 |
+
}
|
646 |
+
|
647 |
+
return $transient_data;
|
648 |
+
}
|
649 |
+
|
650 |
+
/**
|
651 |
+
* Get module's required data for the updates mechanism.
|
652 |
+
*
|
653 |
+
* @author Vova Feldman (@svovaf)
|
654 |
+
* @since 2.0.0
|
655 |
+
*
|
656 |
+
* @param \FS_Plugin_Tag $new_version
|
657 |
+
*
|
658 |
+
* @return object
|
659 |
+
*/
|
660 |
+
function get_update_details( FS_Plugin_Tag $new_version ) {
|
661 |
+
$update = new stdClass();
|
662 |
+
$update->slug = $this->_fs->get_slug();
|
663 |
+
$update->new_version = $new_version->version;
|
664 |
+
$update->url = WP_FS__ADDRESS;
|
665 |
+
$update->package = $new_version->url;
|
666 |
+
$update->tested = $new_version->tested_up_to_version;
|
667 |
+
$update->requires = $new_version->requires_platform_version;
|
668 |
+
|
669 |
+
$icon = $this->_fs->get_local_icon_url();
|
670 |
+
|
671 |
+
if ( ! empty( $icon ) ) {
|
672 |
+
$update->icons = array(
|
673 |
+
// '1x' => $icon,
|
674 |
+
// '2x' => $icon,
|
675 |
+
'default' => $icon,
|
676 |
+
);
|
677 |
+
}
|
678 |
+
|
679 |
+
if ( $this->_fs->is_premium() ) {
|
680 |
+
$latest_tag = $this->_fs->_fetch_latest_version( $this->_fs->get_id(), false );
|
681 |
+
|
682 |
+
if (
|
683 |
+
isset( $latest_tag->readme ) &&
|
684 |
+
isset( $latest_tag->readme->upgrade_notice ) &&
|
685 |
+
! empty( $latest_tag->readme->upgrade_notice )
|
686 |
+
) {
|
687 |
+
$update->upgrade_notice = $latest_tag->readme->upgrade_notice;
|
688 |
+
}
|
689 |
+
}
|
690 |
+
|
691 |
+
$update->{$this->_fs->get_module_type()} = $this->_fs->get_plugin_basename();
|
692 |
+
|
693 |
+
return $update;
|
694 |
+
}
|
695 |
+
|
696 |
+
/**
|
697 |
+
* @author Leo Fajardo (@leorw)
|
698 |
+
* @since 2.3.0
|
699 |
+
*
|
700 |
+
* @param FS_Plugin_Tag $new_version
|
701 |
+
*
|
702 |
+
* @return bool
|
703 |
+
*/
|
704 |
+
private function is_new_version_premium( FS_Plugin_Tag $new_version ) {
|
705 |
+
$query_str = parse_url( $new_version->url, PHP_URL_QUERY );
|
706 |
+
if ( empty( $query_str ) ) {
|
707 |
+
return false;
|
708 |
+
}
|
709 |
+
|
710 |
+
parse_str( $query_str, $params );
|
711 |
+
|
712 |
+
return ( isset( $params['is_premium'] ) && 'true' == $params['is_premium'] );
|
713 |
+
}
|
714 |
+
|
715 |
+
/**
|
716 |
+
* Update the updates transient with the module's update information.
|
717 |
+
*
|
718 |
+
* This method is required for multisite environment.
|
719 |
+
* If a module is site activated (not network) and not on the main site,
|
720 |
+
* the module will NOT be executed on the network level, therefore, the
|
721 |
+
* custom updates logic will not be executed as well, so unless we force
|
722 |
+
* the injection of the update into the updates transient, premium updates
|
723 |
+
* will not work.
|
724 |
+
*
|
725 |
+
* @author Vova Feldman (@svovaf)
|
726 |
+
* @since 2.0.0
|
727 |
+
*
|
728 |
+
* @param \FS_Plugin_Tag $new_version
|
729 |
+
*/
|
730 |
+
function set_update_data( FS_Plugin_Tag $new_version ) {
|
731 |
+
$this->_logger->entrance();
|
732 |
+
|
733 |
+
if ( ! $this->is_new_version_premium( $new_version ) ) {
|
734 |
+
return;
|
735 |
+
}
|
736 |
+
|
737 |
+
$transient_key = "update_{$this->_fs->get_module_type()}s";
|
738 |
+
|
739 |
+
$transient_data = get_site_transient( $transient_key );
|
740 |
+
|
741 |
+
$transient_data = is_object( $transient_data ) ?
|
742 |
+
$transient_data :
|
743 |
+
new stdClass();
|
744 |
+
|
745 |
+
// Alias.
|
746 |
+
$basename = $this->_fs->get_plugin_basename();
|
747 |
+
$is_plugin = $this->_fs->is_plugin();
|
748 |
+
|
749 |
+
if ( ! isset( $transient_data->response ) ||
|
750 |
+
! is_array( $transient_data->response )
|
751 |
+
) {
|
752 |
+
$transient_data->response = array();
|
753 |
+
} else if ( ! empty( $transient_data->response[ $basename ] ) ) {
|
754 |
+
$version = $is_plugin ?
|
755 |
+
( ! empty( $transient_data->response[ $basename ]->new_version ) ?
|
756 |
+
$transient_data->response[ $basename ]->new_version :
|
757 |
+
null
|
758 |
+
) : ( ! empty( $transient_data->response[ $basename ]['new_version'] ) ?
|
759 |
+
$transient_data->response[ $basename ]['new_version'] :
|
760 |
+
null
|
761 |
+
);
|
762 |
+
|
763 |
+
if ( $version == $new_version->version ) {
|
764 |
+
// The update data is already set.
|
765 |
+
return;
|
766 |
+
}
|
767 |
+
}
|
768 |
+
|
769 |
+
// Remove the added filters.
|
770 |
+
$this->remove_transient_filters();
|
771 |
+
|
772 |
+
$this->_update_details = $this->get_update_details( $new_version );
|
773 |
+
|
774 |
+
// Set update data in transient.
|
775 |
+
$transient_data->response[ $basename ] = $is_plugin ?
|
776 |
+
$this->_update_details :
|
777 |
+
(array) $this->_update_details;
|
778 |
+
|
779 |
+
if ( ! isset( $transient_data->checked ) ||
|
780 |
+
! is_array( $transient_data->checked )
|
781 |
+
) {
|
782 |
+
$transient_data->checked = array();
|
783 |
+
}
|
784 |
+
|
785 |
+
// Flag the module as if it was already checked.
|
786 |
+
$transient_data->checked[ $basename ] = $this->_fs->get_plugin_version();
|
787 |
+
$transient_data->last_checked = time();
|
788 |
+
|
789 |
+
set_site_transient( $transient_key, $transient_data );
|
790 |
+
|
791 |
+
$this->add_transient_filters();
|
792 |
+
}
|
793 |
+
|
794 |
+
/**
|
795 |
+
* @author Leo Fajardo (@leorw)
|
796 |
+
* @since 2.0.2
|
797 |
+
*/
|
798 |
+
function delete_update_data() {
|
799 |
+
$this->_logger->entrance();
|
800 |
+
|
801 |
+
$transient_key = "update_{$this->_fs->get_module_type()}s";
|
802 |
+
|
803 |
+
$transient_data = get_site_transient( $transient_key );
|
804 |
+
|
805 |
+
// Alias
|
806 |
+
$basename = $this->_fs->get_plugin_basename();
|
807 |
+
|
808 |
+
if ( ! is_object( $transient_data ) ||
|
809 |
+
! isset( $transient_data->response ) ||
|
810 |
+
! is_array( $transient_data->response ) ||
|
811 |
+
empty( $transient_data->response[ $basename ] )
|
812 |
+
) {
|
813 |
+
return;
|
814 |
+
}
|
815 |
+
|
816 |
+
unset( $transient_data->response[ $basename ] );
|
817 |
+
|
818 |
+
// Remove the added filters.
|
819 |
+
$this->remove_transient_filters();
|
820 |
+
|
821 |
+
set_site_transient( $transient_key, $transient_data );
|
822 |
+
|
823 |
+
$this->add_transient_filters();
|
824 |
+
}
|
825 |
+
|
826 |
+
/**
|
827 |
+
* Try to fetch plugin's info from .org repository.
|
828 |
+
*
|
829 |
+
* @author Vova Feldman (@svovaf)
|
830 |
+
* @since 1.0.5
|
831 |
+
*
|
832 |
+
* @param string $action
|
833 |
+
* @param object $args
|
834 |
+
*
|
835 |
+
* @return bool|mixed
|
836 |
+
*/
|
837 |
+
static function _fetch_plugin_info_from_repository( $action, $args ) {
|
838 |
+
$url = $http_url = 'http://api.wordpress.org/plugins/info/1.0/';
|
839 |
+
if ( $ssl = wp_http_supports( array( 'ssl' ) ) ) {
|
840 |
+
$url = set_url_scheme( $url, 'https' );
|
841 |
+
}
|
842 |
+
|
843 |
+
$args = array(
|
844 |
+
'timeout' => 15,
|
845 |
+
'body' => array(
|
846 |
+
'action' => $action,
|
847 |
+
'request' => serialize( $args )
|
848 |
+
)
|
849 |
+
);
|
850 |
+
|
851 |
+
$request = wp_remote_post( $url, $args );
|
852 |
+
|
853 |
+
if ( is_wp_error( $request ) ) {
|
854 |
+
return false;
|
855 |
+
}
|
856 |
+
|
857 |
+
$res = maybe_unserialize( wp_remote_retrieve_body( $request ) );
|
858 |
+
|
859 |
+
if ( ! is_object( $res ) && ! is_array( $res ) ) {
|
860 |
+
return false;
|
861 |
+
}
|
862 |
+
|
863 |
+
return $res;
|
864 |
+
}
|
865 |
+
|
866 |
+
/**
|
867 |
+
* Fetches module translation updates from wordpress.org.
|
868 |
+
*
|
869 |
+
* @author Leo Fajardo (@leorw)
|
870 |
+
* @since 2.1.2
|
871 |
+
*
|
872 |
+
* @param string $module_type
|
873 |
+
* @param string $slug
|
874 |
+
*
|
875 |
+
* @return array|null
|
876 |
+
*/
|
877 |
+
private function fetch_wp_org_module_translation_updates( $module_type, $slug ) {
|
878 |
+
$plugin_data = $this->_fs->get_plugin_data();
|
879 |
+
|
880 |
+
$locales = array_values( get_available_languages() );
|
881 |
+
$locales = apply_filters( "{$module_type}_update_check_locales", $locales );
|
882 |
+
$locales = array_unique( $locales );
|
883 |
+
|
884 |
+
$plugin_basename = $this->_fs->get_plugin_basename();
|
885 |
+
if ( 'themes' === $module_type ) {
|
886 |
+
$plugin_basename = $slug;
|
887 |
+
}
|
888 |
+
|
889 |
+
global $wp_version;
|
890 |
+
|
891 |
+
$request_args = array(
|
892 |
+
'timeout' => 15,
|
893 |
+
'body' => array(
|
894 |
+
"{$module_type}" => json_encode(
|
895 |
+
array(
|
896 |
+
"{$module_type}" => array(
|
897 |
+
$plugin_basename => array(
|
898 |
+
'Name' => trim( str_replace( $this->_fs->get_plugin()->premium_suffix, '', $plugin_data['Name'] ) ),
|
899 |
+
'Author' => $plugin_data['Author'],
|
900 |
+
)
|
901 |
+
)
|
902 |
+
)
|
903 |
+
),
|
904 |
+
'translations' => json_encode( $this->get_installed_translations( $module_type, $slug ) ),
|
905 |
+
'locale' => json_encode( $locales )
|
906 |
+
),
|
907 |
+
'user-agent' => ( 'WordPress/' . $wp_version . '; ' . home_url( '/' ) )
|
908 |
+
);
|
909 |
+
|
910 |
+
$url = "http://api.wordpress.org/{$module_type}/update-check/1.1/";
|
911 |
+
if ( $ssl = wp_http_supports( array( 'ssl' ) ) ) {
|
912 |
+
$url = set_url_scheme( $url, 'https' );
|
913 |
+
}
|
914 |
+
|
915 |
+
$raw_response = Freemius::safe_remote_post(
|
916 |
+
$url,
|
917 |
+
$request_args,
|
918 |
+
WP_FS__TIME_24_HOURS_IN_SEC,
|
919 |
+
WP_FS__TIME_12_HOURS_IN_SEC,
|
920 |
+
false
|
921 |
+
);
|
922 |
+
|
923 |
+
if ( is_wp_error( $raw_response ) ) {
|
924 |
+
return null;
|
925 |
+
}
|
926 |
+
|
927 |
+
$response = json_decode( wp_remote_retrieve_body( $raw_response ), true );
|
928 |
+
|
929 |
+
if ( ! is_array( $response ) ) {
|
930 |
+
return null;
|
931 |
+
}
|
932 |
+
|
933 |
+
if ( ! isset( $response['translations'] ) || empty( $response['translations'] ) ) {
|
934 |
+
return null;
|
935 |
+
}
|
936 |
+
|
937 |
+
return $response['translations'];
|
938 |
+
}
|
939 |
+
|
940 |
+
/**
|
941 |
+
* @author Leo Fajardo (@leorw)
|
942 |
+
* @since 2.1.2
|
943 |
+
*
|
944 |
+
* @param string $module_type
|
945 |
+
* @param string $slug
|
946 |
+
*
|
947 |
+
* @return array
|
948 |
+
*/
|
949 |
+
private function get_installed_translations( $module_type, $slug ) {
|
950 |
+
if ( function_exists( 'wp_get_installed_translations' ) ) {
|
951 |
+
return wp_get_installed_translations( $module_type );
|
952 |
+
}
|
953 |
+
|
954 |
+
$dir = "/{$module_type}";
|
955 |
+
|
956 |
+
if ( ! is_dir( WP_LANG_DIR . $dir ) )
|
957 |
+
return array();
|
958 |
+
|
959 |
+
$files = scandir( WP_LANG_DIR . $dir );
|
960 |
+
if ( ! $files )
|
961 |
+
return array();
|
962 |
+
|
963 |
+
$language_data = array();
|
964 |
+
|
965 |
+
foreach ( $files as $file ) {
|
966 |
+
if ( 0 !== strpos( $file, $slug ) ) {
|
967 |
+
continue;
|
968 |
+
}
|
969 |
+
|
970 |
+
if ( '.' === $file[0] || is_dir( WP_LANG_DIR . "{$dir}/{$file}" ) ) {
|
971 |
+
continue;
|
972 |
+
}
|
973 |
+
|
974 |
+
if ( substr( $file, -3 ) !== '.po' ) {
|
975 |
+
continue;
|
976 |
+
}
|
977 |
+
|
978 |
+
if ( ! preg_match( '/(?:(.+)-)?([a-z]{2,3}(?:_[A-Z]{2})?(?:_[a-z0-9]+)?).po/', $file, $match ) ) {
|
979 |
+
continue;
|
980 |
+
}
|
981 |
+
|
982 |
+
if ( ! in_array( substr( $file, 0, -3 ) . '.mo', $files ) ) {
|
983 |
+
continue;
|
984 |
+
}
|
985 |
+
|
986 |
+
list( , $textdomain, $language ) = $match;
|
987 |
+
|
988 |
+
if ( '' === $textdomain ) {
|
989 |
+
$textdomain = 'default';
|
990 |
+
}
|
991 |
+
|
992 |
+
$language_data[ $textdomain ][ $language ] = wp_get_pomo_file_data( WP_LANG_DIR . "{$dir}/{$file}" );
|
993 |
+
}
|
994 |
+
|
995 |
+
return $language_data;
|
996 |
+
}
|
997 |
+
|
998 |
+
/**
|
999 |
+
* Updates information on the "View version x.x details" page with custom data.
|
1000 |
+
*
|
1001 |
+
* @author Vova Feldman (@svovaf)
|
1002 |
+
* @since 1.0.4
|
1003 |
+
*
|
1004 |
+
* @uses FS_Api
|
1005 |
+
*
|
1006 |
+
* @param object $data
|
1007 |
+
* @param string $action
|
1008 |
+
* @param mixed $args
|
1009 |
+
*
|
1010 |
+
* @return object
|
1011 |
+
*/
|
1012 |
+
function plugins_api_filter( $data, $action = '', $args = null ) {
|
1013 |
+
$this->_logger->entrance();
|
1014 |
+
|
1015 |
+
if ( ( 'plugin_information' !== $action ) ||
|
1016 |
+
! isset( $args->slug )
|
1017 |
+
) {
|
1018 |
+
return $data;
|
1019 |
+
}
|
1020 |
+
|
1021 |
+
$addon = false;
|
1022 |
+
$is_addon = false;
|
1023 |
+
$addon_version = false;
|
1024 |
+
|
1025 |
+
if ( $this->_fs->get_slug() !== $args->slug ) {
|
1026 |
+
$addon = $this->_fs->get_addon_by_slug( $args->slug );
|
1027 |
+
|
1028 |
+
if ( ! is_object( $addon ) ) {
|
1029 |
+
return $data;
|
1030 |
+
}
|
1031 |
+
|
1032 |
+
if ( $this->_fs->is_addon_activated( $addon->id ) ) {
|
1033 |
+
$addon_version = $this->_fs->get_addon_instance( $addon->id )->get_plugin_version();
|
1034 |
+
} else if ( $this->_fs->is_addon_installed( $addon->id ) ) {
|
1035 |
+
$addon_plugin_data = get_plugin_data(
|
1036 |
+
( WP_PLUGIN_DIR . '/' . $this->_fs->get_addon_basename( $addon->id ) ),
|
1037 |
+
false,
|
1038 |
+
false
|
1039 |
+
);
|
1040 |
+
|
1041 |
+
if ( ! empty( $addon_plugin_data ) ) {
|
1042 |
+
$addon_version = $addon_plugin_data['Version'];
|
1043 |
+
}
|
1044 |
+
}
|
1045 |
+
|
1046 |
+
$is_addon = true;
|
1047 |
+
}
|
1048 |
+
|
1049 |
+
$plugin_in_repo = false;
|
1050 |
+
if ( ! $is_addon ) {
|
1051 |
+
// Try to fetch info from .org repository.
|
1052 |
+
$data = self::_fetch_plugin_info_from_repository( $action, $args );
|
1053 |
+
|
1054 |
+
$plugin_in_repo = ( false !== $data );
|
1055 |
+
}
|
1056 |
+
|
1057 |
+
if ( ! $plugin_in_repo ) {
|
1058 |
+
$data = $args;
|
1059 |
+
|
1060 |
+
// Fetch as much as possible info from local files.
|
1061 |
+
$plugin_local_data = $this->_fs->get_plugin_data();
|
1062 |
+
$data->name = $plugin_local_data['Name'];
|
1063 |
+
$data->author = $plugin_local_data['Author'];
|
1064 |
+
$data->sections = array(
|
1065 |
+
'description' => 'Upgrade ' . $plugin_local_data['Name'] . ' to latest.',
|
1066 |
+
);
|
1067 |
+
|
1068 |
+
// @todo Store extra plugin info on Freemius or parse readme.txt markup.
|
1069 |
+
/*$info = $this->_fs->get_api_site_scope()->call('/information.json');
|
1070 |
+
|
1071 |
+
if ( !isset($info->error) ) {
|
1072 |
+
$data = $info;
|
1073 |
+
}*/
|
1074 |
+
}
|
1075 |
+
|
1076 |
+
$plugin_version = $is_addon ?
|
1077 |
+
$addon_version :
|
1078 |
+
$this->_fs->get_plugin_version();
|
1079 |
+
|
1080 |
+
// Get plugin's newest update.
|
1081 |
+
$new_version = $this->get_latest_download_details( $is_addon ? $addon->id : false, $plugin_version );
|
1082 |
+
|
1083 |
+
if ( ! is_object( $new_version ) || empty( $new_version->version ) ) {
|
1084 |
+
$data->version = $plugin_version;
|
1085 |
+
} else {
|
1086 |
+
if ( $is_addon ) {
|
1087 |
+
$data->name = $addon->title . ' ' . $this->_fs->get_text_inline( 'Add-On', 'addon' );
|
1088 |
+
$data->slug = $addon->slug;
|
1089 |
+
$data->url = WP_FS__ADDRESS;
|
1090 |
+
$data->package = $new_version->url;
|
1091 |
+
}
|
1092 |
+
|
1093 |
+
if ( ! $plugin_in_repo ) {
|
1094 |
+
$data->last_updated = ! is_null( $new_version->updated ) ? $new_version->updated : $new_version->created;
|
1095 |
+
$data->requires = $new_version->requires_platform_version;
|
1096 |
+
$data->tested = $new_version->tested_up_to_version;
|
1097 |
+
}
|
1098 |
+
|
1099 |
+
$data->version = $new_version->version;
|
1100 |
+
$data->download_link = $new_version->url;
|
1101 |
+
|
1102 |
+
if ( isset( $new_version->readme ) && is_object( $new_version->readme ) ) {
|
1103 |
+
$new_version_readme_data = $new_version->readme;
|
1104 |
+
if ( isset( $new_version_readme_data->sections ) ) {
|
1105 |
+
$new_version_readme_data->sections = (array) $new_version_readme_data->sections;
|
1106 |
+
} else {
|
1107 |
+
$new_version_readme_data->sections = array();
|
1108 |
+
}
|
1109 |
+
|
1110 |
+
if ( isset( $data->sections ) ) {
|
1111 |
+
if ( isset( $data->sections['screenshots'] ) ) {
|
1112 |
+
$new_version_readme_data->sections['screenshots'] = $data->sections['screenshots'];
|
1113 |
+
}
|
1114 |
+
|
1115 |
+
if ( isset( $data->sections['reviews'] ) ) {
|
1116 |
+
$new_version_readme_data->sections['reviews'] = $data->sections['reviews'];
|
1117 |
+
}
|
1118 |
+
}
|
1119 |
+
|
1120 |
+
if ( isset( $new_version_readme_data->banners ) ) {
|
1121 |
+
$new_version_readme_data->banners = (array) $new_version_readme_data->banners;
|
1122 |
+
} else if ( isset( $data->banners ) ) {
|
1123 |
+
$new_version_readme_data->banners = $data->banners;
|
1124 |
+
}
|
1125 |
+
|
1126 |
+
$wp_org_sections = array(
|
1127 |
+
'author',
|
1128 |
+
'author_profile',
|
1129 |
+
'rating',
|
1130 |
+
'ratings',
|
1131 |
+
'num_ratings',
|
1132 |
+
'support_threads',
|
1133 |
+
'support_threads_resolved',
|
1134 |
+
'active_installs',
|
1135 |
+
'added',
|
1136 |
+
'homepage'
|
1137 |
+
);
|
1138 |
+
|
1139 |
+
foreach ( $wp_org_sections as $wp_org_section ) {
|
1140 |
+
if ( isset( $data->{$wp_org_section} ) ) {
|
1141 |
+
$new_version_readme_data->{$wp_org_section} = $data->{$wp_org_section};
|
1142 |
+
}
|
1143 |
+
}
|
1144 |
+
|
1145 |
+
$data = $new_version_readme_data;
|
1146 |
+
}
|
1147 |
+
}
|
1148 |
+
|
1149 |
+
return $data;
|
1150 |
+
}
|
1151 |
+
|
1152 |
+
/**
|
1153 |
+
* @author Vova Feldman (@svovaf)
|
1154 |
+
* @since 1.2.1.7
|
1155 |
+
*
|
1156 |
+
* @param number|bool $addon_id
|
1157 |
+
* @param bool|string $newer_than Since 2.2.1
|
1158 |
+
* @param bool|string $fetch_readme Since 2.2.1
|
1159 |
+
*
|
1160 |
+
* @return object
|
1161 |
+
*/
|
1162 |
+
private function get_latest_download_details( $addon_id = false, $newer_than = false, $fetch_readme = true ) {
|
1163 |
+
return $this->_fs->_fetch_latest_version( $addon_id, true, WP_FS__TIME_24_HOURS_IN_SEC, $newer_than, $fetch_readme );
|
1164 |
+
}
|
1165 |
+
|
1166 |
+
/**
|
1167 |
+
* Checks if a given basename has a matching folder name
|
1168 |
+
* with the current context plugin.
|
1169 |
+
*
|
1170 |
+
* @author Vova Feldman (@svovaf)
|
1171 |
+
* @since 1.2.1.6
|
1172 |
+
*
|
1173 |
+
* @return bool
|
1174 |
+
*/
|
1175 |
+
private function is_correct_folder_name() {
|
1176 |
+
return ( $this->_fs->get_target_folder_name() == trim( dirname( $this->_fs->get_plugin_basename() ), '/\\' ) );
|
1177 |
+
}
|
1178 |
+
|
1179 |
+
/**
|
1180 |
+
* This is a special after upgrade handler for migrating modules
|
1181 |
+
* that didn't use the '-premium' suffix folder structure before
|
1182 |
+
* the migration.
|
1183 |
+
*
|
1184 |
+
* @author Vova Feldman (@svovaf)
|
1185 |
+
* @since 1.2.1.6
|
1186 |
+
*
|
1187 |
+
* @param bool $response Install response.
|
1188 |
+
* @param array $hook_extra Extra arguments passed to hooked filters.
|
1189 |
+
* @param array $result Installation result data.
|
1190 |
+
*
|
1191 |
+
* @return bool
|
1192 |
+
*/
|
1193 |
+
function _maybe_update_folder_name( $response, $hook_extra, $result ) {
|
1194 |
+
$basename = $this->_fs->get_plugin_basename();
|
1195 |
+
|
1196 |
+
if ( true !== $response ||
|
1197 |
+
empty( $hook_extra ) ||
|
1198 |
+
empty( $hook_extra['plugin'] ) ||
|
1199 |
+
$basename !== $hook_extra['plugin']
|
1200 |
+
) {
|
1201 |
+
return $response;
|
1202 |
+
}
|
1203 |
+
|
1204 |
+
$active_plugins_basenames = get_option( 'active_plugins' );
|
1205 |
+
|
1206 |
+
foreach ( $active_plugins_basenames as $key => $active_plugin_basename ) {
|
1207 |
+
if ( $basename === $active_plugin_basename ) {
|
1208 |
+
// Get filename including extension.
|
1209 |
+
$filename = basename( $basename );
|
1210 |
+
|
1211 |
+
$new_basename = plugin_basename(
|
1212 |
+
trailingslashit( $this->_fs->is_premium() ? $this->_fs->get_premium_slug() : $this->_fs->get_slug() ) .
|
1213 |
+
$filename
|
1214 |
+
);
|
1215 |
+
|
1216 |
+
// Verify that the expected correct path exists.
|
1217 |
+
if ( file_exists( fs_normalize_path( WP_PLUGIN_DIR . '/' . $new_basename ) ) ) {
|
1218 |
+
// Override active plugin name.
|
1219 |
+
$active_plugins_basenames[ $key ] = $new_basename;
|
1220 |
+
update_option( 'active_plugins', $active_plugins_basenames );
|
1221 |
+
}
|
1222 |
+
|
1223 |
+
break;
|
1224 |
+
}
|
1225 |
+
}
|
1226 |
+
|
1227 |
+
return $response;
|
1228 |
+
}
|
1229 |
+
|
1230 |
+
#----------------------------------------------------------------------------------
|
1231 |
+
#region Auto Activation
|
1232 |
+
#----------------------------------------------------------------------------------
|
1233 |
+
|
1234 |
+
/**
|
1235 |
+
* Installs and active a plugin when explicitly requested that from a 3rd party service.
|
1236 |
+
*
|
1237 |
+
* This logic was inspired by the TGMPA GPL licensed library by Thomas Griffin.
|
1238 |
+
*
|
1239 |
+
* @link http://tgmpluginactivation.com/
|
1240 |
+
*
|
1241 |
+
* @author Vova Feldman
|
1242 |
+
* @since 1.2.1.7
|
1243 |
+
*
|
1244 |
+
* @link https://make.wordpress.org/plugins/2017/03/16/clarification-of-guideline-8-executable-code-and-installs/
|
1245 |
+
*
|
1246 |
+
* @uses WP_Filesystem
|
1247 |
+
* @uses WP_Error
|
1248 |
+
* @uses WP_Upgrader
|
1249 |
+
* @uses Plugin_Upgrader
|
1250 |
+
* @uses Plugin_Installer_Skin
|
1251 |
+
* @uses Plugin_Upgrader_Skin
|
1252 |
+
*
|
1253 |
+
* @param number|bool $plugin_id
|
1254 |
+
*
|
1255 |
+
* @return array
|
1256 |
+
*/
|
1257 |
+
function install_and_activate_plugin( $plugin_id = false ) {
|
1258 |
+
if ( ! empty( $plugin_id ) && ! FS_Plugin::is_valid_id( $plugin_id ) ) {
|
1259 |
+
// Invalid plugin ID.
|
1260 |
+
return array(
|
1261 |
+
'message' => $this->_fs->get_text_inline( 'Invalid module ID.', 'auto-install-error-invalid-id' ),
|
1262 |
+
'code' => 'invalid_module_id',
|
1263 |
+
);
|
1264 |
+
}
|
1265 |
+
|
1266 |
+
$is_addon = false;
|
1267 |
+
if ( FS_Plugin::is_valid_id( $plugin_id ) &&
|
1268 |
+
$plugin_id != $this->_fs->get_id()
|
1269 |
+
) {
|
1270 |
+
$addon = $this->_fs->get_addon( $plugin_id );
|
1271 |
+
|
1272 |
+
if ( ! is_object( $addon ) ) {
|
1273 |
+
// Invalid add-on ID.
|
1274 |
+
return array(
|
1275 |
+
'message' => $this->_fs->get_text_inline( 'Invalid module ID.', 'auto-install-error-invalid-id' ),
|
1276 |
+
'code' => 'invalid_module_id',
|
1277 |
+
);
|
1278 |
+
}
|
1279 |
+
|
1280 |
+
$slug = $addon->slug;
|
1281 |
+
$premium_slug = $addon->premium_slug;
|
1282 |
+
$title = $addon->title . ' ' . $this->_fs->get_text_inline( 'Add-On', 'addon' );
|
1283 |
+
|
1284 |
+
$is_addon = true;
|
1285 |
+
} else {
|
1286 |
+
$slug = $this->_fs->get_slug();
|
1287 |
+
$premium_slug = $this->_fs->get_premium_slug();
|
1288 |
+
$title = $this->_fs->get_plugin_title() .
|
1289 |
+
( $this->_fs->is_addon() ? ' ' . $this->_fs->get_text_inline( 'Add-On', 'addon' ) : '' );
|
1290 |
+
}
|
1291 |
+
|
1292 |
+
if ( $this->is_premium_plugin_active( $plugin_id ) ) {
|
1293 |
+
// Premium version already activated.
|
1294 |
+
return array(
|
1295 |
+
'message' => $is_addon ?
|
1296 |
+
$this->_fs->get_text_inline( 'Premium add-on version already installed.', 'auto-install-error-premium-addon-activated' ) :
|
1297 |
+
$this->_fs->get_text_inline( 'Premium version already active.', 'auto-install-error-premium-activated' ),
|
1298 |
+
'code' => 'premium_installed',
|
1299 |
+
);
|
1300 |
+
}
|
1301 |
+
|
1302 |
+
$latest_version = $this->get_latest_download_details( $plugin_id, false, false );
|
1303 |
+
$target_folder = $premium_slug;
|
1304 |
+
|
1305 |
+
// Prep variables for Plugin_Installer_Skin class.
|
1306 |
+
$extra = array();
|
1307 |
+
$extra['slug'] = $target_folder;
|
1308 |
+
$source = $latest_version->url;
|
1309 |
+
$api = null;
|
1310 |
+
|
1311 |
+
$install_url = add_query_arg(
|
1312 |
+
array(
|
1313 |
+
'action' => 'install-plugin',
|
1314 |
+
'plugin' => urlencode( $slug ),
|
1315 |
+
),
|
1316 |
+
'update.php'
|
1317 |
+
);
|
1318 |
+
|
1319 |
+
if ( ! class_exists( 'Plugin_Upgrader', false ) ) {
|
1320 |
+
// Include required resources for the installation.
|
1321 |
+
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
|
1322 |
+
}
|
1323 |
+
|
1324 |
+
$skin_args = array(
|
1325 |
+
'type' => 'web',
|
1326 |
+
'title' => sprintf( $this->_fs->get_text_inline( 'Installing plugin: %s', 'installing-plugin-x' ), $title ),
|
1327 |
+
'url' => esc_url_raw( $install_url ),
|
1328 |
+
'nonce' => 'install-plugin_' . $slug,
|
1329 |
+
'plugin' => '',
|
1330 |
+
'api' => $api,
|
1331 |
+
'extra' => $extra,
|
1332 |
+
);
|
1333 |
+
|
1334 |
+
// $skin = new Automatic_Upgrader_Skin( $skin_args );
|
1335 |
+
// $skin = new Plugin_Installer_Skin( $skin_args );
|
1336 |
+
$skin = new WP_Ajax_Upgrader_Skin( $skin_args );
|
1337 |
+
|
1338 |
+
// Create a new instance of Plugin_Upgrader.
|
1339 |
+
$upgrader = new Plugin_Upgrader( $skin );
|
1340 |
+
|
1341 |
+
// Perform the action and install the plugin from the $source urldecode().
|
1342 |
+
add_filter( 'upgrader_source_selection', array( 'FS_Plugin_Updater', '_maybe_adjust_source_dir' ), 1, 3 );
|
1343 |
+
|
1344 |
+
$install_result = $upgrader->install( $source );
|
1345 |
+
|
1346 |
+
remove_filter( 'upgrader_source_selection', array( 'FS_Plugin_Updater', '_maybe_adjust_source_dir' ), 1 );
|
1347 |
+
|
1348 |
+
if ( is_wp_error( $install_result ) ) {
|
1349 |
+
return array(
|
1350 |
+
'message' => $install_result->get_error_message(),
|
1351 |
+
'code' => $install_result->get_error_code(),
|
1352 |
+
);
|
1353 |
+
} elseif ( is_wp_error( $skin->result ) ) {
|
1354 |
+
return array(
|
1355 |
+
'message' => $skin->result->get_error_message(),
|
1356 |
+
'code' => $skin->result->get_error_code(),
|
1357 |
+
);
|
1358 |
+
} elseif ( $skin->get_errors()->get_error_code() ) {
|
1359 |
+
return array(
|
1360 |
+
'message' => $skin->get_error_messages(),
|
1361 |
+
'code' => 'unknown',
|
1362 |
+
);
|
1363 |
+
} elseif ( is_null( $install_result ) ) {
|
1364 |
+
global $wp_filesystem;
|
1365 |
+
|
1366 |
+
$error_code = 'unable_to_connect_to_filesystem';
|
1367 |
+
$error_message = $this->_fs->get_text_inline( 'Unable to connect to the filesystem. Please confirm your credentials.' );
|
1368 |
+
|
1369 |
+
// Pass through the error from WP_Filesystem if one was raised.
|
1370 |
+
if ( $wp_filesystem instanceof WP_Filesystem_Base &&
|
1371 |
+
is_wp_error( $wp_filesystem->errors ) &&
|
1372 |
+
$wp_filesystem->errors->get_error_code()
|
1373 |
+
) {
|
1374 |
+
$error_message = $wp_filesystem->errors->get_error_message();
|
1375 |
+
}
|
1376 |
+
|
1377 |
+
return array(
|
1378 |
+
'message' => $error_message,
|
1379 |
+
'code' => $error_code,
|
1380 |
+
);
|
1381 |
+
}
|
1382 |
+
|
1383 |
+
// Grab the full path to the main plugin's file.
|
1384 |
+
$plugin_activate = $upgrader->plugin_info();
|
1385 |
+
|
1386 |
+
// Try to activate the plugin.
|
1387 |
+
$activation_result = $this->try_activate_plugin( $plugin_activate );
|
1388 |
+
|
1389 |
+
if ( is_wp_error( $activation_result ) ) {
|
1390 |
+
return array(
|
1391 |
+
'message' => $activation_result->get_error_message(),
|
1392 |
+
'code' => $activation_result->get_error_code(),
|
1393 |
+
);
|
1394 |
+
}
|
1395 |
+
|
1396 |
+
return $skin->get_upgrade_messages();
|
1397 |
+
}
|
1398 |
+
|
1399 |
+
/**
|
1400 |
+
* Tries to activate a plugin. If fails, returns the error.
|
1401 |
+
*
|
1402 |
+
* @author Vova Feldman
|
1403 |
+
* @since 1.2.1.7
|
1404 |
+
*
|
1405 |
+
* @param string $file_path Path within wp-plugins/ to main plugin file.
|
1406 |
+
* This determines the styling of the output messages.
|
1407 |
+
*
|
1408 |
+
* @return bool|WP_Error
|
1409 |
+
*/
|
1410 |
+
protected function try_activate_plugin( $file_path ) {
|
1411 |
+
$activate = activate_plugin( $file_path, '', $this->_fs->is_network_active() );
|
1412 |
+
|
1413 |
+
return is_wp_error( $activate ) ?
|
1414 |
+
$activate :
|
1415 |
+
true;
|
1416 |
+
}
|
1417 |
+
|
1418 |
+
/**
|
1419 |
+
* Check if a premium module version is already active.
|
1420 |
+
*
|
1421 |
+
* @author Vova Feldman
|
1422 |
+
* @since 1.2.1.7
|
1423 |
+
*
|
1424 |
+
* @param number|bool $plugin_id
|
1425 |
+
*
|
1426 |
+
* @return bool
|
1427 |
+
*/
|
1428 |
+
private function is_premium_plugin_active( $plugin_id = false ) {
|
1429 |
+
if ( $plugin_id != $this->_fs->get_id() ) {
|
1430 |
+
return $this->_fs->is_addon_activated( $plugin_id, true );
|
1431 |
+
}
|
1432 |
+
|
1433 |
+
return is_plugin_active( $this->_fs->premium_plugin_basename() );
|
1434 |
+
}
|
1435 |
+
|
1436 |
+
/**
|
1437 |
+
* Store the basename since it's not always available in the `_maybe_adjust_source_dir` method below.
|
1438 |
+
*
|
1439 |
+
* @author Leo Fajardo (@leorw)
|
1440 |
+
* @since 2.2.1
|
1441 |
+
*
|
1442 |
+
* @param bool|WP_Error $response Response.
|
1443 |
+
* @param array $hook_extra Extra arguments passed to hooked filters.
|
1444 |
+
*
|
1445 |
+
* @return bool|WP_Error
|
1446 |
+
*/
|
1447 |
+
static function _store_basename_for_source_adjustment( $response, $hook_extra ) {
|
1448 |
+
if ( isset( $hook_extra['plugin'] ) ) {
|
1449 |
+
self::$_upgrade_basename = $hook_extra['plugin'];
|
1450 |
+
} else if ( isset( $hook_extra['theme'] ) ) {
|
1451 |
+
self::$_upgrade_basename = $hook_extra['theme'];
|
1452 |
+
} else {
|
1453 |
+
self::$_upgrade_basename = null;
|
1454 |
+
}
|
1455 |
+
|
1456 |
+
return $response;
|
1457 |
+
}
|
1458 |
+
|
1459 |
+
/**
|
1460 |
+
* Adjust the plugin directory name if necessary.
|
1461 |
+
* Assumes plugin has a folder (not a single file plugin).
|
1462 |
+
*
|
1463 |
+
* The final destination directory of a plugin is based on the subdirectory name found in the
|
1464 |
+
* (un)zipped source. In some cases this subdirectory name is not the same as the expected
|
1465 |
+
* slug and the plugin will not be recognized as installed. This is fixed by adjusting
|
1466 |
+
* the temporary unzipped source subdirectory name to the expected plugin slug.
|
1467 |
+
*
|
1468 |
+
* @author Vova Feldman
|
1469 |
+
* @since 1.2.1.7
|
1470 |
+
* @since 2.2.1 The method was converted to static since when the admin update bulk products via the Updates section, the logic applies the `upgrader_source_selection` filter for every product that is being updated.
|
1471 |
+
*
|
1472 |
+
* @param string $source Path to upgrade/zip-file-name.tmp/subdirectory/.
|
1473 |
+
* @param string $remote_source Path to upgrade/zip-file-name.tmp.
|
1474 |
+
* @param \WP_Upgrader $upgrader Instance of the upgrader which installs the plugin.
|
1475 |
+
*
|
1476 |
+
* @return string|WP_Error
|
1477 |
+
*/
|
1478 |
+
static function _maybe_adjust_source_dir( $source, $remote_source, $upgrader ) {
|
1479 |
+
if ( ! is_object( $GLOBALS['wp_filesystem'] ) ) {
|
1480 |
+
return $source;
|
1481 |
+
}
|
1482 |
+
|
1483 |
+
$basename = self::$_upgrade_basename;
|
1484 |
+
$is_theme = false;
|
1485 |
+
|
1486 |
+
// Figure out what the slug is supposed to be.
|
1487 |
+
if ( isset( $upgrader->skin->options['extra'] ) ) {
|
1488 |
+
// Set by the auto-install logic.
|
1489 |
+
$desired_slug = $upgrader->skin->options['extra']['slug'];
|
1490 |
+
} else if ( ! empty( $basename ) ) {
|
1491 |
+
/**
|
1492 |
+
* If it doesn't end with ".php", it's a theme.
|
1493 |
+
*
|
1494 |
+
* @author Leo Fajardo (@leorw)
|
1495 |
+
* @since 2.2.1
|
1496 |
+
*/
|
1497 |
+
$is_theme = ( ! fs_ends_with( $basename, '.php' ) );
|
1498 |
+
|
1499 |
+
$desired_slug = ( ! $is_theme ) ?
|
1500 |
+
dirname( $basename ) :
|
1501 |
+
// Theme slug
|
1502 |
+
$basename;
|
1503 |
+
} else {
|
1504 |
+
// Can't figure out the desired slug, stop the execution.
|
1505 |
+
return $source;
|
1506 |
+
}
|
1507 |
+
|
1508 |
+
if ( is_multisite() ) {
|
1509 |
+
/**
|
1510 |
+
* If we are running in a multisite environment and the product is not network activated,
|
1511 |
+
* the instance will not exist anyway. Therefore, try to update the source if necessary
|
1512 |
+
* regardless if the Freemius instance of the product exists or not.
|
1513 |
+
*
|
1514 |
+
* @author Vova Feldman
|
1515 |
+
*/
|
1516 |
+
} else if ( ! empty( $basename ) ) {
|
1517 |
+
$fs = Freemius::get_instance_by_file(
|
1518 |
+
$basename,
|
1519 |
+
$is_theme ?
|
1520 |
+
WP_FS__MODULE_TYPE_THEME :
|
1521 |
+
WP_FS__MODULE_TYPE_PLUGIN
|
1522 |
+
);
|
1523 |
+
|
1524 |
+
if ( ! is_object( $fs ) ) {
|
1525 |
+
/**
|
1526 |
+
* If the Freemius instance does not exist on a non-multisite network environment, it means that:
|
1527 |
+
* 1. The product is not powered by Freemius; OR
|
1528 |
+
* 2. The product is not activated, therefore, we don't mind if after the update the folder name will change.
|
1529 |
+
*
|
1530 |
+
* @author Leo Fajardo (@leorw)
|
1531 |
+
* @since 2.2.1
|
1532 |
+
*/
|
1533 |
+
return $source;
|
1534 |
+
}
|
1535 |
+
}
|
1536 |
+
|
1537 |
+
$subdir_name = untrailingslashit( str_replace( trailingslashit( $remote_source ), '', $source ) );
|
1538 |
+
|
1539 |
+
if ( ! empty( $subdir_name ) && $subdir_name !== $desired_slug ) {
|
1540 |
+
$from_path = untrailingslashit( $source );
|
1541 |
+
$to_path = trailingslashit( $remote_source ) . $desired_slug;
|
1542 |
+
|
1543 |
+
if ( true === $GLOBALS['wp_filesystem']->move( $from_path, $to_path ) ) {
|
1544 |
+
return trailingslashit( $to_path );
|
1545 |
+
}
|
1546 |
+
|
1547 |
+
return new WP_Error(
|
1548 |
+
'rename_failed',
|
1549 |
+
fs_text_inline( 'The remote plugin package does not contain a folder with the desired slug and renaming did not work.', 'module-package-rename-failure' ),
|
1550 |
+
array(
|
1551 |
+
'found' => $subdir_name,
|
1552 |
+
'expected' => $desired_slug
|
1553 |
+
)
|
1554 |
+
);
|
1555 |
+
}
|
1556 |
+
|
1557 |
+
return $source;
|
1558 |
+
}
|
1559 |
+
|
1560 |
+
#endregion
|
1561 |
+
}
|
freemius/includes/class-fs-storage.php
CHANGED
@@ -1,532 +1,532 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* @package Freemius
|
4 |
-
* @copyright Copyright (c) 2015, Freemius, Inc.
|
5 |
-
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
|
6 |
-
* @since 1.2.3
|
7 |
-
*/
|
8 |
-
|
9 |
-
if ( ! defined( 'ABSPATH' ) ) {
|
10 |
-
exit;
|
11 |
-
}
|
12 |
-
|
13 |
-
/**
|
14 |
-
* Class FS_Storage
|
15 |
-
*
|
16 |
-
* A wrapper class for handling network level and single site level storage.
|
17 |
-
*
|
18 |
-
* @property bool $is_network_activation
|
19 |
-
* @property int $network_install_blog_id
|
20 |
-
* @property object $sync_cron
|
21 |
-
*/
|
22 |
-
class FS_Storage {
|
23 |
-
/**
|
24 |
-
* @var FS_Storage[]
|
25 |
-
*/
|
26 |
-
private static $_instances = array();
|
27 |
-
/**
|
28 |
-
* @var FS_Key_Value_Storage Site level storage.
|
29 |
-
*/
|
30 |
-
private $_storage;
|
31 |
-
|
32 |
-
/**
|
33 |
-
* @var FS_Key_Value_Storage Network level storage.
|
34 |
-
*/
|
35 |
-
private $_network_storage;
|
36 |
-
|
37 |
-
/**
|
38 |
-
* @var string
|
39 |
-
*/
|
40 |
-
private $_module_type;
|
41 |
-
|
42 |
-
/**
|
43 |
-
* @var string
|
44 |
-
*/
|
45 |
-
private $_module_slug;
|
46 |
-
|
47 |
-
/**
|
48 |
-
* @var int The ID of the blog that is associated with the current site level options.
|
49 |
-
*/
|
50 |
-
private $_blog_id = 0;
|
51 |
-
|
52 |
-
/**
|
53 |
-
* @var bool
|
54 |
-
*/
|
55 |
-
private $_is_multisite;
|
56 |
-
|
57 |
-
/**
|
58 |
-
* @var bool
|
59 |
-
*/
|
60 |
-
private $_is_network_active = false;
|
61 |
-
|
62 |
-
/**
|
63 |
-
* @var bool
|
64 |
-
*/
|
65 |
-
private $_is_delegated_connection = false;
|
66 |
-
|
67 |
-
/**
|
68 |
-
* @var array {
|
69 |
-
* @key string Option name.
|
70 |
-
* @value int If 0 store on the network level. If 1, store on the network level only if module was network level activated. If 2, store on the network level only if network activated and NOT delegated the connection.
|
71 |
-
* }
|
72 |
-
*/
|
73 |
-
private static $_NETWORK_OPTIONS_MAP;
|
74 |
-
|
75 |
-
/**
|
76 |
-
* @author Leo Fajardo (@leorw)
|
77 |
-
*
|
78 |
-
* @param string $module_type
|
79 |
-
* @param string $slug
|
80 |
-
*
|
81 |
-
* @return FS_Storage
|
82 |
-
*/
|
83 |
-
static function instance( $module_type, $slug ) {
|
84 |
-
$key = $module_type . ':' . $slug;
|
85 |
-
|
86 |
-
if ( ! isset( self::$_instances[ $key ] ) ) {
|
87 |
-
self::$_instances[ $key ] = new FS_Storage( $module_type, $slug );
|
88 |
-
}
|
89 |
-
|
90 |
-
return self::$_instances[ $key ];
|
91 |
-
}
|
92 |
-
|
93 |
-
/**
|
94 |
-
* @author Leo Fajardo (@leorw)
|
95 |
-
*
|
96 |
-
* @param string $module_type
|
97 |
-
* @param string $slug
|
98 |
-
*/
|
99 |
-
private function __construct( $module_type, $slug ) {
|
100 |
-
$this->_module_type = $module_type;
|
101 |
-
$this->_module_slug = $slug;
|
102 |
-
$this->_is_multisite = is_multisite();
|
103 |
-
|
104 |
-
if ( $this->_is_multisite ) {
|
105 |
-
$this->_blog_id = get_current_blog_id();
|
106 |
-
$this->_network_storage = FS_Key_Value_Storage::instance( $module_type . '_data', $slug, true );
|
107 |
-
}
|
108 |
-
|
109 |
-
$this->_storage = FS_Key_Value_Storage::instance( $module_type . '_data', $slug, $this->_blog_id );
|
110 |
-
}
|
111 |
-
|
112 |
-
/**
|
113 |
-
* Tells this storage wrapper class that the context plugin is network active. This flag will affect how values
|
114 |
-
* are retrieved/stored from/into the storage.
|
115 |
-
*
|
116 |
-
* @author Leo Fajardo (@leorw)
|
117 |
-
*
|
118 |
-
* @param bool $is_network_active
|
119 |
-
* @param bool $is_delegated_connection
|
120 |
-
*/
|
121 |
-
function set_network_active( $is_network_active = true, $is_delegated_connection = false ) {
|
122 |
-
$this->_is_network_active = $is_network_active;
|
123 |
-
$this->_is_delegated_connection = $is_delegated_connection;
|
124 |
-
}
|
125 |
-
|
126 |
-
/**
|
127 |
-
* Switch the context of the site level storage manager.
|
128 |
-
*
|
129 |
-
* @author Vova Feldman (@svovaf)
|
130 |
-
* @since 2.0.0
|
131 |
-
*
|
132 |
-
* @param int $blog_id
|
133 |
-
*/
|
134 |
-
function set_site_blog_context( $blog_id ) {
|
135 |
-
$this->_storage = $this->get_site_storage( $blog_id );
|
136 |
-
$this->_blog_id = $blog_id;
|
137 |
-
}
|
138 |
-
|
139 |
-
/**
|
140 |
-
* @author Leo Fajardo (@leorw)
|
141 |
-
*
|
142 |
-
* @param string $key
|
143 |
-
* @param mixed $value
|
144 |
-
* @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_BINARY_MAP).
|
145 |
-
* @param bool $flush
|
146 |
-
*/
|
147 |
-
function store( $key, $value, $network_level_or_blog_id = null, $flush = true ) {
|
148 |
-
if ( $this->should_use_network_storage( $key, $network_level_or_blog_id ) ) {
|
149 |
-
$this->_network_storage->store( $key, $value, $flush );
|
150 |
-
} else {
|
151 |
-
$storage = $this->get_site_storage( $network_level_or_blog_id );
|
152 |
-
$storage->store( $key, $value, $flush );
|
153 |
-
}
|
154 |
-
}
|
155 |
-
|
156 |
-
/**
|
157 |
-
* @author Leo Fajardo (@leorw)
|
158 |
-
*
|
159 |
-
* @param bool $store
|
160 |
-
* @param string[] $exceptions Set of keys to keep and not clear.
|
161 |
-
* @param int|null|bool $network_level_or_blog_id
|
162 |
-
*/
|
163 |
-
function clear_all( $store = true, $exceptions = array(), $network_level_or_blog_id = null ) {
|
164 |
-
if ( ! $this->_is_multisite ||
|
165 |
-
false === $network_level_or_blog_id ||
|
166 |
-
is_null( $network_level_or_blog_id ) ||
|
167 |
-
is_numeric( $network_level_or_blog_id )
|
168 |
-
) {
|
169 |
-
$storage = $this->get_site_storage( $network_level_or_blog_id );
|
170 |
-
$storage->clear_all( $store, $exceptions );
|
171 |
-
}
|
172 |
-
|
173 |
-
if ( $this->_is_multisite &&
|
174 |
-
( true === $network_level_or_blog_id || is_null( $network_level_or_blog_id ) )
|
175 |
-
) {
|
176 |
-
$this->_network_storage->clear_all( $store, $exceptions );
|
177 |
-
}
|
178 |
-
}
|
179 |
-
|
180 |
-
/**
|
181 |
-
* @author Leo Fajardo (@leorw)
|
182 |
-
*
|
183 |
-
* @param string $key
|
184 |
-
* @param bool $store
|
185 |
-
* @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_BINARY_MAP).
|
186 |
-
*/
|
187 |
-
function remove( $key, $store = true, $network_level_or_blog_id = null ) {
|
188 |
-
if ( $this->should_use_network_storage( $key, $network_level_or_blog_id ) ) {
|
189 |
-
$this->_network_storage->remove( $key, $store );
|
190 |
-
} else {
|
191 |
-
$storage = $this->get_site_storage( $network_level_or_blog_id );
|
192 |
-
$storage->remove( $key, $store );
|
193 |
-
}
|
194 |
-
}
|
195 |
-
|
196 |
-
/**
|
197 |
-
* @author Leo Fajardo (@leorw)
|
198 |
-
*
|
199 |
-
* @param string $key
|
200 |
-
* @param mixed $default
|
201 |
-
* @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_BINARY_MAP).
|
202 |
-
*
|
203 |
-
* @return mixed
|
204 |
-
*/
|
205 |
-
function get( $key, $default = false, $network_level_or_blog_id = null ) {
|
206 |
-
if ( $this->should_use_network_storage( $key, $network_level_or_blog_id ) ) {
|
207 |
-
return $this->_network_storage->get( $key, $default );
|
208 |
-
} else {
|
209 |
-
$storage = $this->get_site_storage( $network_level_or_blog_id );
|
210 |
-
|
211 |
-
return $storage->get( $key, $default );
|
212 |
-
}
|
213 |
-
}
|
214 |
-
|
215 |
-
/**
|
216 |
-
* Multisite activated:
|
217 |
-
* true: Save network storage.
|
218 |
-
* int: Save site specific storage.
|
219 |
-
* false|0: Save current site storage.
|
220 |
-
* null: Save network and current site storage.
|
221 |
-
* Site level activated:
|
222 |
-
* Save site storage.
|
223 |
-
*
|
224 |
-
* @author Vova Feldman (@svovaf)
|
225 |
-
* @since 2.0.0
|
226 |
-
*
|
227 |
-
* @param bool|int|null $network_level_or_blog_id
|
228 |
-
*/
|
229 |
-
function save( $network_level_or_blog_id = null ) {
|
230 |
-
if ( $this->_is_network_active &&
|
231 |
-
( true === $network_level_or_blog_id || is_null( $network_level_or_blog_id ) )
|
232 |
-
) {
|
233 |
-
$this->_network_storage->save();
|
234 |
-
}
|
235 |
-
|
236 |
-
if ( ! $this->_is_network_active || true !== $network_level_or_blog_id ) {
|
237 |
-
$storage = $this->get_site_storage( $network_level_or_blog_id );
|
238 |
-
$storage->save();
|
239 |
-
}
|
240 |
-
}
|
241 |
-
|
242 |
-
/**
|
243 |
-
* @author Vova Feldman (@svovaf)
|
244 |
-
* @since 2.0.0
|
245 |
-
*
|
246 |
-
* @return string
|
247 |
-
*/
|
248 |
-
function get_module_slug() {
|
249 |
-
return $this->_module_slug;
|
250 |
-
}
|
251 |
-
|
252 |
-
/**
|
253 |
-
* @author Vova Feldman (@svovaf)
|
254 |
-
* @since 2.0.0
|
255 |
-
*
|
256 |
-
* @return string
|
257 |
-
*/
|
258 |
-
function get_module_type() {
|
259 |
-
return $this->_module_type;
|
260 |
-
}
|
261 |
-
|
262 |
-
/**
|
263 |
-
* Migration script to the new storage data structure that is network compatible.
|
264 |
-
*
|
265 |
-
* IMPORTANT:
|
266 |
-
* This method should be executed only after it is determined if this is a network
|
267 |
-
* level compatible product activation.
|
268 |
-
*
|
269 |
-
* @author Vova Feldman (@svovaf)
|
270 |
-
* @since 2.0.0
|
271 |
-
*/
|
272 |
-
function migrate_to_network() {
|
273 |
-
if ( ! $this->_is_multisite ) {
|
274 |
-
return;
|
275 |
-
}
|
276 |
-
|
277 |
-
$updated = false;
|
278 |
-
|
279 |
-
if ( ! isset( self::$_NETWORK_OPTIONS_MAP ) ) {
|
280 |
-
self::load_network_options_map();
|
281 |
-
}
|
282 |
-
|
283 |
-
foreach ( self::$_NETWORK_OPTIONS_MAP as $option => $storage_level ) {
|
284 |
-
if ( ! $this->is_multisite_option( $option ) ) {
|
285 |
-
continue;
|
286 |
-
}
|
287 |
-
|
288 |
-
if ( isset( $this->_storage->{$option} ) && ! isset( $this->_network_storage->{$option} ) ) {
|
289 |
-
// Migrate option to the network storage.
|
290 |
-
$this->_network_storage->store( $option, $this->_storage->{$option}, false );
|
291 |
-
|
292 |
-
/**
|
293 |
-
* Remove the option from site level storage.
|
294 |
-
*
|
295 |
-
* IMPORTANT:
|
296 |
-
* The line below is intentionally commented since we want to preserve the option
|
297 |
-
* on the site storage level for "downgrade compatibility". Basically, if the user
|
298 |
-
* will downgrade to an older version of the plugin with the prev storage structure,
|
299 |
-
* it will continue working.
|
300 |
-
*
|
301 |
-
* @todo After a few releases we can remove this.
|
302 |
-
*/
|
303 |
-
// $this->_storage->remove($option, false);
|
304 |
-
|
305 |
-
$updated = true;
|
306 |
-
}
|
307 |
-
}
|
308 |
-
|
309 |
-
if ( ! $updated ) {
|
310 |
-
return;
|
311 |
-
}
|
312 |
-
|
313 |
-
// Update network level storage.
|
314 |
-
$this->_network_storage->save();
|
315 |
-
// $this->_storage->save();
|
316 |
-
}
|
317 |
-
|
318 |
-
#--------------------------------------------------------------------------------
|
319 |
-
#region Helper Methods
|
320 |
-
#--------------------------------------------------------------------------------
|
321 |
-
|
322 |
-
/**
|
323 |
-
* We don't want to load the map right away since it's not even needed in a non-MS environment.
|
324 |
-
*
|
325 |
-
* Example:
|
326 |
-
* array(
|
327 |
-
* 'option1' => 0, // Means that the option should always be stored on the network level.
|
328 |
-
* 'option2' => 1, // Means that the option should be stored on the network level only when the module was network level activated.
|
329 |
-
* 'option2' => 2, // Means that the option should be stored on the network level only when the module was network level activated AND the connection was NOT delegated.
|
330 |
-
* 'option3' => 3, // Means that the option should always be stored on the site level.
|
331 |
-
* )
|
332 |
-
*
|
333 |
-
* @author Vova Feldman (@svovaf)
|
334 |
-
* @since 2.0.0
|
335 |
-
*/
|
336 |
-
private static function load_network_options_map() {
|
337 |
-
self::$_NETWORK_OPTIONS_MAP = array(
|
338 |
-
// Network level options.
|
339 |
-
'affiliate_application_data' => 0,
|
340 |
-
'beta_data' => 0,
|
341 |
-
'connectivity_test' => 0,
|
342 |
-
'handle_gdpr_admin_notice' => 0,
|
343 |
-
'has_trial_plan' => 0,
|
344 |
-
'install_sync_timestamp' => 0,
|
345 |
-
'install_sync_cron' => 0,
|
346 |
-
'is_anonymous_ms' => 0,
|
347 |
-
'is_network_activated' => 0,
|
348 |
-
'is_on' => 0,
|
349 |
-
'is_plugin_new_install' => 0,
|
350 |
-
'network_install_blog_id' => 0,
|
351 |
-
'pending_sites_info' => 0,
|
352 |
-
'plugin_last_version' => 0,
|
353 |
-
'plugin_main_file' => 0,
|
354 |
-
'plugin_version' => 0,
|
355 |
-
'sdk_downgrade_mode' => 0,
|
356 |
-
'sdk_last_version' => 0,
|
357 |
-
'sdk_upgrade_mode' => 0,
|
358 |
-
'sdk_version' => 0,
|
359 |
-
'sticky_optin_added_ms' => 0,
|
360 |
-
'subscriptions' => 0,
|
361 |
-
'sync_timestamp' => 0,
|
362 |
-
'sync_cron' => 0,
|
363 |
-
'was_plugin_loaded' => 0,
|
364 |
-
'network_user_id' => 0,
|
365 |
-
'plugin_upgrade_mode' => 0,
|
366 |
-
'plugin_downgrade_mode' => 0,
|
367 |
-
'is_network_connected' => 0,
|
368 |
-
/**
|
369 |
-
* Special flag that is used when a super-admin upgrades to the new version of the SDK that
|
370 |
-
* supports network level integration, when the connection decision wasn't made for all of the
|
371 |
-
* sites in the network.
|
372 |
-
*/
|
373 |
-
'is_network_activation' => 0,
|
374 |
-
'license_migration' => 0,
|
375 |
-
|
376 |
-
// When network activated, then network level.
|
377 |
-
'install_timestamp' => 1,
|
378 |
-
'prev_is_premium' => 1,
|
379 |
-
'require_license_activation' => 1,
|
380 |
-
|
381 |
-
// If not network activated OR delegated, then site level.
|
382 |
-
'activation_timestamp' => 2,
|
383 |
-
'expired_license_notice_shown' => 2,
|
384 |
-
'is_whitelabeled' => 2,
|
385 |
-
'last_license_key' => 2,
|
386 |
-
'last_license_user_id' => 2,
|
387 |
-
'prev_user_id' => 2,
|
388 |
-
'sticky_optin_added' => 2,
|
389 |
-
'uninstall_reason' => 2,
|
390 |
-
'is_pending_activation' => 2,
|
391 |
-
'pending_license_key' => 2,
|
392 |
-
'is_extensions_tracking_allowed' => 2,
|
393 |
-
|
394 |
-
// Site level options.
|
395 |
-
'is_anonymous' => 3,
|
396 |
-
);
|
397 |
-
}
|
398 |
-
|
399 |
-
/**
|
400 |
-
* This method will and should only be executed when is_multisite() is true.
|
401 |
-
*
|
402 |
-
* @author Vova Feldman (@svovaf)
|
403 |
-
* @since 2.0.0
|
404 |
-
*
|
405 |
-
* @param string $key
|
406 |
-
*
|
407 |
-
* @return bool|mixed
|
408 |
-
*/
|
409 |
-
private function is_multisite_option( $key ) {
|
410 |
-
if ( ! isset( self::$_NETWORK_OPTIONS_MAP ) ) {
|
411 |
-
self::load_network_options_map();
|
412 |
-
}
|
413 |
-
|
414 |
-
if ( ! isset( self::$_NETWORK_OPTIONS_MAP[ $key ] ) ) {
|
415 |
-
// Option not found -> use site level storage.
|
416 |
-
return false;
|
417 |
-
}
|
418 |
-
|
419 |
-
if ( 0 === self::$_NETWORK_OPTIONS_MAP[ $key ] ) {
|
420 |
-
// Option found and set to always use the network level storage on a multisite.
|
421 |
-
return true;
|
422 |
-
}
|
423 |
-
|
424 |
-
if ( 3 === self::$_NETWORK_OPTIONS_MAP[ $key ] ) {
|
425 |
-
// Option found and set to always use the site level storage on a multisite.
|
426 |
-
return false;
|
427 |
-
}
|
428 |
-
|
429 |
-
if ( ! $this->_is_network_active ) {
|
430 |
-
return false;
|
431 |
-
}
|
432 |
-
|
433 |
-
if ( 1 === self::$_NETWORK_OPTIONS_MAP[ $key ] ) {
|
434 |
-
// Network activated.
|
435 |
-
return true;
|
436 |
-
}
|
437 |
-
|
438 |
-
if ( 2 === self::$_NETWORK_OPTIONS_MAP[ $key ] && ! $this->_is_delegated_connection ) {
|
439 |
-
// Network activated and not delegated.
|
440 |
-
return true;
|
441 |
-
}
|
442 |
-
|
443 |
-
return false;
|
444 |
-
}
|
445 |
-
|
446 |
-
/**
|
447 |
-
* @author Leo Fajardo
|
448 |
-
*
|
449 |
-
* @param string $key
|
450 |
-
* @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_BINARY_MAP).
|
451 |
-
*
|
452 |
-
* @return bool
|
453 |
-
*/
|
454 |
-
private function should_use_network_storage( $key, $network_level_or_blog_id = null ) {
|
455 |
-
if ( ! $this->_is_multisite ) {
|
456 |
-
// Not a multisite environment.
|
457 |
-
return false;
|
458 |
-
}
|
459 |
-
|
460 |
-
if ( is_numeric( $network_level_or_blog_id ) ) {
|
461 |
-
// Explicitly asked to use a specified blog storage.
|
462 |
-
return false;
|
463 |
-
}
|
464 |
-
|
465 |
-
if ( is_bool( $network_level_or_blog_id ) ) {
|
466 |
-
// Explicitly specified whether should use the network or blog level storage.
|
467 |
-
return $network_level_or_blog_id;
|
468 |
-
}
|
469 |
-
|
470 |
-
// Determine which storage to use based on the option.
|
471 |
-
return $this->is_multisite_option( $key );
|
472 |
-
}
|
473 |
-
|
474 |
-
/**
|
475 |
-
* @author Vova Feldman (@svovaf)
|
476 |
-
* @since 2.0.0
|
477 |
-
*
|
478 |
-
* @param int $blog_id
|
479 |
-
*
|
480 |
-
* @return \FS_Key_Value_Storage
|
481 |
-
*/
|
482 |
-
private function get_site_storage( $blog_id = 0 ) {
|
483 |
-
if ( ! is_numeric( $blog_id ) ||
|
484 |
-
$blog_id == $this->_blog_id ||
|
485 |
-
0 == $blog_id
|
486 |
-
) {
|
487 |
-
return $this->_storage;
|
488 |
-
}
|
489 |
-
|
490 |
-
return FS_Key_Value_Storage::instance(
|
491 |
-
$this->_module_type . '_data',
|
492 |
-
$this->_storage->get_secondary_id(),
|
493 |
-
$blog_id
|
494 |
-
);
|
495 |
-
}
|
496 |
-
|
497 |
-
#endregion
|
498 |
-
|
499 |
-
#--------------------------------------------------------------------------------
|
500 |
-
#region Magic methods
|
501 |
-
#--------------------------------------------------------------------------------
|
502 |
-
|
503 |
-
function __set( $k, $v ) {
|
504 |
-
if ( $this->should_use_network_storage( $k ) ) {
|
505 |
-
$this->_network_storage->{$k} = $v;
|
506 |
-
} else {
|
507 |
-
$this->_storage->{$k} = $v;
|
508 |
-
}
|
509 |
-
}
|
510 |
-
|
511 |
-
function __isset( $k ) {
|
512 |
-
return $this->should_use_network_storage( $k ) ?
|
513 |
-
isset( $this->_network_storage->{$k} ) :
|
514 |
-
isset( $this->_storage->{$k} );
|
515 |
-
}
|
516 |
-
|
517 |
-
function __unset( $k ) {
|
518 |
-
if ( $this->should_use_network_storage( $k ) ) {
|
519 |
-
unset( $this->_network_storage->{$k} );
|
520 |
-
} else {
|
521 |
-
unset( $this->_storage->{$k} );
|
522 |
-
}
|
523 |
-
}
|
524 |
-
|
525 |
-
function __get( $k ) {
|
526 |
-
return $this->should_use_network_storage( $k ) ?
|
527 |
-
$this->_network_storage->{$k} :
|
528 |
-
$this->_storage->{$k};
|
529 |
-
}
|
530 |
-
|
531 |
-
#endregion
|
532 |
}
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package Freemius
|
4 |
+
* @copyright Copyright (c) 2015, Freemius, Inc.
|
5 |
+
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
|
6 |
+
* @since 1.2.3
|
7 |
+
*/
|
8 |
+
|
9 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
10 |
+
exit;
|
11 |
+
}
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Class FS_Storage
|
15 |
+
*
|
16 |
+
* A wrapper class for handling network level and single site level storage.
|
17 |
+
*
|
18 |
+
* @property bool $is_network_activation
|
19 |
+
* @property int $network_install_blog_id
|
20 |
+
* @property object $sync_cron
|
21 |
+
*/
|
22 |
+
class FS_Storage {
|
23 |
+
/**
|
24 |
+
* @var FS_Storage[]
|
25 |
+
*/
|
26 |
+
private static $_instances = array();
|
27 |
+
/**
|
28 |
+
* @var FS_Key_Value_Storage Site level storage.
|
29 |
+
*/
|
30 |
+
private $_storage;
|
31 |
+
|
32 |
+
/**
|
33 |
+
* @var FS_Key_Value_Storage Network level storage.
|
34 |
+
*/
|
35 |
+
private $_network_storage;
|
36 |
+
|
37 |
+
/**
|
38 |
+
* @var string
|
39 |
+
*/
|
40 |
+
private $_module_type;
|
41 |
+
|
42 |
+
/**
|
43 |
+
* @var string
|
44 |
+
*/
|
45 |
+
private $_module_slug;
|
46 |
+
|
47 |
+
/**
|
48 |
+
* @var int The ID of the blog that is associated with the current site level options.
|
49 |
+
*/
|
50 |
+
private $_blog_id = 0;
|
51 |
+
|
52 |
+
/**
|
53 |
+
* @var bool
|
54 |
+
*/
|
55 |
+
private $_is_multisite;
|
56 |
+
|
57 |
+
/**
|
58 |
+
* @var bool
|
59 |
+
*/
|
60 |
+
private $_is_network_active = false;
|
61 |
+
|
62 |
+
/**
|
63 |
+
* @var bool
|
64 |
+
*/
|
65 |
+
private $_is_delegated_connection = false;
|
66 |
+
|
67 |
+
/**
|
68 |
+
* @var array {
|
69 |
+
* @key string Option name.
|
70 |
+
* @value int If 0 store on the network level. If 1, store on the network level only if module was network level activated. If 2, store on the network level only if network activated and NOT delegated the connection.
|
71 |
+
* }
|
72 |
+
*/
|
73 |
+
private static $_NETWORK_OPTIONS_MAP;
|
74 |
+
|
75 |
+
/**
|
76 |
+
* @author Leo Fajardo (@leorw)
|
77 |
+
*
|
78 |
+
* @param string $module_type
|
79 |
+
* @param string $slug
|
80 |
+
*
|
81 |
+
* @return FS_Storage
|
82 |
+
*/
|
83 |
+
static function instance( $module_type, $slug ) {
|
84 |
+
$key = $module_type . ':' . $slug;
|
85 |
+
|
86 |
+
if ( ! isset( self::$_instances[ $key ] ) ) {
|
87 |
+
self::$_instances[ $key ] = new FS_Storage( $module_type, $slug );
|
88 |
+
}
|
89 |
+
|
90 |
+
return self::$_instances[ $key ];
|
91 |
+
}
|
92 |
+
|
93 |
+
/**
|
94 |
+
* @author Leo Fajardo (@leorw)
|
95 |
+
*
|
96 |
+
* @param string $module_type
|
97 |
+
* @param string $slug
|
98 |
+
*/
|
99 |
+
private function __construct( $module_type, $slug ) {
|
100 |
+
$this->_module_type = $module_type;
|
101 |
+
$this->_module_slug = $slug;
|
102 |
+
$this->_is_multisite = is_multisite();
|
103 |
+
|
104 |
+
if ( $this->_is_multisite ) {
|
105 |
+
$this->_blog_id = get_current_blog_id();
|
106 |
+
$this->_network_storage = FS_Key_Value_Storage::instance( $module_type . '_data', $slug, true );
|
107 |
+
}
|
108 |
+
|
109 |
+
$this->_storage = FS_Key_Value_Storage::instance( $module_type . '_data', $slug, $this->_blog_id );
|
110 |
+
}
|
111 |
+
|
112 |
+
/**
|
113 |
+
* Tells this storage wrapper class that the context plugin is network active. This flag will affect how values
|
114 |
+
* are retrieved/stored from/into the storage.
|
115 |
+
*
|
116 |
+
* @author Leo Fajardo (@leorw)
|
117 |
+
*
|
118 |
+
* @param bool $is_network_active
|
119 |
+
* @param bool $is_delegated_connection
|
120 |
+
*/
|
121 |
+
function set_network_active( $is_network_active = true, $is_delegated_connection = false ) {
|
122 |
+
$this->_is_network_active = $is_network_active;
|
123 |
+
$this->_is_delegated_connection = $is_delegated_connection;
|
124 |
+
}
|
125 |
+
|
126 |
+
/**
|
127 |
+
* Switch the context of the site level storage manager.
|
128 |
+
*
|
129 |
+
* @author Vova Feldman (@svovaf)
|
130 |
+
* @since 2.0.0
|
131 |
+
*
|
132 |
+
* @param int $blog_id
|
133 |
+
*/
|
134 |
+
function set_site_blog_context( $blog_id ) {
|
135 |
+
$this->_storage = $this->get_site_storage( $blog_id );
|
136 |
+
$this->_blog_id = $blog_id;
|
137 |
+
}
|
138 |
+
|
139 |
+
/**
|
140 |
+
* @author Leo Fajardo (@leorw)
|
141 |
+
*
|
142 |
+
* @param string $key
|
143 |
+
* @param mixed $value
|
144 |
+
* @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_BINARY_MAP).
|
145 |
+
* @param bool $flush
|
146 |
+
*/
|
147 |
+
function store( $key, $value, $network_level_or_blog_id = null, $flush = true ) {
|
148 |
+
if ( $this->should_use_network_storage( $key, $network_level_or_blog_id ) ) {
|
149 |
+
$this->_network_storage->store( $key, $value, $flush );
|
150 |
+
} else {
|
151 |
+
$storage = $this->get_site_storage( $network_level_or_blog_id );
|
152 |
+
$storage->store( $key, $value, $flush );
|
153 |
+
}
|
154 |
+
}
|
155 |
+
|
156 |
+
/**
|
157 |
+
* @author Leo Fajardo (@leorw)
|
158 |
+
*
|
159 |
+
* @param bool $store
|
160 |
+
* @param string[] $exceptions Set of keys to keep and not clear.
|
161 |
+
* @param int|null|bool $network_level_or_blog_id
|
162 |
+
*/
|
163 |
+
function clear_all( $store = true, $exceptions = array(), $network_level_or_blog_id = null ) {
|
164 |
+
if ( ! $this->_is_multisite ||
|
165 |
+
false === $network_level_or_blog_id ||
|
166 |
+
is_null( $network_level_or_blog_id ) ||
|
167 |
+
is_numeric( $network_level_or_blog_id )
|
168 |
+
) {
|
169 |
+
$storage = $this->get_site_storage( $network_level_or_blog_id );
|
170 |
+
$storage->clear_all( $store, $exceptions );
|
171 |
+
}
|
172 |
+
|
173 |
+
if ( $this->_is_multisite &&
|
174 |
+
( true === $network_level_or_blog_id || is_null( $network_level_or_blog_id ) )
|
175 |
+
) {
|
176 |
+
$this->_network_storage->clear_all( $store, $exceptions );
|
177 |
+
}
|
178 |
+
}
|
179 |
+
|
180 |
+
/**
|
181 |
+
* @author Leo Fajardo (@leorw)
|
182 |
+
*
|
183 |
+
* @param string $key
|
184 |
+
* @param bool $store
|
185 |
+
* @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_BINARY_MAP).
|
186 |
+
*/
|
187 |
+
function remove( $key, $store = true, $network_level_or_blog_id = null ) {
|
188 |
+
if ( $this->should_use_network_storage( $key, $network_level_or_blog_id ) ) {
|
189 |
+
$this->_network_storage->remove( $key, $store );
|
190 |
+
} else {
|
191 |
+
$storage = $this->get_site_storage( $network_level_or_blog_id );
|
192 |
+
$storage->remove( $key, $store );
|
193 |
+
}
|
194 |
+
}
|
195 |
+
|
196 |
+
/**
|
197 |
+
* @author Leo Fajardo (@leorw)
|
198 |
+
*
|
199 |
+
* @param string $key
|
200 |
+
* @param mixed $default
|
201 |
+
* @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_BINARY_MAP).
|
202 |
+
*
|
203 |
+
* @return mixed
|
204 |
+
*/
|
205 |
+
function get( $key, $default = false, $network_level_or_blog_id = null ) {
|
206 |
+
if ( $this->should_use_network_storage( $key, $network_level_or_blog_id ) ) {
|
207 |
+
return $this->_network_storage->get( $key, $default );
|
208 |
+
} else {
|
209 |
+
$storage = $this->get_site_storage( $network_level_or_blog_id );
|
210 |
+
|
211 |
+
return $storage->get( $key, $default );
|
212 |
+
}
|
213 |
+
}
|
214 |
+
|
215 |
+
/**
|
216 |
+
* Multisite activated:
|
217 |
+
* true: Save network storage.
|
218 |
+
* int: Save site specific storage.
|
219 |
+
* false|0: Save current site storage.
|
220 |
+
* null: Save network and current site storage.
|
221 |
+
* Site level activated:
|
222 |
+
* Save site storage.
|
223 |
+
*
|
224 |
+
* @author Vova Feldman (@svovaf)
|
225 |
+
* @since 2.0.0
|
226 |
+
*
|
227 |
+
* @param bool|int|null $network_level_or_blog_id
|
228 |
+
*/
|
229 |
+
function save( $network_level_or_blog_id = null ) {
|
230 |
+
if ( $this->_is_network_active &&
|
231 |
+
( true === $network_level_or_blog_id || is_null( $network_level_or_blog_id ) )
|
232 |
+
) {
|
233 |
+
$this->_network_storage->save();
|
234 |
+
}
|
235 |
+
|
236 |
+
if ( ! $this->_is_network_active || true !== $network_level_or_blog_id ) {
|
237 |
+
$storage = $this->get_site_storage( $network_level_or_blog_id );
|
238 |
+
$storage->save();
|
239 |
+
}
|
240 |
+
}
|
241 |
+
|
242 |
+
/**
|
243 |
+
* @author Vova Feldman (@svovaf)
|
244 |
+
* @since 2.0.0
|
245 |
+
*
|
246 |
+
* @return string
|
247 |
+
*/
|
248 |
+
function get_module_slug() {
|
249 |
+
return $this->_module_slug;
|
250 |
+
}
|
251 |
+
|
252 |
+
/**
|
253 |
+
* @author Vova Feldman (@svovaf)
|
254 |
+
* @since 2.0.0
|
255 |
+
*
|
256 |
+
* @return string
|
257 |
+
*/
|
258 |
+
function get_module_type() {
|
259 |
+
return $this->_module_type;
|
260 |
+
}
|
261 |
+
|
262 |
+
/**
|
263 |
+
* Migration script to the new storage data structure that is network compatible.
|
264 |
+
*
|
265 |
+
* IMPORTANT:
|
266 |
+
* This method should be executed only after it is determined if this is a network
|
267 |
+
* level compatible product activation.
|
268 |
+
*
|
269 |
+
* @author Vova Feldman (@svovaf)
|
270 |
+
* @since 2.0.0
|
271 |
+
*/
|
272 |
+
function migrate_to_network() {
|
273 |
+
if ( ! $this->_is_multisite ) {
|
274 |
+
return;
|
275 |
+
}
|
276 |
+
|
277 |
+
$updated = false;
|
278 |
+
|
279 |
+
if ( ! isset( self::$_NETWORK_OPTIONS_MAP ) ) {
|
280 |
+
self::load_network_options_map();
|
281 |
+
}
|
282 |
+
|
283 |
+
foreach ( self::$_NETWORK_OPTIONS_MAP as $option => $storage_level ) {
|
284 |
+
if ( ! $this->is_multisite_option( $option ) ) {
|
285 |
+
continue;
|
286 |
+
}
|
287 |
+
|
288 |
+
if ( isset( $this->_storage->{$option} ) && ! isset( $this->_network_storage->{$option} ) ) {
|
289 |
+
// Migrate option to the network storage.
|
290 |
+
$this->_network_storage->store( $option, $this->_storage->{$option}, false );
|
291 |
+
|
292 |
+
/**
|
293 |
+
* Remove the option from site level storage.
|
294 |
+
*
|
295 |
+
* IMPORTANT:
|
296 |
+
* The line below is intentionally commented since we want to preserve the option
|
297 |
+
* on the site storage level for "downgrade compatibility". Basically, if the user
|
298 |
+
* will downgrade to an older version of the plugin with the prev storage structure,
|
299 |
+
* it will continue working.
|
300 |
+
*
|
301 |
+
* @todo After a few releases we can remove this.
|
302 |
+
*/
|
303 |
+
// $this->_storage->remove($option, false);
|
304 |
+
|
305 |
+
$updated = true;
|
306 |
+
}
|
307 |
+
}
|
308 |
+
|
309 |
+
if ( ! $updated ) {
|
310 |
+
return;
|
311 |
+
}
|
312 |
+
|
313 |
+
// Update network level storage.
|
314 |
+
$this->_network_storage->save();
|
315 |
+
// $this->_storage->save();
|
316 |
+
}
|
317 |
+
|
318 |
+
#--------------------------------------------------------------------------------
|
319 |
+
#region Helper Methods
|
320 |
+
#--------------------------------------------------------------------------------
|
321 |
+
|
322 |
+
/**
|
323 |
+
* We don't want to load the map right away since it's not even needed in a non-MS environment.
|
324 |
+
*
|
325 |
+
* Example:
|
326 |
+
* array(
|
327 |
+
* 'option1' => 0, // Means that the option should always be stored on the network level.
|
328 |
+
* 'option2' => 1, // Means that the option should be stored on the network level only when the module was network level activated.
|
329 |
+
* 'option2' => 2, // Means that the option should be stored on the network level only when the module was network level activated AND the connection was NOT delegated.
|
330 |
+
* 'option3' => 3, // Means that the option should always be stored on the site level.
|
331 |
+
* )
|
332 |
+
*
|
333 |
+
* @author Vova Feldman (@svovaf)
|
334 |
+
* @since 2.0.0
|
335 |
+
*/
|
336 |
+
private static function load_network_options_map() {
|
337 |
+
self::$_NETWORK_OPTIONS_MAP = array(
|
338 |
+
// Network level options.
|
339 |
+
'affiliate_application_data' => 0,
|
340 |
+
'beta_data' => 0,
|
341 |
+
'connectivity_test' => 0,
|
342 |
+
'handle_gdpr_admin_notice' => 0,
|
343 |
+
'has_trial_plan' => 0,
|
344 |
+
'install_sync_timestamp' => 0,
|
345 |
+
'install_sync_cron' => 0,
|
346 |
+
'is_anonymous_ms' => 0,
|
347 |
+
'is_network_activated' => 0,
|
348 |
+
'is_on' => 0,
|
349 |
+
'is_plugin_new_install' => 0,
|
350 |
+
'network_install_blog_id' => 0,
|
351 |
+
'pending_sites_info' => 0,
|
352 |
+
'plugin_last_version' => 0,
|
353 |
+
'plugin_main_file' => 0,
|
354 |
+
'plugin_version' => 0,
|
355 |
+
'sdk_downgrade_mode' => 0,
|
356 |
+
'sdk_last_version' => 0,
|
357 |
+
'sdk_upgrade_mode' => 0,
|
358 |
+
'sdk_version' => 0,
|
359 |
+
'sticky_optin_added_ms' => 0,
|
360 |
+
'subscriptions' => 0,
|
361 |
+
'sync_timestamp' => 0,
|
362 |
+
'sync_cron' => 0,
|
363 |
+
'was_plugin_loaded' => 0,
|
364 |
+
'network_user_id' => 0,
|
365 |
+
'plugin_upgrade_mode' => 0,
|
366 |
+
'plugin_downgrade_mode' => 0,
|
367 |
+
'is_network_connected' => 0,
|
368 |
+
/**
|
369 |
+
* Special flag that is used when a super-admin upgrades to the new version of the SDK that
|
370 |
+
* supports network level integration, when the connection decision wasn't made for all of the
|
371 |
+
* sites in the network.
|
372 |
+
*/
|
373 |
+
'is_network_activation' => 0,
|
374 |
+
'license_migration' => 0,
|
375 |
+
|
376 |
+
// When network activated, then network level.
|
377 |
+
'install_timestamp' => 1,
|
378 |
+
'prev_is_premium' => 1,
|
379 |
+
'require_license_activation' => 1,
|
380 |
+
|
381 |
+
// If not network activated OR delegated, then site level.
|
382 |
+
'activation_timestamp' => 2,
|
383 |
+
'expired_license_notice_shown' => 2,
|
384 |
+
'is_whitelabeled' => 2,
|
385 |
+
'last_license_key' => 2,
|
386 |
+
'last_license_user_id' => 2,
|
387 |
+
'prev_user_id' => 2,
|
388 |
+
'sticky_optin_added' => 2,
|
389 |
+
'uninstall_reason' => 2,
|
390 |
+
'is_pending_activation' => 2,
|
391 |
+
'pending_license_key' => 2,
|
392 |
+
'is_extensions_tracking_allowed' => 2,
|
393 |
+
|
394 |
+
// Site level options.
|
395 |
+
'is_anonymous' => 3,
|
396 |
+
);
|
397 |
+
}
|
398 |
+
|
399 |
+
/**
|
400 |
+
* This method will and should only be executed when is_multisite() is true.
|
401 |
+
*
|
402 |
+
* @author Vova Feldman (@svovaf)
|
403 |
+
* @since 2.0.0
|
404 |
+
*
|
405 |
+
* @param string $key
|
406 |
+
*
|
407 |
+
* @return bool|mixed
|
408 |
+
*/
|
409 |
+
private function is_multisite_option( $key ) {
|
410 |
+
if ( ! isset( self::$_NETWORK_OPTIONS_MAP ) ) {
|
411 |
+
self::load_network_options_map();
|
412 |
+
}
|
413 |
+
|
414 |
+
if ( ! isset( self::$_NETWORK_OPTIONS_MAP[ $key ] ) ) {
|
415 |
+
// Option not found -> use site level storage.
|
416 |
+
return false;
|
417 |
+
}
|
418 |
+
|
419 |
+
if ( 0 === self::$_NETWORK_OPTIONS_MAP[ $key ] ) {
|
420 |
+
// Option found and set to always use the network level storage on a multisite.
|
421 |
+
return true;
|
422 |
+
}
|
423 |
+
|
424 |
+
if ( 3 === self::$_NETWORK_OPTIONS_MAP[ $key ] ) {
|
425 |
+
// Option found and set to always use the site level storage on a multisite.
|
426 |
+
return false;
|
427 |
+
}
|
428 |
+
|
429 |
+
if ( ! $this->_is_network_active ) {
|
430 |
+
return false;
|
431 |
+
}
|
432 |
+
|
433 |
+
if ( 1 === self::$_NETWORK_OPTIONS_MAP[ $key ] ) {
|
434 |
+
// Network activated.
|
435 |
+
return true;
|
436 |
+
}
|
437 |
+
|
438 |
+
if ( 2 === self::$_NETWORK_OPTIONS_MAP[ $key ] && ! $this->_is_delegated_connection ) {
|
439 |
+
// Network activated and not delegated.
|
440 |
+
return true;
|
441 |
+
}
|
442 |
+
|
443 |
+
return false;
|
444 |
+
}
|
445 |
+
|
446 |
+
/**
|
447 |
+
* @author Leo Fajardo
|
448 |
+
*
|
449 |
+
* @param string $key
|
450 |
+
* @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_BINARY_MAP).
|
451 |
+
*
|
452 |
+
* @return bool
|
453 |
+
*/
|
454 |
+
private function should_use_network_storage( $key, $network_level_or_blog_id = null ) {
|
455 |
+
if ( ! $this->_is_multisite ) {
|
456 |
+
// Not a multisite environment.
|
457 |
+
return false;
|
458 |
+
}
|
459 |
+
|
460 |
+
if ( is_numeric( $network_level_or_blog_id ) ) {
|
461 |
+
// Explicitly asked to use a specified blog storage.
|
462 |
+
return false;
|
463 |
+
}
|
464 |
+
|
465 |
+
if ( is_bool( $network_level_or_blog_id ) ) {
|
466 |
+
// Explicitly specified whether should use the network or blog level storage.
|
467 |
+
return $network_level_or_blog_id;
|
468 |
+
}
|
469 |
+
|
470 |
+
// Determine which storage to use based on the option.
|
471 |
+
return $this->is_multisite_option( $key );
|
472 |
+
}
|
473 |
+
|
474 |
+
/**
|
475 |
+
* @author Vova Feldman (@svovaf)
|
476 |
+
* @since 2.0.0
|
477 |
+
*
|
478 |
+
* @param int $blog_id
|
479 |
+
*
|
480 |
+
* @return \FS_Key_Value_Storage
|
481 |
+
*/
|
482 |
+
private function get_site_storage( $blog_id = 0 ) {
|
483 |
+
if ( ! is_numeric( $blog_id ) ||
|
484 |
+
$blog_id == $this->_blog_id ||
|
485 |
+
0 == $blog_id
|
486 |
+
) {
|
487 |
+
return $this->_storage;
|
488 |
+
}
|
489 |
+
|
490 |
+
return FS_Key_Value_Storage::instance(
|
491 |
+
$this->_module_type . '_data',
|
492 |
+
$this->_storage->get_secondary_id(),
|
493 |
+
$blog_id
|
494 |
+
);
|
495 |
+
}
|
496 |
+
|
497 |
+
#endregion
|
498 |
+
|
499 |
+
#--------------------------------------------------------------------------------
|
500 |
+
#region Magic methods
|
501 |
+
#--------------------------------------------------------------------------------
|
502 |
+
|
503 |
+
function __set( $k, $v ) {
|
504 |
+
if ( $this->should_use_network_storage( $k ) ) {
|
505 |
+
$this->_network_storage->{$k} = $v;
|
506 |
+
} else {
|
507 |
+
$this->_storage->{$k} = $v;
|
508 |
+
}
|
509 |
+
}
|
510 |
+
|
511 |
+
function __isset( $k ) {
|
512 |
+
return $this->should_use_network_storage( $k ) ?
|
513 |
+
isset( $this->_network_storage->{$k} ) :
|
514 |
+
isset( $this->_storage->{$k} );
|
515 |
+
}
|
516 |
+
|
517 |
+
function __unset( $k ) {
|
518 |
+
if ( $this->should_use_network_storage( $k ) ) {
|
519 |
+
unset( $this->_network_storage->{$k} );
|
520 |
+
} else {
|
521 |
+
unset( $this->_storage->{$k} );
|
522 |
+
}
|
523 |
+
}
|
524 |
+
|
525 |
+
function __get( $k ) {
|
526 |
+
return $this->should_use_network_storage( $k ) ?
|
527 |
+
$this->_network_storage->{$k} :
|
528 |
+
$this->_storage->{$k};
|
529 |
+
}
|
530 |
+
|
531 |
+
#endregion
|
532 |
}
|
freemius/includes/fs-core-functions.php
CHANGED
@@ -1,1416 +1,1416 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* @package Freemius
|
4 |
-
* @copyright Copyright (c) 2015, Freemius, Inc.
|
5 |
-
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
|
6 |
-
* @since 1.0.3
|
7 |
-
*/
|
8 |
-
|
9 |
-
if ( ! defined( 'ABSPATH' ) ) {
|
10 |
-
exit;
|
11 |
-
}
|
12 |
-
|
13 |
-
if ( ! function_exists( 'fs_dummy' ) ) {
|
14 |
-
function fs_dummy() {
|
15 |
-
}
|
16 |
-
}
|
17 |
-
|
18 |
-
/* Url.
|
19 |
-
--------------------------------------------------------------------------------------------*/
|
20 |
-
if ( ! function_exists( 'fs_get_url_daily_cache_killer' ) ) {
|
21 |
-
function fs_get_url_daily_cache_killer() {
|
22 |
-
return date( '\YY\Mm\Dd' );
|
23 |
-
}
|
24 |
-
}
|
25 |
-
|
26 |
-
/* Templates / Views.
|
27 |
-
--------------------------------------------------------------------------------------------*/
|
28 |
-
if ( ! function_exists( 'fs_get_template_path' ) ) {
|
29 |
-
function fs_get_template_path( $path ) {
|
30 |
-
return WP_FS__DIR_TEMPLATES . '/' . trim( $path, '/' );
|
31 |
-
}
|
32 |
-
|
33 |
-
function fs_include_template( $path, &$params = null ) {
|
34 |
-
$VARS = &$params;
|
35 |
-
include fs_get_template_path( $path );
|
36 |
-
}
|
37 |
-
|
38 |
-
function fs_include_once_template( $path, &$params = null ) {
|
39 |
-
$VARS = &$params;
|
40 |
-
include_once fs_get_template_path( $path );
|
41 |
-
}
|
42 |
-
|
43 |
-
function fs_require_template( $path, &$params = null ) {
|
44 |
-
$VARS = &$params;
|
45 |
-
require fs_get_template_path( $path );
|
46 |
-
}
|
47 |
-
|
48 |
-
function fs_require_once_template( $path, &$params = null ) {
|
49 |
-
$VARS = &$params;
|
50 |
-
require_once fs_get_template_path( $path );
|
51 |
-
}
|
52 |
-
|
53 |
-
function fs_get_template( $path, &$params = null ) {
|
54 |
-
ob_start();
|
55 |
-
|
56 |
-
$VARS = &$params;
|
57 |
-
require fs_get_template_path( $path );
|
58 |
-
|
59 |
-
return ob_get_clean();
|
60 |
-
}
|
61 |
-
}
|
62 |
-
|
63 |
-
/* Scripts and styles including.
|
64 |
-
--------------------------------------------------------------------------------------------*/
|
65 |
-
|
66 |
-
if ( ! function_exists( 'fs_asset_url' ) ) {
|
67 |
-
/**
|
68 |
-
* Generates an absolute URL to the given path. This function ensures that the URL will be correct whether the asset
|
69 |
-
* is inside a plugin's folder or a theme's folder.
|
70 |
-
*
|
71 |
-
* Examples:
|
72 |
-
* 1. "themes" folder
|
73 |
-
* Path: C:/xampp/htdocs/fswp/wp-content/themes/twentytwelve/freemius/assets/css/admin/common.css
|
74 |
-
* URL: http://fswp:8080/wp-content/themes/twentytwelve/freemius/assets/css/admin/common.css
|
75 |
-
*
|
76 |
-
* 2. "plugins" folder
|
77 |
-
* Path: C:/xampp/htdocs/fswp/wp-content/plugins/rating-widget-premium/freemius/assets/css/admin/common.css
|
78 |
-
* URL: http://fswp:8080/wp-content/plugins/rating-widget-premium/freemius/assets/css/admin/common.css
|
79 |
-
*
|
80 |
-
* @author Leo Fajardo (@leorw)
|
81 |
-
* @since 1.2.2
|
82 |
-
*
|
83 |
-
* @param string $asset_abs_path Asset's absolute path.
|
84 |
-
*
|
85 |
-
* @return string Asset's URL.
|
86 |
-
*/
|
87 |
-
function fs_asset_url( $asset_abs_path ) {
|
88 |
-
$wp_content_dir = fs_normalize_path( WP_CONTENT_DIR );
|
89 |
-
$asset_abs_path = fs_normalize_path( $asset_abs_path );
|
90 |
-
|
91 |
-
if ( 0 === strpos( $asset_abs_path, $wp_content_dir ) ) {
|
92 |
-
// Handle both theme and plugin assets located in the standard directories.
|
93 |
-
$asset_rel_path = str_replace( $wp_content_dir, '', $asset_abs_path );
|
94 |
-
$asset_url = content_url( fs_normalize_path( $asset_rel_path ) );
|
95 |
-
} else {
|
96 |
-
$wp_plugins_dir = fs_normalize_path( WP_PLUGIN_DIR );
|
97 |
-
if ( 0 === strpos( $asset_abs_path, $wp_plugins_dir ) ) {
|
98 |
-
// Try to handle plugin assets that may be located in a non-standard plugins directory.
|
99 |
-
$asset_rel_path = str_replace( $wp_plugins_dir, '', $asset_abs_path );
|
100 |
-
$asset_url = plugins_url( fs_normalize_path( $asset_rel_path ) );
|
101 |
-
} else {
|
102 |
-
// Try to handle theme assets that may be located in a non-standard themes directory.
|
103 |
-
$active_theme_stylesheet = get_stylesheet();
|
104 |
-
$wp_themes_dir = fs_normalize_path( trailingslashit( get_theme_root( $active_theme_stylesheet ) ) );
|
105 |
-
$asset_rel_path = str_replace( $wp_themes_dir, '', fs_normalize_path( $asset_abs_path ) );
|
106 |
-
$asset_url = trailingslashit( get_theme_root_uri( $active_theme_stylesheet ) ) . fs_normalize_path( $asset_rel_path );
|
107 |
-
}
|
108 |
-
}
|
109 |
-
|
110 |
-
return $asset_url;
|
111 |
-
}
|
112 |
-
}
|
113 |
-
|
114 |
-
if ( ! function_exists( 'fs_enqueue_local_style' ) ) {
|
115 |
-
function fs_enqueue_local_style( $handle, $path, $deps = array(), $ver = false, $media = 'all' ) {
|
116 |
-
wp_enqueue_style( $handle, fs_asset_url( WP_FS__DIR_CSS . '/' . trim( $path, '/' ) ), $deps, $ver, $media );
|
117 |
-
}
|
118 |
-
}
|
119 |
-
|
120 |
-
if ( ! function_exists( 'fs_enqueue_local_script' ) ) {
|
121 |
-
function fs_enqueue_local_script( $handle, $path, $deps = array(), $ver = false, $in_footer = 'all' ) {
|
122 |
-
wp_enqueue_script( $handle, fs_asset_url( WP_FS__DIR_JS . '/' . trim( $path, '/' ) ), $deps, $ver, $in_footer );
|
123 |
-
}
|
124 |
-
}
|
125 |
-
|
126 |
-
if ( ! function_exists( 'fs_img_url' ) ) {
|
127 |
-
function fs_img_url( $path, $img_dir = WP_FS__DIR_IMG ) {
|
128 |
-
return ( fs_asset_url( $img_dir . '/' . trim( $path, '/' ) ) );
|
129 |
-
}
|
130 |
-
}
|
131 |
-
|
132 |
-
#--------------------------------------------------------------------------------
|
133 |
-
#region Request handlers.
|
134 |
-
#--------------------------------------------------------------------------------
|
135 |
-
|
136 |
-
if ( ! function_exists( 'fs_request_get' ) ) {
|
137 |
-
/**
|
138 |
-
* A helper method to fetch GET/POST user input with an optional default value when the input is not set.
|
139 |
-
* @author Vova Feldman (@svovaf)
|
140 |
-
*
|
141 |
-
* @param string $key
|
142 |
-
* @param mixed $def
|
143 |
-
* @param string|bool $type Since 1.2.1.7 - when set to 'get' will look for the value passed via querystring, when
|
144 |
-
* set to 'post' will look for the value passed via the POST request's body, otherwise,
|
145 |
-
* will check if the parameter was passed in any of the two.
|
146 |
-
*
|
147 |
-
* @return mixed
|
148 |
-
*/
|
149 |
-
function fs_request_get( $key, $def = false, $type = false ) {
|
150 |
-
if ( is_string( $type ) ) {
|
151 |
-
$type = strtolower( $type );
|
152 |
-
}
|
153 |
-
|
154 |
-
/**
|
155 |
-
* Note to WordPress.org Reviewers:
|
156 |
-
* This is a helper method to fetch GET/POST user input with an optional default value when the input is not set. The actual sanitization is done in the scope of the function's usage.
|
157 |
-
*/
|
158 |
-
switch ( $type ) {
|
159 |
-
case 'post':
|
160 |
-
$value = isset( $_POST[ $key ] ) ? $_POST[ $key ] : $def;
|
161 |
-
break;
|
162 |
-
case 'get':
|
163 |
-
$value = isset( $_GET[ $key ] ) ? $_GET[ $key ] : $def;
|
164 |
-
break;
|
165 |
-
default:
|
166 |
-
$value = isset( $_REQUEST[ $key ] ) ? $_REQUEST[ $key ] : $def;
|
167 |
-
break;
|
168 |
-
}
|
169 |
-
|
170 |
-
return $value;
|
171 |
-
}
|
172 |
-
}
|
173 |
-
|
174 |
-
if ( ! function_exists( 'fs_request_has' ) ) {
|
175 |
-
function fs_request_has( $key ) {
|
176 |
-
return isset( $_REQUEST[ $key ] );
|
177 |
-
}
|
178 |
-
}
|
179 |
-
|
180 |
-
if ( ! function_exists( 'fs_request_get_bool' ) ) {
|
181 |
-
/**
|
182 |
-
* A helper method to fetch GET/POST user boolean input with an optional default value when the input is not set.
|
183 |
-
*
|
184 |
-
* @author Vova Feldman (@svovaf)
|
185 |
-
*
|
186 |
-
* @param string $key
|
187 |
-
* @param bool $def
|
188 |
-
*
|
189 |
-
* @return bool|mixed
|
190 |
-
*/
|
191 |
-
function fs_request_get_bool( $key, $def = false ) {
|
192 |
-
$val = fs_request_get( $key, null );
|
193 |
-
|
194 |
-
if ( is_null( $val ) ) {
|
195 |
-
return $def;
|
196 |
-
}
|
197 |
-
|
198 |
-
if ( is_bool( $val ) ) {
|
199 |
-
return $val;
|
200 |
-
} else if ( is_numeric( $val ) ) {
|
201 |
-
if ( 1 == $val ) {
|
202 |
-
return true;
|
203 |
-
} else if ( 0 == $val ) {
|
204 |
-
return false;
|
205 |
-
}
|
206 |
-
} else if ( is_string( $val ) ) {
|
207 |
-
$val = strtolower( $val );
|
208 |
-
|
209 |
-
if ( 'true' === $val ) {
|
210 |
-
return true;
|
211 |
-
} else if ( 'false' === $val ) {
|
212 |
-
return false;
|
213 |
-
}
|
214 |
-
}
|
215 |
-
|
216 |
-
return $def;
|
217 |
-
}
|
218 |
-
}
|
219 |
-
|
220 |
-
if ( ! function_exists( 'fs_request_is_post' ) ) {
|
221 |
-
function fs_request_is_post() {
|
222 |
-
return ( 'post' === strtolower( $_SERVER['REQUEST_METHOD'] ) );
|
223 |
-
}
|
224 |
-
}
|
225 |
-
|
226 |
-
if ( ! function_exists( 'fs_request_is_get' ) ) {
|
227 |
-
function fs_request_is_get() {
|
228 |
-
return ( 'get' === strtolower( $_SERVER['REQUEST_METHOD'] ) );
|
229 |
-
}
|
230 |
-
}
|
231 |
-
|
232 |
-
if ( ! function_exists( 'fs_get_action' ) ) {
|
233 |
-
function fs_get_action( $action_key = 'action' ) {
|
234 |
-
if ( ! empty( $_REQUEST[ $action_key ] ) && is_string( $_REQUEST[ $action_key ] ) ) {
|
235 |
-
return strtolower( $_REQUEST[ $action_key ] );
|
236 |
-
}
|
237 |
-
|
238 |
-
if ( 'action' == $action_key ) {
|
239 |
-
$action_key = 'fs_action';
|
240 |
-
|
241 |
-
if ( ! empty( $_REQUEST[ $action_key ] ) && is_string( $_REQUEST[ $action_key ] ) ) {
|
242 |
-
return strtolower( $_REQUEST[ $action_key ] );
|
243 |
-
}
|
244 |
-
}
|
245 |
-
|
246 |
-
return false;
|
247 |
-
}
|
248 |
-
}
|
249 |
-
|
250 |
-
if ( ! function_exists( 'fs_request_is_action' ) ) {
|
251 |
-
function fs_request_is_action( $action, $action_key = 'action' ) {
|
252 |
-
return ( strtolower( $action ) === fs_get_action( $action_key ) );
|
253 |
-
}
|
254 |
-
}
|
255 |
-
|
256 |
-
if ( ! function_exists( 'fs_request_is_action_secure' ) ) {
|
257 |
-
/**
|
258 |
-
* @author Vova Feldman (@svovaf)
|
259 |
-
* @since 1.0.0
|
260 |
-
*
|
261 |
-
* @since 1.2.1.5 Allow nonce verification.
|
262 |
-
*
|
263 |
-
* @param string $action
|
264 |
-
* @param string $action_key
|
265 |
-
* @param string $nonce_key
|
266 |
-
*
|
267 |
-
* @return bool
|
268 |
-
*/
|
269 |
-
function fs_request_is_action_secure(
|
270 |
-
$action,
|
271 |
-
$action_key = 'action',
|
272 |
-
$nonce_key = 'nonce'
|
273 |
-
) {
|
274 |
-
if ( strtolower( $action ) !== fs_get_action( $action_key ) ) {
|
275 |
-
return false;
|
276 |
-
}
|
277 |
-
|
278 |
-
$nonce = ! empty( $_REQUEST[ $nonce_key ] ) ?
|
279 |
-
$_REQUEST[ $nonce_key ] :
|
280 |
-
'';
|
281 |
-
|
282 |
-
if ( empty( $nonce ) ||
|
283 |
-
( false === wp_verify_nonce( $nonce, $action ) )
|
284 |
-
) {
|
285 |
-
return false;
|
286 |
-
}
|
287 |
-
|
288 |
-
return true;
|
289 |
-
}
|
290 |
-
}
|
291 |
-
|
292 |
-
#endregion
|
293 |
-
|
294 |
-
if ( ! function_exists( 'fs_is_plugin_page' ) ) {
|
295 |
-
function fs_is_plugin_page( $page_slug ) {
|
296 |
-
return ( is_admin() && $page_slug === fs_request_get( 'page' ) );
|
297 |
-
}
|
298 |
-
}
|
299 |
-
|
300 |
-
if ( ! function_exists( 'fs_get_raw_referer' ) ) {
|
301 |
-
/**
|
302 |
-
* Retrieves unvalidated referer from '_wp_http_referer' or HTTP referer.
|
303 |
-
*
|
304 |
-
* Do not use for redirects, use {@see wp_get_referer()} instead.
|
305 |
-
*
|
306 |
-
* @since 1.2.3
|
307 |
-
*
|
308 |
-
* @return string|false Referer URL on success, false on failure.
|
309 |
-
*/
|
310 |
-
function fs_get_raw_referer() {
|
311 |
-
if ( function_exists( 'wp_get_raw_referer' ) ) {
|
312 |
-
return wp_get_raw_referer();
|
313 |
-
}
|
314 |
-
if ( ! empty( $_REQUEST['_wp_http_referer'] ) ) {
|
315 |
-
return wp_unslash( $_REQUEST['_wp_http_referer'] );
|
316 |
-
} else if ( ! empty( $_SERVER['HTTP_REFERER'] ) ) {
|
317 |
-
return wp_unslash( $_SERVER['HTTP_REFERER'] );
|
318 |
-
}
|
319 |
-
|
320 |
-
return false;
|
321 |
-
}
|
322 |
-
}
|
323 |
-
|
324 |
-
/* Core UI.
|
325 |
-
--------------------------------------------------------------------------------------------*/
|
326 |
-
if ( ! function_exists( 'fs_ui_action_button' ) ) {
|
327 |
-
/**
|
328 |
-
* @param number $module_id
|
329 |
-
* @param string $page
|
330 |
-
* @param string $action
|
331 |
-
* @param string $title
|
332 |
-
* @param string $button_class
|
333 |
-
* @param array $params
|
334 |
-
* @param bool $is_primary
|
335 |
-
* @param bool $is_small
|
336 |
-
* @param string|bool $icon_class Optional class for an icon (since 1.1.7).
|
337 |
-
* @param string|bool $confirmation Optional confirmation message before submit (since 1.1.7).
|
338 |
-
* @param string $method Since 1.1.7
|
339 |
-
*
|
340 |
-
* @uses fs_ui_get_action_button()
|
341 |
-
*/
|
342 |
-
function fs_ui_action_button(
|
343 |
-
$module_id,
|
344 |
-
$page,
|
345 |
-
$action,
|
346 |
-
$title,
|
347 |
-
$button_class = '',
|
348 |
-
$params = array(),
|
349 |
-
$is_primary = true,
|
350 |
-
$is_small = false,
|
351 |
-
$icon_class = false,
|
352 |
-
$confirmation = false,
|
353 |
-
$method = 'GET'
|
354 |
-
) {
|
355 |
-
echo fs_ui_get_action_button(
|
356 |
-
$module_id,
|
357 |
-
$page,
|
358 |
-
$action,
|
359 |
-
$title,
|
360 |
-
$button_class,
|
361 |
-
$params,
|
362 |
-
$is_primary,
|
363 |
-
$is_small,
|
364 |
-
$icon_class,
|
365 |
-
$confirmation,
|
366 |
-
$method
|
367 |
-
);
|
368 |
-
}
|
369 |
-
}
|
370 |
-
|
371 |
-
if ( ! function_exists( 'fs_ui_get_action_button' ) ) {
|
372 |
-
/**
|
373 |
-
* @author Vova Feldman (@svovaf)
|
374 |
-
* @since 1.1.7
|
375 |
-
*
|
376 |
-
* @param number $module_id
|
377 |
-
* @param string $page
|
378 |
-
* @param string $action
|
379 |
-
* @param string $title
|
380 |
-
* @param string $button_class
|
381 |
-
* @param array $params
|
382 |
-
* @param bool $is_primary
|
383 |
-
* @param bool $is_small
|
384 |
-
* @param string|bool $icon_class Optional class for an icon.
|
385 |
-
* @param string|bool $confirmation Optional confirmation message before submit.
|
386 |
-
* @param string $method
|
387 |
-
*
|
388 |
-
* @return string
|
389 |
-
*/
|
390 |
-
function fs_ui_get_action_button(
|
391 |
-
$module_id,
|
392 |
-
$page,
|
393 |
-
$action,
|
394 |
-
$title,
|
395 |
-
$button_class = '',
|
396 |
-
$params = array(),
|
397 |
-
$is_primary = true,
|
398 |
-
$is_small = false,
|
399 |
-
$icon_class = false,
|
400 |
-
$confirmation = false,
|
401 |
-
$method = 'GET'
|
402 |
-
) {
|
403 |
-
// Prepend icon (if set).
|
404 |
-
$title = ( is_string( $icon_class ) ? '<i class="' . $icon_class . '"></i> ' : '' ) . $title;
|
405 |
-
|
406 |
-
if ( is_string( $confirmation ) ) {
|
407 |
-
return sprintf( '<form action="%s" method="%s"><input type="hidden" name="fs_action" value="%s">%s<a href="#" class="%s" onclick="if (confirm(\'%s\')) this.parentNode.submit(); return false;">%s</a></form>',
|
408 |
-
freemius( $module_id )->_get_admin_page_url( $page, $params ),
|
409 |
-
$method,
|
410 |
-
$action,
|
411 |
-
wp_nonce_field( $action, '_wpnonce', true, false ),
|
412 |
-
'button' . ( ! empty( $button_class ) ? ' ' . $button_class : '' ) . ( $is_primary ? ' button-primary' : '' ) . ( $is_small ? ' button-small' : '' ),
|
413 |
-
$confirmation,
|
414 |
-
$title
|
415 |
-
);
|
416 |
-
} else if ( 'GET' !== strtoupper( $method ) ) {
|
417 |
-
return sprintf( '<form action="%s" method="%s"><input type="hidden" name="fs_action" value="%s">%s<a href="#" class="%s" onclick="this.parentNode.submit(); return false;">%s</a></form>',
|
418 |
-
freemius( $module_id )->_get_admin_page_url( $page, $params ),
|
419 |
-
$method,
|
420 |
-
$action,
|
421 |
-
wp_nonce_field( $action, '_wpnonce', true, false ),
|
422 |
-
'button' . ( ! empty( $button_class ) ? ' ' . $button_class : '' ) . ( $is_primary ? ' button-primary' : '' ) . ( $is_small ? ' button-small' : '' ),
|
423 |
-
$title
|
424 |
-
);
|
425 |
-
} else {
|
426 |
-
return sprintf( '<a href="%s" class="%s">%s</a></form>',
|
427 |
-
wp_nonce_url( freemius( $module_id )->_get_admin_page_url( $page, array_merge( $params, array( 'fs_action' => $action ) ) ), $action ),
|
428 |
-
'button' . ( ! empty( $button_class ) ? ' ' . $button_class : '' ) . ( $is_primary ? ' button-primary' : '' ) . ( $is_small ? ' button-small' : '' ),
|
429 |
-
$title
|
430 |
-
);
|
431 |
-
}
|
432 |
-
}
|
433 |
-
|
434 |
-
function fs_ui_action_link( $module_id, $page, $action, $title, $params = array() ) {
|
435 |
-
?><a class=""
|
436 |
-
href="<?php echo wp_nonce_url( freemius( $module_id )->_get_admin_page_url( $page, array_merge( $params, array( 'fs_action' => $action ) ) ), $action ) ?>"><?php echo $title ?></a><?php
|
437 |
-
}
|
438 |
-
}
|
439 |
-
|
440 |
-
if ( ! function_exists( 'fs_get_entity' ) ) {
|
441 |
-
/**
|
442 |
-
* @author Leo Fajardo (@leorw)
|
443 |
-
* @since 2.3.1
|
444 |
-
*
|
445 |
-
* @param mixed $entity
|
446 |
-
* @param string $class
|
447 |
-
*
|
448 |
-
* @return FS_Plugin|FS_User|FS_Site|FS_Plugin_License|FS_Plugin_Plan|FS_Plugin_Tag|FS_Subscription
|
449 |
-
*/
|
450 |
-
function fs_get_entity( $entity, $class ) {
|
451 |
-
if ( ! is_object( $entity ) || $entity instanceof $class ) {
|
452 |
-
return $entity;
|
453 |
-
}
|
454 |
-
|
455 |
-
return new $class( $entity );
|
456 |
-
}
|
457 |
-
}
|
458 |
-
|
459 |
-
if ( ! function_exists( 'fs_get_entities' ) ) {
|
460 |
-
/**
|
461 |
-
* @author Leo Fajardo (@leorw)
|
462 |
-
* @since 2.3.1
|
463 |
-
*
|
464 |
-
* @param mixed $entities
|
465 |
-
* @param string $class_name
|
466 |
-
*
|
467 |
-
* @return FS_Plugin[]|FS_User[]|FS_Site[]|FS_Plugin_License[]|FS_Plugin_Plan[]|FS_Plugin_Tag[]|FS_Subscription[]
|
468 |
-
*/
|
469 |
-
function fs_get_entities( $entities, $class_name ) {
|
470 |
-
if ( ! is_array( $entities ) || empty( $entities ) ) {
|
471 |
-
return $entities;
|
472 |
-
}
|
473 |
-
|
474 |
-
// Get first element.
|
475 |
-
$first_array_element = reset( $entities );
|
476 |
-
|
477 |
-
if ( $first_array_element instanceof $class_name ) {
|
478 |
-
/**
|
479 |
-
* If the first element of the array is an instance of the context class, assume that all other
|
480 |
-
* elements are instances of the class.
|
481 |
-
*/
|
482 |
-
return $entities;
|
483 |
-
}
|
484 |
-
|
485 |
-
if (
|
486 |
-
is_array( $first_array_element ) &&
|
487 |
-
! empty( $first_array_element )
|
488 |
-
) {
|
489 |
-
$first_array_element = reset( $first_array_element );
|
490 |
-
|
491 |
-
if ( $first_array_element instanceof $class_name ) {
|
492 |
-
/**
|
493 |
-
* If the first element of the `$entities` array is an array whose first element is an instance of the
|
494 |
-
* context class, assume that all other objects are instances of the class.
|
495 |
-
*/
|
496 |
-
return $entities;
|
497 |
-
}
|
498 |
-
}
|
499 |
-
|
500 |
-
foreach ( $entities as $key => $entities_or_entity ) {
|
501 |
-
if ( is_array( $entities_or_entity ) ) {
|
502 |
-
$entities[ $key ] = fs_get_entities( $entities_or_entity, $class_name );
|
503 |
-
} else {
|
504 |
-
$entities[ $key ] = fs_get_entity( $entities_or_entity, $class_name );
|
505 |
-
}
|
506 |
-
}
|
507 |
-
|
508 |
-
return $entities;
|
509 |
-
}
|
510 |
-
}
|
511 |
-
|
512 |
-
if ( ! function_exists( 'fs_nonce_url' ) ) {
|
513 |
-
/**
|
514 |
-
* Retrieve URL with nonce added to URL query.
|
515 |
-
*
|
516 |
-
* Originally was using `wp_nonce_url()` but the new version
|
517 |
-
* changed the return value to escaped URL, that's not the expected
|
518 |
-
* behaviour.
|
519 |
-
*
|
520 |
-
* @author Vova Feldman (@svovaf)
|
521 |
-
* @since ~1.1.3
|
522 |
-
*
|
523 |
-
* @param string $actionurl URL to add nonce action.
|
524 |
-
* @param int|string $action Optional. Nonce action name. Default -1.
|
525 |
-
* @param string $name Optional. Nonce name. Default '_wpnonce'.
|
526 |
-
*
|
527 |
-
* @return string Escaped URL with nonce action added.
|
528 |
-
*/
|
529 |
-
function fs_nonce_url( $actionurl, $action = - 1, $name = '_wpnonce' ) {
|
530 |
-
return add_query_arg( $name, wp_create_nonce( $action ), $actionurl );
|
531 |
-
}
|
532 |
-
}
|
533 |
-
|
534 |
-
if ( ! function_exists( 'fs_starts_with' ) ) {
|
535 |
-
/**
|
536 |
-
* Check if string starts with.
|
537 |
-
*
|
538 |
-
* @author Vova Feldman (@svovaf)
|
539 |
-
* @since 1.1.3
|
540 |
-
*
|
541 |
-
* @param string $haystack
|
542 |
-
* @param string $needle
|
543 |
-
*
|
544 |
-
* @return bool
|
545 |
-
*/
|
546 |
-
function fs_starts_with( $haystack, $needle ) {
|
547 |
-
$length = strlen( $needle );
|
548 |
-
|
549 |
-
return ( substr( $haystack, 0, $length ) === $needle );
|
550 |
-
}
|
551 |
-
}
|
552 |
-
|
553 |
-
if ( ! function_exists( 'fs_ends_with' ) ) {
|
554 |
-
/**
|
555 |
-
* Check if string ends with.
|
556 |
-
*
|
557 |
-
* @author Vova Feldman (@svovaf)
|
558 |
-
* @since 2.0.0
|
559 |
-
*
|
560 |
-
* @param string $haystack
|
561 |
-
* @param string $needle
|
562 |
-
*
|
563 |
-
* @return bool
|
564 |
-
*/
|
565 |
-
function fs_ends_with( $haystack, $needle ) {
|
566 |
-
$length = strlen( $needle );
|
567 |
-
$start = $length * - 1; // negative
|
568 |
-
|
569 |
-
return ( substr( $haystack, $start ) === $needle );
|
570 |
-
}
|
571 |
-
}
|
572 |
-
|
573 |
-
if ( ! function_exists( 'fs_strip_url_protocol' ) ) {
|
574 |
-
function fs_strip_url_protocol( $url ) {
|
575 |
-
if ( ! fs_starts_with( $url, 'http' ) ) {
|
576 |
-
return $url;
|
577 |
-
}
|
578 |
-
|
579 |
-
$protocol_pos = strpos( $url, '://' );
|
580 |
-
|
581 |
-
if ( $protocol_pos > 5 ) {
|
582 |
-
return $url;
|
583 |
-
}
|
584 |
-
|
585 |
-
return substr( $url, $protocol_pos + 3 );
|
586 |
-
}
|
587 |
-
}
|
588 |
-
|
589 |
-
#region Url Canonization ------------------------------------------------------------------
|
590 |
-
|
591 |
-
if ( ! function_exists( 'fs_canonize_url' ) ) {
|
592 |
-
/**
|
593 |
-
* @author Vova Feldman (@svovaf)
|
594 |
-
* @since 1.1.3
|
595 |
-
*
|
596 |
-
* @param string $url
|
597 |
-
* @param bool $omit_host
|
598 |
-
* @param array $ignore_params
|
599 |
-
*
|
600 |
-
* @return string
|
601 |
-
*/
|
602 |
-
function fs_canonize_url( $url, $omit_host = false, $ignore_params = array() ) {
|
603 |
-
$parsed_url = parse_url( strtolower( $url ) );
|
604 |
-
|
605 |
-
// if ( ! isset( $parsed_url['host'] ) ) {
|
606 |
-
// return $url;
|
607 |
-
// }
|
608 |
-
|
609 |
-
$canonical = ( ( $omit_host || ! isset( $parsed_url['host'] ) ) ? '' : $parsed_url['host'] ) . $parsed_url['path'];
|
610 |
-
|
611 |
-
if ( isset( $parsed_url['query'] ) ) {
|
612 |
-
parse_str( $parsed_url['query'], $queryString );
|
613 |
-
$canonical .= '?' . fs_canonize_query_string( $queryString, $ignore_params );
|
614 |
-
}
|
615 |
-
|
616 |
-
return $canonical;
|
617 |
-
}
|
618 |
-
}
|
619 |
-
|
620 |
-
if ( ! function_exists( 'fs_canonize_query_string' ) ) {
|
621 |
-
/**
|
622 |
-
* @author Vova Feldman (@svovaf)
|
623 |
-
* @since 1.1.3
|
624 |
-
*
|
625 |
-
* @param array $params
|
626 |
-
* @param array $ignore_params
|
627 |
-
* @param bool $params_prefix
|
628 |
-
*
|
629 |
-
* @return string
|
630 |
-
*/
|
631 |
-
function fs_canonize_query_string( array $params, array &$ignore_params, $params_prefix = false ) {
|
632 |
-
if ( ! is_array( $params ) || 0 === count( $params ) ) {
|
633 |
-
return '';
|
634 |
-
}
|
635 |
-
|
636 |
-
// Url encode both keys and values
|
637 |
-
$keys = fs_urlencode_rfc3986( array_keys( $params ) );
|
638 |
-
$values = fs_urlencode_rfc3986( array_values( $params ) );
|
639 |
-
$params = array_combine( $keys, $values );
|
640 |
-
|
641 |
-
// Parameters are sorted by name, using lexicographical byte value ordering.
|
642 |
-
// Ref: Spec: 9.1.1 (1)
|
643 |
-
uksort( $params, 'strcmp' );
|
644 |
-
|
645 |
-
$pairs = array();
|
646 |
-
foreach ( $params as $parameter => $value ) {
|
647 |
-
$lower_param = strtolower( $parameter );
|
648 |
-
|
649 |
-
// Skip ignore params.
|
650 |
-
if ( in_array( $lower_param, $ignore_params ) ||
|
651 |
-
( false !== $params_prefix && fs_starts_with( $lower_param, $params_prefix ) )
|
652 |
-
) {
|
653 |
-
continue;
|
654 |
-
}
|
655 |
-
|
656 |
-
if ( is_array( $value ) ) {
|
657 |
-
// If two or more parameters share the same name, they are sorted by their value
|
658 |
-
// Ref: Spec: 9.1.1 (1)
|
659 |
-
natsort( $value );
|
660 |
-
foreach ( $value as $duplicate_value ) {
|
661 |
-
$pairs[] = $lower_param . '=' . $duplicate_value;
|
662 |
-
}
|
663 |
-
} else {
|
664 |
-
$pairs[] = $lower_param . '=' . $value;
|
665 |
-
}
|
666 |
-
}
|
667 |
-
|
668 |
-
if ( 0 === count( $pairs ) ) {
|
669 |
-
return '';
|
670 |
-
}
|
671 |
-
|
672 |
-
return implode( "&", $pairs );
|
673 |
-
}
|
674 |
-
}
|
675 |
-
|
676 |
-
if ( ! function_exists( 'fs_urlencode_rfc3986' ) ) {
|
677 |
-
/**
|
678 |
-
* @author Vova Feldman (@svovaf)
|
679 |
-
* @since 1.1.3
|
680 |
-
*
|
681 |
-
* @param string|string[] $input
|
682 |
-
*
|
683 |
-
* @return array|mixed|string
|
684 |
-
*/
|
685 |
-
function fs_urlencode_rfc3986( $input ) {
|
686 |
-
if ( is_array( $input ) ) {
|
687 |
-
return array_map( 'fs_urlencode_rfc3986', $input );
|
688 |
-
} else if ( is_scalar( $input ) ) {
|
689 |
-
return str_replace( '+', ' ', str_replace( '%7E', '~', rawurlencode( $input ) ) );
|
690 |
-
}
|
691 |
-
|
692 |
-
return '';
|
693 |
-
}
|
694 |
-
}
|
695 |
-
|
696 |
-
#endregion Url Canonization ------------------------------------------------------------------
|
697 |
-
|
698 |
-
if ( ! function_exists( 'fs_download_image' ) ) {
|
699 |
-
/**
|
700 |
-
* @author Vova Feldman (@svovaf)
|
701 |
-
*
|
702 |
-
* @since 1.2.2 Changed to usage of WP_Filesystem_Direct.
|
703 |
-
*
|
704 |
-
* @param string $from URL
|
705 |
-
* @param string $to File path.
|
706 |
-
*
|
707 |
-
* @return bool Is successfully downloaded.
|
708 |
-
*/
|
709 |
-
function fs_download_image( $from, $to ) {
|
710 |
-
$dir = dirname( $to );
|
711 |
-
|
712 |
-
if ( 'direct' !== get_filesystem_method( array(), $dir ) ) {
|
713 |
-
return false;
|
714 |
-
}
|
715 |
-
|
716 |
-
if ( ! class_exists( 'WP_Filesystem_Direct' ) ) {
|
717 |
-
require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php';
|
718 |
-
require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php';
|
719 |
-
}
|
720 |
-
|
721 |
-
$fs = new WP_Filesystem_Direct( '' );
|
722 |
-
$tmpfile = download_url( $from );
|
723 |
-
|
724 |
-
if ( $tmpfile instanceof WP_Error ) {
|
725 |
-
// Issue downloading the file.
|
726 |
-
return false;
|
727 |
-
}
|
728 |
-
|
729 |
-
$fs->copy( $tmpfile, $to );
|
730 |
-
$fs->delete( $tmpfile );
|
731 |
-
|
732 |
-
return true;
|
733 |
-
}
|
734 |
-
}
|
735 |
-
|
736 |
-
/* General Utilities
|
737 |
-
--------------------------------------------------------------------------------------------*/
|
738 |
-
|
739 |
-
if ( ! function_exists( 'fs_sort_by_priority' ) ) {
|
740 |
-
/**
|
741 |
-
* Sorts an array by the value of the priority key.
|
742 |
-
*
|
743 |
-
* @author Daniel Iser (@danieliser)
|
744 |
-
* @since 1.1.7
|
745 |
-
*
|
746 |
-
* @param $a
|
747 |
-
* @param $b
|
748 |
-
*
|
749 |
-
* @return int
|
750 |
-
*/
|
751 |
-
function fs_sort_by_priority( $a, $b ) {
|
752 |
-
|
753 |
-
// If b has a priority and a does not, b wins.
|
754 |
-
if ( ! isset( $a['priority'] ) && isset( $b['priority'] ) ) {
|
755 |
-
return 1;
|
756 |
-
} // If b has a priority and a does not, b wins.
|
757 |
-
elseif ( isset( $a['priority'] ) && ! isset( $b['priority'] ) ) {
|
758 |
-
return - 1;
|
759 |
-
} // If neither has a priority or both priorities are equal its a tie.
|
760 |
-
elseif ( ( ! isset( $a['priority'] ) && ! isset( $b['priority'] ) ) || $a['priority'] === $b['priority'] ) {
|
761 |
-
return 0;
|
762 |
-
}
|
763 |
-
|
764 |
-
// If both have priority return the winner.
|
765 |
-
return ( $a['priority'] < $b['priority'] ) ? - 1 : 1;
|
766 |
-
}
|
767 |
-
}
|
768 |
-
|
769 |
-
#--------------------------------------------------------------------------------
|
770 |
-
#region Localization
|
771 |
-
#--------------------------------------------------------------------------------
|
772 |
-
|
773 |
-
if ( ! function_exists( 'fs_text' ) ) {
|
774 |
-
/**
|
775 |
-
* Retrieve a translated text by key.
|
776 |
-
*
|
777 |
-
* @author Vova Feldman (@svovaf)
|
778 |
-
* @since 1.2.1.7
|
779 |
-
*
|
780 |
-
* @param string $key
|
781 |
-
* @param string $slug
|
782 |
-
*
|
783 |
-
* @return string
|
784 |
-
*
|
785 |
-
* @global $fs_text , $fs_text_overrides
|
786 |
-
*/
|
787 |
-
function fs_text( $key, $slug = 'freemius' ) {
|
788 |
-
global $fs_text,
|
789 |
-
$fs_module_info_text,
|
790 |
-
$fs_text_overrides;
|
791 |
-
|
792 |
-
if ( isset( $fs_text_overrides[ $slug ] ) ) {
|
793 |
-
if ( isset( $fs_text_overrides[ $slug ][ $key ] ) ) {
|
794 |
-
return $fs_text_overrides[ $slug ][ $key ];
|
795 |
-
}
|
796 |
-
|
797 |
-
$lower_key = strtolower( $key );
|
798 |
-
if ( isset( $fs_text_overrides[ $slug ][ $lower_key ] ) ) {
|
799 |
-
return $fs_text_overrides[ $slug ][ $lower_key ];
|
800 |
-
}
|
801 |
-
}
|
802 |
-
|
803 |
-
if ( ! isset( $fs_text ) ) {
|
804 |
-
$dir = defined( 'WP_FS__DIR_INCLUDES' ) ?
|
805 |
-
WP_FS__DIR_INCLUDES :
|
806 |
-
dirname( __FILE__ );
|
807 |
-
|
808 |
-
require_once $dir . '/i18n.php';
|
809 |
-
}
|
810 |
-
|
811 |
-
if ( isset( $fs_text[ $key ] ) ) {
|
812 |
-
return $fs_text[ $key ];
|
813 |
-
}
|
814 |
-
|
815 |
-
if ( isset( $fs_module_info_text[ $key ] ) ) {
|
816 |
-
return $fs_module_info_text[ $key ];
|
817 |
-
}
|
818 |
-
|
819 |
-
return $key;
|
820 |
-
}
|
821 |
-
|
822 |
-
#region Private
|
823 |
-
|
824 |
-
/**
|
825 |
-
* Retrieve an inline translated text by key with a context.
|
826 |
-
*
|
827 |
-
* @author Vova Feldman (@svovaf)
|
828 |
-
* @since 1.2.3
|
829 |
-
*
|
830 |
-
* @param string $text Translatable string.
|
831 |
-
* @param string $context Context information for the translators.
|
832 |
-
* @param string $key String key for overrides.
|
833 |
-
* @param string $slug Module slug for overrides.
|
834 |
-
*
|
835 |
-
* @return string
|
836 |
-
*
|
837 |
-
* @global $fs_text_overrides
|
838 |
-
*/
|
839 |
-
function _fs_text_x_inline( $text, $context, $key = '', $slug = 'freemius' ) {
|
840 |
-
list( $text, $text_domain ) = fs_text_and_domain( $text, $key, $slug );
|
841 |
-
|
842 |
-
// Avoid misleading Theme Check warning.
|
843 |
-
$fn = 'translate_with_gettext_context';
|
844 |
-
|
845 |
-
return $fn( $text, $context, $text_domain );
|
846 |
-
}
|
847 |
-
|
848 |
-
#endregion
|
849 |
-
|
850 |
-
/**
|
851 |
-
* Retrieve an inline translated text by key with a context.
|
852 |
-
*
|
853 |
-
* @author Vova Feldman (@svovaf)
|
854 |
-
* @since 1.2.3
|
855 |
-
*
|
856 |
-
* @param string $text Translatable string.
|
857 |
-
* @param string $context Context information for the translators.
|
858 |
-
* @param string $key String key for overrides.
|
859 |
-
* @param string $slug Module slug for overrides.
|
860 |
-
*
|
861 |
-
* @return string
|
862 |
-
*
|
863 |
-
* @global $fs_text_overrides
|
864 |
-
*/
|
865 |
-
function fs_text_x_inline( $text, $context, $key = '', $slug = 'freemius' ) {
|
866 |
-
return _fs_text_x_inline( $text, $context, $key, $slug );
|
867 |
-
}
|
868 |
-
|
869 |
-
/**
|
870 |
-
* Output a translated text by key.
|
871 |
-
*
|
872 |
-
* @author Vova Feldman (@svovaf)
|
873 |
-
* @since 1.2.1.7
|
874 |
-
*
|
875 |
-
* @param string $key
|
876 |
-
* @param string $slug
|
877 |
-
*/
|
878 |
-
function fs_echo( $key, $slug = 'freemius' ) {
|
879 |
-
echo fs_text( $key, $slug );
|
880 |
-
}
|
881 |
-
|
882 |
-
/**
|
883 |
-
* Output an inline translated text.
|
884 |
-
*
|
885 |
-
* @author Vova Feldman (@svovaf)
|
886 |
-
* @since 1.2.3
|
887 |
-
*
|
888 |
-
* @param string $text Translatable string.
|
889 |
-
* @param string $key String key for overrides.
|
890 |
-
* @param string $slug Module slug for overrides.
|
891 |
-
*/
|
892 |
-
function fs_echo_inline( $text, $key = '', $slug = 'freemius' ) {
|
893 |
-
echo _fs_text_inline( $text, $key, $slug );
|
894 |
-
}
|
895 |
-
|
896 |
-
/**
|
897 |
-
* Output an inline translated text with a context.
|
898 |
-
*
|
899 |
-
* @author Vova Feldman (@svovaf)
|
900 |
-
* @since 1.2.3
|
901 |
-
*
|
902 |
-
* @param string $text Translatable string.
|
903 |
-
* @param string $context Context information for the translators.
|
904 |
-
* @param string $key String key for overrides.
|
905 |
-
* @param string $slug Module slug for overrides.
|
906 |
-
*/
|
907 |
-
function fs_echo_x_inline( $text, $context, $key = '', $slug = 'freemius' ) {
|
908 |
-
echo _fs_text_x_inline( $text, $context, $key, $slug );
|
909 |
-
}
|
910 |
-
}
|
911 |
-
|
912 |
-
if ( ! function_exists( 'fs_text_override' ) ) {
|
913 |
-
/**
|
914 |
-
* Get a translatable text override if exists, or `false`.
|
915 |
-
*
|
916 |
-
* @author Vova Feldman (@svovaf)
|
917 |
-
* @since 1.2.1.7
|
918 |
-
*
|
919 |
-
* @param string $text Translatable string.
|
920 |
-
* @param string $key String key for overrides.
|
921 |
-
* @param string $slug Module slug for overrides.
|
922 |
-
*
|
923 |
-
* @return string|false
|
924 |
-
*/
|
925 |
-
function fs_text_override( $text, $key, $slug ) {
|
926 |
-
global $fs_text_overrides;
|
927 |
-
|
928 |
-
/**
|
929 |
-
* Check if string is overridden.
|
930 |
-
*/
|
931 |
-
if ( ! isset( $fs_text_overrides[ $slug ] ) ) {
|
932 |
-
return false;
|
933 |
-
}
|
934 |
-
|
935 |
-
if ( empty( $key ) ) {
|
936 |
-
$key = strtolower( str_replace( ' ', '-', $text ) );
|
937 |
-
}
|
938 |
-
|
939 |
-
if ( isset( $fs_text_overrides[ $slug ][ $key ] ) ) {
|
940 |
-
return $fs_text_overrides[ $slug ][ $key ];
|
941 |
-
}
|
942 |
-
|
943 |
-
$lower_key = strtolower( $key );
|
944 |
-
if ( isset( $fs_text_overrides[ $slug ][ $lower_key ] ) ) {
|
945 |
-
return $fs_text_overrides[ $slug ][ $lower_key ];
|
946 |
-
}
|
947 |
-
|
948 |
-
return false;
|
949 |
-
}
|
950 |
-
}
|
951 |
-
|
952 |
-
if ( ! function_exists( 'fs_text_and_domain' ) ) {
|
953 |
-
/**
|
954 |
-
* Get a translatable text and its text domain.
|
955 |
-
*
|
956 |
-
* When the text is overridden by the module, returns the overridden text and the text domain of the module. Otherwise, returns the original text and 'freemius' as the text domain.
|
957 |
-
*
|
958 |
-
* @author Vova Feldman (@svovaf)
|
959 |
-
* @since 1.2.1.7
|
960 |
-
*
|
961 |
-
* @param string $text Translatable string.
|
962 |
-
* @param string $key String key for overrides.
|
963 |
-
* @param string $slug Module slug for overrides.
|
964 |
-
*
|
965 |
-
* @return string[]
|
966 |
-
*/
|
967 |
-
function fs_text_and_domain( $text, $key, $slug ) {
|
968 |
-
$override = fs_text_override( $text, $key, $slug );
|
969 |
-
|
970 |
-
if ( false === $override ) {
|
971 |
-
// No override, use FS text domain.
|
972 |
-
$text_domain = 'freemius';
|
973 |
-
} else {
|
974 |
-
// Found an override.
|
975 |
-
$text = $override;
|
976 |
-
// Use the module's text domain.
|
977 |
-
$text_domain = $slug;
|
978 |
-
}
|
979 |
-
|
980 |
-
return array( $text, $text_domain );
|
981 |
-
}
|
982 |
-
}
|
983 |
-
|
984 |
-
if ( ! function_exists( '_fs_text_inline' ) ) {
|
985 |
-
/**
|
986 |
-
* Retrieve an inline translated text by key.
|
987 |
-
*
|
988 |
-
* @author Vova Feldman (@svovaf)
|
989 |
-
* @since 1.2.3
|
990 |
-
*
|
991 |
-
* @param string $text Translatable string.
|
992 |
-
* @param string $key String key for overrides.
|
993 |
-
* @param string $slug Module slug for overrides.
|
994 |
-
*
|
995 |
-
* @return string
|
996 |
-
*
|
997 |
-
* @global $fs_text_overrides
|
998 |
-
*/
|
999 |
-
function _fs_text_inline( $text, $key = '', $slug = 'freemius' ) {
|
1000 |
-
list( $text, $text_domain ) = fs_text_and_domain( $text, $key, $slug );
|
1001 |
-
|
1002 |
-
// Avoid misleading Theme Check warning.
|
1003 |
-
$fn = 'translate';
|
1004 |
-
|
1005 |
-
return $fn( $text, $text_domain );
|
1006 |
-
}
|
1007 |
-
}
|
1008 |
-
|
1009 |
-
if ( ! function_exists( 'fs_text_inline' ) ) {
|
1010 |
-
/**
|
1011 |
-
* Retrieve an inline translated text by key.
|
1012 |
-
*
|
1013 |
-
* @author Vova Feldman (@svovaf)
|
1014 |
-
* @since 1.2.3
|
1015 |
-
*
|
1016 |
-
* @param string $text Translatable string.
|
1017 |
-
* @param string $key String key for overrides.
|
1018 |
-
* @param string $slug Module slug for overrides.
|
1019 |
-
*
|
1020 |
-
* @return string
|
1021 |
-
*
|
1022 |
-
* @global $fs_text_overrides
|
1023 |
-
*/
|
1024 |
-
function fs_text_inline( $text, $key = '', $slug = 'freemius' ) {
|
1025 |
-
return _fs_text_inline( $text, $key, $slug );
|
1026 |
-
}
|
1027 |
-
}
|
1028 |
-
|
1029 |
-
if ( ! function_exists( 'fs_esc_attr' ) ) {
|
1030 |
-
/**
|
1031 |
-
* @author Vova Feldman
|
1032 |
-
* @since 1.2.1.6
|
1033 |
-
*
|
1034 |
-
* @param string $key
|
1035 |
-
* @param string $slug
|
1036 |
-
*
|
1037 |
-
* @return string
|
1038 |
-
*/
|
1039 |
-
function fs_esc_attr( $key, $slug ) {
|
1040 |
-
return esc_attr( fs_text( $key, $slug ) );
|
1041 |
-
}
|
1042 |
-
}
|
1043 |
-
|
1044 |
-
if ( ! function_exists( 'fs_esc_attr_inline' ) ) {
|
1045 |
-
/**
|
1046 |
-
* @author Vova Feldman (@svovaf)
|
1047 |
-
* @since 1.2.3
|
1048 |
-
*
|
1049 |
-
* @param string $text Translatable string.
|
1050 |
-
* @param string $key String key for overrides.
|
1051 |
-
* @param string $slug Module slug for overrides.
|
1052 |
-
*
|
1053 |
-
* @return string
|
1054 |
-
*/
|
1055 |
-
function fs_esc_attr_inline( $text, $key = '', $slug = 'freemius' ) {
|
1056 |
-
return esc_attr( _fs_text_inline( $text, $key, $slug ) );
|
1057 |
-
}
|
1058 |
-
}
|
1059 |
-
|
1060 |
-
if ( ! function_exists( 'fs_esc_attr_x_inline' ) ) {
|
1061 |
-
/**
|
1062 |
-
* @author Vova Feldman (@svovaf)
|
1063 |
-
* @since 1.2.3
|
1064 |
-
*
|
1065 |
-
* @param string $text Translatable string.
|
1066 |
-
* @param string $context Context information for the translators.
|
1067 |
-
* @param string $key String key for overrides.
|
1068 |
-
* @param string $slug Module slug for overrides.
|
1069 |
-
*
|
1070 |
-
* @return string
|
1071 |
-
*/
|
1072 |
-
function fs_esc_attr_x_inline( $text, $context, $key = '', $slug = 'freemius' ) {
|
1073 |
-
return esc_attr( _fs_text_x_inline( $text, $context, $key, $slug ) );
|
1074 |
-
}
|
1075 |
-
}
|
1076 |
-
|
1077 |
-
if ( ! function_exists( 'fs_esc_attr_echo' ) ) {
|
1078 |
-
/**
|
1079 |
-
* @author Vova Feldman
|
1080 |
-
* @since 1.2.1.6
|
1081 |
-
*
|
1082 |
-
* @param string $key
|
1083 |
-
* @param string $slug
|
1084 |
-
*/
|
1085 |
-
function fs_esc_attr_echo( $key, $slug ) {
|
1086 |
-
echo esc_attr( fs_text( $key, $slug ) );
|
1087 |
-
}
|
1088 |
-
}
|
1089 |
-
|
1090 |
-
if ( ! function_exists( 'fs_esc_attr_echo_inline' ) ) {
|
1091 |
-
/**
|
1092 |
-
* @author Vova Feldman (@svovaf)
|
1093 |
-
* @since 1.2.3
|
1094 |
-
*
|
1095 |
-
* @param string $text Translatable string.
|
1096 |
-
* @param string $key String key for overrides.
|
1097 |
-
* @param string $slug Module slug for overrides.
|
1098 |
-
*/
|
1099 |
-
function fs_esc_attr_echo_inline( $text, $key = '', $slug = 'freemius' ) {
|
1100 |
-
echo esc_attr( _fs_text_inline( $text, $key, $slug ) );
|
1101 |
-
}
|
1102 |
-
}
|
1103 |
-
|
1104 |
-
if ( ! function_exists( 'fs_esc_js' ) ) {
|
1105 |
-
/**
|
1106 |
-
* @author Vova Feldman
|
1107 |
-
* @since 1.2.1.6
|
1108 |
-
*
|
1109 |
-
* @param string $key
|
1110 |
-
* @param string $slug
|
1111 |
-
*
|
1112 |
-
* @return string
|
1113 |
-
*/
|
1114 |
-
function fs_esc_js( $key, $slug ) {
|
1115 |
-
return esc_js( fs_text( $key, $slug ) );
|
1116 |
-
}
|
1117 |
-
}
|
1118 |
-
|
1119 |
-
if ( ! function_exists( 'fs_esc_js_inline' ) ) {
|
1120 |
-
/**
|
1121 |
-
* @author Vova Feldman (@svovaf)
|
1122 |
-
* @since 1.2.3
|
1123 |
-
*
|
1124 |
-
* @param string $text Translatable string.
|
1125 |
-
* @param string $key String key for overrides.
|
1126 |
-
* @param string $slug Module slug for overrides.
|
1127 |
-
*
|
1128 |
-
* @return string
|
1129 |
-
*/
|
1130 |
-
function fs_esc_js_inline( $text, $key = '', $slug = 'freemius' ) {
|
1131 |
-
return esc_js( _fs_text_inline( $text, $key, $slug ) );
|
1132 |
-
}
|
1133 |
-
}
|
1134 |
-
|
1135 |
-
if ( ! function_exists( 'fs_esc_js_x_inline' ) ) {
|
1136 |
-
/**
|
1137 |
-
* @author Vova Feldman (@svovaf)
|
1138 |
-
* @since 1.2.3
|
1139 |
-
*
|
1140 |
-
* @param string $text Translatable string.
|
1141 |
-
* @param string $context Context information for the translators.
|
1142 |
-
* @param string $key String key for overrides.
|
1143 |
-
* @param string $slug Module slug for overrides.
|
1144 |
-
*
|
1145 |
-
* @return string
|
1146 |
-
*/
|
1147 |
-
function fs_esc_js_x_inline( $text, $context, $key = '', $slug = 'freemius' ) {
|
1148 |
-
return esc_js( _fs_text_x_inline( $text, $context, $key, $slug ) );
|
1149 |
-
}
|
1150 |
-
}
|
1151 |
-
|
1152 |
-
if ( ! function_exists( 'fs_esc_js_echo_x_inline' ) ) {
|
1153 |
-
/**
|
1154 |
-
* @author Vova Feldman (@svovaf)
|
1155 |
-
* @since 1.2.3
|
1156 |
-
*
|
1157 |
-
* @param string $text Translatable string.
|
1158 |
-
* @param string $context Context information for the translators.
|
1159 |
-
* @param string $key String key for overrides.
|
1160 |
-
* @param string $slug Module slug for overrides.
|
1161 |
-
*
|
1162 |
-
* @return string
|
1163 |
-
*/
|
1164 |
-
function fs_esc_js_echo_x_inline( $text, $context, $key = '', $slug = 'freemius' ) {
|
1165 |
-
echo esc_js( _fs_text_x_inline( $text, $context, $key, $slug ) );
|
1166 |
-
}
|
1167 |
-
}
|
1168 |
-
|
1169 |
-
if ( ! function_exists( 'fs_esc_js_echo' ) ) {
|
1170 |
-
/**
|
1171 |
-
* @author Vova Feldman
|
1172 |
-
* @since 1.2.1.6
|
1173 |
-
*
|
1174 |
-
* @param string $key
|
1175 |
-
* @param string $slug
|
1176 |
-
*/
|
1177 |
-
function fs_esc_js_echo( $key, $slug ) {
|
1178 |
-
echo esc_js( fs_text( $key, $slug ) );
|
1179 |
-
}
|
1180 |
-
}
|
1181 |
-
|
1182 |
-
if ( ! function_exists( 'fs_esc_js_echo_inline' ) ) {
|
1183 |
-
/**
|
1184 |
-
* @author Vova Feldman (@svovaf)
|
1185 |
-
* @since 1.2.3
|
1186 |
-
*
|
1187 |
-
* @param string $text Translatable string.
|
1188 |
-
* @param string $key String key for overrides.
|
1189 |
-
* @param string $slug Module slug for overrides.
|
1190 |
-
*/
|
1191 |
-
function fs_esc_js_echo_inline( $text, $key = '', $slug = 'freemius' ) {
|
1192 |
-
echo esc_js( _fs_text_inline( $text, $key, $slug ) );
|
1193 |
-
}
|
1194 |
-
}
|
1195 |
-
|
1196 |
-
if ( ! function_exists( 'fs_json_encode_echo' ) ) {
|
1197 |
-
/**
|
1198 |
-
* @author Vova Feldman
|
1199 |
-
* @since 1.2.1.6
|
1200 |
-
*
|
1201 |
-
* @param string $key
|
1202 |
-
* @param string $slug
|
1203 |
-
*/
|
1204 |
-
function fs_json_encode_echo( $key, $slug ) {
|
1205 |
-
echo json_encode( fs_text( $key, $slug ) );
|
1206 |
-
}
|
1207 |
-
}
|
1208 |
-
|
1209 |
-
if ( ! function_exists( 'fs_json_encode_echo_inline' ) ) {
|
1210 |
-
/**
|
1211 |
-
* @author Vova Feldman (@svovaf)
|
1212 |
-
* @since 1.2.3
|
1213 |
-
*
|
1214 |
-
* @param string $text Translatable string.
|
1215 |
-
* @param string $key String key for overrides.
|
1216 |
-
* @param string $slug Module slug for overrides.
|
1217 |
-
*/
|
1218 |
-
function fs_json_encode_echo_inline( $text, $key = '', $slug = 'freemius' ) {
|
1219 |
-
echo json_encode( _fs_text_inline( $text, $key, $slug ) );
|
1220 |
-
}
|
1221 |
-
}
|
1222 |
-
|
1223 |
-
if ( ! function_exists( 'fs_esc_html' ) ) {
|
1224 |
-
/**
|
1225 |
-
* @author Vova Feldman
|
1226 |
-
* @since 1.2.1.6
|
1227 |
-
*
|
1228 |
-
* @param string $key
|
1229 |
-
* @param string $slug
|
1230 |
-
*
|
1231 |
-
* @return string
|
1232 |
-
*/
|
1233 |
-
function fs_esc_html( $key, $slug ) {
|
1234 |
-
return esc_html( fs_text( $key, $slug ) );
|
1235 |
-
}
|
1236 |
-
}
|
1237 |
-
|
1238 |
-
if ( ! function_exists( 'fs_esc_html_inline' ) ) {
|
1239 |
-
/**
|
1240 |
-
* @author Vova Feldman (@svovaf)
|
1241 |
-
* @since 1.2.3
|
1242 |
-
*
|
1243 |
-
* @param string $text Translatable string.
|
1244 |
-
* @param string $key String key for overrides.
|
1245 |
-
* @param string $slug Module slug for overrides.
|
1246 |
-
*
|
1247 |
-
* @return string
|
1248 |
-
*/
|
1249 |
-
function fs_esc_html_inline( $text, $key = '', $slug = 'freemius' ) {
|
1250 |
-
return esc_html( _fs_text_inline( $text, $key, $slug ) );
|
1251 |
-
}
|
1252 |
-
}
|
1253 |
-
|
1254 |
-
if ( ! function_exists( 'fs_esc_html_x_inline' ) ) {
|
1255 |
-
/**
|
1256 |
-
* @author Vova Feldman (@svovaf)
|
1257 |
-
* @since 1.2.3
|
1258 |
-
*
|
1259 |
-
* @param string $text Translatable string.
|
1260 |
-
* @param string $context Context information for the translators.
|
1261 |
-
* @param string $key String key for overrides.
|
1262 |
-
* @param string $slug Module slug for overrides.
|
1263 |
-
*
|
1264 |
-
* @return string
|
1265 |
-
*/
|
1266 |
-
function fs_esc_html_x_inline( $text, $context, $key = '', $slug = 'freemius' ) {
|
1267 |
-
return esc_html( _fs_text_x_inline( $text, $context, $key, $slug ) );
|
1268 |
-
}
|
1269 |
-
}
|
1270 |
-
|
1271 |
-
if ( ! function_exists( 'fs_esc_html_echo_x_inline' ) ) {
|
1272 |
-
/**
|
1273 |
-
* @author Vova Feldman (@svovaf)
|
1274 |
-
* @since 1.2.3
|
1275 |
-
*
|
1276 |
-
* @param string $text Translatable string.
|
1277 |
-
* @param string $context Context information for the translators.
|
1278 |
-
* @param string $key String key for overrides.
|
1279 |
-
* @param string $slug Module slug for overrides.
|
1280 |
-
*/
|
1281 |
-
function fs_esc_html_echo_x_inline( $text, $context, $key = '', $slug = 'freemius' ) {
|
1282 |
-
echo esc_html( _fs_text_x_inline( $text, $context, $key, $slug ) );
|
1283 |
-
}
|
1284 |
-
}
|
1285 |
-
|
1286 |
-
if ( ! function_exists( 'fs_esc_html_echo' ) ) {
|
1287 |
-
/**
|
1288 |
-
* @author Vova Feldman
|
1289 |
-
* @since 1.2.1.6
|
1290 |
-
*
|
1291 |
-
* @param string $key
|
1292 |
-
* @param string $slug
|
1293 |
-
*/
|
1294 |
-
function fs_esc_html_echo( $key, $slug ) {
|
1295 |
-
echo esc_html( fs_text( $key, $slug ) );
|
1296 |
-
}
|
1297 |
-
}
|
1298 |
-
|
1299 |
-
if ( ! function_exists( 'fs_esc_html_echo_inline' ) ) {
|
1300 |
-
/**
|
1301 |
-
* @author Vova Feldman (@svovaf)
|
1302 |
-
* @since 1.2.3
|
1303 |
-
*
|
1304 |
-
* @param string $text Translatable string.
|
1305 |
-
* @param string $key String key for overrides.
|
1306 |
-
* @param string $slug Module slug for overrides.
|
1307 |
-
*/
|
1308 |
-
function fs_esc_html_echo_inline( $text, $key = '', $slug = 'freemius' ) {
|
1309 |
-
echo esc_html( _fs_text_inline( $text, $key, $slug ) );
|
1310 |
-
}
|
1311 |
-
}
|
1312 |
-
|
1313 |
-
if ( ! function_exists( 'fs_override_i18n' ) ) {
|
1314 |
-
/**
|
1315 |
-
* Override default i18n text phrases.
|
1316 |
-
*
|
1317 |
-
* @author Vova Feldman (@svovaf)
|
1318 |
-
* @since 1.1.6
|
1319 |
-
*
|
1320 |
-
* @param array[string]string $key_value
|
1321 |
-
* @param string $slug
|
1322 |
-
*
|
1323 |
-
* @global $fs_text_overrides
|
1324 |
-
*/
|
1325 |
-
function fs_override_i18n( array $key_value, $slug = 'freemius' ) {
|
1326 |
-
global $fs_text_overrides;
|
1327 |
-
|
1328 |
-
if ( ! isset( $fs_text_overrides[ $slug ] ) ) {
|
1329 |
-
$fs_text_overrides[ $slug ] = array();
|
1330 |
-
}
|
1331 |
-
|
1332 |
-
foreach ( $key_value as $key => $value ) {
|
1333 |
-
$fs_text_overrides[ $slug ][ $key ] = $value;
|
1334 |
-
}
|
1335 |
-
}
|
1336 |
-
}
|
1337 |
-
|
1338 |
-
#endregion
|
1339 |
-
|
1340 |
-
#--------------------------------------------------------------------------------
|
1341 |
-
#region Multisite Network
|
1342 |
-
#--------------------------------------------------------------------------------
|
1343 |
-
|
1344 |
-
if ( ! function_exists( 'fs_is_plugin_uninstall' ) ) {
|
1345 |
-
/**
|
1346 |
-
* @author Vova Feldman (@svovaf)
|
1347 |
-
* @since 2.0.0
|
1348 |
-
*/
|
1349 |
-
function fs_is_plugin_uninstall() {
|
1350 |
-
return (
|
1351 |
-
defined( 'WP_UNINSTALL_PLUGIN' ) ||
|
1352 |
-
( 0 < did_action( 'update_option_uninstall_plugins' ) )
|
1353 |
-
);
|
1354 |
-
}
|
1355 |
-
}
|
1356 |
-
|
1357 |
-
if ( ! function_exists( 'fs_is_network_admin' ) ) {
|
1358 |
-
/**
|
1359 |
-
* Unlike is_network_admin(), this one will also work properly when
|
1360 |
-
* the context execution is WP AJAX handler, and during plugin
|
1361 |
-
* uninstall.
|
1362 |
-
*
|
1363 |
-
* @author Vova Feldman (@svovaf)
|
1364 |
-
* @since 2.0.0
|
1365 |
-
*/
|
1366 |
-
function fs_is_network_admin() {
|
1367 |
-
return (
|
1368 |
-
WP_FS__IS_NETWORK_ADMIN ||
|
1369 |
-
( is_multisite() && fs_is_plugin_uninstall() )
|
1370 |
-
);
|
1371 |
-
}
|
1372 |
-
}
|
1373 |
-
|
1374 |
-
if ( ! function_exists( 'fs_is_blog_admin' ) ) {
|
1375 |
-
/**
|
1376 |
-
* Unlike is_blog_admin(), this one will also work properly when
|
1377 |
-
* the context execution is WP AJAX handler, and during plugin
|
1378 |
-
* uninstall.
|
1379 |
-
*
|
1380 |
-
* @author Vova Feldman (@svovaf)
|
1381 |
-
* @since 2.0.0
|
1382 |
-
*/
|
1383 |
-
function fs_is_blog_admin() {
|
1384 |
-
return (
|
1385 |
-
WP_FS__IS_BLOG_ADMIN ||
|
1386 |
-
( ! is_multisite() && fs_is_plugin_uninstall() )
|
1387 |
-
);
|
1388 |
-
}
|
1389 |
-
}
|
1390 |
-
|
1391 |
-
#endregion
|
1392 |
-
|
1393 |
-
if ( ! function_exists( 'fs_apply_filter' ) ) {
|
1394 |
-
/**
|
1395 |
-
* Apply filter for specific plugin.
|
1396 |
-
*
|
1397 |
-
* @author Vova Feldman (@svovaf)
|
1398 |
-
* @since 1.0.9
|
1399 |
-
*
|
1400 |
-
* @param string $module_unique_affix Module's unique affix.
|
1401 |
-
* @param string $tag The name of the filter hook.
|
1402 |
-
* @param mixed $value The value on which the filters hooked to `$tag` are applied on.
|
1403 |
-
*
|
1404 |
-
* @return mixed The filtered value after all hooked functions are applied to it.
|
1405 |
-
*
|
1406 |
-
* @uses apply_filters()
|
1407 |
-
*/
|
1408 |
-
function fs_apply_filter( $module_unique_affix, $tag, $value ) {
|
1409 |
-
$args = func_get_args();
|
1410 |
-
|
1411 |
-
return call_user_func_array( 'apply_filters', array_merge(
|
1412 |
-
array( "fs_{$tag}_{$module_unique_affix}" ),
|
1413 |
-
array_slice( $args, 2 ) )
|
1414 |
-
);
|
1415 |
-
}
|
1416 |
}
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package Freemius
|
4 |
+
* @copyright Copyright (c) 2015, Freemius, Inc.
|
5 |
+
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
|
6 |
+
* @since 1.0.3
|
7 |
+
*/
|
8 |
+
|
9 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
10 |
+
exit;
|
11 |
+
}
|
12 |
+
|
13 |
+
if ( ! function_exists( 'fs_dummy' ) ) {
|
14 |
+
function fs_dummy() {
|
15 |
+
}
|
16 |
+
}
|
17 |
+
|
18 |
+
/* Url.
|
19 |
+
--------------------------------------------------------------------------------------------*/
|
20 |
+
if ( ! function_exists( 'fs_get_url_daily_cache_killer' ) ) {
|
21 |
+
function fs_get_url_daily_cache_killer() {
|
22 |
+
return date( '\YY\Mm\Dd' );
|
23 |
+
}
|
24 |
+
}
|
25 |
+
|
26 |
+
/* Templates / Views.
|
27 |
+
--------------------------------------------------------------------------------------------*/
|
28 |
+
if ( ! function_exists( 'fs_get_template_path' ) ) {
|
29 |
+
function fs_get_template_path( $path ) {
|
30 |
+
return WP_FS__DIR_TEMPLATES . '/' . trim( $path, '/' );
|
31 |
+
}
|
32 |
+
|
33 |
+
function fs_include_template( $path, &$params = null ) {
|
34 |
+
$VARS = &$params;
|
35 |
+
include fs_get_template_path( $path );
|
36 |
+
}
|
37 |
+
|
38 |
+
function fs_include_once_template( $path, &$params = null ) {
|
39 |
+
$VARS = &$params;
|
40 |
+
include_once fs_get_template_path( $path );
|
41 |
+
}
|
42 |
+
|
43 |
+
function fs_require_template( $path, &$params = null ) {
|
44 |
+
$VARS = &$params;
|
45 |
+
require fs_get_template_path( $path );
|
46 |
+
}
|
47 |
+
|
48 |
+
function fs_require_once_template( $path, &$params = null ) {
|
49 |
+
$VARS = &$params;
|
50 |
+
require_once fs_get_template_path( $path );
|
51 |
+
}
|
52 |
+
|
53 |
+
function fs_get_template( $path, &$params = null ) {
|
54 |
+
ob_start();
|
55 |
+
|
56 |
+
$VARS = &$params;
|
57 |
+
require fs_get_template_path( $path );
|
58 |
+
|
59 |
+
return ob_get_clean();
|
60 |
+
}
|
61 |
+
}
|
62 |
+
|
63 |
+
/* Scripts and styles including.
|
64 |
+
--------------------------------------------------------------------------------------------*/
|
65 |
+
|
66 |
+
if ( ! function_exists( 'fs_asset_url' ) ) {
|
67 |
+
/**
|
68 |
+
* Generates an absolute URL to the given path. This function ensures that the URL will be correct whether the asset
|
69 |
+
* is inside a plugin's folder or a theme's folder.
|
70 |
+
*
|
71 |
+
* Examples:
|
72 |
+
* 1. "themes" folder
|
73 |
+
* Path: C:/xampp/htdocs/fswp/wp-content/themes/twentytwelve/freemius/assets/css/admin/common.css
|
74 |
+
* URL: http://fswp:8080/wp-content/themes/twentytwelve/freemius/assets/css/admin/common.css
|
75 |
+
*
|
76 |
+
* 2. "plugins" folder
|
77 |
+
* Path: C:/xampp/htdocs/fswp/wp-content/plugins/rating-widget-premium/freemius/assets/css/admin/common.css
|
78 |
+
* URL: http://fswp:8080/wp-content/plugins/rating-widget-premium/freemius/assets/css/admin/common.css
|
79 |
+
*
|
80 |
+
* @author Leo Fajardo (@leorw)
|
81 |
+
* @since 1.2.2
|
82 |
+
*
|
83 |
+
* @param string $asset_abs_path Asset's absolute path.
|
84 |
+
*
|
85 |
+
* @return string Asset's URL.
|
86 |
+
*/
|
87 |
+
function fs_asset_url( $asset_abs_path ) {
|
88 |
+
$wp_content_dir = fs_normalize_path( WP_CONTENT_DIR );
|
89 |
+
$asset_abs_path = fs_normalize_path( $asset_abs_path );
|
90 |
+
|
91 |
+
if ( 0 === strpos( $asset_abs_path, $wp_content_dir ) ) {
|
92 |
+
// Handle both theme and plugin assets located in the standard directories.
|
93 |
+
$asset_rel_path = str_replace( $wp_content_dir, '', $asset_abs_path );
|
94 |
+
$asset_url = content_url( fs_normalize_path( $asset_rel_path ) );
|
95 |
+
} else {
|
96 |
+
$wp_plugins_dir = fs_normalize_path( WP_PLUGIN_DIR );
|
97 |
+
if ( 0 === strpos( $asset_abs_path, $wp_plugins_dir ) ) {
|
98 |
+
// Try to handle plugin assets that may be located in a non-standard plugins directory.
|
99 |
+
$asset_rel_path = str_replace( $wp_plugins_dir, '', $asset_abs_path );
|
100 |
+
$asset_url = plugins_url( fs_normalize_path( $asset_rel_path ) );
|
101 |
+
} else {
|
102 |
+
// Try to handle theme assets that may be located in a non-standard themes directory.
|
103 |
+
$active_theme_stylesheet = get_stylesheet();
|
104 |
+
$wp_themes_dir = fs_normalize_path( trailingslashit( get_theme_root( $active_theme_stylesheet ) ) );
|
105 |
+
$asset_rel_path = str_replace( $wp_themes_dir, '', fs_normalize_path( $asset_abs_path ) );
|
106 |
+
$asset_url = trailingslashit( get_theme_root_uri( $active_theme_stylesheet ) ) . fs_normalize_path( $asset_rel_path );
|
107 |
+
}
|
108 |
+
}
|
109 |
+
|
110 |
+
return $asset_url;
|
111 |
+
}
|
112 |
+
}
|
113 |
+
|
114 |
+
if ( ! function_exists( 'fs_enqueue_local_style' ) ) {
|
115 |
+
function fs_enqueue_local_style( $handle, $path, $deps = array(), $ver = false, $media = 'all' ) {
|
116 |
+
wp_enqueue_style( $handle, fs_asset_url( WP_FS__DIR_CSS . '/' . trim( $path, '/' ) ), $deps, $ver, $media );
|
117 |
+
}
|
118 |
+
}
|
119 |
+
|
120 |
+
if ( ! function_exists( 'fs_enqueue_local_script' ) ) {
|
121 |
+
function fs_enqueue_local_script( $handle, $path, $deps = array(), $ver = false, $in_footer = 'all' ) {
|
122 |
+
wp_enqueue_script( $handle, fs_asset_url( WP_FS__DIR_JS . '/' . trim( $path, '/' ) ), $deps, $ver, $in_footer );
|
123 |
+
}
|
124 |
+
}
|
125 |
+
|
126 |
+
if ( ! function_exists( 'fs_img_url' ) ) {
|
127 |
+
function fs_img_url( $path, $img_dir = WP_FS__DIR_IMG ) {
|
128 |
+
return ( fs_asset_url( $img_dir . '/' . trim( $path, '/' ) ) );
|
129 |
+
}
|
130 |
+
}
|
131 |
+
|
132 |
+
#--------------------------------------------------------------------------------
|
133 |
+
#region Request handlers.
|
134 |
+
#--------------------------------------------------------------------------------
|
135 |
+
|
136 |
+
if ( ! function_exists( 'fs_request_get' ) ) {
|
137 |
+
/**
|
138 |
+
* A helper method to fetch GET/POST user input with an optional default value when the input is not set.
|
139 |
+
* @author Vova Feldman (@svovaf)
|
140 |
+
*
|
141 |
+
* @param string $key
|
142 |
+
* @param mixed $def
|
143 |
+
* @param string|bool $type Since 1.2.1.7 - when set to 'get' will look for the value passed via querystring, when
|
144 |
+
* set to 'post' will look for the value passed via the POST request's body, otherwise,
|
145 |
+
* will check if the parameter was passed in any of the two.
|
146 |
+
*
|
147 |
+
* @return mixed
|
148 |
+
*/
|
149 |
+
function fs_request_get( $key, $def = false, $type = false ) {
|
150 |
+
if ( is_string( $type ) ) {
|
151 |
+
$type = strtolower( $type );
|
152 |
+
}
|
153 |
+
|
154 |
+
/**
|
155 |
+
* Note to WordPress.org Reviewers:
|
156 |
+
* This is a helper method to fetch GET/POST user input with an optional default value when the input is not set. The actual sanitization is done in the scope of the function's usage.
|
157 |
+
*/
|
158 |
+
switch ( $type ) {
|
159 |
+
case 'post':
|
160 |
+
$value = isset( $_POST[ $key ] ) ? $_POST[ $key ] : $def;
|
161 |
+
break;
|
162 |
+
case 'get':
|
163 |
+
$value = isset( $_GET[ $key ] ) ? $_GET[ $key ] : $def;
|
164 |
+
break;
|
165 |
+
default:
|
166 |
+
$value = isset( $_REQUEST[ $key ] ) ? $_REQUEST[ $key ] : $def;
|
167 |
+
break;
|
168 |
+
}
|
169 |
+
|
170 |
+
return $value;
|
171 |
+
}
|
172 |
+
}
|
173 |
+
|
174 |
+
if ( ! function_exists( 'fs_request_has' ) ) {
|
175 |
+
function fs_request_has( $key ) {
|
176 |
+
return isset( $_REQUEST[ $key ] );
|
177 |
+
}
|
178 |
+
}
|
179 |
+
|
180 |
+
if ( ! function_exists( 'fs_request_get_bool' ) ) {
|
181 |
+
/**
|
182 |
+
* A helper method to fetch GET/POST user boolean input with an optional default value when the input is not set.
|
183 |
+
*
|
184 |
+
* @author Vova Feldman (@svovaf)
|
185 |
+
*
|
186 |
+
* @param string $key
|
187 |
+
* @param bool $def
|
188 |
+
*
|
189 |
+
* @return bool|mixed
|
190 |
+
*/
|
191 |
+
function fs_request_get_bool( $key, $def = false ) {
|
192 |
+
$val = fs_request_get( $key, null );
|
193 |
+
|
194 |
+
if ( is_null( $val ) ) {
|
195 |
+
return $def;
|
196 |
+
}
|
197 |
+
|
198 |
+
if ( is_bool( $val ) ) {
|
199 |
+
return $val;
|
200 |
+
} else if ( is_numeric( $val ) ) {
|
201 |
+
if ( 1 == $val ) {
|
202 |
+
return true;
|
203 |
+
} else if ( 0 == $val ) {
|
204 |
+
return false;
|
205 |
+
}
|
206 |
+
} else if ( is_string( $val ) ) {
|
207 |
+
$val = strtolower( $val );
|
208 |
+
|
209 |
+
if ( 'true' === $val ) {
|
210 |
+
return true;
|
211 |
+
} else if ( 'false' === $val ) {
|
212 |
+
return false;
|
213 |
+
}
|
214 |
+
}
|
215 |
+
|
216 |
+
return $def;
|
217 |
+
}
|
218 |
+
}
|
219 |
+
|
220 |
+
if ( ! function_exists( 'fs_request_is_post' ) ) {
|
221 |
+
function fs_request_is_post() {
|
222 |
+
return ( 'post' === strtolower( $_SERVER['REQUEST_METHOD'] ) );
|
223 |
+
}
|
224 |
+
}
|
225 |
+
|
226 |
+
if ( ! function_exists( 'fs_request_is_get' ) ) {
|
227 |
+
function fs_request_is_get() {
|
228 |
+
return ( 'get' === strtolower( $_SERVER['REQUEST_METHOD'] ) );
|
229 |
+
}
|
230 |
+
}
|
231 |
+
|
232 |
+
if ( ! function_exists( 'fs_get_action' ) ) {
|
233 |
+
function fs_get_action( $action_key = 'action' ) {
|
234 |
+
if ( ! empty( $_REQUEST[ $action_key ] ) && is_string( $_REQUEST[ $action_key ] ) ) {
|
235 |
+
return strtolower( $_REQUEST[ $action_key ] );
|
236 |
+
}
|
237 |
+
|
238 |
+
if ( 'action' == $action_key ) {
|
239 |
+
$action_key = 'fs_action';
|
240 |
+
|
241 |
+
if ( ! empty( $_REQUEST[ $action_key ] ) && is_string( $_REQUEST[ $action_key ] ) ) {
|
242 |
+
return strtolower( $_REQUEST[ $action_key ] );
|
243 |
+
}
|
244 |
+
}
|
245 |
+
|
246 |
+
return false;
|
247 |
+
}
|
248 |
+
}
|
249 |
+
|
250 |
+
if ( ! function_exists( 'fs_request_is_action' ) ) {
|
251 |
+
function fs_request_is_action( $action, $action_key = 'action' ) {
|
252 |
+
return ( strtolower( $action ) === fs_get_action( $action_key ) );
|
253 |
+
}
|
254 |
+
}
|
255 |
+
|
256 |
+
if ( ! function_exists( 'fs_request_is_action_secure' ) ) {
|
257 |
+
/**
|
258 |
+
* @author Vova Feldman (@svovaf)
|
259 |
+
* @since 1.0.0
|
260 |
+
*
|
261 |
+
* @since 1.2.1.5 Allow nonce verification.
|
262 |
+
*
|
263 |
+
* @param string $action
|
264 |
+
* @param string $action_key
|
265 |
+
* @param string $nonce_key
|
266 |
+
*
|
267 |
+
* @return bool
|
268 |
+
*/
|
269 |
+
function fs_request_is_action_secure(
|
270 |
+
$action,
|
271 |
+
$action_key = 'action',
|
272 |
+
$nonce_key = 'nonce'
|
273 |
+
) {
|
274 |
+
if ( strtolower( $action ) !== fs_get_action( $action_key ) ) {
|
275 |
+
return false;
|
276 |
+
}
|
277 |
+
|
278 |
+
$nonce = ! empty( $_REQUEST[ $nonce_key ] ) ?
|
279 |
+
$_REQUEST[ $nonce_key ] :
|
280 |
+
'';
|
281 |
+
|
282 |
+
if ( empty( $nonce ) ||
|
283 |
+
( false === wp_verify_nonce( $nonce, $action ) )
|
284 |
+
) {
|
285 |
+
return false;
|
286 |
+
}
|
287 |
+
|
288 |
+
return true;
|
289 |
+
}
|
290 |
+
}
|
291 |
+
|
292 |
+
#endregion
|
293 |
+
|
294 |
+
if ( ! function_exists( 'fs_is_plugin_page' ) ) {
|
295 |
+
function fs_is_plugin_page( $page_slug ) {
|
296 |
+
return ( is_admin() && $page_slug === fs_request_get( 'page' ) );
|
297 |
+
}
|
298 |
+
}
|
299 |
+
|
300 |
+
if ( ! function_exists( 'fs_get_raw_referer' ) ) {
|
301 |
+
/**
|
302 |
+
* Retrieves unvalidated referer from '_wp_http_referer' or HTTP referer.
|
303 |
+
*
|
304 |
+
* Do not use for redirects, use {@see wp_get_referer()} instead.
|
305 |
+
*
|
306 |
+
* @since 1.2.3
|
307 |
+
*
|
308 |
+
* @return string|false Referer URL on success, false on failure.
|
309 |
+
*/
|
310 |
+
function fs_get_raw_referer() {
|
311 |
+
if ( function_exists( 'wp_get_raw_referer' ) ) {
|
312 |
+
return wp_get_raw_referer();
|
313 |
+
}
|
314 |
+
if ( ! empty( $_REQUEST['_wp_http_referer'] ) ) {
|
315 |
+
return wp_unslash( $_REQUEST['_wp_http_referer'] );
|
316 |
+
} else if ( ! empty( $_SERVER['HTTP_REFERER'] ) ) {
|
317 |
+
return wp_unslash( $_SERVER['HTTP_REFERER'] );
|
318 |
+
}
|
319 |
+
|
320 |
+
return false;
|
321 |
+
}
|
322 |
+
}
|
323 |
+
|
324 |
+
/* Core UI.
|
325 |
+
--------------------------------------------------------------------------------------------*/
|
326 |
+
if ( ! function_exists( 'fs_ui_action_button' ) ) {
|
327 |
+
/**
|
328 |
+
* @param number $module_id
|
329 |
+
* @param string $page
|
330 |
+
* @param string $action
|
331 |
+
* @param string $title
|
332 |
+
* @param string $button_class
|
333 |
+
* @param array $params
|
334 |
+
* @param bool $is_primary
|
335 |
+
* @param bool $is_small
|
336 |
+
* @param string|bool $icon_class Optional class for an icon (since 1.1.7).
|
337 |
+
* @param string|bool $confirmation Optional confirmation message before submit (since 1.1.7).
|
338 |
+
* @param string $method Since 1.1.7
|
339 |
+
*
|
340 |
+
* @uses fs_ui_get_action_button()
|
341 |
+
*/
|
342 |
+
function fs_ui_action_button(
|
343 |
+
$module_id,
|
344 |
+
$page,
|
345 |
+
$action,
|
346 |
+
$title,
|
347 |
+
$button_class = '',
|
348 |
+
$params = array(),
|
349 |
+
$is_primary = true,
|
350 |
+
$is_small = false,
|
351 |
+
$icon_class = false,
|
352 |
+
$confirmation = false,
|
353 |
+
$method = 'GET'
|
354 |
+
) {
|
355 |
+
echo fs_ui_get_action_button(
|
356 |
+
$module_id,
|
357 |
+
$page,
|
358 |
+
$action,
|
359 |
+
$title,
|
360 |
+
$button_class,
|
361 |
+
$params,
|
362 |
+
$is_primary,
|
363 |
+
$is_small,
|
364 |
+
$icon_class,
|
365 |
+
$confirmation,
|
366 |
+
$method
|
367 |
+
);
|
368 |
+
}
|
369 |
+
}
|
370 |
+
|
371 |
+
if ( ! function_exists( 'fs_ui_get_action_button' ) ) {
|
372 |
+
/**
|
373 |
+
* @author Vova Feldman (@svovaf)
|
374 |
+
* @since 1.1.7
|
375 |
+
*
|
376 |
+
* @param number $module_id
|
377 |
+
* @param string $page
|
378 |
+
* @param string $action
|
379 |
+
* @param string $title
|
380 |
+
* @param string $button_class
|
381 |
+
* @param array $params
|
382 |
+
* @param bool $is_primary
|
383 |
+
* @param bool $is_small
|
384 |
+
* @param string|bool $icon_class Optional class for an icon.
|
385 |
+
* @param string|bool $confirmation Optional confirmation message before submit.
|
386 |
+
* @param string $method
|
387 |
+
*
|
388 |
+
* @return string
|
389 |
+
*/
|
390 |
+
function fs_ui_get_action_button(
|
391 |
+
$module_id,
|
392 |
+
$page,
|
393 |
+
$action,
|
394 |
+
$title,
|
395 |
+
$button_class = '',
|
396 |
+
$params = array(),
|
397 |
+
$is_primary = true,
|
398 |
+
$is_small = false,
|
399 |
+
$icon_class = false,
|
400 |
+
$confirmation = false,
|
401 |
+
$method = 'GET'
|
402 |
+
) {
|
403 |
+
// Prepend icon (if set).
|
404 |
+
$title = ( is_string( $icon_class ) ? '<i class="' . $icon_class . '"></i> ' : '' ) . $title;
|
405 |
+
|
406 |
+
if ( is_string( $confirmation ) ) {
|
407 |
+
return sprintf( '<form action="%s" method="%s"><input type="hidden" name="fs_action" value="%s">%s<a href="#" class="%s" onclick="if (confirm(\'%s\')) this.parentNode.submit(); return false;">%s</a></form>',
|
408 |
+
freemius( $module_id )->_get_admin_page_url( $page, $params ),
|
409 |
+
$method,
|
410 |
+
$action,
|
411 |
+
wp_nonce_field( $action, '_wpnonce', true, false ),
|
412 |
+
'button' . ( ! empty( $button_class ) ? ' ' . $button_class : '' ) . ( $is_primary ? ' button-primary' : '' ) . ( $is_small ? ' button-small' : '' ),
|
413 |
+
$confirmation,
|
414 |
+
$title
|
415 |
+
);
|
416 |
+
} else if ( 'GET' !== strtoupper( $method ) ) {
|
417 |
+
return sprintf( '<form action="%s" method="%s"><input type="hidden" name="fs_action" value="%s">%s<a href="#" class="%s" onclick="this.parentNode.submit(); return false;">%s</a></form>',
|
418 |
+
freemius( $module_id )->_get_admin_page_url( $page, $params ),
|
419 |
+
$method,
|
420 |
+
$action,
|
421 |
+
wp_nonce_field( $action, '_wpnonce', true, false ),
|
422 |
+
'button' . ( ! empty( $button_class ) ? ' ' . $button_class : '' ) . ( $is_primary ? ' button-primary' : '' ) . ( $is_small ? ' button-small' : '' ),
|
423 |
+
$title
|
424 |
+
);
|
425 |
+
} else {
|
426 |
+
return sprintf( '<a href="%s" class="%s">%s</a></form>',
|
427 |
+
wp_nonce_url( freemius( $module_id )->_get_admin_page_url( $page, array_merge( $params, array( 'fs_action' => $action ) ) ), $action ),
|
428 |
+
'button' . ( ! empty( $button_class ) ? ' ' . $button_class : '' ) . ( $is_primary ? ' button-primary' : '' ) . ( $is_small ? ' button-small' : '' ),
|
429 |
+
$title
|
430 |
+
);
|
431 |
+
}
|
432 |
+
}
|
433 |
+
|
434 |
+
function fs_ui_action_link( $module_id, $page, $action, $title, $params = array() ) {
|
435 |
+
?><a class=""
|
436 |
+
href="<?php echo wp_nonce_url( freemius( $module_id )->_get_admin_page_url( $page, array_merge( $params, array( 'fs_action' => $action ) ) ), $action ) ?>"><?php echo $title ?></a><?php
|
437 |
+
}
|
438 |
+
}
|
439 |
+
|
440 |
+
if ( ! function_exists( 'fs_get_entity' ) ) {
|
441 |
+
/**
|
442 |
+
* @author Leo Fajardo (@leorw)
|
443 |
+
* @since 2.3.1
|
444 |
+
*
|
445 |
+
* @param mixed $entity
|
446 |
+
* @param string $class
|
447 |
+
*
|
448 |
+
* @return FS_Plugin|FS_User|FS_Site|FS_Plugin_License|FS_Plugin_Plan|FS_Plugin_Tag|FS_Subscription
|
449 |
+
*/
|
450 |
+
function fs_get_entity( $entity, $class ) {
|
451 |
+
if ( ! is_object( $entity ) || $entity instanceof $class ) {
|
452 |
+
return $entity;
|
453 |
+
}
|
454 |
+
|
455 |
+
return new $class( $entity );
|
456 |
+
}
|
457 |
+
}
|
458 |
+
|
459 |
+
if ( ! function_exists( 'fs_get_entities' ) ) {
|
460 |
+
/**
|
461 |
+
* @author Leo Fajardo (@leorw)
|
462 |
+
* @since 2.3.1
|
463 |
+
*
|
464 |
+
* @param mixed $entities
|
465 |
+
* @param string $class_name
|
466 |
+
*
|
467 |
+
* @return FS_Plugin[]|FS_User[]|FS_Site[]|FS_Plugin_License[]|FS_Plugin_Plan[]|FS_Plugin_Tag[]|FS_Subscription[]
|
468 |
+
*/
|
469 |
+
function fs_get_entities( $entities, $class_name ) {
|
470 |
+
if ( ! is_array( $entities ) || empty( $entities ) ) {
|
471 |
+
return $entities;
|
472 |
+
}
|
473 |
+
|
474 |
+
// Get first element.
|
475 |
+
$first_array_element = reset( $entities );
|
476 |
+
|
477 |
+
if ( $first_array_element instanceof $class_name ) {
|
478 |
+
/**
|
479 |
+
* If the first element of the array is an instance of the context class, assume that all other
|
480 |
+
* elements are instances of the class.
|
481 |
+
*/
|
482 |
+
return $entities;
|
483 |
+
}
|
484 |
+
|
485 |
+
if (
|
486 |
+
is_array( $first_array_element ) &&
|
487 |
+
! empty( $first_array_element )
|
488 |
+
) {
|
489 |
+
$first_array_element = reset( $first_array_element );
|
490 |
+
|
491 |
+
if ( $first_array_element instanceof $class_name ) {
|
492 |
+
/**
|
493 |
+
* If the first element of the `$entities` array is an array whose first element is an instance of the
|
494 |
+
* context class, assume that all other objects are instances of the class.
|
495 |
+
*/
|
496 |
+
return $entities;
|
497 |
+
}
|
498 |
+
}
|
499 |
+
|
500 |
+
foreach ( $entities as $key => $entities_or_entity ) {
|
501 |
+
if ( is_array( $entities_or_entity ) ) {
|
502 |
+
$entities[ $key ] = fs_get_entities( $entities_or_entity, $class_name );
|
503 |
+
} else {
|
504 |
+
$entities[ $key ] = fs_get_entity( $entities_or_entity, $class_name );
|
505 |
+
}
|
506 |
+
}
|
507 |
+
|
508 |
+
return $entities;
|
509 |
+
}
|
510 |
+
}
|
511 |
+
|
512 |
+
if ( ! function_exists( 'fs_nonce_url' ) ) {
|
513 |
+
/**
|
514 |
+
* Retrieve URL with nonce added to URL query.
|
515 |
+
*
|
516 |
+
* Originally was using `wp_nonce_url()` but the new version
|
517 |
+
* changed the return value to escaped URL, that's not the expected
|
518 |
+
* behaviour.
|
519 |
+
*
|
520 |
+
* @author Vova Feldman (@svovaf)
|
521 |
+
* @since ~1.1.3
|
522 |
+
*
|
523 |
+
* @param string $actionurl URL to add nonce action.
|
524 |
+
* @param int|string $action Optional. Nonce action name. Default -1.
|
525 |
+
* @param string $name Optional. Nonce name. Default '_wpnonce'.
|
526 |
+
*
|
527 |
+
* @return string Escaped URL with nonce action added.
|
528 |
+
*/
|
529 |
+
function fs_nonce_url( $actionurl, $action = - 1, $name = '_wpnonce' ) {
|
530 |
+
return add_query_arg( $name, wp_create_nonce( $action ), $actionurl );
|
531 |
+
}
|
532 |
+
}
|
533 |
+
|
534 |
+
if ( ! function_exists( 'fs_starts_with' ) ) {
|
535 |
+
/**
|
536 |
+
* Check if string starts with.
|
537 |
+
*
|
538 |
+
* @author Vova Feldman (@svovaf)
|
539 |
+
* @since 1.1.3
|
540 |
+
*
|
541 |
+
* @param string $haystack
|
542 |
+
* @param string $needle
|
543 |
+
*
|
544 |
+
* @return bool
|
545 |
+
*/
|
546 |
+
function fs_starts_with( $haystack, $needle ) {
|
547 |
+
$length = strlen( $needle );
|
548 |
+
|
549 |
+
return ( substr( $haystack, 0, $length ) === $needle );
|
550 |
+
}
|
551 |
+
}
|
552 |
+
|
553 |
+
if ( ! function_exists( 'fs_ends_with' ) ) {
|
554 |
+
/**
|
555 |
+
* Check if string ends with.
|
556 |
+
*
|
557 |
+
* @author Vova Feldman (@svovaf)
|
558 |
+
* @since 2.0.0
|
559 |
+
*
|
560 |
+
* @param string $haystack
|
561 |
+
* @param string $needle
|
562 |
+
*
|
563 |
+
* @return bool
|
564 |
+
*/
|
565 |
+
function fs_ends_with( $haystack, $needle ) {
|
566 |
+
$length = strlen( $needle );
|
567 |
+
$start = $length * - 1; // negative
|
568 |
+
|
569 |
+
return ( substr( $haystack, $start ) === $needle );
|
570 |
+
}
|
571 |
+
}
|
572 |
+
|
573 |
+
if ( ! function_exists( 'fs_strip_url_protocol' ) ) {
|
574 |
+
function fs_strip_url_protocol( $url ) {
|
575 |
+
if ( ! fs_starts_with( $url, 'http' ) ) {
|
576 |
+
return $url;
|
577 |
+
}
|
578 |
+
|
579 |
+
$protocol_pos = strpos( $url, '://' );
|
580 |
+
|
581 |
+
if ( $protocol_pos > 5 ) {
|
582 |
+
return $url;
|
583 |
+
}
|
584 |
+
|
585 |
+
return substr( $url, $protocol_pos + 3 );
|
586 |
+
}
|
587 |
+
}
|
588 |
+
|
589 |
+
#region Url Canonization ------------------------------------------------------------------
|
590 |
+
|
591 |
+
if ( ! function_exists( 'fs_canonize_url' ) ) {
|
592 |
+
/**
|
593 |
+
* @author Vova Feldman (@svovaf)
|
594 |
+
* @since 1.1.3
|
595 |
+
*
|
596 |
+
* @param string $url
|
597 |
+
* @param bool $omit_host
|
598 |
+
* @param array $ignore_params
|
599 |
+
*
|
600 |
+
* @return string
|
601 |
+
*/
|
602 |
+
function fs_canonize_url( $url, $omit_host = false, $ignore_params = array() ) {
|
603 |
+
$parsed_url = parse_url( strtolower( $url ) );
|
604 |
+
|
605 |
+
// if ( ! isset( $parsed_url['host'] ) ) {
|
606 |
+
// return $url;
|
607 |
+
// }
|
608 |
+
|
609 |
+
$canonical = ( ( $omit_host || ! isset( $parsed_url['host'] ) ) ? '' : $parsed_url['host'] ) . $parsed_url['path'];
|
610 |
+
|
611 |
+
if ( isset( $parsed_url['query'] ) ) {
|
612 |
+
parse_str( $parsed_url['query'], $queryString );
|
613 |
+
$canonical .= '?' . fs_canonize_query_string( $queryString, $ignore_params );
|
614 |
+
}
|
615 |
+
|
616 |
+
return $canonical;
|
617 |
+
}
|
618 |
+
}
|
619 |
+
|
620 |
+
if ( ! function_exists( 'fs_canonize_query_string' ) ) {
|
621 |
+
/**
|
622 |
+
* @author Vova Feldman (@svovaf)
|
623 |
+
* @since 1.1.3
|
624 |
+
*
|
625 |
+
* @param array $params
|
626 |
+
* @param array $ignore_params
|
627 |
+
* @param bool $params_prefix
|
628 |
+
*
|
629 |
+
* @return string
|
630 |
+
*/
|
631 |
+
function fs_canonize_query_string( array $params, array &$ignore_params, $params_prefix = false ) {
|
632 |
+
if ( ! is_array( $params ) || 0 === count( $params ) ) {
|
633 |
+
return '';
|
634 |
+
}
|
635 |
+
|
636 |
+
// Url encode both keys and values
|
637 |
+
$keys = fs_urlencode_rfc3986( array_keys( $params ) );
|
638 |
+
$values = fs_urlencode_rfc3986( array_values( $params ) );
|
639 |
+
$params = array_combine( $keys, $values );
|
640 |
+
|
641 |
+
// Parameters are sorted by name, using lexicographical byte value ordering.
|
642 |
+
// Ref: Spec: 9.1.1 (1)
|
643 |
+
uksort( $params, 'strcmp' );
|
644 |
+
|
645 |
+
$pairs = array();
|
646 |
+
foreach ( $params as $parameter => $value ) {
|
647 |
+
$lower_param = strtolower( $parameter );
|
648 |
+
|
649 |
+
// Skip ignore params.
|
650 |
+
if ( in_array( $lower_param, $ignore_params ) ||
|
651 |
+
( false !== $params_prefix && fs_starts_with( $lower_param, $params_prefix ) )
|
652 |
+
) {
|
653 |
+
continue;
|
654 |
+
}
|
655 |
+
|
656 |
+
if ( is_array( $value ) ) {
|
657 |
+
// If two or more parameters share the same name, they are sorted by their value
|
658 |
+
// Ref: Spec: 9.1.1 (1)
|
659 |
+
natsort( $value );
|
660 |
+
foreach ( $value as $duplicate_value ) {
|
661 |
+
$pairs[] = $lower_param . '=' . $duplicate_value;
|
662 |
+
}
|
663 |
+
} else {
|
664 |
+
$pairs[] = $lower_param . '=' . $value;
|
665 |
+
}
|
666 |
+
}
|
667 |
+
|
668 |
+
if ( 0 === count( $pairs ) ) {
|
669 |
+
return '';
|
670 |
+
}
|
671 |
+
|
672 |
+
return implode( "&", $pairs );
|
673 |
+
}
|
674 |
+
}
|
675 |
+
|
676 |
+
if ( ! function_exists( 'fs_urlencode_rfc3986' ) ) {
|
677 |
+
/**
|
678 |
+
* @author Vova Feldman (@svovaf)
|
679 |
+
* @since 1.1.3
|
680 |
+
*
|
681 |
+
* @param string|string[] $input
|
682 |
+
*
|
683 |
+
* @return array|mixed|string
|
684 |
+
*/
|
685 |
+
function fs_urlencode_rfc3986( $input ) {
|
686 |
+
if ( is_array( $input ) ) {
|
687 |
+
return array_map( 'fs_urlencode_rfc3986', $input );
|
688 |
+
} else if ( is_scalar( $input ) ) {
|
689 |
+
return str_replace( '+', ' ', str_replace( '%7E', '~', rawurlencode( $input ) ) );
|
690 |
+
}
|
691 |
+
|
692 |
+
return '';
|
693 |
+
}
|
694 |
+
}
|
695 |
+
|
696 |
+
#endregion Url Canonization ------------------------------------------------------------------
|
697 |
+
|
698 |
+
if ( ! function_exists( 'fs_download_image' ) ) {
|
699 |
+
/**
|
700 |
+
* @author Vova Feldman (@svovaf)
|
701 |
+
*
|
702 |
+
* @since 1.2.2 Changed to usage of WP_Filesystem_Direct.
|
703 |
+
*
|
704 |
+
* @param string $from URL
|
705 |
+
* @param string $to File path.
|
706 |
+
*
|
707 |
+
* @return bool Is successfully downloaded.
|
708 |
+
*/
|
709 |
+
function fs_download_image( $from, $to ) {
|
710 |
+
$dir = dirname( $to );
|
711 |
+
|
712 |
+
if ( 'direct' !== get_filesystem_method( array(), $dir ) ) {
|
713 |
+
return false;
|
714 |
+
}
|
715 |
+
|
716 |
+
if ( ! class_exists( 'WP_Filesystem_Direct' ) ) {
|
717 |
+
require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php';
|
718 |
+
require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php';
|
719 |
+
}
|
720 |
+
|
721 |
+
$fs = new WP_Filesystem_Direct( '' );
|
722 |
+
$tmpfile = download_url( $from );
|
723 |
+
|
724 |
+
if ( $tmpfile instanceof WP_Error ) {
|
725 |
+
// Issue downloading the file.
|
726 |
+
return false;
|
727 |
+
}
|
728 |
+
|
729 |
+
$fs->copy( $tmpfile, $to );
|
730 |
+
$fs->delete( $tmpfile );
|
731 |
+
|
732 |
+
return true;
|
733 |
+
}
|
734 |
+
}
|
735 |
+
|
736 |
+
/* General Utilities
|
737 |
+
--------------------------------------------------------------------------------------------*/
|
738 |
+
|
739 |
+
if ( ! function_exists( 'fs_sort_by_priority' ) ) {
|
740 |
+
/**
|
741 |
+
* Sorts an array by the value of the priority key.
|
742 |
+
*
|
743 |
+
* @author Daniel Iser (@danieliser)
|
744 |
+
* @since 1.1.7
|
745 |
+
*
|
746 |
+
* @param $a
|
747 |
+
* @param $b
|
748 |
+
*
|
749 |
+
* @return int
|
750 |
+
*/
|
751 |
+
function fs_sort_by_priority( $a, $b ) {
|
752 |
+
|
753 |
+
// If b has a priority and a does not, b wins.
|
754 |
+
if ( ! isset( $a['priority'] ) && isset( $b['priority'] ) ) {
|
755 |
+
return 1;
|
756 |
+
} // If b has a priority and a does not, b wins.
|
757 |
+
elseif ( isset( $a['priority'] ) && ! isset( $b['priority'] ) ) {
|
758 |
+
return - 1;
|
759 |
+
} // If neither has a priority or both priorities are equal its a tie.
|
760 |
+
elseif ( ( ! isset( $a['priority'] ) && ! isset( $b['priority'] ) ) || $a['priority'] === $b['priority'] ) {
|
761 |
+
return 0;
|
762 |
+
}
|
763 |
+
|
764 |
+
// If both have priority return the winner.
|
765 |
+
return ( $a['priority'] < $b['priority'] ) ? - 1 : 1;
|
766 |
+
}
|
767 |
+
}
|
768 |
+
|
769 |
+
#--------------------------------------------------------------------------------
|
770 |
+
#region Localization
|
771 |
+
#--------------------------------------------------------------------------------
|
772 |
+
|
773 |
+
if ( ! function_exists( 'fs_text' ) ) {
|
774 |
+
/**
|
775 |
+
* Retrieve a translated text by key.
|
776 |
+
*
|
777 |
+
* @author Vova Feldman (@svovaf)
|
778 |
+
* @since 1.2.1.7
|
779 |
+
*
|
780 |
+
* @param string $key
|
781 |
+
* @param string $slug
|
782 |
+
*
|
783 |
+
* @return string
|
784 |
+
*
|
785 |
+
* @global $fs_text , $fs_text_overrides
|
786 |
+
*/
|
787 |
+
function fs_text( $key, $slug = 'freemius' ) {
|
788 |
+
global $fs_text,
|
789 |
+
$fs_module_info_text,
|
790 |
+
$fs_text_overrides;
|
791 |
+
|
792 |
+
if ( isset( $fs_text_overrides[ $slug ] ) ) {
|
793 |
+
if ( isset( $fs_text_overrides[ $slug ][ $key ] ) ) {
|
794 |
+
return $fs_text_overrides[ $slug ][ $key ];
|
795 |
+
}
|
796 |
+
|
797 |
+
$lower_key = strtolower( $key );
|
798 |
+
if ( isset( $fs_text_overrides[ $slug ][ $lower_key ] ) ) {
|
799 |
+
return $fs_text_overrides[ $slug ][ $lower_key ];
|
800 |
+
}
|
801 |
+
}
|
802 |
+
|
803 |
+
if ( ! isset( $fs_text ) ) {
|
804 |
+
$dir = defined( 'WP_FS__DIR_INCLUDES' ) ?
|
805 |
+
WP_FS__DIR_INCLUDES :
|
806 |
+
dirname( __FILE__ );
|
807 |
+
|
808 |
+
require_once $dir . '/i18n.php';
|
809 |
+
}
|
810 |
+
|
811 |
+
if ( isset( $fs_text[ $key ] ) ) {
|
812 |
+
return $fs_text[ $key ];
|
813 |
+
}
|
814 |
+
|
815 |
+
if ( isset( $fs_module_info_text[ $key ] ) ) {
|
816 |
+
return $fs_module_info_text[ $key ];
|
817 |
+
}
|
818 |
+
|
819 |
+
return $key;
|
820 |
+
}
|
821 |
+
|
822 |
+
#region Private
|
823 |
+
|
824 |
+
/**
|
825 |
+
* Retrieve an inline translated text by key with a context.
|
826 |
+
*
|
827 |
+
* @author Vova Feldman (@svovaf)
|
828 |
+
* @since 1.2.3
|
829 |
+
*
|
830 |
+
* @param string $text Translatable string.
|
831 |
+
* @param string $context Context information for the translators.
|
832 |
+
* @param string $key String key for overrides.
|
833 |
+
* @param string $slug Module slug for overrides.
|
834 |
+
*
|
835 |
+
* @return string
|
836 |
+
*
|
837 |
+
* @global $fs_text_overrides
|
838 |
+
*/
|
839 |
+
function _fs_text_x_inline( $text, $context, $key = '', $slug = 'freemius' ) {
|
840 |
+
list( $text, $text_domain ) = fs_text_and_domain( $text, $key, $slug );
|
841 |
+
|
842 |
+
// Avoid misleading Theme Check warning.
|
843 |
+
$fn = 'translate_with_gettext_context';
|
844 |
+
|
845 |
+
return $fn( $text, $context, $text_domain );
|
846 |
+
}
|
847 |
+
|
848 |
+
#endregion
|
849 |
+
|
850 |
+
/**
|
851 |
+
* Retrieve an inline translated text by key with a context.
|
852 |
+
*
|
853 |
+
* @author Vova Feldman (@svovaf)
|
854 |
+
* @since 1.2.3
|
855 |
+
*
|
856 |
+
* @param string $text Translatable string.
|
857 |
+
* @param string $context Context information for the translators.
|
858 |
+
* @param string $key String key for overrides.
|
859 |
+
* @param string $slug Module slug for overrides.
|
860 |
+
*
|
861 |
+
* @return string
|
862 |
+
*
|
863 |
+
* @global $fs_text_overrides
|
864 |
+
*/
|
865 |
+
function fs_text_x_inline( $text, $context, $key = '', $slug = 'freemius' ) {
|
866 |
+
return _fs_text_x_inline( $text, $context, $key, $slug );
|
867 |
+
}
|
868 |
+
|
869 |
+
/**
|
870 |
+
* Output a translated text by key.
|
871 |
+
*
|
872 |
+
* @author Vova Feldman (@svovaf)
|
873 |
+
* @since 1.2.1.7
|
874 |
+
*
|
875 |
+
* @param string $key
|
876 |
+
* @param string $slug
|
877 |
+
*/
|
878 |
+
function fs_echo( $key, $slug = 'freemius' ) {
|
879 |
+
echo fs_text( $key, $slug );
|
880 |
+
}
|
881 |
+
|
882 |
+
/**
|
883 |
+
* Output an inline translated text.
|
884 |
+
*
|
885 |
+
* @author Vova Feldman (@svovaf)
|
886 |
+
* @since 1.2.3
|
887 |
+
*
|
888 |
+
* @param string $text Translatable string.
|
889 |
+
* @param string $key String key for overrides.
|
890 |
+
* @param string $slug Module slug for overrides.
|
891 |
+
*/
|
892 |
+
function fs_echo_inline( $text, $key = '', $slug = 'freemius' ) {
|
893 |
+
echo _fs_text_inline( $text, $key, $slug );
|
894 |
+
}
|
895 |
+
|
896 |
+
/**
|
897 |
+
* Output an inline translated text with a context.
|
898 |
+
*
|
899 |
+
* @author Vova Feldman (@svovaf)
|
900 |
+
* @since 1.2.3
|
901 |
+
*
|
902 |
+
* @param string $text Translatable string.
|
903 |
+
* @param string $context Context information for the translators.
|
904 |
+
* @param string $key String key for overrides.
|
905 |
+
* @param string $slug Module slug for overrides.
|
906 |
+
*/
|
907 |
+
function fs_echo_x_inline( $text, $context, $key = '', $slug = 'freemius' ) {
|
908 |
+
echo _fs_text_x_inline( $text, $context, $key, $slug );
|
909 |
+
}
|
910 |
+
}
|
911 |
+
|
912 |
+
if ( ! function_exists( 'fs_text_override' ) ) {
|
913 |
+
/**
|
914 |
+
* Get a translatable text override if exists, or `false`.
|
915 |
+
*
|
916 |
+
* @author Vova Feldman (@svovaf)
|
917 |
+
* @since 1.2.1.7
|
918 |
+
*
|
919 |
+
* @param string $text Translatable string.
|
920 |
+
* @param string $key String key for overrides.
|
921 |
+
* @param string $slug Module slug for overrides.
|
922 |
+
*
|
923 |
+
* @return string|false
|
924 |
+
*/
|
925 |
+
function fs_text_override( $text, $key, $slug ) {
|
926 |
+
global $fs_text_overrides;
|
927 |
+
|
928 |
+
/**
|
929 |
+
* Check if string is overridden.
|
930 |
+
*/
|
931 |
+
if ( ! isset( $fs_text_overrides[ $slug ] ) ) {
|
932 |
+
return false;
|
933 |
+
}
|
934 |
+
|
935 |
+
if ( empty( $key ) ) {
|
936 |
+
$key = strtolower( str_replace( ' ', '-', $text ) );
|
937 |
+
}
|
938 |
+
|
939 |
+
if ( isset( $fs_text_overrides[ $slug ][ $key ] ) ) {
|
940 |
+
return $fs_text_overrides[ $slug ][ $key ];
|
941 |
+
}
|
942 |
+
|
943 |
+
$lower_key = strtolower( $key );
|
944 |
+
if ( isset( $fs_text_overrides[ $slug ][ $lower_key ] ) ) {
|
945 |
+
return $fs_text_overrides[ $slug ][ $lower_key ];
|
946 |
+
}
|
947 |
+
|
948 |
+
return false;
|
949 |
+
}
|
950 |
+
}
|
951 |
+
|
952 |
+
if ( ! function_exists( 'fs_text_and_domain' ) ) {
|
953 |
+
/**
|
954 |
+
* Get a translatable text and its text domain.
|
955 |
+
*
|
956 |
+
* When the text is overridden by the module, returns the overridden text and the text domain of the module. Otherwise, returns the original text and 'freemius' as the text domain.
|
957 |
+
*
|
958 |
+
* @author Vova Feldman (@svovaf)
|
959 |
+
* @since 1.2.1.7
|
960 |
+
*
|
961 |
+
* @param string $text Translatable string.
|
962 |
+
* @param string $key String key for overrides.
|
963 |
+
* @param string $slug Module slug for overrides.
|
964 |
+
*
|
965 |
+
* @return string[]
|
966 |
+
*/
|
967 |
+
function fs_text_and_domain( $text, $key, $slug ) {
|
968 |
+
$override = fs_text_override( $text, $key, $slug );
|
969 |
+
|
970 |
+
if ( false === $override ) {
|
971 |
+
// No override, use FS text domain.
|
972 |
+
$text_domain = 'freemius';
|
973 |
+
} else {
|
974 |
+
// Found an override.
|
975 |
+
$text = $override;
|
976 |
+
// Use the module's text domain.
|
977 |
+
$text_domain = $slug;
|
978 |
+
}
|
979 |
+
|
980 |
+
return array( $text, $text_domain );
|
981 |
+
}
|
982 |
+
}
|
983 |
+
|
984 |
+
if ( ! function_exists( '_fs_text_inline' ) ) {
|
985 |
+
/**
|
986 |
+
* Retrieve an inline translated text by key.
|
987 |
+
*
|
988 |
+
* @author Vova Feldman (@svovaf)
|
989 |
+
* @since 1.2.3
|
990 |
+
*
|
991 |
+
* @param string $text Translatable string.
|
992 |
+
* @param string $key String key for overrides.
|
993 |
+
* @param string $slug Module slug for overrides.
|
994 |
+
*
|
995 |
+
* @return string
|
996 |
+
*
|
997 |
+
* @global $fs_text_overrides
|
998 |
+
*/
|
999 |
+
function _fs_text_inline( $text, $key = '', $slug = 'freemius' ) {
|
1000 |
+
list( $text, $text_domain ) = fs_text_and_domain( $text, $key, $slug );
|
1001 |
+
|
1002 |
+
// Avoid misleading Theme Check warning.
|
1003 |
+
$fn = 'translate';
|
1004 |
+
|
1005 |
+
return $fn( $text, $text_domain );
|
1006 |
+
}
|
1007 |
+
}
|
1008 |
+
|
1009 |
+
if ( ! function_exists( 'fs_text_inline' ) ) {
|
1010 |
+
/**
|
1011 |
+
* Retrieve an inline translated text by key.
|
1012 |
+
*
|
1013 |
+
* @author Vova Feldman (@svovaf)
|
1014 |
+
* @since 1.2.3
|
1015 |
+
*
|
1016 |
+
* @param string $text Translatable string.
|
1017 |
+
* @param string $key String key for overrides.
|
1018 |
+
* @param string $slug Module slug for overrides.
|
1019 |
+
*
|
1020 |
+
* @return string
|
1021 |
+
*
|
1022 |
+
* @global $fs_text_overrides
|
1023 |
+
*/
|
1024 |
+
function fs_text_inline( $text, $key = '', $slug = 'freemius' ) {
|
1025 |
+
return _fs_text_inline( $text, $key, $slug );
|
1026 |
+
}
|
1027 |
+
}
|
1028 |
+
|
1029 |
+
if ( ! function_exists( 'fs_esc_attr' ) ) {
|
1030 |
+
/**
|
1031 |
+
* @author Vova Feldman
|
1032 |
+
* @since 1.2.1.6
|
1033 |
+
*
|
1034 |
+
* @param string $key
|
1035 |
+
* @param string $slug
|
1036 |
+
*
|
1037 |
+
* @return string
|
1038 |
+
*/
|
1039 |
+
function fs_esc_attr( $key, $slug ) {
|
1040 |
+
return esc_attr( fs_text( $key, $slug ) );
|
1041 |
+
}
|
1042 |
+
}
|
1043 |
+
|
1044 |
+
if ( ! function_exists( 'fs_esc_attr_inline' ) ) {
|
1045 |
+
/**
|
1046 |
+
* @author Vova Feldman (@svovaf)
|
1047 |
+
* @since 1.2.3
|
1048 |
+
*
|
1049 |
+
* @param string $text Translatable string.
|
1050 |
+
* @param string $key String key for overrides.
|
1051 |
+
* @param string $slug Module slug for overrides.
|
1052 |
+
*
|
1053 |
+
* @return string
|
1054 |
+
*/
|
1055 |
+
function fs_esc_attr_inline( $text, $key = '', $slug = 'freemius' ) {
|
1056 |
+
return esc_attr( _fs_text_inline( $text, $key, $slug ) );
|
1057 |
+
}
|
1058 |
+
}
|
1059 |
+
|
1060 |
+
if ( ! function_exists( 'fs_esc_attr_x_inline' ) ) {
|
1061 |
+
/**
|
1062 |
+
* @author Vova Feldman (@svovaf)
|
1063 |
+
* @since 1.2.3
|
1064 |
+
*
|
1065 |
+
* @param string $text Translatable string.
|
1066 |
+
* @param string $context Context information for the translators.
|
1067 |
+
* @param string $key String key for overrides.
|
1068 |
+
* @param string $slug Module slug for overrides.
|
1069 |
+
*
|
1070 |
+
* @return string
|
1071 |
+
*/
|
1072 |
+
function fs_esc_attr_x_inline( $text, $context, $key = '', $slug = 'freemius' ) {
|
1073 |
+
return esc_attr( _fs_text_x_inline( $text, $context, $key, $slug ) );
|
1074 |
+
}
|
1075 |
+
}
|
1076 |
+
|
1077 |
+
if ( ! function_exists( 'fs_esc_attr_echo' ) ) {
|
1078 |
+
/**
|
1079 |
+
* @author Vova Feldman
|
1080 |
+
* @since 1.2.1.6
|
1081 |
+
*
|
1082 |
+
* @param string $key
|
1083 |
+
* @param string $slug
|
1084 |
+
*/
|
1085 |
+
function fs_esc_attr_echo( $key, $slug ) {
|
1086 |
+
echo esc_attr( fs_text( $key, $slug ) );
|
1087 |
+
}
|
1088 |
+
}
|
1089 |
+
|
1090 |
+
if ( ! function_exists( 'fs_esc_attr_echo_inline' ) ) {
|
1091 |
+
/**
|
1092 |
+
* @author Vova Feldman (@svovaf)
|
1093 |
+
* @since 1.2.3
|
1094 |
+
*
|
1095 |
+
* @param string $text Translatable string.
|
1096 |
+
* @param string $key String key for overrides.
|
1097 |
+
* @param string $slug Module slug for overrides.
|
1098 |
+
*/
|
1099 |
+
function fs_esc_attr_echo_inline( $text, $key = '', $slug = 'freemius' ) {
|
1100 |
+
echo esc_attr( _fs_text_inline( $text, $key, $slug ) );
|
1101 |
+
}
|
1102 |
+
}
|
1103 |
+
|
1104 |
+
if ( ! function_exists( 'fs_esc_js' ) ) {
|
1105 |
+
/**
|
1106 |
+
* @author Vova Feldman
|
1107 |
+
* @since 1.2.1.6
|
1108 |
+
*
|
1109 |
+
* @param string $key
|
1110 |
+
* @param string $slug
|
1111 |
+
*
|
1112 |
+
* @return string
|
1113 |
+
*/
|
1114 |
+
function fs_esc_js( $key, $slug ) {
|
1115 |
+
return esc_js( fs_text( $key, $slug ) );
|
1116 |
+
}
|
1117 |
+
}
|
1118 |
+
|
1119 |
+
if ( ! function_exists( 'fs_esc_js_inline' ) ) {
|
1120 |
+
/**
|
1121 |
+
* @author Vova Feldman (@svovaf)
|
1122 |
+
* @since 1.2.3
|
1123 |
+
*
|
1124 |
+
* @param string $text Translatable string.
|
1125 |
+
* @param string $key String key for overrides.
|
1126 |
+
* @param string $slug Module slug for overrides.
|
1127 |
+
*
|
1128 |
+
* @return string
|
1129 |
+
*/
|
1130 |
+
function fs_esc_js_inline( $text, $key = '', $slug = 'freemius' ) {
|
1131 |
+
return esc_js( _fs_text_inline( $text, $key, $slug ) );
|
1132 |
+
}
|
1133 |
+
}
|
1134 |
+
|
1135 |
+
if ( ! function_exists( 'fs_esc_js_x_inline' ) ) {
|
1136 |
+
/**
|
1137 |
+
* @author Vova Feldman (@svovaf)
|
1138 |
+
* @since 1.2.3
|
1139 |
+
*
|
1140 |
+
* @param string $text Translatable string.
|
1141 |
+
* @param string $context Context information for the translators.
|
1142 |
+
* @param string $key String key for overrides.
|
1143 |
+
* @param string $slug Module slug for overrides.
|
1144 |
+
*
|
1145 |
+
* @return string
|
1146 |
+
*/
|
1147 |
+
function fs_esc_js_x_inline( $text, $context, $key = '', $slug = 'freemius' ) {
|
1148 |
+
return esc_js( _fs_text_x_inline( $text, $context, $key, $slug ) );
|
1149 |
+
}
|
1150 |
+
}
|
1151 |
+
|
1152 |
+
if ( ! function_exists( 'fs_esc_js_echo_x_inline' ) ) {
|
1153 |
+
/**
|
1154 |
+
* @author Vova Feldman (@svovaf)
|
1155 |
+
* @since 1.2.3
|
1156 |
+
*
|
1157 |
+
* @param string $text Translatable string.
|
1158 |
+
* @param string $context Context information for the translators.
|
1159 |
+
* @param string $key String key for overrides.
|
1160 |
+
* @param string $slug Module slug for overrides.
|
1161 |
+
*
|
1162 |
+
* @return string
|
1163 |
+
*/
|
1164 |
+
function fs_esc_js_echo_x_inline( $text, $context, $key = '', $slug = 'freemius' ) {
|
1165 |
+
echo esc_js( _fs_text_x_inline( $text, $context, $key, $slug ) );
|
1166 |
+
}
|
1167 |
+
}
|
1168 |
+
|
1169 |
+
if ( ! function_exists( 'fs_esc_js_echo' ) ) {
|
1170 |
+
/**
|
1171 |
+
* @author Vova Feldman
|
1172 |
+
* @since 1.2.1.6
|
1173 |
+
*
|
1174 |
+
* @param string $key
|
1175 |
+
* @param string $slug
|
1176 |
+
*/
|
1177 |
+
function fs_esc_js_echo( $key, $slug ) {
|
1178 |
+
echo esc_js( fs_text( $key, $slug ) );
|
1179 |
+
}
|
1180 |
+
}
|
1181 |
+
|
1182 |
+
if ( ! function_exists( 'fs_esc_js_echo_inline' ) ) {
|
1183 |
+
/**
|
1184 |
+
* @author Vova Feldman (@svovaf)
|
1185 |
+
* @since 1.2.3
|
1186 |
+
*
|
1187 |
+
* @param string $text Translatable string.
|
1188 |
+
* @param string $key String key for overrides.
|
1189 |
+
* @param string $slug Module slug for overrides.
|
1190 |
+
*/
|
1191 |
+
function fs_esc_js_echo_inline( $text, $key = '', $slug = 'freemius' ) {
|
1192 |
+
echo esc_js( _fs_text_inline( $text, $key, $slug ) );
|
1193 |
+
}
|
1194 |
+
}
|
1195 |
+
|
1196 |
+
if ( ! function_exists( 'fs_json_encode_echo' ) ) {
|
1197 |
+
/**
|
1198 |
+
* @author Vova Feldman
|
1199 |
+
* @since 1.2.1.6
|
1200 |
+
*
|
1201 |
+
* @param string $key
|
1202 |
+
* @param string $slug
|
1203 |
+
*/
|
1204 |
+
function fs_json_encode_echo( $key, $slug ) {
|
1205 |
+
echo json_encode( fs_text( $key, $slug ) );
|
1206 |
+
}
|
1207 |
+
}
|
1208 |
+
|
1209 |
+
if ( ! function_exists( 'fs_json_encode_echo_inline' ) ) {
|
1210 |
+
/**
|
1211 |
+
* @author Vova Feldman (@svovaf)
|
1212 |
+
* @since 1.2.3
|
1213 |
+
*
|
1214 |
+
* @param string $text Translatable string.
|
1215 |
+
* @param string $key String key for overrides.
|
1216 |
+
* @param string $slug Module slug for overrides.
|
1217 |
+
*/
|
1218 |
+
function fs_json_encode_echo_inline( $text, $key = '', $slug = 'freemius' ) {
|
1219 |
+
echo json_encode( _fs_text_inline( $text, $key, $slug ) );
|
1220 |
+
}
|
1221 |
+
}
|
1222 |
+
|
1223 |
+
if ( ! function_exists( 'fs_esc_html' ) ) {
|
1224 |
+
/**
|
1225 |
+
* @author Vova Feldman
|
1226 |
+
* @since 1.2.1.6
|
1227 |
+
*
|
1228 |
+
* @param string $key
|
1229 |
+
* @param string $slug
|
1230 |
+
*
|
1231 |
+
* @return string
|
1232 |
+
*/
|
1233 |
+
function fs_esc_html( $key, $slug ) {
|
1234 |
+
return esc_html( fs_text( $key, $slug ) );
|
1235 |
+
}
|
1236 |
+
}
|
1237 |
+
|
1238 |
+
if ( ! function_exists( 'fs_esc_html_inline' ) ) {
|
1239 |
+
/**
|
1240 |
+
* @author Vova Feldman (@svovaf)
|
1241 |
+
* @since 1.2.3
|
1242 |
+
*
|
1243 |
+
* @param string $text Translatable string.
|
1244 |
+
* @param string $key String key for overrides.
|
1245 |
+
* @param string $slug Module slug for overrides.
|
1246 |
+
*
|
1247 |
+
* @return string
|
1248 |
+
*/
|
1249 |
+
function fs_esc_html_inline( $text, $key = '', $slug = 'freemius' ) {
|
1250 |
+
return esc_html( _fs_text_inline( $text, $key, $slug ) );
|
1251 |
+
}
|
1252 |
+
}
|
1253 |
+
|
1254 |
+
if ( ! function_exists( 'fs_esc_html_x_inline' ) ) {
|
1255 |
+
/**
|
1256 |
+
* @author Vova Feldman (@svovaf)
|
1257 |
+
* @since 1.2.3
|
1258 |
+
*
|
1259 |
+
* @param string $text Translatable string.
|
1260 |
+
* @param string $context Context information for the translators.
|
1261 |
+
* @param string $key String key for overrides.
|
1262 |
+
* @param string $slug Module slug for overrides.
|
1263 |
+
*
|
1264 |
+
* @return string
|
1265 |
+
*/
|
1266 |
+
function fs_esc_html_x_inline( $text, $context, $key = '', $slug = 'freemius' ) {
|
1267 |
+
return esc_html( _fs_text_x_inline( $text, $context, $key, $slug ) );
|
1268 |
+
}
|
1269 |
+
}
|
1270 |
+
|
1271 |
+
if ( ! function_exists( 'fs_esc_html_echo_x_inline' ) ) {
|
1272 |
+
/**
|
1273 |
+
* @author Vova Feldman (@svovaf)
|
1274 |
+
* @since 1.2.3
|
1275 |
+
*
|
1276 |
+
* @param string $text Translatable string.
|
1277 |
+
* @param string $context Context information for the translators.
|
1278 |
+
* @param string $key String key for overrides.
|
1279 |
+
* @param string $slug Module slug for overrides.
|
1280 |
+
*/
|
1281 |
+
function fs_esc_html_echo_x_inline( $text, $context, $key = '', $slug = 'freemius' ) {
|
1282 |
+
echo esc_html( _fs_text_x_inline( $text, $context, $key, $slug ) );
|
1283 |
+
}
|
1284 |
+
}
|
1285 |
+
|
1286 |
+
if ( ! function_exists( 'fs_esc_html_echo' ) ) {
|
1287 |
+
/**
|
1288 |
+
* @author Vova Feldman
|
1289 |
+
* @since 1.2.1.6
|
1290 |
+
*
|
1291 |
+
* @param string $key
|
1292 |
+
* @param string $slug
|
1293 |
+
*/
|
1294 |
+
function fs_esc_html_echo( $key, $slug ) {
|
1295 |
+
echo esc_html( fs_text( $key, $slug ) );
|
1296 |
+
}
|
1297 |
+
}
|
1298 |
+
|
1299 |
+
if ( ! function_exists( 'fs_esc_html_echo_inline' ) ) {
|
1300 |
+
/**
|
1301 |
+
* @author Vova Feldman (@svovaf)
|
1302 |
+
* @since 1.2.3
|
1303 |
+
*
|
1304 |
+
* @param string $text Translatable string.
|
1305 |
+
* @param string $key String key for overrides.
|
1306 |
+
* @param string $slug Module slug for overrides.
|
1307 |
+
*/
|
1308 |
+
function fs_esc_html_echo_inline( $text, $key = '', $slug = 'freemius' ) {
|
1309 |
+
echo esc_html( _fs_text_inline( $text, $key, $slug ) );
|
1310 |
+
}
|
1311 |
+
}
|
1312 |
+
|
1313 |
+
if ( ! function_exists( 'fs_override_i18n' ) ) {
|
1314 |
+
/**
|
1315 |
+
* Override default i18n text phrases.
|
1316 |
+
*
|
1317 |
+
* @author Vova Feldman (@svovaf)
|
1318 |
+
* @since 1.1.6
|
1319 |
+
*
|
1320 |
+
* @param array[string]string $key_value
|
1321 |
+
* @param string $slug
|
1322 |
+
*
|
1323 |
+
* @global $fs_text_overrides
|
1324 |
+
*/
|
1325 |
+
function fs_override_i18n( array $key_value, $slug = 'freemius' ) {
|
1326 |
+
global $fs_text_overrides;
|
1327 |
+
|
1328 |
+
if ( ! isset( $fs_text_overrides[ $slug ] ) ) {
|
1329 |
+
$fs_text_overrides[ $slug ] = array();
|
1330 |
+
}
|
1331 |
+
|
1332 |
+
foreach ( $key_value as $key => $value ) {
|
1333 |
+
$fs_text_overrides[ $slug ][ $key ] = $value;
|
1334 |
+
}
|
1335 |
+
}
|
1336 |
+
}
|
1337 |
+
|
1338 |
+
#endregion
|
1339 |
+
|
1340 |
+
#--------------------------------------------------------------------------------
|
1341 |
+
#region Multisite Network
|
1342 |
+
#--------------------------------------------------------------------------------
|
1343 |
+
|
1344 |
+
if ( ! function_exists( 'fs_is_plugin_uninstall' ) ) {
|
1345 |
+
/**
|
1346 |
+
* @author Vova Feldman (@svovaf)
|
1347 |
+
* @since 2.0.0
|
1348 |
+
*/
|
1349 |
+
function fs_is_plugin_uninstall() {
|
1350 |
+
return (
|
1351 |
+
defined( 'WP_UNINSTALL_PLUGIN' ) ||
|
1352 |
+
( 0 < did_action( 'update_option_uninstall_plugins' ) )
|
1353 |
+
);
|
1354 |
+
}
|
1355 |
+
}
|
1356 |
+
|
1357 |
+
if ( ! function_exists( 'fs_is_network_admin' ) ) {
|
1358 |
+
/**
|
1359 |
+
* Unlike is_network_admin(), this one will also work properly when
|
1360 |
+
* the context execution is WP AJAX handler, and during plugin
|
1361 |
+
* uninstall.
|
1362 |
+
*
|
1363 |
+
* @author Vova Feldman (@svovaf)
|
1364 |
+
* @since 2.0.0
|
1365 |
+
*/
|
1366 |
+
function fs_is_network_admin() {
|
1367 |
+
return (
|
1368 |
+
WP_FS__IS_NETWORK_ADMIN ||
|
1369 |
+
( is_multisite() && fs_is_plugin_uninstall() )
|
1370 |
+
);
|
1371 |
+
}
|
1372 |
+
}
|
1373 |
+
|
1374 |
+
if ( ! function_exists( 'fs_is_blog_admin' ) ) {
|
1375 |
+
/**
|
1376 |
+
* Unlike is_blog_admin(), this one will also work properly when
|
1377 |
+
* the context execution is WP AJAX handler, and during plugin
|
1378 |
+
* uninstall.
|
1379 |
+
*
|
1380 |
+
* @author Vova Feldman (@svovaf)
|
1381 |
+
* @since 2.0.0
|
1382 |
+
*/
|
1383 |
+
function fs_is_blog_admin() {
|
1384 |
+
return (
|
1385 |
+
WP_FS__IS_BLOG_ADMIN ||
|
1386 |
+
( ! is_multisite() && fs_is_plugin_uninstall() )
|
1387 |
+
);
|
1388 |
+
}
|
1389 |
+
}
|
1390 |
+
|
1391 |
+
#endregion
|
1392 |
+
|
1393 |
+
if ( ! function_exists( 'fs_apply_filter' ) ) {
|
1394 |
+
/**
|
1395 |
+
* Apply filter for specific plugin.
|
1396 |
+
*
|
1397 |
+
* @author Vova Feldman (@svovaf)
|
1398 |
+
* @since 1.0.9
|
1399 |
+
*
|
1400 |
+
* @param string $module_unique_affix Module's unique affix.
|
1401 |
+
* @param string $tag The name of the filter hook.
|
1402 |
+
* @param mixed $value The value on which the filters hooked to `$tag` are applied on.
|
1403 |
+
*
|
1404 |
+
* @return mixed The filtered value after all hooked functions are applied to it.
|
1405 |
+
*
|
1406 |
+
* @uses apply_filters()
|
1407 |
+
*/
|
1408 |
+
function fs_apply_filter( $module_unique_affix, $tag, $value ) {
|
1409 |
+
$args = func_get_args();
|
1410 |
+
|
1411 |
+
return call_user_func_array( 'apply_filters', array_merge(
|
1412 |
+
array( "fs_{$tag}_{$module_unique_affix}" ),
|
1413 |
+
array_slice( $args, 2 ) )
|
1414 |
+
);
|
1415 |
+
}
|
1416 |
}
|
freemius/includes/fs-essential-functions.php
CHANGED
@@ -1,500 +1,500 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* IMPORTANT:
|
4 |
-
* This file will be loaded based on the order of the plugins/themes load.
|
5 |
-
* If there's a theme and a plugin using Freemius, the plugin's essential
|
6 |
-
* file will always load first.
|
7 |
-
*
|
8 |
-
* @package Freemius
|
9 |
-
* @copyright Copyright (c) 2015, Freemius, Inc.
|
10 |
-
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
|
11 |
-
* @since 1.1.5
|
12 |
-
*/
|
13 |
-
|
14 |
-
if ( ! function_exists( 'fs_normalize_path' ) ) {
|
15 |
-
if ( function_exists( 'wp_normalize_path' ) ) {
|
16 |
-
/**
|
17 |
-
* Normalize a filesystem path.
|
18 |
-
*
|
19 |
-
* Replaces backslashes with forward slashes for Windows systems, and ensures
|
20 |
-
* no duplicate slashes exist.
|
21 |
-
*
|
22 |
-
* @param string $path Path to normalize.
|
23 |
-
*
|
24 |
-
* @return string Normalized path.
|
25 |
-
*/
|
26 |
-
function fs_normalize_path( $path ) {
|
27 |
-
return wp_normalize_path( $path );
|
28 |
-
}
|
29 |
-
} else {
|
30 |
-
function fs_normalize_path( $path ) {
|
31 |
-
$path = str_replace( '\\', '/', $path );
|
32 |
-
$path = preg_replace( '|/+|', '/', $path );
|
33 |
-
|
34 |
-
return $path;
|
35 |
-
}
|
36 |
-
}
|
37 |
-
}
|
38 |
-
|
39 |
-
require_once dirname( __FILE__ ) . '/supplements/fs-essential-functions-2.2.1.php';
|
40 |
-
|
41 |
-
#region Core Redirect (copied from BuddyPress) -----------------------------------------
|
42 |
-
|
43 |
-
if ( ! function_exists( 'fs_redirect' ) ) {
|
44 |
-
/**
|
45 |
-
* Redirects to another page, with a workaround for the IIS Set-Cookie bug.
|
46 |
-
*
|
47 |
-
* @link http://support.microsoft.com/kb/q176113/
|
48 |
-
* @since 1.5.1
|
49 |
-
* @uses apply_filters() Calls 'wp_redirect' hook on $location and $status.
|
50 |
-
*
|
51 |
-
* @param string $location The path to redirect to.
|
52 |
-
* @param bool $exit If true, exit after redirect (Since 1.2.1.5).
|
53 |
-
* @param int $status Status code to use.
|
54 |
-
*
|
55 |
-
* @return bool False if $location is not set
|
56 |
-
*/
|
57 |
-
function fs_redirect( $location, $exit = true, $status = 302 ) {
|
58 |
-
global $is_IIS;
|
59 |
-
|
60 |
-
$file = '';
|
61 |
-
$line = '';
|
62 |
-
if ( headers_sent($file, $line) ) {
|
63 |
-
if ( WP_FS__DEBUG_SDK && class_exists( 'FS_Admin_Notices' ) ) {
|
64 |
-
$notices = FS_Admin_Notices::instance( 'global' );
|
65 |
-
|
66 |
-
$notices->add( "Freemius failed to redirect the page because the headers have been already sent from line <b><code>{$line}</code></b> in file <b><code>{$file}</code></b>. If it's unexpected, it usually happens due to invalid space and/or EOL character(s).", 'Oops...', 'error' );
|
67 |
-
}
|
68 |
-
|
69 |
-
return false;
|
70 |
-
}
|
71 |
-
|
72 |
-
if ( defined( 'DOING_AJAX' ) ) {
|
73 |
-
// Don't redirect on AJAX calls.
|
74 |
-
return false;
|
75 |
-
}
|
76 |
-
|
77 |
-
if ( ! $location ) // allows the wp_redirect filter to cancel a redirect
|
78 |
-
{
|
79 |
-
return false;
|
80 |
-
}
|
81 |
-
|
82 |
-
$location = fs_sanitize_redirect( $location );
|
83 |
-
|
84 |
-
if ( $is_IIS ) {
|
85 |
-
header( "Refresh: 0;url=$location" );
|
86 |
-
} else {
|
87 |
-
if ( php_sapi_name() != 'cgi-fcgi' ) {
|
88 |
-
status_header( $status );
|
89 |
-
} // This causes problems on IIS and some FastCGI setups
|
90 |
-
header( "Location: $location" );
|
91 |
-
}
|
92 |
-
|
93 |
-
if ( $exit ) {
|
94 |
-
exit();
|
95 |
-
}
|
96 |
-
|
97 |
-
return true;
|
98 |
-
}
|
99 |
-
|
100 |
-
if ( ! function_exists( 'fs_sanitize_redirect' ) ) {
|
101 |
-
/**
|
102 |
-
* Sanitizes a URL for use in a redirect.
|
103 |
-
*
|
104 |
-
* @since 2.3
|
105 |
-
*
|
106 |
-
* @param string $location
|
107 |
-
*
|
108 |
-
* @return string redirect-sanitized URL
|
109 |
-
*/
|
110 |
-
function fs_sanitize_redirect( $location ) {
|
111 |
-
$location = preg_replace( '|[^a-z0-9-~+_.?#=&;,/:%!]|i', '', $location );
|
112 |
-
$location = fs_kses_no_null( $location );
|
113 |
-
|
114 |
-
// remove %0d and %0a from location
|
115 |
-
$strip = array( '%0d', '%0a' );
|
116 |
-
$found = true;
|
117 |
-
while ( $found ) {
|
118 |
-
$found = false;
|
119 |
-
foreach ( (array) $strip as $val ) {
|
120 |
-
while ( strpos( $location, $val ) !== false ) {
|
121 |
-
$found = true;
|
122 |
-
$location = str_replace( $val, '', $location );
|
123 |
-
}
|
124 |
-
}
|
125 |
-
}
|
126 |
-
|
127 |
-
return $location;
|
128 |
-
}
|
129 |
-
}
|
130 |
-
|
131 |
-
if ( ! function_exists( 'fs_kses_no_null' ) ) {
|
132 |
-
/**
|
133 |
-
* Removes any NULL characters in $string.
|
134 |
-
*
|
135 |
-
* @since 1.0.0
|
136 |
-
*
|
137 |
-
* @param string $string
|
138 |
-
*
|
139 |
-
* @return string
|
140 |
-
*/
|
141 |
-
function fs_kses_no_null( $string ) {
|
142 |
-
$string = preg_replace( '/\0+/', '', $string );
|
143 |
-
$string = preg_replace( '/(\\\\0)+/', '', $string );
|
144 |
-
|
145 |
-
return $string;
|
146 |
-
}
|
147 |
-
}
|
148 |
-
}
|
149 |
-
|
150 |
-
#endregion Core Redirect (copied from BuddyPress) -----------------------------------------
|
151 |
-
|
152 |
-
if ( ! function_exists( '__fs' ) ) {
|
153 |
-
global $fs_text_overrides;
|
154 |
-
|
155 |
-
if ( ! isset( $fs_text_overrides ) ) {
|
156 |
-
$fs_text_overrides = array();
|
157 |
-
}
|
158 |
-
|
159 |
-
/**
|
160 |
-
* Retrieve a translated text by key.
|
161 |
-
*
|
162 |
-
* @deprecated Use `fs_text()` instead since methods starting with `__` trigger warnings in Php 7.
|
163 |
-
* @todo Remove this method in the future.
|
164 |
-
*
|
165 |
-
* @author Vova Feldman (@svovaf)
|
166 |
-
* @since 1.1.4
|
167 |
-
*
|
168 |
-
* @param string $key
|
169 |
-
* @param string $slug
|
170 |
-
*
|
171 |
-
* @return string
|
172 |
-
*
|
173 |
-
* @global $fs_text, $fs_text_overrides
|
174 |
-
*/
|
175 |
-
function __fs( $key, $slug = 'freemius' ) {
|
176 |
-
_deprecated_function( __FUNCTION__, '2.0.0', 'fs_text()' );
|
177 |
-
|
178 |
-
global $fs_text,
|
179 |
-
$fs_module_info_text,
|
180 |
-
$fs_text_overrides;
|
181 |
-
|
182 |
-
if ( isset( $fs_text_overrides[ $slug ] ) ) {
|
183 |
-
if ( isset( $fs_text_overrides[ $slug ][ $key ] ) ) {
|
184 |
-
return $fs_text_overrides[ $slug ][ $key ];
|
185 |
-
}
|
186 |
-
|
187 |
-
$lower_key = strtolower( $key );
|
188 |
-
if ( isset( $fs_text_overrides[ $slug ][ $lower_key ] ) ) {
|
189 |
-
return $fs_text_overrides[ $slug ][ $lower_key ];
|
190 |
-
}
|
191 |
-
}
|
192 |
-
|
193 |
-
if ( ! isset( $fs_text ) ) {
|
194 |
-
$dir = defined( 'WP_FS__DIR_INCLUDES' ) ?
|
195 |
-
WP_FS__DIR_INCLUDES :
|
196 |
-
dirname( __FILE__ );
|
197 |
-
|
198 |
-
require_once $dir . '/i18n.php';
|
199 |
-
}
|
200 |
-
|
201 |
-
if ( isset( $fs_text[ $key ] ) ) {
|
202 |
-
return $fs_text[ $key ];
|
203 |
-
}
|
204 |
-
|
205 |
-
if ( isset( $fs_module_info_text[ $key ] ) ) {
|
206 |
-
return $fs_module_info_text[ $key ];
|
207 |
-
}
|
208 |
-
|
209 |
-
return $key;
|
210 |
-
}
|
211 |
-
|
212 |
-
/**
|
213 |
-
* Output a translated text by key.
|
214 |
-
*
|
215 |
-
* @deprecated Use `fs_echo()` instead for consistency with `fs_text()`.
|
216 |
-
*
|
217 |
-
* @todo Remove this method in the future.
|
218 |
-
*
|
219 |
-
* @author Vova Feldman (@svovaf)
|
220 |
-
* @since 1.1.4
|
221 |
-
*
|
222 |
-
* @param string $key
|
223 |
-
* @param string $slug
|
224 |
-
*/
|
225 |
-
function _efs( $key, $slug = 'freemius' ) {
|
226 |
-
fs_echo( $key, $slug );
|
227 |
-
}
|
228 |
-
}
|
229 |
-
|
230 |
-
if ( ! function_exists( 'fs_get_ip' ) ) {
|
231 |
-
/**
|
232 |
-
* Get client IP.
|
233 |
-
*
|
234 |
-
* @author Vova Feldman (@svovaf)
|
235 |
-
* @since 1.1.2
|
236 |
-
*
|
237 |
-
* @return string|null
|
238 |
-
*/
|
239 |
-
function fs_get_ip() {
|
240 |
-
$fields = array(
|
241 |
-
'HTTP_CF_CONNECTING_IP',
|
242 |
-
'HTTP_CLIENT_IP',
|
243 |
-
'HTTP_X_FORWARDED_FOR',
|
244 |
-
'HTTP_X_FORWARDED',
|
245 |
-
'HTTP_FORWARDED_FOR',
|
246 |
-
'HTTP_FORWARDED',
|
247 |
-
'REMOTE_ADDR',
|
248 |
-
);
|
249 |
-
|
250 |
-
foreach ( $fields as $ip_field ) {
|
251 |
-
if ( ! empty( $_SERVER[ $ip_field ] ) ) {
|
252 |
-
return $_SERVER[ $ip_field ];
|
253 |
-
}
|
254 |
-
}
|
255 |
-
|
256 |
-
return null;
|
257 |
-
}
|
258 |
-
}
|
259 |
-
|
260 |
-
/**
|
261 |
-
* Leverage backtrace to find caller plugin main file path.
|
262 |
-
*
|
263 |
-
* @author Vova Feldman (@svovaf)
|
264 |
-
* @since 1.0.6
|
265 |
-
*
|
266 |
-
* @return string
|
267 |
-
*/
|
268 |
-
function fs_find_caller_plugin_file() {
|
269 |
-
/**
|
270 |
-
* All the code below will be executed once on activation.
|
271 |
-
* If the user changes the main plugin's file name, the file_exists()
|
272 |
-
* will catch it.
|
273 |
-
*/
|
274 |
-
if ( ! function_exists( 'get_plugins' ) ) {
|
275 |
-
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
276 |
-
}
|
277 |
-
|
278 |
-
$all_plugins = fs_get_plugins( true );
|
279 |
-
$all_plugins_paths = array();
|
280 |
-
|
281 |
-
// Get active plugin's main files real full names (might be symlinks).
|
282 |
-
foreach ( $all_plugins as $relative_path => $data ) {
|
283 |
-
$all_plugins_paths[] = fs_normalize_path( realpath( WP_PLUGIN_DIR . '/' . $relative_path ) );
|
284 |
-
}
|
285 |
-
|
286 |
-
$plugin_file = null;
|
287 |
-
for ( $i = 1, $bt = debug_backtrace(), $len = count( $bt ); $i < $len; $i ++ ) {
|
288 |
-
if ( empty( $bt[ $i ]['file'] ) ) {
|
289 |
-
continue;
|
290 |
-
}
|
291 |
-
|
292 |
-
if ( in_array( fs_normalize_path( $bt[ $i ]['file'] ), $all_plugins_paths ) ) {
|
293 |
-
$plugin_file = $bt[ $i ]['file'];
|
294 |
-
break;
|
295 |
-
}
|
296 |
-
}
|
297 |
-
|
298 |
-
if ( is_null( $plugin_file ) ) {
|
299 |
-
// Throw an error to the developer in case of some edge case dev environment.
|
300 |
-
wp_die(
|
301 |
-
'Freemius SDK couldn\'t find the plugin\'s main file. Please contact sdk@freemius.com with the current error.',
|
302 |
-
'Error',
|
303 |
-
array( 'back_link' => true )
|
304 |
-
);
|
305 |
-
}
|
306 |
-
|
307 |
-
return $plugin_file;
|
308 |
-
}
|
309 |
-
|
310 |
-
require_once dirname( __FILE__ ) . '/supplements/fs-essential-functions-1.1.7.1.php';
|
311 |
-
|
312 |
-
/**
|
313 |
-
* Update SDK newest version reference.
|
314 |
-
*
|
315 |
-
* @author Vova Feldman (@svovaf)
|
316 |
-
* @since 1.1.6
|
317 |
-
*
|
318 |
-
* @param string $sdk_relative_path
|
319 |
-
* @param string|bool $plugin_file
|
320 |
-
*
|
321 |
-
* @global $fs_active_plugins
|
322 |
-
*/
|
323 |
-
function fs_update_sdk_newest_version( $sdk_relative_path, $plugin_file = false ) {
|
324 |
-
/**
|
325 |
-
* If there is a plugin running an older version of FS (1.2.1 or below), the `fs_update_sdk_newest_version()`
|
326 |
-
* function in the older version will be used instead of this one. But since the older version is using
|
327 |
-
* the `is_plugin_active` function to check if a plugin is active, passing the theme's `plugin_path` to the
|
328 |
-
* `is_plugin_active` function will return false since the path is not a plugin path, so `in_activation` will be
|
329 |
-
* `true` for theme modules and the upgrading of the SDK version to 1.2.2 or newer version will work fine.
|
330 |
-
*
|
331 |
-
* Future versions that will call this function will use the proper logic here instead of just relying on the
|
332 |
-
* `is_plugin_active` function to fail for themes.
|
333 |
-
*
|
334 |
-
* @author Leo Fajardo (@leorw)
|
335 |
-
* @since 1.2.2
|
336 |
-
*/
|
337 |
-
|
338 |
-
global $fs_active_plugins;
|
339 |
-
|
340 |
-
$newest_sdk = $fs_active_plugins->plugins[ $sdk_relative_path ];
|
341 |
-
|
342 |
-
if ( ! is_string( $plugin_file ) ) {
|
343 |
-
$plugin_file = plugin_basename( fs_find_caller_plugin_file() );
|
344 |
-
}
|
345 |
-
|
346 |
-
if ( ! isset( $newest_sdk->type ) || 'theme' !== $newest_sdk->type ) {
|
347 |
-
if ( ! function_exists( 'is_plugin_active' ) ) {
|
348 |
-
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
349 |
-
}
|
350 |
-
|
351 |
-
$in_activation = ( ! is_plugin_active( $plugin_file ) );
|
352 |
-
} else {
|
353 |
-
$theme = wp_get_theme();
|
354 |
-
$in_activation = ( $newest_sdk->plugin_path == $theme->stylesheet );
|
355 |
-
}
|
356 |
-
|
357 |
-
$fs_active_plugins->newest = (object) array(
|
358 |
-
'plugin_path' => $plugin_file,
|
359 |
-
'sdk_path' => $sdk_relative_path,
|
360 |
-
'version' => $newest_sdk->version,
|
361 |
-
'in_activation' => $in_activation,
|
362 |
-
'timestamp' => time(),
|
363 |
-
);
|
364 |
-
|
365 |
-
// Update DB with latest SDK version and path.
|
366 |
-
update_option( 'fs_active_plugins', $fs_active_plugins );
|
367 |
-
}
|
368 |
-
|
369 |
-
/**
|
370 |
-
* Reorder the plugins load order so the plugin with the newest Freemius SDK is loaded first.
|
371 |
-
*
|
372 |
-
* @author Vova Feldman (@svovaf)
|
373 |
-
* @since 1.1.6
|
374 |
-
*
|
375 |
-
* @return bool Was plugin order changed. Return false if plugin was loaded first anyways.
|
376 |
-
*
|
377 |
-
* @global $fs_active_plugins
|
378 |
-
*/
|
379 |
-
function fs_newest_sdk_plugin_first() {
|
380 |
-
global $fs_active_plugins;
|
381 |
-
|
382 |
-
/**
|
383 |
-
* @todo Multi-site network activated plugin are always loaded prior to site plugins so if there's a plugin activated in the network mode that has an older version of the SDK of another plugin which is site activated that has new SDK version, the fs-essential-functions.php will be loaded from the older SDK. Same thing about MU plugins (loaded even before network activated plugins).
|
384 |
-
*
|
385 |
-
* @link https://github.com/Freemius/wordpress-sdk/issues/26
|
386 |
-
*/
|
387 |
-
|
388 |
-
$newest_sdk_plugin_path = $fs_active_plugins->newest->plugin_path;
|
389 |
-
|
390 |
-
$active_plugins = get_option( 'active_plugins', array() );
|
391 |
-
$updated_active_plugins = array( $newest_sdk_plugin_path );
|
392 |
-
|
393 |
-
$plugin_found = false;
|
394 |
-
$is_first_path = true;
|
395 |
-
|
396 |
-
foreach ( $active_plugins as $key => $plugin_path ) {
|
397 |
-
if ( $plugin_path === $newest_sdk_plugin_path ) {
|
398 |
-
if ( $is_first_path ) {
|
399 |
-
// if it's the first plugin already, no need to continue
|
400 |
-
return false;
|
401 |
-
}
|
402 |
-
|
403 |
-
$plugin_found = true;
|
404 |
-
|
405 |
-
// Skip the plugin (it is already added as the 1st item of $updated_active_plugins).
|
406 |
-
continue;
|
407 |
-
}
|
408 |
-
|
409 |
-
$updated_active_plugins[] = $plugin_path;
|
410 |
-
|
411 |
-
if ( $is_first_path ) {
|
412 |
-
$is_first_path = false;
|
413 |
-
}
|
414 |
-
}
|
415 |
-
|
416 |
-
if ( $plugin_found ) {
|
417 |
-
update_option( 'active_plugins', $updated_active_plugins );
|
418 |
-
|
419 |
-
return true;
|
420 |
-
}
|
421 |
-
|
422 |
-
if ( is_multisite() ) {
|
423 |
-
// Plugin is network active.
|
424 |
-
$network_active_plugins = get_site_option( 'active_sitewide_plugins', array() );
|
425 |
-
|
426 |
-
if ( isset( $network_active_plugins[ $newest_sdk_plugin_path ] ) ) {
|
427 |
-
reset( $network_active_plugins );
|
428 |
-
if ( $newest_sdk_plugin_path === key( $network_active_plugins ) ) {
|
429 |
-
// Plugin is already activated first on the network level.
|
430 |
-
return false;
|
431 |
-
} else {
|
432 |
-
$time = $network_active_plugins[ $newest_sdk_plugin_path ];
|
433 |
-
|
434 |
-
// Remove plugin from its current position.
|
435 |
-
unset( $network_active_plugins[ $newest_sdk_plugin_path ] );
|
436 |
-
|
437 |
-
// Set it to be included first.
|
438 |
-
$network_active_plugins = array( $newest_sdk_plugin_path => $time ) + $network_active_plugins;
|
439 |
-
|
440 |
-
update_site_option( 'active_sitewide_plugins', $network_active_plugins );
|
441 |
-
|
442 |
-
return true;
|
443 |
-
}
|
444 |
-
}
|
445 |
-
}
|
446 |
-
|
447 |
-
return false;
|
448 |
-
}
|
449 |
-
|
450 |
-
/**
|
451 |
-
* Go over all Freemius SDKs in the system and find and "remember"
|
452 |
-
* the newest SDK which is associated with an active plugin.
|
453 |
-
*
|
454 |
-
* @author Vova Feldman (@svovaf)
|
455 |
-
* @since 1.1.6
|
456 |
-
*
|
457 |
-
* @global $fs_active_plugins
|
458 |
-
*/
|
459 |
-
function fs_fallback_to_newest_active_sdk() {
|
460 |
-
global $fs_active_plugins;
|
461 |
-
|
462 |
-
/**
|
463 |
-
* @var object $newest_sdk_data
|
464 |
-
*/
|
465 |
-
$newest_sdk_data = null;
|
466 |
-
$newest_sdk_path = null;
|
467 |
-
|
468 |
-
foreach ( $fs_active_plugins->plugins as $sdk_relative_path => $data ) {
|
469 |
-
if ( is_null( $newest_sdk_data ) || version_compare( $data->version, $newest_sdk_data->version, '>' )
|
470 |
-
) {
|
471 |
-
// If plugin inactive or SDK starter file doesn't exist, remove SDK reference.
|
472 |
-
if ( 'plugin' === $data->type ) {
|
473 |
-
$is_module_active = is_plugin_active( $data->plugin_path );
|
474 |
-
} else {
|
475 |
-
$active_theme = wp_get_theme();
|
476 |
-
$is_module_active = ( $data->plugin_path === $active_theme->get_template() );
|
477 |
-
}
|
478 |
-
|
479 |
-
$is_sdk_exists = file_exists( fs_normalize_path( WP_PLUGIN_DIR . '/' . $sdk_relative_path . '/start.php' ) );
|
480 |
-
|
481 |
-
if ( ! $is_module_active || ! $is_sdk_exists ) {
|
482 |
-
unset( $fs_active_plugins->plugins[ $sdk_relative_path ] );
|
483 |
-
|
484 |
-
// No need to store the data since it will be stored in fs_update_sdk_newest_version()
|
485 |
-
// or explicitly with update_option().
|
486 |
-
} else {
|
487 |
-
$newest_sdk_data = $data;
|
488 |
-
$newest_sdk_path = $sdk_relative_path;
|
489 |
-
}
|
490 |
-
}
|
491 |
-
}
|
492 |
-
|
493 |
-
if ( is_null( $newest_sdk_data ) ) {
|
494 |
-
// Couldn't find any SDK reference.
|
495 |
-
$fs_active_plugins = new stdClass();
|
496 |
-
update_option( 'fs_active_plugins', $fs_active_plugins );
|
497 |
-
} else {
|
498 |
-
fs_update_sdk_newest_version( $newest_sdk_path, $newest_sdk_data->plugin_path );
|
499 |
-
}
|
500 |
}
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* IMPORTANT:
|
4 |
+
* This file will be loaded based on the order of the plugins/themes load.
|
5 |
+
* If there's a theme and a plugin using Freemius, the plugin's essential
|
6 |
+
* file will always load first.
|
7 |
+
*
|
8 |
+
* @package Freemius
|
9 |
+
* @copyright Copyright (c) 2015, Freemius, Inc.
|
10 |
+
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
|
11 |
+
* @since 1.1.5
|
12 |
+
*/
|
13 |
+
|
14 |
+
if ( ! function_exists( 'fs_normalize_path' ) ) {
|
15 |
+
if ( function_exists( 'wp_normalize_path' ) ) {
|
16 |
+
/**
|
17 |
+
* Normalize a filesystem path.
|
18 |
+
*
|
19 |
+
* Replaces backslashes with forward slashes for Windows systems, and ensures
|
20 |
+
* no duplicate slashes exist.
|
21 |
+
*
|
22 |
+
* @param string $path Path to normalize.
|
23 |
+
*
|
24 |
+
* @return string Normalized path.
|
25 |
+
*/
|
26 |
+
function fs_normalize_path( $path ) {
|
27 |
+
return wp_normalize_path( $path );
|
28 |
+
}
|
29 |
+
} else {
|
30 |
+
function fs_normalize_path( $path ) {
|
31 |
+
$path = str_replace( '\\', '/', $path );
|
32 |
+
$path = preg_replace( '|/+|', '/', $path );
|
33 |
+
|
34 |
+
return $path;
|
35 |
+
}
|
36 |
+
}
|
37 |
+
}
|
38 |
+
|
39 |
+
require_once dirname( __FILE__ ) . '/supplements/fs-essential-functions-2.2.1.php';
|
40 |
+
|
41 |
+
#region Core Redirect (copied from BuddyPress) -----------------------------------------
|
42 |
+
|
43 |
+
if ( ! function_exists( 'fs_redirect' ) ) {
|
44 |
+
/**
|
45 |
+
* Redirects to another page, with a workaround for the IIS Set-Cookie bug.
|
46 |
+
*
|
47 |
+
* @link http://support.microsoft.com/kb/q176113/
|
48 |
+
* @since 1.5.1
|
49 |
+
* @uses apply_filters() Calls 'wp_redirect' hook on $location and $status.
|
50 |
+
*
|
51 |
+
* @param string $location The path to redirect to.
|
52 |
+
* @param bool $exit If true, exit after redirect (Since 1.2.1.5).
|
53 |
+
* @param int $status Status code to use.
|
54 |
+
*
|
55 |
+
* @return bool False if $location is not set
|
56 |
+
*/
|
57 |
+
function fs_redirect( $location, $exit = true, $status = 302 ) {
|
58 |
+
global $is_IIS;
|
59 |
+
|
60 |
+
$file = '';
|
61 |
+
$line = '';
|
62 |
+
if ( headers_sent($file, $line) ) {
|
63 |
+
if ( WP_FS__DEBUG_SDK && class_exists( 'FS_Admin_Notices' ) ) {
|
64 |
+
$notices = FS_Admin_Notices::instance( 'global' );
|
65 |
+
|
66 |
+
$notices->add( "Freemius failed to redirect the page because the headers have been already sent from line <b><code>{$line}</code></b> in file <b><code>{$file}</code></b>. If it's unexpected, it usually happens due to invalid space and/or EOL character(s).", 'Oops...', 'error' );
|
67 |
+
}
|
68 |
+
|
69 |
+
return false;
|
70 |
+
}
|
71 |
+
|
72 |
+
if ( defined( 'DOING_AJAX' ) ) {
|
73 |
+
// Don't redirect on AJAX calls.
|
74 |
+
return false;
|
75 |
+
}
|
76 |
+
|
77 |
+
if ( ! $location ) // allows the wp_redirect filter to cancel a redirect
|
78 |
+
{
|
79 |
+
return false;
|
80 |
+
}
|
81 |
+
|
82 |
+
$location = fs_sanitize_redirect( $location );
|
83 |
+
|
84 |
+
if ( $is_IIS ) {
|
85 |
+
header( "Refresh: 0;url=$location" );
|
86 |
+
} else {
|
87 |
+
if ( php_sapi_name() != 'cgi-fcgi' ) {
|
88 |
+
status_header( $status );
|
89 |
+
} // This causes problems on IIS and some FastCGI setups
|
90 |
+
header( "Location: $location" );
|
91 |
+
}
|
92 |
+
|
93 |
+
if ( $exit ) {
|
94 |
+
exit();
|
95 |
+
}
|
96 |
+
|
97 |
+
return true;
|
98 |
+
}
|
99 |
+
|
100 |
+
if ( ! function_exists( 'fs_sanitize_redirect' ) ) {
|
101 |
+
/**
|
102 |
+
* Sanitizes a URL for use in a redirect.
|
103 |
+
*
|
104 |
+
* @since 2.3
|
105 |
+
*
|
106 |
+
* @param string $location
|
107 |
+
*
|
108 |
+
* @return string redirect-sanitized URL
|
109 |
+
*/
|
110 |
+
function fs_sanitize_redirect( $location ) {
|
111 |
+
$location = preg_replace( '|[^a-z0-9-~+_.?#=&;,/:%!]|i', '', $location );
|
112 |
+
$location = fs_kses_no_null( $location );
|
113 |
+
|
114 |
+
// remove %0d and %0a from location
|
115 |
+
$strip = array( '%0d', '%0a' );
|
116 |
+
$found = true;
|
117 |
+
while ( $found ) {
|
118 |
+
$found = false;
|
119 |
+
foreach ( (array) $strip as $val ) {
|
120 |
+
while ( strpos( $location, $val ) !== false ) {
|
121 |
+
$found = true;
|
122 |
+
$location = str_replace( $val, '', $location );
|
123 |
+
}
|
124 |
+
}
|
125 |
+
}
|
126 |
+
|
127 |
+
return $location;
|
128 |
+
}
|
129 |
+
}
|
130 |
+
|
131 |
+
if ( ! function_exists( 'fs_kses_no_null' ) ) {
|
132 |
+
/**
|
133 |
+
* Removes any NULL characters in $string.
|
134 |
+
*
|
135 |
+
* @since 1.0.0
|
136 |
+
*
|
137 |
+
* @param string $string
|
138 |
+
*
|
139 |
+
* @return string
|
140 |
+
*/
|
141 |
+
function fs_kses_no_null( $string ) {
|
142 |
+
$string = preg_replace( '/\0+/', '', $string );
|
143 |
+
$string = preg_replace( '/(\\\\0)+/', '', $string );
|
144 |
+
|
145 |
+
return $string;
|
146 |
+
}
|
147 |
+
}
|
148 |
+
}
|
149 |
+
|
150 |
+
#endregion Core Redirect (copied from BuddyPress) -----------------------------------------
|
151 |
+
|
152 |
+
if ( ! function_exists( '__fs' ) ) {
|
153 |
+
global $fs_text_overrides;
|
154 |
+
|
155 |
+
if ( ! isset( $fs_text_overrides ) ) {
|
156 |
+
$fs_text_overrides = array();
|
157 |
+
}
|
158 |
+
|
159 |
+
/**
|
160 |
+
* Retrieve a translated text by key.
|
161 |
+
*
|
162 |
+
* @deprecated Use `fs_text()` instead since methods starting with `__` trigger warnings in Php 7.
|
163 |
+
* @todo Remove this method in the future.
|
164 |
+
*
|
165 |
+
* @author Vova Feldman (@svovaf)
|
166 |
+
* @since 1.1.4
|
167 |
+
*
|
168 |
+
* @param string $key
|
169 |
+
* @param string $slug
|
170 |
+
*
|
171 |
+
* @return string
|
172 |
+
*
|
173 |
+
* @global $fs_text, $fs_text_overrides
|
174 |
+
*/
|
175 |
+
function __fs( $key, $slug = 'freemius' ) {
|
176 |
+
_deprecated_function( __FUNCTION__, '2.0.0', 'fs_text()' );
|
177 |
+
|
178 |
+
global $fs_text,
|
179 |
+
$fs_module_info_text,
|
180 |
+
$fs_text_overrides;
|
181 |
+
|
182 |
+
if ( isset( $fs_text_overrides[ $slug ] ) ) {
|
183 |
+
if ( isset( $fs_text_overrides[ $slug ][ $key ] ) ) {
|
184 |
+
return $fs_text_overrides[ $slug ][ $key ];
|
185 |
+
}
|
186 |
+
|
187 |
+
$lower_key = strtolower( $key );
|
188 |
+
if ( isset( $fs_text_overrides[ $slug ][ $lower_key ] ) ) {
|
189 |
+
return $fs_text_overrides[ $slug ][ $lower_key ];
|
190 |
+
}
|
191 |
+
}
|
192 |
+
|
193 |
+
if ( ! isset( $fs_text ) ) {
|
194 |
+
$dir = defined( 'WP_FS__DIR_INCLUDES' ) ?
|
195 |
+
WP_FS__DIR_INCLUDES :
|
196 |
+
dirname( __FILE__ );
|
197 |
+
|
198 |
+
require_once $dir . '/i18n.php';
|
199 |
+
}
|
200 |
+
|
201 |
+
if ( isset( $fs_text[ $key ] ) ) {
|
202 |
+
return $fs_text[ $key ];
|
203 |
+
}
|
204 |
+
|
205 |
+
if ( isset( $fs_module_info_text[ $key ] ) ) {
|
206 |
+
return $fs_module_info_text[ $key ];
|
207 |
+
}
|
208 |
+
|
209 |
+
return $key;
|
210 |
+
}
|
211 |
+
|
212 |
+
/**
|
213 |
+
* Output a translated text by key.
|
214 |
+
*
|
215 |
+
* @deprecated Use `fs_echo()` instead for consistency with `fs_text()`.
|
216 |
+
*
|
217 |
+
* @todo Remove this method in the future.
|
218 |
+
*
|
219 |
+
* @author Vova Feldman (@svovaf)
|
220 |
+
* @since 1.1.4
|
221 |
+
*
|
222 |
+
* @param string $key
|
223 |
+
* @param string $slug
|
224 |
+
*/
|
225 |
+
function _efs( $key, $slug = 'freemius' ) {
|
226 |
+
fs_echo( $key, $slug );
|
227 |
+
}
|
228 |
+
}
|
229 |
+
|
230 |
+
if ( ! function_exists( 'fs_get_ip' ) ) {
|
231 |
+
/**
|
232 |
+
* Get client IP.
|
233 |
+
*
|
234 |
+
* @author Vova Feldman (@svovaf)
|
235 |
+
* @since 1.1.2
|
236 |
+
*
|
237 |
+
* @return string|null
|
238 |
+
*/
|
239 |
+
function fs_get_ip() {
|
240 |
+
$fields = array(
|
241 |
+
'HTTP_CF_CONNECTING_IP',
|
242 |
+
'HTTP_CLIENT_IP',
|
243 |
+
'HTTP_X_FORWARDED_FOR',
|
244 |
+
'HTTP_X_FORWARDED',
|
245 |
+
'HTTP_FORWARDED_FOR',
|
246 |
+
'HTTP_FORWARDED',
|
247 |
+
'REMOTE_ADDR',
|
248 |
+
);
|
249 |
+
|
250 |
+
foreach ( $fields as $ip_field ) {
|
251 |
+
if ( ! empty( $_SERVER[ $ip_field ] ) ) {
|
252 |
+
return $_SERVER[ $ip_field ];
|
253 |
+
}
|
254 |
+
}
|
255 |
+
|
256 |
+
return null;
|
257 |
+
}
|
258 |
+
}
|
259 |
+
|
260 |
+
/**
|
261 |
+
* Leverage backtrace to find caller plugin main file path.
|
262 |
+
*
|
263 |
+
* @author Vova Feldman (@svovaf)
|
264 |
+
* @since 1.0.6
|
265 |
+
*
|
266 |
+
* @return string
|
267 |
+
*/
|
268 |
+
function fs_find_caller_plugin_file() {
|
269 |
+
/**
|
270 |
+
* All the code below will be executed once on activation.
|
271 |
+
* If the user changes the main plugin's file name, the file_exists()
|
272 |
+
* will catch it.
|
273 |
+
*/
|
274 |
+
if ( ! function_exists( 'get_plugins' ) ) {
|
275 |
+
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
276 |
+
}
|
277 |
+
|
278 |
+
$all_plugins = fs_get_plugins( true );
|
279 |
+
$all_plugins_paths = array();
|
280 |
+
|
281 |
+
// Get active plugin's main files real full names (might be symlinks).
|
282 |
+
foreach ( $all_plugins as $relative_path => $data ) {
|
283 |
+
$all_plugins_paths[] = fs_normalize_path( realpath( WP_PLUGIN_DIR . '/' . $relative_path ) );
|
284 |
+
}
|
285 |
+
|
286 |
+
$plugin_file = null;
|
287 |
+
for ( $i = 1, $bt = debug_backtrace(), $len = count( $bt ); $i < $len; $i ++ ) {
|
288 |
+
if ( empty( $bt[ $i ]['file'] ) ) {
|
289 |
+
continue;
|
290 |
+
}
|
291 |
+
|
292 |
+
if ( in_array( fs_normalize_path( $bt[ $i ]['file'] ), $all_plugins_paths ) ) {
|
293 |
+
$plugin_file = $bt[ $i ]['file'];
|
294 |
+
break;
|
295 |
+
}
|
296 |
+
}
|
297 |
+
|
298 |
+
if ( is_null( $plugin_file ) ) {
|
299 |
+
// Throw an error to the developer in case of some edge case dev environment.
|
300 |
+
wp_die(
|
301 |
+
'Freemius SDK couldn\'t find the plugin\'s main file. Please contact sdk@freemius.com with the current error.',
|
302 |
+
'Error',
|
303 |
+
array( 'back_link' => true )
|
304 |
+
);
|
305 |
+
}
|
306 |
+
|
307 |
+
return $plugin_file;
|
308 |
+
}
|
309 |
+
|
310 |
+
require_once dirname( __FILE__ ) . '/supplements/fs-essential-functions-1.1.7.1.php';
|
311 |
+
|
312 |
+
/**
|
313 |
+
* Update SDK newest version reference.
|
314 |
+
*
|
315 |
+
* @author Vova Feldman (@svovaf)
|
316 |
+
* @since 1.1.6
|
317 |
+
*
|
318 |
+
* @param string $sdk_relative_path
|
319 |
+
* @param string|bool $plugin_file
|
320 |
+
*
|
321 |
+
* @global $fs_active_plugins
|
322 |
+
*/
|
323 |
+
function fs_update_sdk_newest_version( $sdk_relative_path, $plugin_file = false ) {
|
324 |
+
/**
|
325 |
+
* If there is a plugin running an older version of FS (1.2.1 or below), the `fs_update_sdk_newest_version()`
|
326 |
+
* function in the older version will be used instead of this one. But since the older version is using
|
327 |
+
* the `is_plugin_active` function to check if a plugin is active, passing the theme's `plugin_path` to the
|
328 |
+
* `is_plugin_active` function will return false since the path is not a plugin path, so `in_activation` will be
|
329 |
+
* `true` for theme modules and the upgrading of the SDK version to 1.2.2 or newer version will work fine.
|
330 |
+
*
|
331 |
+
* Future versions that will call this function will use the proper logic here instead of just relying on the
|
332 |
+
* `is_plugin_active` function to fail for themes.
|
333 |
+
*
|
334 |
+
* @author Leo Fajardo (@leorw)
|
335 |
+
* @since 1.2.2
|
336 |
+
*/
|
337 |
+
|
338 |
+
global $fs_active_plugins;
|
339 |
+
|
340 |
+
$newest_sdk = $fs_active_plugins->plugins[ $sdk_relative_path ];
|
341 |
+
|
342 |
+
if ( ! is_string( $plugin_file ) ) {
|
343 |
+
$plugin_file = plugin_basename( fs_find_caller_plugin_file() );
|
344 |
+
}
|
345 |
+
|
346 |
+
if ( ! isset( $newest_sdk->type ) || 'theme' !== $newest_sdk->type ) {
|
347 |
+
if ( ! function_exists( 'is_plugin_active' ) ) {
|
348 |
+
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
349 |
+
}
|
350 |
+
|
351 |
+
$in_activation = ( ! is_plugin_active( $plugin_file ) );
|
352 |
+
} else {
|
353 |
+
$theme = wp_get_theme();
|
354 |
+
$in_activation = ( $newest_sdk->plugin_path == $theme->stylesheet );
|
355 |
+
}
|
356 |
+
|
357 |
+
$fs_active_plugins->newest = (object) array(
|
358 |
+
'plugin_path' => $plugin_file,
|
359 |
+
'sdk_path' => $sdk_relative_path,
|
360 |
+
'version' => $newest_sdk->version,
|
361 |
+
'in_activation' => $in_activation,
|
362 |
+
'timestamp' => time(),
|
363 |
+
);
|
364 |
+
|
365 |
+
// Update DB with latest SDK version and path.
|
366 |
+
update_option( 'fs_active_plugins', $fs_active_plugins );
|
367 |
+
}
|
368 |
+
|
369 |
+
/**
|
370 |
+
* Reorder the plugins load order so the plugin with the newest Freemius SDK is loaded first.
|
371 |
+
*
|
372 |
+
* @author Vova Feldman (@svovaf)
|
373 |
+
* @since 1.1.6
|
374 |
+
*
|
375 |
+
* @return bool Was plugin order changed. Return false if plugin was loaded first anyways.
|
376 |
+
*
|
377 |
+
* @global $fs_active_plugins
|
378 |
+
*/
|
379 |
+
function fs_newest_sdk_plugin_first() {
|
380 |
+
global $fs_active_plugins;
|
381 |
+
|
382 |
+
/**
|
383 |
+
* @todo Multi-site network activated plugin are always loaded prior to site plugins so if there's a plugin activated in the network mode that has an older version of the SDK of another plugin which is site activated that has new SDK version, the fs-essential-functions.php will be loaded from the older SDK. Same thing about MU plugins (loaded even before network activated plugins).
|
384 |
+
*
|
385 |
+
* @link https://github.com/Freemius/wordpress-sdk/issues/26
|
386 |
+
*/
|
387 |
+
|
388 |
+
$newest_sdk_plugin_path = $fs_active_plugins->newest->plugin_path;
|
389 |
+
|
390 |
+
$active_plugins = get_option( 'active_plugins', array() );
|
391 |
+
$updated_active_plugins = array( $newest_sdk_plugin_path );
|
392 |
+
|
393 |
+
$plugin_found = false;
|
394 |
+
$is_first_path = true;
|
395 |
+
|
396 |
+
foreach ( $active_plugins as $key => $plugin_path ) {
|
397 |
+
if ( $plugin_path === $newest_sdk_plugin_path ) {
|
398 |
+
if ( $is_first_path ) {
|
399 |
+
// if it's the first plugin already, no need to continue
|
400 |
+
return false;
|
401 |
+
}
|
402 |
+
|
403 |
+
$plugin_found = true;
|
404 |
+
|
405 |
+
// Skip the plugin (it is already added as the 1st item of $updated_active_plugins).
|
406 |
+
continue;
|
407 |
+
}
|
408 |
+
|
409 |
+
$updated_active_plugins[] = $plugin_path;
|
410 |
+
|
411 |
+
if ( $is_first_path ) {
|
412 |
+
$is_first_path = false;
|
413 |
+
}
|
414 |
+
}
|
415 |
+
|
416 |
+
if ( $plugin_found ) {
|
417 |
+
update_option( 'active_plugins', $updated_active_plugins );
|
418 |
+
|
419 |
+
return true;
|
420 |
+
}
|
421 |
+
|
422 |
+
if ( is_multisite() ) {
|
423 |
+
// Plugin is network active.
|
424 |
+
$network_active_plugins = get_site_option( 'active_sitewide_plugins', array() );
|
425 |
+
|
426 |
+
if ( isset( $network_active_plugins[ $newest_sdk_plugin_path ] ) ) {
|
427 |
+
reset( $network_active_plugins );
|
428 |
+
if ( $newest_sdk_plugin_path === key( $network_active_plugins ) ) {
|
429 |
+
// Plugin is already activated first on the network level.
|
430 |
+
return false;
|
431 |
+
} else {
|
432 |
+
$time = $network_active_plugins[ $newest_sdk_plugin_path ];
|
433 |
+
|
434 |
+
// Remove plugin from its current position.
|
435 |
+
unset( $network_active_plugins[ $newest_sdk_plugin_path ] );
|
436 |
+
|
437 |
+
// Set it to be included first.
|
438 |
+
$network_active_plugins = array( $newest_sdk_plugin_path => $time ) + $network_active_plugins;
|
439 |
+
|
440 |
+
update_site_option( 'active_sitewide_plugins', $network_active_plugins );
|
441 |
+
|
442 |
+
return true;
|
443 |
+
}
|
444 |
+
}
|
445 |
+
}
|
446 |
+
|
447 |
+
return false;
|
448 |
+
}
|
449 |
+
|
450 |
+
/**
|
451 |
+
* Go over all Freemius SDKs in the system and find and "remember"
|
452 |
+
* the newest SDK which is associated with an active plugin.
|
453 |
+
*
|
454 |
+
* @author Vova Feldman (@svovaf)
|
455 |
+
* @since 1.1.6
|
456 |
+
*
|
457 |
+
* @global $fs_active_plugins
|
458 |
+
*/
|
459 |
+
function fs_fallback_to_newest_active_sdk() {
|
460 |
+
global $fs_active_plugins;
|
461 |
+
|
462 |
+
/**
|
463 |
+
* @var object $newest_sdk_data
|
464 |
+
*/
|
465 |
+
$newest_sdk_data = null;
|
466 |
+
$newest_sdk_path = null;
|
467 |
+
|
468 |
+
foreach ( $fs_active_plugins->plugins as $sdk_relative_path => $data ) {
|
469 |
+
if ( is_null( $newest_sdk_data ) || version_compare( $data->version, $newest_sdk_data->version, '>' )
|
470 |
+
) {
|
471 |
+
// If plugin inactive or SDK starter file doesn't exist, remove SDK reference.
|
472 |
+
if ( 'plugin' === $data->type ) {
|
473 |
+
$is_module_active = is_plugin_active( $data->plugin_path );
|
474 |
+
} else {
|
475 |
+
$active_theme = wp_get_theme();
|
476 |
+
$is_module_active = ( $data->plugin_path === $active_theme->get_template() );
|
477 |
+
}
|
478 |
+
|
479 |
+
$is_sdk_exists = file_exists( fs_normalize_path( WP_PLUGIN_DIR . '/' . $sdk_relative_path . '/start.php' ) );
|
480 |
+
|
481 |
+
if ( ! $is_module_active || ! $is_sdk_exists ) {
|
482 |
+
unset( $fs_active_plugins->plugins[ $sdk_relative_path ] );
|
483 |
+
|
484 |
+
// No need to store the data since it will be stored in fs_update_sdk_newest_version()
|
485 |
+
// or explicitly with update_option().
|
486 |
+
} else {
|
487 |
+
$newest_sdk_data = $data;
|
488 |
+
$newest_sdk_path = $sdk_relative_path;
|
489 |
+
}
|
490 |
+
}
|
491 |
+
}
|
492 |
+
|
493 |
+
if ( is_null( $newest_sdk_data ) ) {
|
494 |
+
// Couldn't find any SDK reference.
|
495 |
+
$fs_active_plugins = new stdClass();
|
496 |
+
update_option( 'fs_active_plugins', $fs_active_plugins );
|
497 |
+
} else {
|
498 |
+
fs_update_sdk_newest_version( $newest_sdk_path, $newest_sdk_data->plugin_path );
|
499 |
+
}
|
500 |
}
|
freemius/includes/fs-plugin-info-dialog.php
CHANGED
@@ -1,1644 +1,1644 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* @package Freemius
|
4 |
-
* @copyright Copyright (c) 2015, Freemius, Inc.
|
5 |
-
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
|
6 |
-
* @since 1.0.6
|
7 |
-
*/
|
8 |
-
|
9 |
-
if ( ! defined( 'ABSPATH' ) ) {
|
10 |
-
exit;
|
11 |
-
}
|
12 |
-
|
13 |
-
/**
|
14 |
-
* Class FS_Plugin_Info_Dialog
|
15 |
-
*
|
16 |
-
* @author Vova Feldman (@svovaf)
|
17 |
-
* @since 1.1.7
|
18 |
-
*/
|
19 |
-
class FS_Plugin_Info_Dialog {
|
20 |
-
/**
|
21 |
-
* @since 1.1.7
|
22 |
-
*
|
23 |
-
* @var FS_Logger
|
24 |
-
*/
|
25 |
-
private $_logger;
|
26 |
-
|
27 |
-
/**
|
28 |
-
* @since 1.1.7
|
29 |
-
*
|
30 |
-
* @var Freemius
|
31 |
-
*/
|
32 |
-
private $_fs;
|
33 |
-
|
34 |
-
/**
|
35 |
-
* Collection of plugin installation, update, download, activation, and purchase actions. This is used in
|
36 |
-
* populating the actions dropdown list when there are at least 2 actions. If there's only 1 action, a button
|
37 |
-
* is used instead.
|
38 |
-
*
|
39 |
-
* @author Leo Fajardo (@leorw)
|
40 |
-
* @since 2.3.0
|
41 |
-
*
|
42 |
-
* @var string[]
|
43 |
-
*/
|
44 |
-
private $actions;
|
45 |
-
|
46 |
-
/**
|
47 |
-
* Contains plugin status information that is used to determine which actions should be part of the actions
|
48 |
-
* dropdown list.
|
49 |
-
*
|
50 |
-
* @author Leo Fajardo (@leorw)
|
51 |
-
* @since 2.3.0
|
52 |
-
*
|
53 |
-
* @var string[]
|
54 |
-
*/
|
55 |
-
private $status;
|
56 |
-
|
57 |
-
function __construct( Freemius $fs ) {
|
58 |
-
$this->_fs = $fs;
|
59 |
-
|
60 |
-
$this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $fs->get_slug() . '_info', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
|
61 |
-
|
62 |
-
// Remove default plugin information action.
|
63 |
-
remove_all_actions( 'install_plugins_pre_plugin-information' );
|
64 |
-
|
65 |
-
// Override action with custom plugins function for add-ons.
|
66 |
-
add_action( 'install_plugins_pre_plugin-information', array( &$this, 'install_plugin_information' ) );
|
67 |
-
|
68 |
-
// Override request for plugin information for Add-ons.
|
69 |
-
add_filter(
|
70 |
-
'fs_plugins_api',
|
71 |
-
array( &$this, '_get_addon_info_filter' ),
|
72 |
-
WP_FS__DEFAULT_PRIORITY, 3 );
|
73 |
-
}
|
74 |
-
|
75 |
-
/**
|
76 |
-
* Generate add-on plugin information.
|
77 |
-
*
|
78 |
-
* @author Vova Feldman (@svovaf)
|
79 |
-
* @since 1.0.6
|
80 |
-
*
|
81 |
-
* @param array $data
|
82 |
-
* @param string $action
|
83 |
-
* @param object|null $args
|
84 |
-
*
|
85 |
-
* @return array|null
|
86 |
-
*/
|
87 |
-
function _get_addon_info_filter( $data, $action = '', $args = null ) {
|
88 |
-
$this->_logger->entrance();
|
89 |
-
|
90 |
-
$parent_plugin_id = fs_request_get( 'parent_plugin_id', $this->_fs->get_id() );
|
91 |
-
|
92 |
-
if ( $this->_fs->get_id() != $parent_plugin_id ||
|
93 |
-
( 'plugin_information' !== $action ) ||
|
94 |
-
! isset( $args->slug )
|
95 |
-
) {
|
96 |
-
return $data;
|
97 |
-
}
|
98 |
-
|
99 |
-
// Find add-on by slug.
|
100 |
-
$selected_addon = $this->_fs->get_addon_by_slug( $args->slug, WP_FS__DEV_MODE );
|
101 |
-
|
102 |
-
if ( false === $selected_addon ) {
|
103 |
-
return $data;
|
104 |
-
}
|
105 |
-
|
106 |
-
if ( ! isset( $selected_addon->info ) ) {
|
107 |
-
// Setup some default info.
|
108 |
-
$selected_addon->info = new stdClass();
|
109 |
-
$selected_addon->info->selling_point_0 = 'Selling Point 1';
|
110 |
-
$selected_addon->info->selling_point_1 = 'Selling Point 2';
|
111 |
-
$selected_addon->info->selling_point_2 = 'Selling Point 3';
|
112 |
-
$selected_addon->info->description = '<p>Tell your users all about your add-on</p>';
|
113 |
-
}
|
114 |
-
|
115 |
-
fs_enqueue_local_style( 'fs_addons', '/admin/add-ons.css' );
|
116 |
-
|
117 |
-
$data = $args;
|
118 |
-
|
119 |
-
$has_free_plan = false;
|
120 |
-
$has_paid_plan = false;
|
121 |
-
|
122 |
-
// Load add-on pricing.
|
123 |
-
$has_pricing = false;
|
124 |
-
$has_features = false;
|
125 |
-
$plans = false;
|
126 |
-
|
127 |
-
$result = $this->_fs->get_api_plugin_scope()->get( $this->_fs->add_show_pending( "/addons/{$selected_addon->id}/pricing.json?type=visible" ) );
|
128 |
-
|
129 |
-
if ( ! isset( $result->error ) ) {
|
130 |
-
$plans = $result->plans;
|
131 |
-
|
132 |
-
if ( is_array( $plans ) ) {
|
133 |
-
for ( $i = 0, $len = count( $plans ); $i < $len; $i ++ ) {
|
134 |
-
$pricing = isset( $plans[ $i ]->pricing ) ? $plans[ $i ]->pricing : null;
|
135 |
-
$features = isset( $plans[ $i ]->features ) ? $plans[ $i ]->features : null;
|
136 |
-
|
137 |
-
$plans[ $i ] = new FS_Plugin_Plan( $plans[ $i ] );
|
138 |
-
$plan = $plans[ $i ];
|
139 |
-
|
140 |
-
if ( 'free' == $plans[ $i ]->name ||
|
141 |
-
! is_array( $pricing ) ||
|
142 |
-
0 == count( $pricing )
|
143 |
-
) {
|
144 |
-
$has_free_plan = true;
|
145 |
-
}
|
146 |
-
|
147 |
-
if ( is_array( $pricing ) && 0 < count( $pricing ) ) {
|
148 |
-
$filtered_pricing = array();
|
149 |
-
|
150 |
-
foreach ( $pricing as $prices ) {
|
151 |
-
$prices = new FS_Pricing( $prices );
|
152 |
-
|
153 |
-
if ( ! $prices->is_usd() ) {
|
154 |
-
/**
|
155 |
-
* Skip non-USD pricing.
|
156 |
-
*
|
157 |
-
* @author Leo Fajardo (@leorw)
|
158 |
-
* @since 2.3.1
|
159 |
-
*/
|
160 |
-
continue;
|
161 |
-
}
|
162 |
-
|
163 |
-
if ( ( $prices->has_monthly() && $prices->monthly_price > 1.0 ) ||
|
164 |
-
( $prices->has_annual() && $prices->annual_price > 1.0 ) ||
|
165 |
-
( $prices->has_lifetime() && $prices->lifetime_price > 1.0 )
|
166 |
-
) {
|
167 |
-
$filtered_pricing[] = $prices;
|
168 |
-
}
|
169 |
-
}
|
170 |
-
|
171 |
-
if ( ! empty( $filtered_pricing ) ) {
|
172 |
-
$has_paid_plan = true;
|
173 |
-
|
174 |
-
$plan->pricing = $filtered_pricing;
|
175 |
-
|
176 |
-
$has_pricing = true;
|
177 |
-
}
|
178 |
-
}
|
179 |
-
|
180 |
-
if ( is_array( $features ) && 0 < count( $features ) ) {
|
181 |
-
$plan->features = $features;
|
182 |
-
|
183 |
-
$has_features = true;
|
184 |
-
}
|
185 |
-
}
|
186 |
-
}
|
187 |
-
}
|
188 |
-
|
189 |
-
$latest = null;
|
190 |
-
|
191 |
-
if ( ! $has_paid_plan && $selected_addon->is_wp_org_compliant ) {
|
192 |
-
$repo_data = FS_Plugin_Updater::_fetch_plugin_info_from_repository(
|
193 |
-
'plugin_information', (object) array(
|
194 |
-
'slug' => $selected_addon->slug,
|
195 |
-
'is_ssl' => is_ssl(),
|
196 |
-
'fields' => array(
|
197 |
-
'banners' => true,
|
198 |
-
'reviews' => true,
|
199 |
-
'downloaded' => false,
|
200 |
-
'active_installs' => true
|
201 |
-
)
|
202 |
-
) );
|
203 |
-
|
204 |
-
if ( ! empty( $repo_data ) ) {
|
205 |
-
$data = $repo_data;
|
206 |
-
$data->wp_org_missing = false;
|
207 |
-
} else {
|
208 |
-
// Couldn't find plugin on .org.
|
209 |
-
$selected_addon->is_wp_org_compliant = false;
|
210 |
-
|
211 |
-
// Plugin is missing, not on Freemius nor WP.org.
|
212 |
-
$data->wp_org_missing = true;
|
213 |
-
}
|
214 |
-
|
215 |
-
$data->fs_missing = ( ! $has_free_plan || $data->wp_org_missing );
|
216 |
-
} else {
|
217 |
-
$data->has_purchased_license = false;
|
218 |
-
$data->wp_org_missing = false;
|
219 |
-
|
220 |
-
$fs_addon = null;
|
221 |
-
$current_addon_version = false;
|
222 |
-
if ( $this->_fs->is_addon_activated( $selected_addon->id ) ) {
|
223 |
-
$fs_addon = $this->_fs->get_addon_instance( $selected_addon->id );
|
224 |
-
$current_addon_version = $fs_addon->get_plugin_version();
|
225 |
-
} else if ( $this->_fs->is_addon_installed( $selected_addon->id ) ) {
|
226 |
-
$addon_plugin_data = get_plugin_data(
|
227 |
-
( WP_PLUGIN_DIR . '/' . $this->_fs->get_addon_basename( $selected_addon->id ) ),
|
228 |
-
false,
|
229 |
-
false
|
230 |
-
);
|
231 |
-
|
232 |
-
if ( ! empty( $addon_plugin_data ) ) {
|
233 |
-
$current_addon_version = $addon_plugin_data['Version'];
|
234 |
-
}
|
235 |
-
}
|
236 |
-
|
237 |
-
// Fetch latest version from Freemius.
|
238 |
-
$latest = $this->_fs->_fetch_latest_version(
|
239 |
-
$selected_addon->id,
|
240 |
-
true,
|
241 |
-
WP_FS__TIME_24_HOURS_IN_SEC,
|
242 |
-
$current_addon_version
|
243 |
-
);
|
244 |
-
|
245 |
-
if ( $has_paid_plan ) {
|
246 |
-
$blog_id = fs_request_get( 'fs_blog_id' );
|
247 |
-
$has_valid_blog_id = is_numeric( $blog_id );
|
248 |
-
|
249 |
-
if ( $has_valid_blog_id ) {
|
250 |
-
switch_to_blog( $blog_id );
|
251 |
-
}
|
252 |
-
|
253 |
-
$data->checkout_link = $this->_fs->checkout_url(
|
254 |
-
WP_FS__PERIOD_ANNUALLY,
|
255 |
-
false,
|
256 |
-
array(),
|
257 |
-
( $has_valid_blog_id ? false : null )
|
258 |
-
);
|
259 |
-
|
260 |
-
if ( $has_valid_blog_id ) {
|
261 |
-
restore_current_blog();
|
262 |
-
}
|
263 |
-
}
|
264 |
-
|
265 |
-
/**
|
266 |
-
* Check if there's a purchased license in case the add-on can only be installed/downloaded as part of a purchased bundle.
|
267 |
-
*
|
268 |
-
* @author Leo Fajardo (@leorw)
|
269 |
-
* @since 2.4.1
|
270 |
-
*/
|
271 |
-
if ( is_object( $fs_addon ) ) {
|
272 |
-
$data->has_purchased_license = $fs_addon->has_active_valid_license();
|
273 |
-
} else {
|
274 |
-
$account_addons = $this->_fs->get_account_addons();
|
275 |
-
if ( ! empty( $account_addons ) && in_array( $selected_addon->id, $account_addons ) ) {
|
276 |
-
$data->has_purchased_license = true;
|
277 |
-
}
|
278 |
-
}
|
279 |
-
|
280 |
-
if ( $has_free_plan || $data->has_purchased_license ) {
|
281 |
-
$data->download_link = $this->_fs->_get_latest_download_local_url( $selected_addon->id );
|
282 |
-
}
|
283 |
-
|
284 |
-
$data->fs_missing = (
|
285 |
-
false === $latest &&
|
286 |
-
(
|
287 |
-
empty( $selected_addon->premium_releases_count ) ||
|
288 |
-
! ( $selected_addon->premium_releases_count > 0 )
|
289 |
-
)
|
290 |
-
);
|
291 |
-
|
292 |
-
// Fetch as much as possible info from local files.
|
293 |
-
$plugin_local_data = $this->_fs->get_plugin_data();
|
294 |
-
$data->author = $plugin_local_data['Author'];
|
295 |
-
|
296 |
-
if ( ! empty( $selected_addon->info->banner_url ) ) {
|
297 |
-
$data->banners = array(
|
298 |
-
'low' => $selected_addon->info->banner_url,
|
299 |
-
);
|
300 |
-
}
|
301 |
-
|
302 |
-
if ( ! empty( $selected_addon->info->screenshots ) ) {
|
303 |
-
$view_vars = array(
|
304 |
-
'screenshots' => $selected_addon->info->screenshots,
|
305 |
-
'plugin' => $selected_addon,
|
306 |
-
);
|
307 |
-
$data->sections['screenshots'] = fs_get_template( '/plugin-info/screenshots.php', $view_vars );
|
308 |
-
}
|
309 |
-
|
310 |
-
if ( is_object( $latest ) ) {
|
311 |
-
$data->version = $latest->version;
|
312 |
-
$data->last_updated = $latest->created;
|
313 |
-
$data->requires = $latest->requires_platform_version;
|
314 |
-
$data->tested = $latest->tested_up_to_version;
|
315 |
-
} else if ( ! empty( $current_addon_version ) ) {
|
316 |
-
$data->version = $current_addon_version;
|
317 |
-
} else {
|
318 |
-
// Add dummy version.
|
319 |
-
$data->version = '1.0.0';
|
320 |
-
|
321 |
-
// Add message to developer to deploy the plugin through Freemius.
|
322 |
-
}
|
323 |
-
}
|
324 |
-
|
325 |
-
$data->name = $selected_addon->title;
|
326 |
-
$view_vars = array( 'plugin' => $selected_addon );
|
327 |
-
|
328 |
-
if ( is_object( $latest ) && isset( $latest->readme ) && is_object( $latest->readme ) ) {
|
329 |
-
$latest_version_readme_data = $latest->readme;
|
330 |
-
if ( isset( $latest_version_readme_data->sections ) ) {
|
331 |
-
$data->sections = (array) $latest_version_readme_data->sections;
|
332 |
-
} else {
|
333 |
-
$data->sections = array();
|
334 |
-
}
|
335 |
-
}
|
336 |
-
|
337 |
-
$data->sections['description'] = fs_get_template( '/plugin-info/description.php', $view_vars );
|
338 |
-
|
339 |
-
if ( $has_pricing ) {
|
340 |
-
// Add plans to data.
|
341 |
-
$data->plans = $plans;
|
342 |
-
|
343 |
-
if ( $has_features ) {
|
344 |
-
$view_vars = array(
|
345 |
-
'plans' => $plans,
|
346 |
-
'plugin' => $selected_addon,
|
347 |
-
);
|
348 |
-
$data->sections['features'] = fs_get_template( '/plugin-info/features.php', $view_vars );
|
349 |
-
}
|
350 |
-
}
|
351 |
-
|
352 |
-
$data->has_free_plan = $has_free_plan;
|
353 |
-
$data->has_paid_plan = $has_paid_plan;
|
354 |
-
$data->is_paid = $has_paid_plan;
|
355 |
-
$data->is_wp_org_compliant = $selected_addon->is_wp_org_compliant;
|
356 |
-
$data->premium_slug = $selected_addon->premium_slug;
|
357 |
-
$data->addon_id = $selected_addon->id;
|
358 |
-
|
359 |
-
if ( ! isset( $data->has_purchased_license ) ) {
|
360 |
-
$data->has_purchased_license = false;
|
361 |
-
}
|
362 |
-
|
363 |
-
return $data;
|
364 |
-
}
|
365 |
-
|
366 |
-
/**
|
367 |
-
* @author Vova Feldman (@svovaf)
|
368 |
-
* @since 1.1.7
|
369 |
-
*
|
370 |
-
* @param FS_Plugin_Plan $plan
|
371 |
-
*
|
372 |
-
* @return string
|
373 |
-
*/
|
374 |
-
private function get_billing_cycle( FS_Plugin_Plan $plan ) {
|
375 |
-
$billing_cycle = null;
|
376 |
-
|
377 |
-
if ( 1 === count( $plan->pricing ) && 1 == $plan->pricing[0]->licenses ) {
|
378 |
-
$pricing = $plan->pricing[0];
|
379 |
-
if ( isset( $pricing->annual_price ) ) {
|
380 |
-
$billing_cycle = 'annual';
|
381 |
-
} else if ( isset( $pricing->monthly_price ) ) {
|
382 |
-
$billing_cycle = 'monthly';
|
383 |
-
} else if ( isset( $pricing->lifetime_price ) ) {
|
384 |
-
$billing_cycle = 'lifetime';
|
385 |
-
}
|
386 |
-
} else {
|
387 |
-
foreach ( $plan->pricing as $pricing ) {
|
388 |
-
if ( isset( $pricing->annual_price ) ) {
|
389 |
-
$billing_cycle = 'annual';
|
390 |
-
} else if ( isset( $pricing->monthly_price ) ) {
|
391 |
-
$billing_cycle = 'monthly';
|
392 |
-
} else if ( isset( $pricing->lifetime_price ) ) {
|
393 |
-
$billing_cycle = 'lifetime';
|
394 |
-
}
|
395 |
-
|
396 |
-
if ( ! is_null( $billing_cycle ) ) {
|
397 |
-
break;
|
398 |
-
}
|
399 |
-
}
|
400 |
-
}
|
401 |
-
|
402 |
-
return $billing_cycle;
|
403 |
-
}
|
404 |
-
|
405 |
-
/**
|
406 |
-
* @author Vova Feldman (@svovaf)
|
407 |
-
* @since 2.0.0
|
408 |
-
*
|
409 |
-
* @param FS_Plugin_Plan $plan
|
410 |
-
* @param FS_Pricing $pricing
|
411 |
-
*
|
412 |
-
* @return float|null|string
|
413 |
-
*/
|
414 |
-
private function get_price_tag( FS_Plugin_Plan $plan, FS_Pricing $pricing ) {
|
415 |
-
$price_tag = '';
|
416 |
-
if ( isset( $pricing->annual_price ) ) {
|
417 |
-
$price_tag = $pricing->annual_price . ( $plan->is_block_features ? ' / year' : '' );
|
418 |
-
} else if ( isset( $pricing->monthly_price ) ) {
|
419 |
-
$price_tag = $pricing->monthly_price . ' / mo';
|
420 |
-
} else if ( isset( $pricing->lifetime_price ) ) {
|
421 |
-
$price_tag = $pricing->lifetime_price;
|
422 |
-
}
|
423 |
-
|
424 |
-
return '$' . $price_tag;
|
425 |
-
}
|
426 |
-
|
427 |
-
/**
|
428 |
-
* @author Leo Fajardo (@leorw)
|
429 |
-
* @since 2.3.0
|
430 |
-
*
|
431 |
-
* @param object $api
|
432 |
-
* @param FS_Plugin_Plan $plan
|
433 |
-
*
|
434 |
-
* @return string
|
435 |
-
*/
|
436 |
-
private function get_actions_dropdown( $api, $plan = null ) {
|
437 |
-
$this->actions = isset( $this->actions ) ?
|
438 |
-
$this->actions :
|
439 |
-
$this->get_plugin_actions( $api );
|
440 |
-
|
441 |
-
$actions = $this->actions;
|
442 |
-
|
443 |
-
$checkout_cta = $this->get_checkout_cta( $api, $plan );
|
444 |
-
if ( ! empty( $checkout_cta ) ) {
|
445 |
-
/**
|
446 |
-
* If there's no license yet, make the checkout button the main CTA. Otherwise, make it the last item in
|
447 |
-
* the actions dropdown.
|
448 |
-
*
|
449 |
-
* @author Leo Fajardo (@leorw)
|
450 |
-
* @since 2.3.0
|
451 |
-
*/
|
452 |
-
if ( ! $api->has_purchased_license ) {
|
453 |
-
array_unshift( $actions, $checkout_cta );
|
454 |
-
} else {
|
455 |
-
$actions[] = $checkout_cta;
|
456 |
-
}
|
457 |
-
}
|
458 |
-
|
459 |
-
if ( empty( $actions ) ) {
|
460 |
-
return '';
|
461 |
-
}
|
462 |
-
|
463 |
-
$total_actions = count( $actions );
|
464 |
-
if ( 1 === $total_actions ) {
|
465 |
-
return $actions[0];
|
466 |
-
}
|
467 |
-
|
468 |
-
ob_start();
|
469 |
-
|
470 |
-
?>
|
471 |
-
<div class="fs-cta fs-dropdown">
|
472 |
-
<div class="button-group">
|
473 |
-
<?php
|
474 |
-
// This should NOT be sanitized as the $actions are HTML buttons already.
|
475 |
-
echo $actions[0] ?>
|
476 |
-
<div class="button button-primary fs-dropdown-arrow-button">
|
477 |
-
<span class="fs-dropdown-arrow"></span>
|
478 |
-
<ul class="fs-dropdown-list" style="display: none">
|
479 |
-
<?php for ( $i = 1; $i < $total_actions; $i ++ ) : ?>
|
480 |
-
<li><?php echo str_replace( 'button button-primary', '', $actions[ $i ] ) ?></li>
|
481 |
-
<?php endfor ?>
|
482 |
-
</ul>
|
483 |
-
</div>
|
484 |
-
</div>
|
485 |
-
</div>
|
486 |
-
<?php
|
487 |
-
|
488 |
-
return ob_get_clean();
|
489 |
-
}
|
490 |
-
|
491 |
-
/**
|
492 |
-
* @author Vova Feldman (@svovaf)
|
493 |
-
* @since 1.1.7
|
494 |
-
*
|
495 |
-
* @param object $api
|
496 |
-
* @param FS_Plugin_Plan $plan
|
497 |
-
*
|
498 |
-
* @return string
|
499 |
-
*/
|
500 |
-
private function get_checkout_cta( $api, $plan = null ) {
|
501 |
-
if ( empty( $api->checkout_link ) ||
|
502 |
-
! isset( $api->plans ) ||
|
503 |
-
! is_array( $api->plans ) ||
|
504 |
-
0 == count( $api->plans )
|
505 |
-
) {
|
506 |
-
return '';
|
507 |
-
}
|
508 |
-
|
509 |
-
if ( is_null( $plan ) ) {
|
510 |
-
foreach ( $api->plans as $p ) {
|
511 |
-
if ( ! empty( $p->pricing ) ) {
|
512 |
-
$plan = $p;
|
513 |
-
break;
|
514 |
-
}
|
515 |
-
}
|
516 |
-
}
|
517 |
-
|
518 |
-
$blog_id = fs_request_get( 'fs_blog_id' );
|
519 |
-
$has_valid_blog_id = is_numeric( $blog_id );
|
520 |
-
|
521 |
-
if ( $has_valid_blog_id ) {
|
522 |
-
switch_to_blog( $blog_id );
|
523 |
-
}
|
524 |
-
|
525 |
-
$addon_checkout_url = $this->_fs->addon_checkout_url(
|
526 |
-
$plan->plugin_id,
|
527 |
-
$plan->pricing[0]->id,
|
528 |
-
$this->get_billing_cycle( $plan ),
|
529 |
-
$plan->has_trial(),
|
530 |
-
( $has_valid_blog_id ? false : null )
|
531 |
-
);
|
532 |
-
|
533 |
-
if ( $has_valid_blog_id ) {
|
534 |
-
restore_current_blog();
|
535 |
-
}
|
536 |
-
|
537 |
-
return '<a class="button button-primary fs-checkout-button right" href="' . $addon_checkout_url . '" target="_parent">' .
|
538 |
-
esc_html( ! $plan->has_trial() ?
|
539 |
-
(
|
540 |
-
$api->has_purchased_license ?
|
541 |
-
fs_text_inline( 'Purchase More', 'purchase-more', $api->slug ) :
|
542 |
-
fs_text_x_inline( 'Purchase', 'verb', 'purchase', $api->slug )
|
543 |
-
) :
|
544 |
-
sprintf(
|
545 |
-
/* translators: %s: N-days trial */
|
546 |
-
fs_text_inline( 'Start my free %s', 'start-free-x', $api->slug ),
|
547 |
-
$this->get_trial_period( $plan )
|
548 |
-
)
|
549 |
-
) .
|
550 |
-
'</a>';
|
551 |
-
}
|
552 |
-
|
553 |
-
/**
|
554 |
-
* @author Leo Fajardo (@leorw)
|
555 |
-
* @since 2.3.0
|
556 |
-
*
|
557 |
-
* @param object $api
|
558 |
-
*
|
559 |
-
* @return string[]
|
560 |
-
*/
|
561 |
-
private function get_plugin_actions( $api ) {
|
562 |
-
$this->status = isset( $this->status ) ?
|
563 |
-
$this->status :
|
564 |
-
install_plugin_install_status( $api );
|
565 |
-
|
566 |
-
$is_update_available = ( 'update_available' === $this->status['status'] );
|
567 |
-
|
568 |
-
if ( $is_update_available && empty( $this->status['url'] ) ) {
|
569 |
-
return array();
|
570 |
-
}
|
571 |
-
|
572 |
-
$blog_id = fs_request_get( 'fs_blog_id' );
|
573 |
-
|
574 |
-
$active_plugins_directories_map = Freemius::get_active_plugins_directories_map( $blog_id );
|
575 |
-
|
576 |
-
$actions = array();
|
577 |
-
|
578 |
-
$is_addon_activated = $this->_fs->is_addon_activated( $api->slug );
|
579 |
-
$fs_addon = null;
|
580 |
-
|
581 |
-
$is_free_installed = null;
|
582 |
-
$is_premium_installed = null;
|
583 |
-
|
584 |
-
$has_installed_version = ( 'install' !== $this->status['status'] );
|
585 |
-
|
586 |
-
if ( ! $api->has_paid_plan && ! $api->has_purchased_license ) {
|
587 |
-
/**
|
588 |
-
* Free-only add-on.
|
589 |
-
*
|
590 |
-
* @author Leo Fajardo (@leorw)
|
591 |
-
* @since 2.3.0
|
592 |
-
*/
|
593 |
-
$is_free_installed = $has_installed_version;
|
594 |
-
$is_premium_installed = false;
|
595 |
-
} else if ( ! $api->has_free_plan ) {
|
596 |
-
/**
|
597 |
-
* Premium-only add-on.
|
598 |
-
*
|
599 |
-
* @author Leo Fajardo (@leorw)
|
600 |
-
* @since 2.3.0
|
601 |
-
*/
|
602 |
-
$is_free_installed = false;
|
603 |
-
$is_premium_installed = $has_installed_version;
|
604 |
-
} else {
|
605 |
-
/**
|
606 |
-
* Freemium add-on.
|
607 |
-
*
|
608 |
-
* @author Leo Fajardo (@leorw)
|
609 |
-
* @since 2.3.0
|
610 |
-
*/
|
611 |
-
if ( ! $has_installed_version ) {
|
612 |
-
$is_free_installed = false;
|
613 |
-
$is_premium_installed = false;
|
614 |
-
} else {
|
615 |
-
$fs_addon = $is_addon_activated ?
|
616 |
-
$this->_fs->get_addon_instance( $api->slug ) :
|
617 |
-
null;
|
618 |
-
|
619 |
-
if ( is_object( $fs_addon ) ) {
|
620 |
-
if ( $fs_addon->is_premium() ) {
|
621 |
-
$is_premium_installed = true;
|
622 |
-
} else {
|
623 |
-
$is_free_installed = true;
|
624 |
-
}
|
625 |
-
}
|
626 |
-
|
627 |
-
if ( is_null( $is_free_installed ) ) {
|
628 |
-
$is_free_installed = file_exists( fs_normalize_path( WP_PLUGIN_DIR . "/{$api->slug}/{$api->slug}.php" ) );
|
629 |
-
if ( ! $is_free_installed ) {
|
630 |
-
/**
|
631 |
-
* Check if there's a plugin installed in a directory named `$api->slug`.
|
632 |
-
*
|
633 |
-
* @author Leo Fajardo (@leorw)
|
634 |
-
* @since 2.3.0
|
635 |
-
*/
|
636 |
-
$installed_plugins = get_plugins( '/' . $api->slug );
|
637 |
-
$is_free_installed = ( ! empty( $installed_plugins ) );
|
638 |
-
}
|
639 |
-
}
|
640 |
-
|
641 |
-
if ( is_null( $is_premium_installed ) ) {
|
642 |
-
$is_premium_installed = file_exists( fs_normalize_path( WP_PLUGIN_DIR . "/{$api->premium_slug}/{$api->slug}.php" ) );
|
643 |
-
if ( ! $is_premium_installed ) {
|
644 |
-
/**
|
645 |
-
* Check if there's a plugin installed in a directory named `$api->premium_slug`.
|
646 |
-
*
|
647 |
-
* @author Leo Fajardo (@leorw)
|
648 |
-
* @since 2.3.0
|
649 |
-
*/
|
650 |
-
$installed_plugins = get_plugins( '/' . $api->premium_slug );
|
651 |
-
$is_premium_installed = ( ! empty( $installed_plugins ) );
|
652 |
-
}
|
653 |
-
}
|
654 |
-
}
|
655 |
-
|
656 |
-
$has_installed_version = ( $is_free_installed || $is_premium_installed );
|
657 |
-
}
|
658 |
-
|
659 |
-
$this->status['is_free_installed'] = $is_free_installed;
|
660 |
-
$this->status['is_premium_installed'] = $is_premium_installed;
|
661 |
-
|
662 |
-
$can_install_free_version = false;
|
663 |
-
$can_install_free_version_update = false;
|
664 |
-
$can_download_free_version = false;
|
665 |
-
$can_activate_free_version = false;
|
666 |
-
$can_install_premium_version = false;
|
667 |
-
$can_install_premium_version_update = false;
|
668 |
-
$can_download_premium_version = false;
|
669 |
-
$can_activate_premium_version = false;
|
670 |
-
|
671 |
-
if ( ! $api->has_purchased_license ) {
|
672 |
-
if ( $api->has_free_plan ) {
|
673 |
-
if ( $has_installed_version ) {
|
674 |
-
if ( $is_update_available ) {
|
675 |
-
$can_install_free_version_update = true;
|
676 |
-
} else if ( ! $is_premium_installed && ! isset( $active_plugins_directories_map[ dirname( $this->status['file'] ) ] ) ) {
|
677 |
-
$can_activate_free_version = true;
|
678 |
-
}
|
679 |
-
} else {
|
680 |
-
if (
|
681 |
-
$this->_fs->is_premium() ||
|
682 |
-
! $this->_fs->is_org_repo_compliant() ||
|
683 |
-
$api->is_wp_org_compliant
|
684 |
-
) {
|
685 |
-
$can_install_free_version = true;
|
686 |
-
} else {
|
687 |
-
$can_download_free_version = true;
|
688 |
-
}
|
689 |
-
}
|
690 |
-
}
|
691 |
-
} else {
|
692 |
-
if ( ! is_object( $fs_addon ) && $is_addon_activated ) {
|
693 |
-
$fs_addon = $this->_fs->get_addon_instance( $api->slug );
|
694 |
-
}
|
695 |
-
|
696 |
-
$can_download_premium_version = true;
|
697 |
-
|
698 |
-
if ( ! isset( $active_plugins_directories_map[ dirname( $this->status['file'] ) ] ) ) {
|
699 |
-
if ( $is_premium_installed ) {
|
700 |
-
$can_activate_premium_version = ( ! $is_addon_activated || ! $fs_addon->is_premium() );
|
701 |
-
} else if ( $is_free_installed ) {
|
702 |
-
$can_activate_free_version = ( ! $is_addon_activated );
|
703 |
-
}
|
704 |
-
}
|
705 |
-
|
706 |
-
if ( $this->_fs->is_premium() || ! $this->_fs->is_org_repo_compliant() ) {
|
707 |
-
if ( $is_update_available ) {
|
708 |
-
$can_install_premium_version_update = true;
|
709 |
-
} else if ( ! $is_premium_installed ) {
|
710 |
-
$can_install_premium_version = true;
|
711 |
-
}
|
712 |
-
}
|
713 |
-
}
|
714 |
-
|
715 |
-
if (
|
716 |
-
$can_install_premium_version ||
|
717 |
-
$can_install_premium_version_update
|
718 |
-
) {
|
719 |
-
if ( is_numeric( $blog_id ) ) {
|
720 |
-
/**
|
721 |
-
* Replace the network status URL with a blog admin–based status URL if the `Add-Ons` page is loaded
|
722 |
-
* from a specific blog admin page (when `fs_blog_id` is valid) in order for plugin installation/update
|
723 |
-
* to work.
|
724 |
-
*
|
725 |
-
* @author Leo Fajardo (@leorw)
|
726 |
-
* @since 2.3.0
|
727 |
-
*/
|
728 |
-
$this->status['url'] = self::get_blog_status_url( $blog_id, $this->status['url'], $this->status['status'] );
|
729 |
-
}
|
730 |
-
|
731 |
-
/**
|
732 |
-
* Add the `fs_allow_updater_and_dialog` param to the install/update URL so that the add-on can be
|
733 |
-
* installed/updated.
|
734 |
-
*
|
735 |
-
* @author Leo Fajardo (@leorw)
|
736 |
-
* @since 2.3.0
|
737 |
-
*/
|
738 |
-
$this->status['url'] = str_replace( '?', '?fs_allow_updater_and_dialog=true&', $this->status['url'] );
|
739 |
-
}
|
740 |
-
|
741 |
-
if ( $can_install_free_version_update || $can_install_premium_version_update ) {
|
742 |
-
$actions[] = $this->get_cta(
|
743 |
-
( $can_install_free_version_update ?
|
744 |
-
fs_esc_html_inline( 'Install Free Version Update Now', 'install-free-version-update-now', $api->slug ) :
|
745 |
-
fs_esc_html_inline( 'Install Update Now', 'install-update-now', $api->slug ) ),
|
746 |
-
true,
|
747 |
-
false,
|
748 |
-
$this->status['url'],
|
749 |
-
'_parent'
|
750 |
-
);
|
751 |
-
} else if ( $can_install_free_version || $can_install_premium_version ) {
|
752 |
-
$actions[] = $this->get_cta(
|
753 |
-
( $can_install_free_version ?
|
754 |
-
fs_esc_html_inline( 'Install Free Version Now', 'install-free-version-now', $api->slug ) :
|
755 |
-
fs_esc_html_inline( 'Install Now', 'install-now', $api->slug ) ),
|
756 |
-
true,
|
757 |
-
false,
|
758 |
-
$this->status['url'],
|
759 |
-
'_parent'
|
760 |
-
);
|
761 |
-
}
|
762 |
-
|
763 |
-
$download_latest_action = '';
|
764 |
-
|
765 |
-
if (
|
766 |
-
! empty( $api->download_link ) &&
|
767 |
-
( $can_download_free_version || $can_download_premium_version )
|
768 |
-
) {
|
769 |
-
$download_latest_action = $this->get_cta(
|
770 |
-
( $can_download_free_version ?
|
771 |
-
fs_esc_html_x_inline( 'Download Latest Free Version', 'as download latest version', 'download-latest-free-version', $api->slug ) :
|
772 |
-
fs_esc_html_x_inline( 'Download Latest', 'as download latest version', 'download-latest', $api->slug ) ),
|
773 |
-
true,
|
774 |
-
false,
|
775 |
-
esc_url( $api->download_link )
|
776 |
-
);
|
777 |
-
}
|
778 |
-
|
779 |
-
if ( ! $can_activate_free_version && ! $can_activate_premium_version ) {
|
780 |
-
if ( ! empty( $download_latest_action ) ) {
|
781 |
-
$actions[] = $download_latest_action;
|
782 |
-
}
|
783 |
-
} else {
|
784 |
-
$activate_action = sprintf(
|
785 |
-
'<a class="button button-primary edit" href="%s" title="%s" target="_parent">%s</a>',
|
786 |
-
wp_nonce_url( ( is_numeric( $blog_id ) ? trailingslashit( get_admin_url( $blog_id ) ) : '' ) . 'plugins.php?action=activate&plugin=' . $this->status['file'], 'activate-plugin_' . $this->status['file'] ),
|
787 |
-
fs_esc_attr_inline( 'Activate this add-on', 'activate-this-addon', $api->slug ),
|
788 |
-
$can_activate_free_version ?
|
789 |
-
fs_text_inline( 'Activate Free Version', 'activate-free', $api->slug ) :
|
790 |
-
fs_text_inline( 'Activate', 'activate', $api->slug )
|
791 |
-
);
|
792 |
-
|
793 |
-
if ( ! $can_download_premium_version && ! empty( $download_latest_action ) ) {
|
794 |
-
$actions[] = $download_latest_action;
|
795 |
-
|
796 |
-
$download_latest_action = '';
|
797 |
-
}
|
798 |
-
|
799 |
-
if ( $can_install_premium_version || $can_install_premium_version_update ) {
|
800 |
-
if ( $can_download_premium_version && ! empty( $download_latest_action ) ) {
|
801 |
-
$actions[] = $download_latest_action;
|
802 |
-
|
803 |
-
$download_latest_action = '';
|
804 |
-
}
|
805 |
-
|
806 |
-
$actions[] = $activate_action;
|
807 |
-
} else {
|
808 |
-
array_unshift( $actions, $activate_action );
|
809 |
-
}
|
810 |
-
|
811 |
-
if ( ! empty ($download_latest_action ) ) {
|
812 |
-
$actions[] = $download_latest_action;
|
813 |
-
}
|
814 |
-
}
|
815 |
-
|
816 |
-
return $actions;
|
817 |
-
}
|
818 |
-
|
819 |
-
/**
|
820 |
-
* Rebuilds the status URL based on the admin URL.
|
821 |
-
*
|
822 |
-
* @author Leo Fajardo (@leorw)
|
823 |
-
* @since 2.3.0
|
824 |
-
*
|
825 |
-
* @param int $blog_id
|
826 |
-
* @param string $network_status_url
|
827 |
-
* @param string $status
|
828 |
-
*
|
829 |
-
* @return string
|
830 |
-
*/
|
831 |
-
private static function get_blog_status_url( $blog_id, $network_status_url, $status ) {
|
832 |
-
if ( ! in_array( $status, array( 'install', 'update_available' ) ) ) {
|
833 |
-
return $network_status_url;
|
834 |
-
}
|
835 |
-
|
836 |
-
$action = ( 'install' === $status ) ?
|
837 |
-
'install-plugin' :
|
838 |
-
'upgrade-plugin';
|
839 |
-
|
840 |
-
$query = parse_url( $network_status_url, PHP_URL_QUERY );
|
841 |
-
if ( empty( $query ) ) {
|
842 |
-
return $network_status_url;
|
843 |
-
}
|
844 |
-
|
845 |
-
parse_str( html_entity_decode( $query ), $url_params );
|
846 |
-
if ( empty( $url_params ) || ! isset( $url_params['plugin'] ) ) {
|
847 |
-
return $network_status_url;
|
848 |
-
}
|
849 |
-
|
850 |
-
$plugin = $url_params['plugin'];
|
851 |
-
|
852 |
-
return wp_nonce_url( get_admin_url( $blog_id,"update.php?action={$action}&plugin={$plugin}"), "{$action}_{$plugin}");
|
853 |
-
}
|
854 |
-
|
855 |
-
/**
|
856 |
-
* Helper method to get a CTA button HTML.
|
857 |
-
*
|
858 |
-
* @author Vova Feldman (@svovaf)
|
859 |
-
* @since 2.0.0
|
860 |
-
*
|
861 |
-
* @param string $label
|
862 |
-
* @param bool $is_primary
|
863 |
-
* @param bool $is_disabled
|
864 |
-
* @param string $href
|
865 |
-
* @param string $target
|
866 |
-
*
|
867 |
-
* @return string
|
868 |
-
*/
|
869 |
-
private function get_cta(
|
870 |
-
$label,
|
871 |
-
$is_primary = true,
|
872 |
-
$is_disabled = false,
|
873 |
-
$href = '',
|
874 |
-
$target = '_blank'
|
875 |
-
) {
|
876 |
-
$classes = array();
|
877 |
-
|
878 |
-
if ( ! $is_primary ) {
|
879 |
-
$classes[] = 'left';
|
880 |
-
} else {
|
881 |
-
$classes[] = 'button-primary';
|
882 |
-
$classes[] = 'right';
|
883 |
-
}
|
884 |
-
|
885 |
-
if ( $is_disabled ) {
|
886 |
-
$classes[] = 'disabled';
|
887 |
-
}
|
888 |
-
|
889 |
-
$rel = ( '_blank' === $target ) ? ' rel="noopener noreferrer"' : '';
|
890 |
-
|
891 |
-
return sprintf(
|
892 |
-
'<a %s class="button %s">%s</a>',
|
893 |
-
empty( $href ) ? '' : 'href="' . $href . '" target="' . $target . '"' . $rel,
|
894 |
-
implode( ' ', $classes ),
|
895 |
-
$label
|
896 |
-
);
|
897 |
-
}
|
898 |
-
|
899 |
-
/**
|
900 |
-
* @author Vova Feldman (@svovaf)
|
901 |
-
* @since 1.1.7
|
902 |
-
*
|
903 |
-
* @param FS_Plugin_Plan $plan
|
904 |
-
*
|
905 |
-
* @return string
|
906 |
-
*/
|
907 |
-
private function get_trial_period( $plan ) {
|
908 |
-
$trial_period = (int) $plan->trial_period;
|
909 |
-
|
910 |
-
switch ( $trial_period ) {
|
911 |
-
case 30:
|
912 |
-
return 'month';
|
913 |
-
case 60:
|
914 |
-
return '2 months';
|
915 |
-
default:
|
916 |
-
return "{$plan->trial_period} days";
|
917 |
-
}
|
918 |
-
}
|
919 |
-
|
920 |
-
/**
|
921 |
-
* Display plugin information in dialog box form.
|
922 |
-
*
|
923 |
-
* Based on core install_plugin_information() function.
|
924 |
-
*
|
925 |
-
* @author Vova Feldman (@svovaf)
|
926 |
-
* @since 1.0.6
|
927 |
-
*/
|
928 |
-
function install_plugin_information() {
|
929 |
-
global $tab;
|
930 |
-
|
931 |
-
if ( empty( $_REQUEST['plugin'] ) ) {
|
932 |
-
return;
|
933 |
-
}
|
934 |
-
|
935 |
-
$args = array(
|
936 |
-
'slug' => wp_unslash( $_REQUEST['plugin'] ),
|
937 |
-
'is_ssl' => is_ssl(),
|
938 |
-
'fields' => array(
|
939 |
-
'banners' => true,
|
940 |
-
'reviews' => true,
|
941 |
-
'downloaded' => false,
|
942 |
-
'active_installs' => true
|
943 |
-
)
|
944 |
-
);
|
945 |
-
|
946 |
-
if ( is_array( $args ) ) {
|
947 |
-
$args = (object) $args;
|
948 |
-
}
|
949 |
-
|
950 |
-
if ( ! isset( $args->per_page ) ) {
|
951 |
-
$args->per_page = 24;
|
952 |
-
}
|
953 |
-
|
954 |
-
if ( ! isset( $args->locale ) ) {
|
955 |
-
$args->locale = get_locale();
|
956 |
-
}
|
957 |
-
|
958 |
-
$api = apply_filters( 'fs_plugins_api', false, 'plugin_information', $args );
|
959 |
-
|
960 |
-
if ( is_wp_error( $api ) ) {
|
961 |
-
wp_die( $api );
|
962 |
-
}
|
963 |
-
|
964 |
-
$plugins_allowedtags = array(
|
965 |
-
'a' => array(
|
966 |
-
'href' => array(),
|
967 |
-
'title' => array(),
|
968 |
-
'target' => array(),
|
969 |
-
// Add image style for screenshots.
|
970 |
-
'class' => array()
|
971 |
-
),
|
972 |
-
'style' => array(),
|
973 |
-
'abbr' => array( 'title' => array() ),
|
974 |
-
'acronym' => array( 'title' => array() ),
|
975 |
-
'code' => array(),
|
976 |
-
'pre' => array(),
|
977 |
-
'em' => array(),
|
978 |
-
'strong' => array(),
|
979 |
-
'div' => array( 'class' => array() ),
|
980 |
-
'span' => array( 'class' => array() ),
|
981 |
-
'p' => array(),
|
982 |
-
'ul' => array(),
|
983 |
-
'ol' => array(),
|
984 |
-
'li' => array( 'class' => array() ),
|
985 |
-
'i' => array( 'class' => array() ),
|
986 |
-
'h1' => array(),
|
987 |
-
'h2' => array(),
|
988 |
-
'h3' => array(),
|
989 |
-
'h4' => array(),
|
990 |
-
'h5' => array(),
|
991 |
-
'h6' => array(),
|
992 |
-
'img' => array( 'src' => array(), 'class' => array(), 'alt' => array() ),
|
993 |
-
// 'table' => array(),
|
994 |
-
// 'td' => array(),
|
995 |
-
// 'tr' => array(),
|
996 |
-
// 'th' => array(),
|
997 |
-
// 'thead' => array(),
|
998 |
-
// 'tbody' => array(),
|
999 |
-
);
|
1000 |
-
|
1001 |
-
$plugins_section_titles = array(
|
1002 |
-
'description' => fs_text_x_inline( 'Description', 'Plugin installer section title', 'description', $api->slug ),
|
1003 |
-
'installation' => fs_text_x_inline( 'Installation', 'Plugin installer section title', 'installation', $api->slug ),
|
1004 |
-
'faq' => fs_text_x_inline( 'FAQ', 'Plugin installer section title', 'faq', $api->slug ),
|
1005 |
-
'screenshots' => fs_text_inline( 'Screenshots', 'screenshots', $api->slug ),
|
1006 |
-
'changelog' => fs_text_x_inline( 'Changelog', 'Plugin installer section title', 'changelog', $api->slug ),
|
1007 |
-
'reviews' => fs_text_x_inline( 'Reviews', 'Plugin installer section title', 'reviews', $api->slug ),
|
1008 |
-
'other_notes' => fs_text_x_inline( 'Other Notes', 'Plugin installer section title', 'other-notes', $api->slug ),
|
1009 |
-
);
|
1010 |
-
|
1011 |
-
// Sanitize HTML
|
1012 |
-
// foreach ( (array) $api->sections as $section_name => $content ) {
|
1013 |
-
// $api->sections[$section_name] = wp_kses( $content, $plugins_allowedtags );
|
1014 |
-
// }
|
1015 |
-
|
1016 |
-
foreach ( array( 'version', 'author', 'requires', 'tested', 'homepage', 'downloaded', 'slug' ) as $key ) {
|
1017 |
-
if ( isset( $api->$key ) ) {
|
1018 |
-
$api->$key = wp_kses( $api->$key, $plugins_allowedtags );
|
1019 |
-
}
|
1020 |
-
}
|
1021 |
-
|
1022 |
-
// Add after $api->slug is ready.
|
1023 |
-
$plugins_section_titles['features'] = fs_text_x_inline( 'Features & Pricing', 'Plugin installer section title', 'features-and-pricing', $api->slug );
|
1024 |
-
|
1025 |
-
$_tab = esc_attr( $tab );
|
1026 |
-
|
1027 |
-
$section = isset( $_REQUEST['section'] ) ? wp_unslash( $_REQUEST['section'] ) : 'description'; // Default to the Description tab, Do not translate, API returns English.
|
1028 |
-
if ( empty( $section ) || ! isset( $api->sections[ $section ] ) ) {
|
1029 |
-
$section_titles = array_keys( (array) $api->sections );
|
1030 |
-
$section = array_shift( $section_titles );
|
1031 |
-
}
|
1032 |
-
|
1033 |
-
iframe_header( fs_text_inline( 'Plugin Install', 'plugin-install', $api->slug ) );
|
1034 |
-
|
1035 |
-
$_with_banner = '';
|
1036 |
-
|
1037 |
-
// var_dump($api->banners);
|
1038 |
-
if ( ! empty( $api->banners ) && ( ! empty( $api->banners['low'] ) || ! empty( $api->banners['high'] ) ) ) {
|
1039 |
-
$_with_banner = 'with-banner';
|
1040 |
-
$low = empty( $api->banners['low'] ) ? $api->banners['high'] : $api->banners['low'];
|
1041 |
-
$high = empty( $api->banners['high'] ) ? $api->banners['low'] : $api->banners['high'];
|
1042 |
-
?>
|
1043 |
-
<style type="text/css">
|
1044 |
-
#plugin-information-title.with-banner
|
1045 |
-
{
|
1046 |
-
background-image: url( <?php echo esc_url( $low ); ?> );
|
1047 |
-
}
|
1048 |
-
|
1049 |
-
@media only screen and ( -webkit-min-device-pixel-ratio: 1.5 )
|
1050 |
-
{
|
1051 |
-
#plugin-information-title.with-banner
|
1052 |
-
{
|
1053 |
-
background-image: url( <?php echo esc_url( $high ); ?> );
|
1054 |
-
}
|
1055 |
-
}
|
1056 |
-
</style>
|
1057 |
-
<?php
|
1058 |
-
}
|
1059 |
-
|
1060 |
-
echo '<div id="plugin-information-scrollable">';
|
1061 |
-
echo "<div id='{$_tab}-title' class='{$_with_banner}'><div class='vignette'></div><h2>{$api->name}</h2></div>";
|
1062 |
-
echo "<div id='{$_tab}-tabs' class='{$_with_banner}'>\n";
|
1063 |
-
|
1064 |
-
foreach ( (array) $api->sections as $section_name => $content ) {
|
1065 |
-
if ( 'reviews' === $section_name && ( empty( $api->ratings ) || 0 === array_sum( (array) $api->ratings ) ) ) {
|
1066 |
-
continue;
|
1067 |
-
}
|
1068 |
-
|
1069 |
-
if ( isset( $plugins_section_titles[ $section_name ] ) ) {
|
1070 |
-
$title = $plugins_section_titles[ $section_name ];
|
1071 |
-
} else {
|
1072 |
-
$title = ucwords( str_replace( '_', ' ', $section_name ) );
|
1073 |
-
}
|
1074 |
-
|
1075 |
-
$class = ( $section_name === $section ) ? ' class="current"' : '';
|
1076 |
-
$href = add_query_arg( array( 'tab' => $tab, 'section' => $section_name ) );
|
1077 |
-
$href = esc_url( $href );
|
1078 |
-
$san_section = esc_attr( $section_name );
|
1079 |
-
echo "\t<a name='$san_section' href='$href' $class>" . esc_html( $title ) . "</a>\n";
|
1080 |
-
}
|
1081 |
-
|
1082 |
-
echo "</div>\n";
|
1083 |
-
|
1084 |
-
?>
|
1085 |
-
<div id="<?php echo $_tab; ?>-content" class='<?php echo $_with_banner; ?>'>
|
1086 |
-
<div class="fyi">
|
1087 |
-
<?php if ( $api->is_paid ) : ?>
|
1088 |
-
<?php if ( isset( $api->plans ) ) : ?>
|
1089 |
-
<div class="plugin-information-pricing">
|
1090 |
-
<?php foreach ( $api->plans as $plan ) : ?>
|
1091 |
-
<?php
|
1092 |
-
if ( empty( $plan->pricing ) ) {
|
1093 |
-
continue;
|
1094 |
-
}
|
1095 |
-
|
1096 |
-
/**
|
1097 |
-
* @var FS_Plugin_Plan $plan
|
1098 |
-
*/
|
1099 |
-
?>
|
1100 |
-
<?php $first_pricing = $plan->pricing[0] ?>
|
1101 |
-
<?php $is_multi_cycle = $first_pricing->is_multi_cycle() ?>
|
1102 |
-
<div class="fs-plan<?php if ( ! $is_multi_cycle ) {
|
1103 |
-
echo ' fs-single-cycle';
|
1104 |
-
} ?>" data-plan-id="<?php echo $plan->id ?>">
|
1105 |
-
<h3 data-plan="<?php echo $plan->id ?>"><?php echo esc_html( sprintf( fs_text_x_inline( '%s Plan', 'e.g. Professional Plan', 'x-plan', $api->slug ), $plan->title ) ) ?></h3>
|
1106 |
-
<?php $has_annual = $first_pricing->has_annual() ?>
|
1107 |
-
<?php $has_monthly = $first_pricing->has_monthly() ?>
|
1108 |
-
<div class="nav-tab-wrapper">
|
1109 |
-
<?php $billing_cycles = array( 'monthly', 'annual', 'lifetime' ) ?>
|
1110 |
-
<?php $i = 0;
|
1111 |
-
foreach ( $billing_cycles as $cycle ) : ?>
|
1112 |
-
<?php $prop = "{$cycle}_price";
|
1113 |
-
if ( isset( $first_pricing->{$prop} ) ) : ?>
|
1114 |
-
<?php $is_featured = ( 'annual' === $cycle && $is_multi_cycle ) ?>
|
1115 |
-
<?php
|
1116 |
-
$prices = array();
|
1117 |
-
foreach ( $plan->pricing as $pricing ) {
|
1118 |
-
if ( isset( $pricing->{$prop} ) ) {
|
1119 |
-
$prices[] = array(
|
1120 |
-
'id' => $pricing->id,
|
1121 |
-
'licenses' => $pricing->licenses,
|
1122 |
-
'price' => $pricing->{$prop}
|
1123 |
-
);
|
1124 |
-
}
|
1125 |
-
}
|
1126 |
-
?>
|
1127 |
-
<a class="nav-tab" data-billing-cycle="<?php echo $cycle ?>"
|
1128 |
-
data-pricing="<?php echo esc_attr( json_encode( $prices ) ) ?>">
|
1129 |
-
<?php if ( $is_featured ) : ?>
|
1130 |
-
<label>
|
1131 |
-
★ <?php fs_esc_html_echo_x_inline( 'Best', 'e.g. the best product', 'best', $api->slug ) ?>
|
1132 |
-
★</label>
|
1133 |
-
<?php endif ?>
|
1134 |
-
<?php
|
1135 |
-
switch ( $cycle ) {
|
1136 |
-
case 'monthly':
|
1137 |
-
fs_esc_html_echo_x_inline( 'Monthly', 'as every month', 'monthly', $api->slug );
|
1138 |
-
break;
|
1139 |
-
case 'annual':
|
1140 |
-
fs_esc_html_echo_x_inline( 'Annual', 'as once a year', 'annual', $api->slug );
|
1141 |
-
break;
|
1142 |
-
case 'lifetime':
|
1143 |
-
fs_esc_html_echo_inline( 'Lifetime', 'lifetime', $api->slug );
|
1144 |
-
break;
|
1145 |
-
}
|
1146 |
-
?>
|
1147 |
-
</a>
|
1148 |
-
<?php endif ?>
|
1149 |
-
<?php $i ++; endforeach ?>
|
1150 |
-
<?php wp_enqueue_script( 'jquery' ) ?>
|
1151 |
-
<script type="text/javascript">
|
1152 |
-
(function ($, undef) {
|
1153 |
-
var
|
1154 |
-
_formatBillingFrequency = function (cycle) {
|
1155 |
-
switch (cycle) {
|
1156 |
-
case 'monthly':
|
1157 |
-
return '<?php printf( fs_text_x_inline( 'Billed %s', 'e.g. billed monthly', 'billed-x', $api->slug ), fs_text_x_inline( 'Monthly', 'as every month', 'monthly', $api->slug ) ) ?>';
|
1158 |
-
case 'annual':
|
1159 |
-
return '<?php printf( fs_text_x_inline( 'Billed %s', 'e.g. billed monthly', 'billed-x', $api->slug ), fs_text_x_inline( 'Annually', 'as once a year', 'annually', $api->slug ) ) ?>';
|
1160 |
-
case 'lifetime':
|
1161 |
-
return '<?php printf( fs_text_x_inline( 'Billed %s', 'e.g. billed monthly', 'billed-x', $api->slug ), fs_text_x_inline( 'Once', 'as once a year', 'once', $api->slug ) ) ?>';
|
1162 |
-
}
|
1163 |
-
},
|
1164 |
-
_formatLicensesTitle = function (pricing) {
|
1165 |
-
switch (pricing.licenses) {
|
1166 |
-
case 1:
|
1167 |
-
return '<?php fs_esc_attr_echo_inline( 'Single Site License', 'license-single-site', $api->slug ) ?>';
|
1168 |
-
case null:
|
1169 |
-
return '<?php fs_esc_attr_echo_inline( 'Unlimited Licenses', 'license-unlimited', $api->slug ) ?>';
|
1170 |
-
default:
|
1171 |
-
return '<?php fs_esc_attr_echo_inline( 'Up to %s Sites', 'license-x-sites', $api->slug ) ?>'.replace('%s', pricing.licenses);
|
1172 |
-
}
|
1173 |
-
},
|
1174 |
-
_formatPrice = function (pricing, cycle, multipleLicenses) {
|
1175 |
-
if (undef === multipleLicenses)
|
1176 |
-
multipleLicenses = true;
|
1177 |
-
|
1178 |
-
var priceCycle;
|
1179 |
-
switch (cycle) {
|
1180 |
-
case 'monthly':
|
1181 |
-
priceCycle = ' / <?php fs_echo_x_inline( 'mo', 'as monthly period', 'mo', $api->slug ) ?>';
|
1182 |
-
break;
|
1183 |
-
case 'lifetime':
|
1184 |
-
priceCycle = '';
|
1185 |
-
break;
|
1186 |
-
case 'annual':
|
1187 |
-
default:
|
1188 |
-
priceCycle = ' / <?php fs_echo_x_inline( 'year', 'as annual period', 'year', $api->slug ) ?>';
|
1189 |
-
break;
|
1190 |
-
}
|
1191 |
-
|
1192 |
-
if (!multipleLicenses && 1 == pricing.licenses) {
|
1193 |
-
return '$' + pricing.price + priceCycle;
|
1194 |
-
}
|
1195 |
-
|
1196 |
-
return _formatLicensesTitle(pricing) + ' - <var class="fs-price">$' + pricing.price + priceCycle + '</var>';
|
1197 |
-
},
|
1198 |
-
_checkoutUrl = function (plan, pricing, cycle) {
|
1199 |
-
return '<?php echo esc_url_raw( remove_query_arg( 'billing_cycle', add_query_arg( array( 'plugin_id' => $plan->plugin_id ), $api->checkout_link ) ) ) ?>' +
|
1200 |
-
'&plan_id=' + plan +
|
1201 |
-
'&pricing_id=' + pricing +
|
1202 |
-
'&billing_cycle=' + cycle<?php if ( $plan->has_trial() ) {
|
1203 |
-
echo " + '&trial=true'";
|
1204 |
-
}?>;
|
1205 |
-
},
|
1206 |
-
_updateCtaUrl = function (plan, pricing, cycle) {
|
1207 |
-
$('.plugin-information-pricing .fs-checkout-button, #plugin-information-footer .fs-checkout-button').attr('href', _checkoutUrl(plan, pricing, cycle));
|
1208 |
-
};
|
1209 |
-
|
1210 |
-
$(document).ready(function () {
|
1211 |
-
var $plan = $('.plugin-information-pricing .fs-plan[data-plan-id=<?php echo $plan->id ?>]');
|
1212 |
-
$plan.find('input[type=radio]').on('click', function () {
|
1213 |
-
_updateCtaUrl(
|
1214 |
-
$plan.attr('data-plan-id'),
|
1215 |
-
$(this).val(),
|
1216 |
-
$plan.find('.nav-tab-active').attr('data-billing-cycle')
|
1217 |
-
);
|
1218 |
-
|
1219 |
-
$plan.find('.fs-trial-terms .fs-price').html(
|
1220 |
-
$(this).parents('label').find('.fs-price').html()
|
1221 |
-
);
|
1222 |
-
});
|
1223 |
-
|
1224 |
-
$plan.find('.nav-tab').click(function () {
|
1225 |
-
if ($(this).hasClass('nav-tab-active'))
|
1226 |
-
return;
|
1227 |
-
|
1228 |
-
var $this = $(this),
|
1229 |
-
billingCycle = $this.attr('data-billing-cycle'),
|
1230 |
-
pricing = JSON.parse($this.attr('data-pricing')),
|
1231 |
-
$pricesList = $this.parents('.fs-plan').find('.fs-pricing-body .fs-licenses'),
|
1232 |
-
html = '';
|
1233 |
-
|
1234 |
-
// Un-select previously selected tab.
|
1235 |
-
$plan.find('.nav-tab').removeClass('nav-tab-active');
|
1236 |
-
|
1237 |
-
// Select current tab.
|
1238 |
-
$this.addClass('nav-tab-active');
|
1239 |
-
|
1240 |
-
// Render licenses prices.
|
1241 |
-
if (1 == pricing.length) {
|
1242 |
-
html = '<li><label><?php echo fs_esc_attr_x_inline( 'Price', 'noun', 'price', $api->slug ) ?>: ' + _formatPrice(pricing[0], billingCycle, false) + '</label></li>';
|
1243 |
-
} else {
|
1244 |
-
for (var i = 0; i < pricing.length; i++) {
|
1245 |
-
html += '<li><label><input name="pricing-<?php echo $plan->id ?>" type="radio" value="' + pricing[i].id + '">' + _formatPrice(pricing[i], billingCycle) + '</label></li>';
|
1246 |
-
}
|
1247 |
-
}
|
1248 |
-
$pricesList.html(html);
|
1249 |
-
|
1250 |
-
if (1 < pricing.length) {
|
1251 |
-
// Select first license option.
|
1252 |
-
$pricesList.find('li:first input').click();
|
1253 |
-
}
|
1254 |
-
else {
|
1255 |
-
_updateCtaUrl(
|
1256 |
-
$plan.attr('data-plan-id'),
|
1257 |
-
pricing[0].id,
|
1258 |
-
billingCycle
|
1259 |
-
);
|
1260 |
-
}
|
1261 |
-
|
1262 |
-
// Update billing frequency.
|
1263 |
-
$plan.find('.fs-billing-frequency').html(_formatBillingFrequency(billingCycle));
|
1264 |
-
|
1265 |
-
if ('annual' === billingCycle) {
|
1266 |
-
$plan.find('.fs-annual-discount').show();
|
1267 |
-
} else {
|
1268 |
-
$plan.find('.fs-annual-discount').hide();
|
1269 |
-
}
|
1270 |
-
});
|
1271 |
-
|
1272 |
-
<?php if ( $has_annual ) : ?>
|
1273 |
-
// Select annual by default.
|
1274 |
-
$plan.find('.nav-tab[data-billing-cycle=annual]').click();
|
1275 |
-
<?php else : ?>
|
1276 |
-
// Select first tab.
|
1277 |
-
$plan.find('.nav-tab:first').click();
|
1278 |
-
<?php endif ?>
|
1279 |
-
});
|
1280 |
-
}(jQuery));
|
1281 |
-
</script>
|
1282 |
-
</div>
|
1283 |
-
<div class="fs-pricing-body">
|
1284 |
-
<span class="fs-billing-frequency"></span>
|
1285 |
-
<?php $annual_discount = ( $has_annual && $has_monthly ) ? $plan->pricing[0]->annual_discount_percentage() : 0 ?>
|
1286 |
-
<?php if ( $annual_discount > 0 ) : ?>
|
1287 |
-
<span
|
1288 |
-
class="fs-annual-discount"><?php printf(
|
1289 |
-
/* translators: %s: Discount (e.g. discount of $5 or 10%) */
|
1290 |
-
fs_esc_html_inline( 'Save %s', 'save-x', $api->slug ), $annual_discount . '%' ) ?></span>
|
1291 |
-
<?php endif ?>
|
1292 |
-
<ul class="fs-licenses">
|
1293 |
-
</ul>
|
1294 |
-
<?php echo $this->get_actions_dropdown( $api, $plan ) ?>
|
1295 |
-
<div style="clear:both"></div>
|
1296 |
-
<?php if ( $plan->has_trial() ) : ?>
|
1297 |
-
<?php $trial_period = $this->get_trial_period( $plan ) ?>
|
1298 |
-
<ul class="fs-trial-terms">
|
1299 |
-
<li>
|
1300 |
-
<i class="dashicons dashicons-yes"></i><?php echo esc_html( sprintf( fs_text_inline( 'No commitment for %s - cancel anytime', 'no-commitment-x', $api->slug ), $trial_period ) ) ?>
|
1301 |
-
</li>
|
1302 |
-
<li>
|
1303 |
-
<i class="dashicons dashicons-yes"></i><?php printf( esc_html( fs_text_inline( 'After your free %s, pay as little as %s', 'after-x-pay-as-little-y', $api->slug ) ), $trial_period, '<var class="fs-price">' . $this->get_price_tag( $plan, $plan->pricing[0] ) . '</var>' ) ?>
|
1304 |
-
</li>
|
1305 |
-
</ul>
|
1306 |
-
<?php endif ?>
|
1307 |
-
</div>
|
1308 |
-
</div>
|
1309 |
-
</div>
|
1310 |
-
<?php endforeach ?>
|
1311 |
-
<?php endif ?>
|
1312 |
-
<?php endif ?>
|
1313 |
-
<div>
|
1314 |
-
<h3><?php fs_echo_inline( 'Details', 'details', $api->slug ) ?></h3>
|
1315 |
-
<ul>
|
1316 |
-
<?php if ( ! empty( $api->version ) ) { ?>
|
1317 |
-
<li>
|
1318 |
-
<strong><?php fs_esc_html_echo_x_inline( 'Version', 'product version', 'version', $api->slug ); ?>
|
1319 |
-
:</strong> <?php echo $api->version; ?></li>
|
1320 |
-
<?php
|
1321 |
-
}
|
1322 |
-
if ( ! empty( $api->author ) ) {
|
1323 |
-
?>
|
1324 |
-
<li>
|
1325 |
-
<strong><?php fs_echo_x_inline( 'Author', 'as the plugin author', 'author', $api->slug ); ?>
|
1326 |
-
:</strong> <?php echo links_add_target( $api->author, '_blank' ); ?>
|
1327 |
-
</li>
|
1328 |
-
<?php
|
1329 |
-
}
|
1330 |
-
if ( ! empty( $api->last_updated ) ) {
|
1331 |
-
?>
|
1332 |
-
<li><strong><?php fs_echo_inline( 'Last Updated', 'last-updated', $api->slug ); ?>
|
1333 |
-
:</strong> <span
|
1334 |
-
title="<?php echo $api->last_updated; ?>">
|
1335 |
-
<?php echo esc_html( sprintf(
|
1336 |
-
/* translators: %s: time period (e.g. "2 hours" ago) */
|
1337 |
-
fs_text_x_inline( '%s ago', 'x-ago', $api->slug ),
|
1338 |
-
human_time_diff( strtotime( $api->last_updated ) )
|
1339 |
-
) ) ?>
|
1340 |
-
</span></li>
|
1341 |
-
<?php
|
1342 |
-
}
|
1343 |
-
if ( ! empty( $api->requires ) ) {
|
1344 |
-
?>
|
1345 |
-
<li>
|
1346 |
-
<strong><?php fs_esc_html_echo_inline( 'Requires WordPress Version', 'requires-wordpress-version', $api->slug ) ?>
|
1347 |
-
:</strong> <?php echo esc_html( sprintf( fs_text_inline( '%s or higher', 'x-or-higher', $api->slug ), $api->requires ) ) ?>
|
1348 |
-
</li>
|
1349 |
-
<?php
|
1350 |
-
}
|
1351 |
-
if ( ! empty( $api->tested ) ) {
|
1352 |
-
?>
|
1353 |
-
<li>
|
1354 |
-
<strong><?php fs_esc_html_echo_inline( 'Compatible up to', 'compatible-up-to', $api->slug ); ?>
|
1355 |
-
:</strong> <?php echo $api->tested; ?>
|
1356 |
-
</li>
|
1357 |
-
<?php
|
1358 |
-
}
|
1359 |
-
if ( ! empty( $api->downloaded ) ) {
|
1360 |
-
?>
|
1361 |
-
<li>
|
1362 |
-
<strong><?php fs_esc_html_echo_inline( 'Downloaded', 'downloaded', $api->slug ) ?>
|
1363 |
-
:</strong> <?php echo esc_html( sprintf(
|
1364 |
-
( ( 1 == $api->downloaded ) ?
|
1365 |
-
/* translators: %s: 1 or One (Number of times downloaded) */
|
1366 |
-
fs_text_inline( '%s time', 'x-time', $api->slug ) :
|
1367 |
-
/* translators: %s: Number of times downloaded */
|
1368 |
-
fs_text_inline( '%s times', 'x-times', $api->slug )
|
1369 |
-
),
|
1370 |
-
number_format_i18n( $api->downloaded )
|
1371 |
-
) ); ?>
|
1372 |
-
</li>
|
1373 |
-
<?php
|
1374 |
-
}
|
1375 |
-
if ( ! empty( $api->slug ) && true == $api->is_wp_org_compliant ) {
|
1376 |
-
?>
|
1377 |
-
<li><a target="_blank"
|
1378 |
-
rel="noopener noreferrer"
|
1379 |
-
href="https://wordpress.org/plugins/<?php echo $api->slug; ?>/"><?php fs_esc_html_echo_inline( 'WordPress.org Plugin Page', 'wp-org-plugin-page', $api->slug ) ?>
|
1380 |
-
»</a>
|
1381 |
-
</li>
|
1382 |
-
<?php
|
1383 |
-
}
|
1384 |
-
if ( ! empty( $api->homepage ) ) {
|
1385 |
-
?>
|
1386 |
-
<li><a target="_blank"
|
1387 |
-
rel="noopener noreferrer"
|
1388 |
-
href="<?php echo esc_url( $api->homepage ); ?>"><?php fs_esc_html_echo_inline( 'Plugin Homepage', 'plugin-homepage', $api->slug ) ?>
|
1389 |
-
»</a>
|
1390 |
-
</li>
|
1391 |
-
<?php
|
1392 |
-
}
|
1393 |
-
if ( ! empty( $api->donate_link ) && empty( $api->contributors ) ) {
|
1394 |
-
?>
|
1395 |
-
<li><a target="_blank"
|
1396 |
-
rel="noopener noreferrer"
|
1397 |
-
href="<?php echo esc_url( $api->donate_link ); ?>"><?php fs_esc_html_echo_inline( 'Donate to this plugin', 'donate-to-plugin', $api->slug ) ?>
|
1398 |
-
»</a>
|
1399 |
-
</li>
|
1400 |
-
<?php } ?>
|
1401 |
-
</ul>
|
1402 |
-
</div>
|
1403 |
-
<?php if ( ! empty( $api->rating ) ) { ?>
|
1404 |
-
<h3><?php fs_echo_inline( 'Average Rating', 'average-rating', $api->slug ); ?></h3>
|
1405 |
-
<?php wp_star_rating( array(
|
1406 |
-
'rating' => $api->rating,
|
1407 |
-
'type' => 'percent',
|
1408 |
-
'number' => $api->num_ratings
|
1409 |
-
) ); ?>
|
1410 |
-
<small>(<?php echo esc_html( sprintf(
|
1411 |
-
fs_text_inline( 'based on %s', 'based-on-x', $api->slug ),
|
1412 |
-
sprintf(
|
1413 |
-
( ( 1 == $api->num_ratings ) ?
|
1414 |
-
/* translators: %s: 1 or One */
|
1415 |
-
fs_text_inline( '%s rating', 'x-rating', $api->slug ) :
|
1416 |
-
/* translators: %s: Number larger than 1 */
|
1417 |
-
fs_text_inline( '%s ratings', 'x-ratings', $api->slug )
|
1418 |
-
),
|
1419 |
-
number_format_i18n( $api->num_ratings )
|
1420 |
-
) ) ) ?>)
|
1421 |
-
</small>
|
1422 |
-
<?php
|
1423 |
-
}
|
1424 |
-
|
1425 |
-
if ( ! empty( $api->ratings ) && array_sum( (array) $api->ratings ) > 0 ) {
|
1426 |
-
foreach ( $api->ratings as $key => $ratecount ) {
|
1427 |
-
// Avoid div-by-zero.
|
1428 |
-
$_rating = $api->num_ratings ? ( $ratecount / $api->num_ratings ) : 0;
|
1429 |
-
$stars_label = sprintf(
|
1430 |
-
( ( 1 == $key ) ?
|
1431 |
-
/* translators: %s: 1 or One */
|
1432 |
-
fs_text_inline( '%s star', 'x-star', $api->slug ) :
|
1433 |
-
/* translators: %s: Number larger than 1 */
|
1434 |
-
fs_text_inline( '%s stars', 'x-stars', $api->slug )
|
1435 |
-
),
|
1436 |
-
number_format_i18n( $key )
|
1437 |
-
);
|
1438 |
-
?>
|
1439 |
-
<div class="counter-container">
|
1440 |
-
<span class="counter-label"><a
|
1441 |
-
href="https://wordpress.org/support/view/plugin-reviews/<?php echo $api->slug; ?>?filter=<?php echo $key; ?>"
|
1442 |
-
target="_blank"
|
1443 |
-
rel="noopener noreferrer"
|
1444 |
-
title="<?php echo esc_attr( sprintf(
|
1445 |
-
/* translators: %s: # of stars (e.g. 5 stars) */
|
1446 |
-
fs_text_inline( 'Click to see reviews that provided a rating of %s', 'click-to-reviews', $api->slug ),
|
1447 |
-
$stars_label
|
1448 |
-
) ) ?>"><?php echo $stars_label ?></a></span>
|
1449 |
-
<span class="counter-back">
|
1450 |
-
<span class="counter-bar" style="width: <?php echo absint(92 * $_rating); ?>px;"></span>
|
1451 |
-
</span>
|
1452 |
-
<span class="counter-count"><?php echo number_format_i18n( $ratecount ); ?></span>
|
1453 |
-
</div>
|
1454 |
-
<?php
|
1455 |
-
}
|
1456 |
-
}
|
1457 |
-
if ( ! empty( $api->contributors ) ) {
|
1458 |
-
?>
|
1459 |
-
<h3><?php fs_echo_inline( 'Contributors', 'contributors', $api->slug ); ?></h3>
|
1460 |
-
<ul class="contributors">
|
1461 |
-
<?php
|
1462 |
-
foreach ( (array) $api->contributors as $contrib_username => $contrib_profile ) {
|
1463 |
-
if ( empty( $contrib_username ) && empty( $contrib_profile ) ) {
|
1464 |
-
continue;
|
1465 |
-
}
|
1466 |
-
if ( empty( $contrib_username ) ) {
|
1467 |
-
$contrib_username = preg_replace( '/^.+\/(.+)\/?$/', '\1', $contrib_profile );
|
1468 |
-
}
|
1469 |
-
$contrib_username = sanitize_user( $contrib_username );
|
1470 |
-
if ( empty( $contrib_profile ) ) {
|
1471 |
-
echo "<li><img src='https://wordpress.org/grav-redirect.php?user={$contrib_username}&s=36' width='18' height='18' />{$contrib_username}</li>";
|
1472 |
-
} else {
|
1473 |
-
echo "<li><a href='{$contrib_profile}' target='_blank' rel='noopener noreferrer'><img src='https://wordpress.org/grav-redirect.php?user={$contrib_username}&s=36' width='18' height='18' />{$contrib_username}</a></li>";
|
1474 |
-
}
|
1475 |
-
}
|
1476 |
-
?>
|
1477 |
-
</ul>
|
1478 |
-
<?php if ( ! empty( $api->donate_link ) ) { ?>
|
1479 |
-
<a target="_blank"
|
1480 |
-
rel="noopener noreferrer"
|
1481 |
-
href="<?php echo esc_url( $api->donate_link ); ?>"><?php fs_echo_inline( 'Donate to this plugin', 'donate-to-plugin', $api->slug ) ?>
|
1482 |
-
»</a>
|
1483 |
-
<?php } ?>
|
1484 |
-
<?php } ?>
|
1485 |
-
</div>
|
1486 |
-
<div id="section-holder" class="wrap">
|
1487 |
-
<?php
|
1488 |
-
if ( ! empty( $api->tested ) && version_compare( substr( $GLOBALS['wp_version'], 0, strlen( $api->tested ) ), $api->tested, '>' ) ) {
|
1489 |
-
echo '<div class="notice notice-warning"><p>' . '<strong>' . fs_text_inline( 'Warning', 'warning', $api->slug ) . ':</strong> ' . fs_text_inline( 'This plugin has not been tested with your current version of WordPress.', 'not-tested-warning', $api->slug ) . '</p></div>';
|
1490 |
-
} else if ( ! empty( $api->requires ) && version_compare( substr( $GLOBALS['wp_version'], 0, strlen( $api->requires ) ), $api->requires, '<' ) ) {
|
1491 |
-
echo '<div class="notice notice-warning"><p>' . '<strong>' . fs_text_inline( 'Warning', 'warning', $api->slug ) . ':</strong> ' . fs_text_inline( 'This plugin has not been marked as compatible with your version of WordPress.', 'not-compatible-warning', $api->slug ) . '</p></div>';
|
1492 |
-
}
|
1493 |
-
|
1494 |
-
foreach ( (array) $api->sections as $section_name => $content ) {
|
1495 |
-
$content = links_add_base_url( $content, 'https://wordpress.org/plugins/' . $api->slug . '/' );
|
1496 |
-
$content = links_add_target( $content, '_blank' );
|
1497 |
-
|
1498 |
-
$san_section = esc_attr( $section_name );
|
1499 |
-
|
1500 |
-
$display = ( $section_name === $section ) ? 'block' : 'none';
|
1501 |
-
|
1502 |
-
if ( 'description' === $section_name &&
|
1503 |
-
( ( $api->is_wp_org_compliant && $api->wp_org_missing ) ||
|
1504 |
-
( ! $api->is_wp_org_compliant && $api->fs_missing ) )
|
1505 |
-
) {
|
1506 |
-
$missing_notice = array(
|
1507 |
-
'type' => 'error',
|
1508 |
-
'id' => md5( microtime() ),
|
1509 |
-
'message' => $api->is_paid ?
|
1510 |
-
fs_text_inline( 'Paid add-on must be deployed to Freemius.', 'paid-addon-not-deployed', $api->slug ) :
|
1511 |
-
fs_text_inline( 'Add-on must be deployed to WordPress.org or Freemius.', 'free-addon-not-deployed', $api->slug ),
|
1512 |
-
);
|
1513 |
-
fs_require_template( 'admin-notice.php', $missing_notice );
|
1514 |
-
}
|
1515 |
-
echo "\t<div id='section-{$san_section}' class='section' style='display: {$display};'>\n";
|
1516 |
-
echo $content;
|
1517 |
-
echo "\t</div>\n";
|
1518 |
-
}
|
1519 |
-
echo "</div>\n";
|
1520 |
-
echo "</div>\n";
|
1521 |
-
echo "</div>\n"; // #plugin-information-scrollable
|
1522 |
-
echo "<div id='$tab-footer'>\n";
|
1523 |
-
|
1524 |
-
if (
|
1525 |
-
! empty( $api->download_link ) &&
|
1526 |
-
! empty( $this->status ) &&
|
1527 |
-
in_array( $this->status['status'], array( 'newer_installed', 'latest_installed' ) )
|
1528 |
-
) {
|
1529 |
-
if ( 'newer_installed' === $this->status['status'] ) {
|
1530 |
-
echo $this->get_cta(
|
1531 |
-
( $this->status['is_premium_installed'] ?
|
1532 |
-
esc_html( sprintf( fs_text_inline( 'Newer Version (%s) Installed', 'newer-installed', $api->slug ), $this->status['version'] ) ) :
|
1533 |
-
esc_html( sprintf( fs_text_inline( 'Newer Free Version (%s) Installed', 'newer-free-installed', $api->slug ), $this->status['version'] ) ) ),
|
1534 |
-
false,
|
1535 |
-
true
|
1536 |
-
);
|
1537 |
-
} else {
|
1538 |
-
echo $this->get_cta(
|
1539 |
-
( $this->status['is_premium_installed'] ?
|
1540 |
-
fs_esc_html_inline( 'Latest Version Installed', 'latest-installed', $api->slug ) :
|
1541 |
-
fs_esc_html_inline( 'Latest Free Version Installed', 'latest-free-installed', $api->slug ) ),
|
1542 |
-
false,
|
1543 |
-
true
|
1544 |
-
);
|
1545 |
-
}
|
1546 |
-
}
|
1547 |
-
|
1548 |
-
echo $this->get_actions_dropdown( $api, null );
|
1549 |
-
|
1550 |
-
echo "</div>\n";
|
1551 |
-
?>
|
1552 |
-
<script type="text/javascript">
|
1553 |
-
( function( $, undef ) {
|
1554 |
-
var $dropdowns = $( '.fs-dropdown' );
|
1555 |
-
|
1556 |
-
$( '#plugin-information' )
|
1557 |
-
.click( function( evt ) {
|
1558 |
-
var $target = $( evt.target );
|
1559 |
-
|
1560 |
-
if (
|
1561 |
-
$target.hasClass( 'fs-dropdown-arrow-button' ) ||
|
1562 |
-
( 0 !== $target.parents( '.fs-dropdown-arrow-button' ).length )
|
1563 |
-
) {
|
1564 |
-
var $dropdown = $target.parents( '.fs-dropdown' ),
|
1565 |
-
isActive = $dropdown.hasClass( 'active' );
|
1566 |
-
|
1567 |
-
if ( ! isActive ) {
|
1568 |
-
/**
|
1569 |
-
* Close the other dropdown if it's active.
|
1570 |
-
*
|
1571 |
-
* @author Leo Fajardo (@leorw)
|
1572 |
-
* @since 2.3.0
|
1573 |
-
*/
|
1574 |
-
$( '.fs-dropdown.active' ).each( function() {
|
1575 |
-
toggleDropdown( $( this ), false );
|
1576 |
-
} );
|
1577 |
-
}
|
1578 |
-
|
1579 |
-
/**
|
1580 |
-
* Toggle the current dropdown.
|
1581 |
-
*
|
1582 |
-
* @author Leo Fajardo (@leorw)
|
1583 |
-
* @since 2.3.0
|
1584 |
-
*/
|
1585 |
-
toggleDropdown( $dropdown, ! isActive );
|
1586 |
-
|
1587 |
-
return true;
|
1588 |
-
}
|
1589 |
-
|
1590 |
-
/**
|
1591 |
-
* Close all dropdowns.
|
1592 |
-
*
|
1593 |
-
* @author Leo Fajardo (@leorw)
|
1594 |
-
* @since 2.3.0
|
1595 |
-
*/
|
1596 |
-
toggleDropdown( $( this ).find( '.fs-dropdown' ), false );
|
1597 |
-
});
|
1598 |
-
|
1599 |
-
if ( 0 !== $dropdowns.length ) {
|
1600 |
-
/**
|
1601 |
-
* Add the `up` class so that the bottom dropdown's content will be shown above its buttons.
|
1602 |
-
*
|
1603 |
-
* @author Leo Fajardo (@leorw)
|
1604 |
-
* @since 2.3.0
|
1605 |
-
*/
|
1606 |
-
$( '#plugin-information-footer' ).find( '.fs-dropdown' ).addClass( 'up' );
|
1607 |
-
}
|
1608 |
-
|
1609 |
-
/**
|
1610 |
-
* Returns the default state of the dropdown arrow button and hides the dropdown list.
|
1611 |
-
*
|
1612 |
-
* @author Leo Fajardo (@leorw)
|
1613 |
-
* @since 2.3.0
|
1614 |
-
*
|
1615 |
-
* @param {Object} [$dropdown]
|
1616 |
-
* @param {Boolean} [state]
|
1617 |
-
*/
|
1618 |
-
function toggleDropdown( $dropdown, state ) {
|
1619 |
-
if ( undef === $dropdown ) {
|
1620 |
-
var $activeDropdown = $dropdowns.find( '.active' );
|
1621 |
-
if ( 0 !== $activeDropdown.length ) {
|
1622 |
-
$dropdown = $activeDropdown;
|
1623 |
-
}
|
1624 |
-
}
|
1625 |
-
|
1626 |
-
if ( undef === $dropdown ) {
|
1627 |
-
return;
|
1628 |
-
}
|
1629 |
-
|
1630 |
-
if ( undef === state ) {
|
1631 |
-
state = false;
|
1632 |
-
}
|
1633 |
-
|
1634 |
-
$dropdown.toggleClass( 'active', state );
|
1635 |
-
$dropdown.find( '.fs-dropdown-list' ).toggle( state );
|
1636 |
-
$dropdown.find( '.fs-dropdown-arrow-button' ).toggleClass( 'active', state );
|
1637 |
-
}
|
1638 |
-
} )( jQuery );
|
1639 |
-
</script>
|
1640 |
-
<?php
|
1641 |
-
iframe_footer();
|
1642 |
-
exit;
|
1643 |
-
}
|
1644 |
-
}
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package Freemius
|
4 |
+
* @copyright Copyright (c) 2015, Freemius, Inc.
|
5 |
+
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
|
6 |
+
* @since 1.0.6
|
7 |
+
*/
|
8 |
+
|
9 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
10 |
+
exit;
|
11 |
+
}
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Class FS_Plugin_Info_Dialog
|
15 |
+
*
|
16 |
+
* @author Vova Feldman (@svovaf)
|
17 |
+
* @since 1.1.7
|
18 |
+
*/
|
19 |
+
class FS_Plugin_Info_Dialog {
|
20 |
+
/**
|
21 |
+
* @since 1.1.7
|
22 |
+
*
|
23 |
+
* @var FS_Logger
|
24 |
+
*/
|
25 |
+
private $_logger;
|
26 |
+
|
27 |
+
/**
|
28 |
+
* @since 1.1.7
|
29 |
+
*
|
30 |
+
* @var Freemius
|
31 |
+
*/
|
32 |
+
private $_fs;
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Collection of plugin installation, update, download, activation, and purchase actions. This is used in
|
36 |
+
* populating the actions dropdown list when there are at least 2 actions. If there's only 1 action, a button
|
37 |
+
* is used instead.
|
38 |
+
*
|
39 |
+
* @author Leo Fajardo (@leorw)
|
40 |
+
* @since 2.3.0
|
41 |
+
*
|
42 |
+
* @var string[]
|
43 |
+
*/
|
44 |
+
private $actions;
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Contains plugin status information that is used to determine which actions should be part of the actions
|
48 |
+
* dropdown list.
|
49 |
+
*
|
50 |
+
* @author Leo Fajardo (@leorw)
|
51 |
+
* @since 2.3.0
|
52 |
+
*
|
53 |
+
* @var string[]
|
54 |
+
*/
|
55 |
+
private $status;
|
56 |
+
|
57 |
+
function __construct( Freemius $fs ) {
|
58 |
+
$this->_fs = $fs;
|
59 |
+
|
60 |
+
$this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $fs->get_slug() . '_info', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
|
61 |
+
|
62 |
+
// Remove default plugin information action.
|
63 |
+
remove_all_actions( 'install_plugins_pre_plugin-information' );
|
64 |
+
|
65 |
+
// Override action with custom plugins function for add-ons.
|
66 |
+
add_action( 'install_plugins_pre_plugin-information', array( &$this, 'install_plugin_information' ) );
|
67 |
+
|
68 |
+
// Override request for plugin information for Add-ons.
|
69 |
+
add_filter(
|
70 |
+
'fs_plugins_api',
|
71 |
+
array( &$this, '_get_addon_info_filter' ),
|
72 |
+
WP_FS__DEFAULT_PRIORITY, 3 );
|
73 |
+
}
|
74 |
+
|
75 |
+
/**
|
76 |
+
* Generate add-on plugin information.
|
77 |
+
*
|
78 |
+
* @author Vova Feldman (@svovaf)
|
79 |
+
* @since 1.0.6
|
80 |
+
*
|
81 |
+
* @param array $data
|
82 |
+
* @param string $action
|
83 |
+
* @param object|null $args
|
84 |
+
*
|
85 |
+
* @return array|null
|
86 |
+
*/
|
87 |
+
function _get_addon_info_filter( $data, $action = '', $args = null ) {
|
88 |
+
$this->_logger->entrance();
|
89 |
+
|
90 |
+
$parent_plugin_id = fs_request_get( 'parent_plugin_id', $this->_fs->get_id() );
|
91 |
+
|
92 |
+
if ( $this->_fs->get_id() != $parent_plugin_id ||
|
93 |
+
( 'plugin_information' !== $action ) ||
|
94 |
+
! isset( $args->slug )
|
95 |
+
) {
|
96 |
+
return $data;
|
97 |
+
}
|
98 |
+
|
99 |
+
// Find add-on by slug.
|
100 |
+
$selected_addon = $this->_fs->get_addon_by_slug( $args->slug, WP_FS__DEV_MODE );
|
101 |
+
|
102 |
+
if ( false === $selected_addon ) {
|
103 |
+
return $data;
|
104 |
+
}
|
105 |
+
|
106 |
+
if ( ! isset( $selected_addon->info ) ) {
|
107 |
+
// Setup some default info.
|
108 |
+
$selected_addon->info = new stdClass();
|
109 |
+
$selected_addon->info->selling_point_0 = 'Selling Point 1';
|
110 |
+
$selected_addon->info->selling_point_1 = 'Selling Point 2';
|
111 |
+
$selected_addon->info->selling_point_2 = 'Selling Point 3';
|
112 |
+
$selected_addon->info->description = '<p>Tell your users all about your add-on</p>';
|
113 |
+
}
|
114 |
+
|
115 |
+
fs_enqueue_local_style( 'fs_addons', '/admin/add-ons.css' );
|
116 |
+
|
117 |
+
$data = $args;
|
118 |
+
|
119 |
+
$has_free_plan = false;
|
120 |
+
$has_paid_plan = false;
|
121 |
+
|
122 |
+
// Load add-on pricing.
|
123 |
+
$has_pricing = false;
|
124 |
+
$has_features = false;
|
125 |
+
$plans = false;
|
126 |
+
|
127 |
+
$result = $this->_fs->get_api_plugin_scope()->get( $this->_fs->add_show_pending( "/addons/{$selected_addon->id}/pricing.json?type=visible" ) );
|
128 |
+
|
129 |
+
if ( ! isset( $result->error ) ) {
|
130 |
+
$plans = $result->plans;
|
131 |
+
|
132 |
+
if ( is_array( $plans ) ) {
|
133 |
+
for ( $i = 0, $len = count( $plans ); $i < $len; $i ++ ) {
|
134 |
+
$pricing = isset( $plans[ $i ]->pricing ) ? $plans[ $i ]->pricing : null;
|
135 |
+
$features = isset( $plans[ $i ]->features ) ? $plans[ $i ]->features : null;
|
136 |
+
|
137 |
+
$plans[ $i ] = new FS_Plugin_Plan( $plans[ $i ] );
|
138 |
+
$plan = $plans[ $i ];
|
139 |
+
|
140 |
+
if ( 'free' == $plans[ $i ]->name ||
|
141 |
+
! is_array( $pricing ) ||
|
142 |
+
0 == count( $pricing )
|
143 |
+
) {
|
144 |
+
$has_free_plan = true;
|
145 |
+
}
|
146 |
+
|
147 |
+
if ( is_array( $pricing ) && 0 < count( $pricing ) ) {
|
148 |
+
$filtered_pricing = array();
|
149 |
+
|
150 |
+
foreach ( $pricing as $prices ) {
|
151 |
+
$prices = new FS_Pricing( $prices );
|
152 |
+
|
153 |
+
if ( ! $prices->is_usd() ) {
|
154 |
+
/**
|
155 |
+
* Skip non-USD pricing.
|
156 |
+
*
|
157 |
+
* @author Leo Fajardo (@leorw)
|
158 |
+
* @since 2.3.1
|
159 |
+
*/
|
160 |
+
continue;
|
161 |
+
}
|
162 |
+
|
163 |
+
if ( ( $prices->has_monthly() && $prices->monthly_price > 1.0 ) ||
|
164 |
+
( $prices->has_annual() && $prices->annual_price > 1.0 ) ||
|
165 |
+
( $prices->has_lifetime() && $prices->lifetime_price > 1.0 )
|
166 |
+
) {
|
167 |
+
$filtered_pricing[] = $prices;
|
168 |
+
}
|
169 |
+
}
|
170 |
+
|
171 |
+
if ( ! empty( $filtered_pricing ) ) {
|
172 |
+
$has_paid_plan = true;
|
173 |
+
|
174 |
+
$plan->pricing = $filtered_pricing;
|
175 |
+
|
176 |
+
$has_pricing = true;
|
177 |
+
}
|
178 |
+
}
|
179 |
+
|
180 |
+
if ( is_array( $features ) && 0 < count( $features ) ) {
|
181 |
+
$plan->features = $features;
|
182 |
+
|
183 |
+
$has_features = true;
|
184 |
+
}
|
185 |
+
}
|
186 |
+
}
|
187 |
+
}
|
188 |
+
|
189 |
+
$latest = null;
|
190 |
+
|
191 |
+
if ( ! $has_paid_plan && $selected_addon->is_wp_org_compliant ) {
|
192 |
+
$repo_data = FS_Plugin_Updater::_fetch_plugin_info_from_repository(
|
193 |
+
'plugin_information', (object) array(
|
194 |
+
'slug' => $selected_addon->slug,
|
195 |
+
'is_ssl' => is_ssl(),
|
196 |
+
'fields' => array(
|
197 |
+
'banners' => true,
|
198 |
+
'reviews' => true,
|
199 |
+
'downloaded' => false,
|
200 |
+
'active_installs' => true
|
201 |
+
)
|
202 |
+
) );
|
203 |
+
|
204 |
+
if ( ! empty( $repo_data ) ) {
|
205 |
+
$data = $repo_data;
|
206 |
+
$data->wp_org_missing = false;
|
207 |
+
} else {
|
208 |
+
// Couldn't find plugin on .org.
|
209 |
+
$selected_addon->is_wp_org_compliant = false;
|
210 |
+
|
211 |
+
// Plugin is missing, not on Freemius nor WP.org.
|
212 |
+
$data->wp_org_missing = true;
|
213 |
+
}
|
214 |
+
|
215 |
+
$data->fs_missing = ( ! $has_free_plan || $data->wp_org_missing );
|
216 |
+
} else {
|
217 |
+
$data->has_purchased_license = false;
|
218 |
+
$data->wp_org_missing = false;
|
219 |
+
|
220 |
+
$fs_addon = null;
|
221 |
+
$current_addon_version = false;
|
222 |
+
if ( $this->_fs->is_addon_activated( $selected_addon->id ) ) {
|
223 |
+
$fs_addon = $this->_fs->get_addon_instance( $selected_addon->id );
|
224 |
+
$current_addon_version = $fs_addon->get_plugin_version();
|
225 |
+
} else if ( $this->_fs->is_addon_installed( $selected_addon->id ) ) {
|
226 |
+
$addon_plugin_data = get_plugin_data(
|
227 |
+
( WP_PLUGIN_DIR . '/' . $this->_fs->get_addon_basename( $selected_addon->id ) ),
|
228 |
+
false,
|
229 |
+
false
|
230 |
+
);
|
231 |
+
|
232 |
+
if ( ! empty( $addon_plugin_data ) ) {
|
233 |
+
$current_addon_version = $addon_plugin_data['Version'];
|
234 |
+
}
|
235 |
+
}
|
236 |
+
|
237 |
+
// Fetch latest version from Freemius.
|
238 |
+
$latest = $this->_fs->_fetch_latest_version(
|
239 |
+
$selected_addon->id,
|
240 |
+
true,
|
241 |
+
WP_FS__TIME_24_HOURS_IN_SEC,
|
242 |
+
$current_addon_version
|
243 |
+
);
|
244 |
+
|
245 |
+
if ( $has_paid_plan ) {
|
246 |
+
$blog_id = fs_request_get( 'fs_blog_id' );
|
247 |
+
$has_valid_blog_id = is_numeric( $blog_id );
|
248 |
+
|
249 |
+
if ( $has_valid_blog_id ) {
|
250 |
+
switch_to_blog( $blog_id );
|
251 |
+
}
|
252 |
+
|
253 |
+
$data->checkout_link = $this->_fs->checkout_url(
|
254 |
+
WP_FS__PERIOD_ANNUALLY,
|
255 |
+
false,
|
256 |
+
array(),
|
257 |
+
( $has_valid_blog_id ? false : null )
|
258 |
+
);
|
259 |
+
|
260 |
+
if ( $has_valid_blog_id ) {
|
261 |
+
restore_current_blog();
|
262 |
+
}
|
263 |
+
}
|
264 |
+
|
265 |
+
/**
|
266 |
+
* Check if there's a purchased license in case the add-on can only be installed/downloaded as part of a purchased bundle.
|
267 |
+
*
|
268 |
+
* @author Leo Fajardo (@leorw)
|
269 |
+
* @since 2.4.1
|
270 |
+
*/
|
271 |
+
if ( is_object( $fs_addon ) ) {
|
272 |
+
$data->has_purchased_license = $fs_addon->has_active_valid_license();
|
273 |
+
} else {
|
274 |
+
$account_addons = $this->_fs->get_account_addons();
|
275 |
+
if ( ! empty( $account_addons ) && in_array( $selected_addon->id, $account_addons ) ) {
|
276 |
+
$data->has_purchased_license = true;
|
277 |
+
}
|
278 |
+
}
|
279 |
+
|
280 |
+
if ( $has_free_plan || $data->has_purchased_license ) {
|
281 |
+
$data->download_link = $this->_fs->_get_latest_download_local_url( $selected_addon->id );
|
282 |
+
}
|
283 |
+
|
284 |
+
$data->fs_missing = (
|
285 |
+
false === $latest &&
|
286 |
+
(
|
287 |
+
empty( $selected_addon->premium_releases_count ) ||
|
288 |
+
! ( $selected_addon->premium_releases_count > 0 )
|
289 |
+
)
|
290 |
+
);
|
291 |
+
|
292 |
+
// Fetch as much as possible info from local files.
|
293 |
+
$plugin_local_data = $this->_fs->get_plugin_data();
|
294 |
+
$data->author = $plugin_local_data['Author'];
|
295 |
+
|
296 |
+
if ( ! empty( $selected_addon->info->banner_url ) ) {
|
297 |
+
$data->banners = array(
|
298 |
+
'low' => $selected_addon->info->banner_url,
|
299 |
+
);
|
300 |
+
}
|
301 |
+
|
302 |
+
if ( ! empty( $selected_addon->info->screenshots ) ) {
|
303 |
+
$view_vars = array(
|
304 |
+
'screenshots' => $selected_addon->info->screenshots,
|
305 |
+
'plugin' => $selected_addon,
|
306 |
+
);
|
307 |
+
$data->sections['screenshots'] = fs_get_template( '/plugin-info/screenshots.php', $view_vars );
|
308 |
+
}
|
309 |
+
|
310 |
+
if ( is_object( $latest ) ) {
|
311 |
+
$data->version = $latest->version;
|
312 |
+
$data->last_updated = $latest->created;
|
313 |
+
$data->requires = $latest->requires_platform_version;
|
314 |
+
$data->tested = $latest->tested_up_to_version;
|
315 |
+
} else if ( ! empty( $current_addon_version ) ) {
|
316 |
+
$data->version = $current_addon_version;
|
317 |
+
} else {
|
318 |
+
// Add dummy version.
|
319 |
+
$data->version = '1.0.0';
|
320 |
+
|
321 |
+
// Add message to developer to deploy the plugin through Freemius.
|
322 |
+
}
|
323 |
+
}
|
324 |
+
|
325 |
+
$data->name = $selected_addon->title;
|
326 |
+
$view_vars = array( 'plugin' => $selected_addon );
|
327 |
+
|
328 |
+
if ( is_object( $latest ) && isset( $latest->readme ) && is_object( $latest->readme ) ) {
|
329 |
+
$latest_version_readme_data = $latest->readme;
|
330 |
+
if ( isset( $latest_version_readme_data->sections ) ) {
|
331 |
+
$data->sections = (array) $latest_version_readme_data->sections;
|
332 |
+
} else {
|
333 |
+
$data->sections = array();
|
334 |
+
}
|
335 |
+
}
|
336 |
+
|
337 |
+
$data->sections['description'] = fs_get_template( '/plugin-info/description.php', $view_vars );
|
338 |
+
|
339 |
+
if ( $has_pricing ) {
|
340 |
+
// Add plans to data.
|
341 |
+
$data->plans = $plans;
|
342 |
+
|
343 |
+
if ( $has_features ) {
|
344 |
+
$view_vars = array(
|
345 |
+
'plans' => $plans,
|
346 |
+
'plugin' => $selected_addon,
|
347 |
+
);
|
348 |
+
$data->sections['features'] = fs_get_template( '/plugin-info/features.php', $view_vars );
|
349 |
+
}
|
350 |
+
}
|
351 |
+
|
352 |
+
$data->has_free_plan = $has_free_plan;
|
353 |
+
$data->has_paid_plan = $has_paid_plan;
|
354 |
+
$data->is_paid = $has_paid_plan;
|
355 |
+
$data->is_wp_org_compliant = $selected_addon->is_wp_org_compliant;
|
356 |
+
$data->premium_slug = $selected_addon->premium_slug;
|
357 |
+
$data->addon_id = $selected_addon->id;
|
358 |
+
|
359 |
+
if ( ! isset( $data->has_purchased_license ) ) {
|
360 |
+
$data->has_purchased_license = false;
|
361 |
+
}
|
362 |
+
|
363 |
+
return $data;
|
364 |
+
}
|
365 |
+
|
366 |
+
/**
|
367 |
+
* @author Vova Feldman (@svovaf)
|
368 |
+
* @since 1.1.7
|
369 |
+
*
|
370 |
+
* @param FS_Plugin_Plan $plan
|
371 |
+
*
|
372 |
+
* @return string
|
373 |
+
*/
|
374 |
+
private function get_billing_cycle( FS_Plugin_Plan $plan ) {
|
375 |
+
$billing_cycle = null;
|
376 |
+
|
377 |
+
if ( 1 === count( $plan->pricing ) && 1 == $plan->pricing[0]->licenses ) {
|
378 |
+
$pricing = $plan->pricing[0];
|
379 |
+
if ( isset( $pricing->annual_price ) ) {
|
380 |
+
$billing_cycle = 'annual';
|
381 |
+
} else if ( isset( $pricing->monthly_price ) ) {
|
382 |
+
$billing_cycle = 'monthly';
|
383 |
+
} else if ( isset( $pricing->lifetime_price ) ) {
|
384 |
+
$billing_cycle = 'lifetime';
|
385 |
+
}
|
386 |
+
} else {
|
387 |
+
foreach ( $plan->pricing as $pricing ) {
|
388 |
+
if ( isset( $pricing->annual_price ) ) {
|
389 |
+
$billing_cycle = 'annual';
|
390 |
+
} else if ( isset( $pricing->monthly_price ) ) {
|
391 |
+
$billing_cycle = 'monthly';
|
392 |
+
} else if ( isset( $pricing->lifetime_price ) ) {
|
393 |
+
$billing_cycle = 'lifetime';
|
394 |
+
}
|
395 |
+
|
396 |
+
if ( ! is_null( $billing_cycle ) ) {
|
397 |
+
break;
|
398 |
+
}
|
399 |
+
}
|
400 |
+
}
|
401 |
+
|
402 |
+
return $billing_cycle;
|
403 |
+
}
|
404 |
+
|
405 |
+
/**
|
406 |
+
* @author Vova Feldman (@svovaf)
|
407 |
+
* @since 2.0.0
|
408 |
+
*
|
409 |
+
* @param FS_Plugin_Plan $plan
|
410 |
+
* @param FS_Pricing $pricing
|
411 |
+
*
|
412 |
+
* @return float|null|string
|
413 |
+
*/
|
414 |
+
private function get_price_tag( FS_Plugin_Plan $plan, FS_Pricing $pricing ) {
|
415 |
+
$price_tag = '';
|
416 |
+
if ( isset( $pricing->annual_price ) ) {
|
417 |
+
$price_tag = $pricing->annual_price . ( $plan->is_block_features ? ' / year' : '' );
|
418 |
+
} else if ( isset( $pricing->monthly_price ) ) {
|
419 |
+
$price_tag = $pricing->monthly_price . ' / mo';
|
420 |
+
} else if ( isset( $pricing->lifetime_price ) ) {
|
421 |
+
$price_tag = $pricing->lifetime_price;
|
422 |
+
}
|
423 |
+
|
424 |
+
return '$' . $price_tag;
|
425 |
+
}
|
426 |
+
|
427 |
+
/**
|
428 |
+
* @author Leo Fajardo (@leorw)
|
429 |
+
* @since 2.3.0
|
430 |
+
*
|
431 |
+
* @param object $api
|
432 |
+
* @param FS_Plugin_Plan $plan
|
433 |
+
*
|
434 |
+
* @return string
|
435 |
+
*/
|
436 |
+
private function get_actions_dropdown( $api, $plan = null ) {
|
437 |
+
$this->actions = isset( $this->actions ) ?
|
438 |
+
$this->actions :
|
439 |
+
$this->get_plugin_actions( $api );
|
440 |
+
|
441 |
+
$actions = $this->actions;
|
442 |
+
|
443 |
+
$checkout_cta = $this->get_checkout_cta( $api, $plan );
|
444 |
+
if ( ! empty( $checkout_cta ) ) {
|
445 |
+
/**
|
446 |
+
* If there's no license yet, make the checkout button the main CTA. Otherwise, make it the last item in
|
447 |
+
* the actions dropdown.
|
448 |
+
*
|
449 |
+
* @author Leo Fajardo (@leorw)
|
450 |
+
* @since 2.3.0
|
451 |
+
*/
|
452 |
+
if ( ! $api->has_purchased_license ) {
|
453 |
+
array_unshift( $actions, $checkout_cta );
|
454 |
+
} else {
|
455 |
+
$actions[] = $checkout_cta;
|
456 |
+
}
|
457 |
+
}
|
458 |
+
|
459 |
+
if ( empty( $actions ) ) {
|
460 |
+
return '';
|
461 |
+
}
|
462 |
+
|
463 |
+
$total_actions = count( $actions );
|
464 |
+
if ( 1 === $total_actions ) {
|
465 |
+
return $actions[0];
|
466 |
+
}
|
467 |
+
|
468 |
+
ob_start();
|
469 |
+
|
470 |
+
?>
|
471 |
+
<div class="fs-cta fs-dropdown">
|
472 |
+
<div class="button-group">
|
473 |
+
<?php
|
474 |
+
// This should NOT be sanitized as the $actions are HTML buttons already.
|
475 |
+
echo $actions[0] ?>
|
476 |
+
<div class="button button-primary fs-dropdown-arrow-button">
|
477 |
+
<span class="fs-dropdown-arrow"></span>
|
478 |
+
<ul class="fs-dropdown-list" style="display: none">
|
479 |
+
<?php for ( $i = 1; $i < $total_actions; $i ++ ) : ?>
|
480 |
+
<li><?php echo str_replace( 'button button-primary', '', $actions[ $i ] ) ?></li>
|
481 |
+
<?php endfor ?>
|
482 |
+
</ul>
|
483 |
+
</div>
|
484 |
+
</div>
|
485 |
+
</div>
|
486 |
+
<?php
|
487 |
+
|
488 |
+
return ob_get_clean();
|
489 |
+
}
|
490 |
+
|
491 |
+
/**
|
492 |
+
* @author Vova Feldman (@svovaf)
|
493 |
+
* @since 1.1.7
|
494 |
+
*
|
495 |
+
* @param object $api
|
496 |
+
* @param FS_Plugin_Plan $plan
|
497 |
+
*
|
498 |
+
* @return string
|
499 |
+
*/
|
500 |
+
private function get_checkout_cta( $api, $plan = null ) {
|
501 |
+
if ( empty( $api->checkout_link ) ||
|
502 |
+
! isset( $api->plans ) ||
|
503 |
+
! is_array( $api->plans ) ||
|
504 |
+
0 == count( $api->plans )
|
505 |
+
) {
|
506 |
+
return '';
|
507 |
+
}
|
508 |
+
|
509 |
+
if ( is_null( $plan ) ) {
|
510 |
+
foreach ( $api->plans as $p ) {
|
511 |
+
if ( ! empty( $p->pricing ) ) {
|
512 |
+
$plan = $p;
|
513 |
+
break;
|
514 |
+
}
|
515 |
+
}
|
516 |
+
}
|
517 |
+
|
518 |
+
$blog_id = fs_request_get( 'fs_blog_id' );
|
519 |
+
$has_valid_blog_id = is_numeric( $blog_id );
|
520 |
+
|
521 |
+
if ( $has_valid_blog_id ) {
|
522 |
+
switch_to_blog( $blog_id );
|
523 |
+
}
|
524 |
+
|
525 |
+
$addon_checkout_url = $this->_fs->addon_checkout_url(
|
526 |
+
$plan->plugin_id,
|
527 |
+
$plan->pricing[0]->id,
|
528 |
+
$this->get_billing_cycle( $plan ),
|
529 |
+
$plan->has_trial(),
|
530 |
+
( $has_valid_blog_id ? false : null )
|
531 |
+
);
|
532 |
+
|
533 |
+
if ( $has_valid_blog_id ) {
|
534 |
+
restore_current_blog();
|
535 |
+
}
|
536 |
+
|
537 |
+
return '<a class="button button-primary fs-checkout-button right" href="' . $addon_checkout_url . '" target="_parent">' .
|
538 |
+
esc_html( ! $plan->has_trial() ?
|
539 |
+
(
|
540 |
+
$api->has_purchased_license ?
|
541 |
+
fs_text_inline( 'Purchase More', 'purchase-more', $api->slug ) :
|
542 |
+
fs_text_x_inline( 'Purchase', 'verb', 'purchase', $api->slug )
|
543 |
+
) :
|
544 |
+
sprintf(
|
545 |
+
/* translators: %s: N-days trial */
|
546 |
+
fs_text_inline( 'Start my free %s', 'start-free-x', $api->slug ),
|
547 |
+
$this->get_trial_period( $plan )
|
548 |
+
)
|
549 |
+
) .
|
550 |
+
'</a>';
|
551 |
+
}
|
552 |
+
|
553 |
+
/**
|
554 |
+
* @author Leo Fajardo (@leorw)
|
555 |
+
* @since 2.3.0
|
556 |
+
*
|
557 |
+
* @param object $api
|
558 |
+
*
|
559 |
+
* @return string[]
|
560 |
+
*/
|
561 |
+
private function get_plugin_actions( $api ) {
|
562 |
+
$this->status = isset( $this->status ) ?
|
563 |
+
$this->status :
|
564 |
+
install_plugin_install_status( $api );
|
565 |
+
|
566 |
+
$is_update_available = ( 'update_available' === $this->status['status'] );
|
567 |
+
|
568 |
+
if ( $is_update_available && empty( $this->status['url'] ) ) {
|
569 |
+
return array();
|
570 |
+
}
|
571 |
+
|
572 |
+
$blog_id = fs_request_get( 'fs_blog_id' );
|
573 |
+
|
574 |
+
$active_plugins_directories_map = Freemius::get_active_plugins_directories_map( $blog_id );
|
575 |
+
|
576 |
+
$actions = array();
|
577 |
+
|
578 |
+
$is_addon_activated = $this->_fs->is_addon_activated( $api->slug );
|
579 |
+
$fs_addon = null;
|
580 |
+
|
581 |
+
$is_free_installed = null;
|
582 |
+
$is_premium_installed = null;
|
583 |
+
|
584 |
+
$has_installed_version = ( 'install' !== $this->status['status'] );
|
585 |
+
|
586 |
+
if ( ! $api->has_paid_plan && ! $api->has_purchased_license ) {
|
587 |
+
/**
|
588 |
+
* Free-only add-on.
|
589 |
+
*
|
590 |
+
* @author Leo Fajardo (@leorw)
|
591 |
+
* @since 2.3.0
|
592 |
+
*/
|
593 |
+
$is_free_installed = $has_installed_version;
|
594 |
+
$is_premium_installed = false;
|
595 |
+
} else if ( ! $api->has_free_plan ) {
|
596 |
+
/**
|
597 |
+
* Premium-only add-on.
|
598 |
+
*
|
599 |
+
* @author Leo Fajardo (@leorw)
|
600 |
+
* @since 2.3.0
|
601 |
+
*/
|
602 |
+
$is_free_installed = false;
|
603 |
+
$is_premium_installed = $has_installed_version;
|
604 |
+
} else {
|
605 |
+
/**
|
606 |
+
* Freemium add-on.
|
607 |
+
*
|
608 |
+
* @author Leo Fajardo (@leorw)
|
609 |
+
* @since 2.3.0
|
610 |
+
*/
|
611 |
+
if ( ! $has_installed_version ) {
|
612 |
+
$is_free_installed = false;
|
613 |
+
$is_premium_installed = false;
|
614 |
+
} else {
|
615 |
+
$fs_addon = $is_addon_activated ?
|
616 |
+
$this->_fs->get_addon_instance( $api->slug ) :
|
617 |
+
null;
|
618 |
+
|
619 |
+
if ( is_object( $fs_addon ) ) {
|
620 |
+
if ( $fs_addon->is_premium() ) {
|
621 |
+
$is_premium_installed = true;
|
622 |
+
} else {
|
623 |
+
$is_free_installed = true;
|
624 |
+
}
|
625 |
+
}
|
626 |
+
|
627 |
+
if ( is_null( $is_free_installed ) ) {
|
628 |
+
$is_free_installed = file_exists( fs_normalize_path( WP_PLUGIN_DIR . "/{$api->slug}/{$api->slug}.php" ) );
|
629 |
+
if ( ! $is_free_installed ) {
|
630 |
+
/**
|
631 |
+
* Check if there's a plugin installed in a directory named `$api->slug`.
|
632 |
+
*
|
633 |
+
* @author Leo Fajardo (@leorw)
|
634 |
+
* @since 2.3.0
|
635 |
+
*/
|
636 |
+
$installed_plugins = get_plugins( '/' . $api->slug );
|
637 |
+
$is_free_installed = ( ! empty( $installed_plugins ) );
|
638 |
+
}
|
639 |
+
}
|
640 |
+
|
641 |
+
if ( is_null( $is_premium_installed ) ) {
|
642 |
+
$is_premium_installed = file_exists( fs_normalize_path( WP_PLUGIN_DIR . "/{$api->premium_slug}/{$api->slug}.php" ) );
|
643 |
+
if ( ! $is_premium_installed ) {
|
644 |
+
/**
|
645 |
+
* Check if there's a plugin installed in a directory named `$api->premium_slug`.
|
646 |
+
*
|
647 |
+
* @author Leo Fajardo (@leorw)
|
648 |
+
* @since 2.3.0
|
649 |
+
*/
|
650 |
+
$installed_plugins = get_plugins( '/' . $api->premium_slug );
|
651 |
+
$is_premium_installed = ( ! empty( $installed_plugins ) );
|
652 |
+
}
|
653 |
+
}
|
654 |
+
}
|
655 |
+
|
656 |
+
$has_installed_version = ( $is_free_installed || $is_premium_installed );
|
657 |
+
}
|
658 |
+
|
659 |
+
$this->status['is_free_installed'] = $is_free_installed;
|
660 |
+
$this->status['is_premium_installed'] = $is_premium_installed;
|
661 |
+
|
662 |
+
$can_install_free_version = false;
|
663 |
+
$can_install_free_version_update = false;
|
664 |
+
$can_download_free_version = false;
|
665 |
+
$can_activate_free_version = false;
|
666 |
+
$can_install_premium_version = false;
|
667 |
+
$can_install_premium_version_update = false;
|
668 |
+
$can_download_premium_version = false;
|
669 |
+
$can_activate_premium_version = false;
|
670 |
+
|
671 |
+
if ( ! $api->has_purchased_license ) {
|
672 |
+
if ( $api->has_free_plan ) {
|
673 |
+
if ( $has_installed_version ) {
|
674 |
+
if ( $is_update_available ) {
|
675 |
+
$can_install_free_version_update = true;
|
676 |
+
} else if ( ! $is_premium_installed && ! isset( $active_plugins_directories_map[ dirname( $this->status['file'] ) ] ) ) {
|
677 |
+
$can_activate_free_version = true;
|
678 |
+
}
|
679 |
+
} else {
|
680 |
+
if (
|
681 |
+
$this->_fs->is_premium() ||
|
682 |
+
! $this->_fs->is_org_repo_compliant() ||
|
683 |
+
$api->is_wp_org_compliant
|
684 |
+
) {
|
685 |
+
$can_install_free_version = true;
|
686 |
+
} else {
|
687 |
+
$can_download_free_version = true;
|
688 |
+
}
|
689 |
+
}
|
690 |
+
}
|
691 |
+
} else {
|
692 |
+
if ( ! is_object( $fs_addon ) && $is_addon_activated ) {
|
693 |
+
$fs_addon = $this->_fs->get_addon_instance( $api->slug );
|
694 |
+
}
|
695 |
+
|
696 |
+
$can_download_premium_version = true;
|
697 |
+
|
698 |
+
if ( ! isset( $active_plugins_directories_map[ dirname( $this->status['file'] ) ] ) ) {
|
699 |
+
if ( $is_premium_installed ) {
|
700 |
+
$can_activate_premium_version = ( ! $is_addon_activated || ! $fs_addon->is_premium() );
|
701 |
+
} else if ( $is_free_installed ) {
|
702 |
+
$can_activate_free_version = ( ! $is_addon_activated );
|
703 |
+
}
|
704 |
+
}
|
705 |
+
|
706 |
+
if ( $this->_fs->is_premium() || ! $this->_fs->is_org_repo_compliant() ) {
|
707 |
+
if ( $is_update_available ) {
|
708 |
+
$can_install_premium_version_update = true;
|
709 |
+
} else if ( ! $is_premium_installed ) {
|
710 |
+
$can_install_premium_version = true;
|
711 |
+
}
|
712 |
+
}
|
713 |
+
}
|
714 |
+
|
715 |
+
if (
|
716 |
+
$can_install_premium_version ||
|
717 |
+
$can_install_premium_version_update
|
718 |
+
) {
|
719 |
+
if ( is_numeric( $blog_id ) ) {
|
720 |
+
/**
|
721 |
+
* Replace the network status URL with a blog admin–based status URL if the `Add-Ons` page is loaded
|
722 |
+
* from a specific blog admin page (when `fs_blog_id` is valid) in order for plugin installation/update
|
723 |
+
* to work.
|
724 |
+
*
|
725 |
+
* @author Leo Fajardo (@leorw)
|
726 |
+
* @since 2.3.0
|
727 |
+
*/
|
728 |
+
$this->status['url'] = self::get_blog_status_url( $blog_id, $this->status['url'], $this->status['status'] );
|
729 |
+
}
|
730 |
+
|
731 |
+
/**
|
732 |
+
* Add the `fs_allow_updater_and_dialog` param to the install/update URL so that the add-on can be
|
733 |
+
* installed/updated.
|
734 |
+
*
|
735 |
+
* @author Leo Fajardo (@leorw)
|
736 |
+
* @since 2.3.0
|
737 |
+
*/
|
738 |
+
$this->status['url'] = str_replace( '?', '?fs_allow_updater_and_dialog=true&', $this->status['url'] );
|
739 |
+
}
|
740 |
+
|
741 |
+
if ( $can_install_free_version_update || $can_install_premium_version_update ) {
|
742 |
+
$actions[] = $this->get_cta(
|
743 |
+
( $can_install_free_version_update ?
|
744 |
+
fs_esc_html_inline( 'Install Free Version Update Now', 'install-free-version-update-now', $api->slug ) :
|
745 |
+
fs_esc_html_inline( 'Install Update Now', 'install-update-now', $api->slug ) ),
|
746 |
+
true,
|
747 |
+
false,
|
748 |
+
$this->status['url'],
|
749 |
+
'_parent'
|
750 |
+
);
|
751 |
+
} else if ( $can_install_free_version || $can_install_premium_version ) {
|
752 |
+
$actions[] = $this->get_cta(
|
753 |
+
( $can_install_free_version ?
|
754 |
+
fs_esc_html_inline( 'Install Free Version Now', 'install-free-version-now', $api->slug ) :
|
755 |
+
fs_esc_html_inline( 'Install Now', 'install-now', $api->slug ) ),
|
756 |
+
true,
|
757 |
+
false,
|
758 |
+
$this->status['url'],
|
759 |
+
'_parent'
|
760 |
+
);
|
761 |
+
}
|
762 |
+
|
763 |
+
$download_latest_action = '';
|
764 |
+
|
765 |
+
if (
|
766 |
+
! empty( $api->download_link ) &&
|
767 |
+
( $can_download_free_version || $can_download_premium_version )
|
768 |
+
) {
|
769 |
+
$download_latest_action = $this->get_cta(
|
770 |
+
( $can_download_free_version ?
|
771 |
+
fs_esc_html_x_inline( 'Download Latest Free Version', 'as download latest version', 'download-latest-free-version', $api->slug ) :
|
772 |
+
fs_esc_html_x_inline( 'Download Latest', 'as download latest version', 'download-latest', $api->slug ) ),
|
773 |
+
true,
|
774 |
+
false,
|
775 |
+
esc_url( $api->download_link )
|
776 |
+
);
|
777 |
+
}
|
778 |
+
|
779 |
+
if ( ! $can_activate_free_version && ! $can_activate_premium_version ) {
|
780 |
+
if ( ! empty( $download_latest_action ) ) {
|
781 |
+
$actions[] = $download_latest_action;
|
782 |
+
}
|
783 |
+
} else {
|
784 |
+
$activate_action = sprintf(
|
785 |
+
'<a class="button button-primary edit" href="%s" title="%s" target="_parent">%s</a>',
|
786 |
+
wp_nonce_url( ( is_numeric( $blog_id ) ? trailingslashit( get_admin_url( $blog_id ) ) : '' ) . 'plugins.php?action=activate&plugin=' . $this->status['file'], 'activate-plugin_' . $this->status['file'] ),
|
787 |
+
fs_esc_attr_inline( 'Activate this add-on', 'activate-this-addon', $api->slug ),
|
788 |
+
$can_activate_free_version ?
|
789 |
+
fs_text_inline( 'Activate Free Version', 'activate-free', $api->slug ) :
|
790 |
+
fs_text_inline( 'Activate', 'activate', $api->slug )
|
791 |
+
);
|
792 |
+
|
793 |
+
if ( ! $can_download_premium_version && ! empty( $download_latest_action ) ) {
|
794 |
+
$actions[] = $download_latest_action;
|
795 |
+
|
796 |
+
$download_latest_action = '';
|
797 |
+
}
|
798 |
+
|
799 |
+
if ( $can_install_premium_version || $can_install_premium_version_update ) {
|
800 |
+
if ( $can_download_premium_version && ! empty( $download_latest_action ) ) {
|
801 |
+
$actions[] = $download_latest_action;
|
802 |
+
|
803 |
+
$download_latest_action = '';
|
804 |
+
}
|
805 |
+
|
806 |
+
$actions[] = $activate_action;
|
807 |
+
} else {
|
808 |
+
array_unshift( $actions, $activate_action );
|
809 |
+
}
|
810 |
+
|
811 |
+
if ( ! empty ($download_latest_action ) ) {
|
812 |
+
$actions[] = $download_latest_action;
|
813 |
+
}
|
814 |
+
}
|
815 |
+
|
816 |
+
return $actions;
|
817 |
+
}
|
818 |
+
|
819 |
+
/**
|
820 |
+
* Rebuilds the status URL based on the admin URL.
|
821 |
+
*
|
822 |
+
* @author Leo Fajardo (@leorw)
|
823 |
+
* @since 2.3.0
|
824 |
+
*
|
825 |
+
* @param int $blog_id
|
826 |
+
* @param string $network_status_url
|
827 |
+
* @param string $status
|
828 |
+
*
|
829 |
+
* @return string
|
830 |
+
*/
|
831 |
+
private static function get_blog_status_url( $blog_id, $network_status_url, $status ) {
|
832 |
+
if ( ! in_array( $status, array( 'install', 'update_available' ) ) ) {
|
833 |
+
return $network_status_url;
|
834 |
+
}
|
835 |
+
|
836 |
+
$action = ( 'install' === $status ) ?
|
837 |
+
'install-plugin' :
|
838 |
+
'upgrade-plugin';
|
839 |
+
|
840 |
+
$query = parse_url( $network_status_url, PHP_URL_QUERY );
|
841 |
+
if ( empty( $query ) ) {
|
842 |
+
return $network_status_url;
|
843 |
+
}
|
844 |
+
|
845 |
+
parse_str( html_entity_decode( $query ), $url_params );
|
846 |
+
if ( empty( $url_params ) || ! isset( $url_params['plugin'] ) ) {
|
847 |
+
return $network_status_url;
|
848 |
+
}
|
849 |
+
|
850 |
+
$plugin = $url_params['plugin'];
|
851 |
+
|
852 |
+
return wp_nonce_url( get_admin_url( $blog_id,"update.php?action={$action}&plugin={$plugin}"), "{$action}_{$plugin}");
|
853 |
+
}
|
854 |
+
|
855 |
+
/**
|
856 |
+
* Helper method to get a CTA button HTML.
|
857 |
+
*
|
858 |
+
* @author Vova Feldman (@svovaf)
|
859 |
+
* @since 2.0.0
|
860 |
+
*
|
861 |
+
* @param string $label
|
862 |
+
* @param bool $is_primary
|
863 |
+
* @param bool $is_disabled
|
864 |
+
* @param string $href
|
865 |
+
* @param string $target
|
866 |
+
*
|
867 |
+
* @return string
|
868 |
+
*/
|
869 |
+
private function get_cta(
|
870 |
+
$label,
|
871 |
+
$is_primary = true,
|
872 |
+
$is_disabled = false,
|
873 |
+
$href = '',
|
874 |
+
$target = '_blank'
|
875 |
+
) {
|
876 |
+
$classes = array();
|
877 |
+
|
878 |
+
if ( ! $is_primary ) {
|
879 |
+
$classes[] = 'left';
|
880 |
+
} else {
|
881 |
+
$classes[] = 'button-primary';
|
882 |
+
$classes[] = 'right';
|
883 |
+
}
|
884 |
+
|
885 |
+
if ( $is_disabled ) {
|
886 |
+
$classes[] = 'disabled';
|
887 |
+
}
|
888 |
+
|
889 |
+
$rel = ( '_blank' === $target ) ? ' rel="noopener noreferrer"' : '';
|
890 |
+
|
891 |
+
return sprintf(
|
892 |
+
'<a %s class="button %s">%s</a>',
|
893 |
+
empty( $href ) ? '' : 'href="' . $href . '" target="' . $target . '"' . $rel,
|
894 |
+
implode( ' ', $classes ),
|
895 |
+
$label
|
896 |
+
);
|
897 |
+
}
|
898 |
+
|
899 |
+
/**
|
900 |
+
* @author Vova Feldman (@svovaf)
|
901 |
+
* @since 1.1.7
|
902 |
+
*
|
903 |
+
* @param FS_Plugin_Plan $plan
|
904 |
+
*
|
905 |
+
* @return string
|
906 |
+
*/
|
907 |
+
private function get_trial_period( $plan ) {
|
908 |
+
$trial_period = (int) $plan->trial_period;
|
909 |
+
|
910 |
+
switch ( $trial_period ) {
|
911 |
+
case 30:
|
912 |
+
return 'month';
|
913 |
+
case 60:
|
914 |
+
return '2 months';
|
915 |
+
default:
|
916 |
+
return "{$plan->trial_period} days";
|
917 |
+
}
|
918 |
+
}
|
919 |
+
|
920 |
+
/**
|
921 |
+
* Display plugin information in dialog box form.
|
922 |
+
*
|
923 |
+
* Based on core install_plugin_information() function.
|
924 |
+
*
|
925 |
+
* @author Vova Feldman (@svovaf)
|
926 |
+
* @since 1.0.6
|
927 |
+
*/
|
928 |
+
function install_plugin_information() {
|
929 |
+
global $tab;
|
930 |
+
|
931 |
+
if ( empty( $_REQUEST['plugin'] ) ) {
|
932 |
+
return;
|
933 |
+
}
|
934 |
+
|
935 |
+
$args = array(
|
936 |
+
'slug' => wp_unslash( $_REQUEST['plugin'] ),
|
937 |
+
'is_ssl' => is_ssl(),
|
938 |
+
'fields' => array(
|
939 |
+
'banners' => true,
|
940 |
+
'reviews' => true,
|
941 |
+
'downloaded' => false,
|
942 |
+
'active_installs' => true
|
943 |
+
)
|
944 |
+
);
|
945 |
+
|
946 |
+
if ( is_array( $args ) ) {
|
947 |
+
$args = (object) $args;
|
948 |
+
}
|
949 |
+
|
950 |
+
if ( ! isset( $args->per_page ) ) {
|
951 |
+
$args->per_page = 24;
|
952 |
+
}
|
953 |
+
|
954 |
+
if ( ! isset( $args->locale ) ) {
|
955 |
+
$args->locale = get_locale();
|
956 |
+
}
|
957 |
+
|
958 |
+
$api = apply_filters( 'fs_plugins_api', false, 'plugin_information', $args );
|
959 |
+
|
960 |
+
if ( is_wp_error( $api ) ) {
|
961 |
+
wp_die( $api );
|
962 |
+
}
|
963 |
+
|
964 |
+
$plugins_allowedtags = array(
|
965 |
+
'a' => array(
|
966 |
+
'href' => array(),
|
967 |
+
'title' => array(),
|
968 |
+
'target' => array(),
|
969 |
+
// Add image style for screenshots.
|
970 |
+
'class' => array()
|
971 |
+
),
|
972 |
+
'style' => array(),
|
973 |
+
'abbr' => array( 'title' => array() ),
|
974 |
+
'acronym' => array( 'title' => array() ),
|
975 |
+
'code' => array(),
|
976 |
+
'pre' => array(),
|
977 |
+
'em' => array(),
|
978 |
+
'strong' => array(),
|
979 |
+
'div' => array( 'class' => array() ),
|
980 |
+
'span' => array( 'class' => array() ),
|
981 |
+
'p' => array(),
|
982 |
+
'ul' => array(),
|
983 |
+
'ol' => array(),
|
984 |
+
'li' => array( 'class' => array() ),
|
985 |
+
'i' => array( 'class' => array() ),
|
986 |
+
'h1' => array(),
|
987 |
+
'h2' => array(),
|
988 |
+
'h3' => array(),
|
989 |
+
'h4' => array(),
|
990 |
+
'h5' => array(),
|
991 |
+
'h6' => array(),
|
992 |
+
'img' => array( 'src' => array(), 'class' => array(), 'alt' => array() ),
|
993 |
+
// 'table' => array(),
|
994 |
+
// 'td' => array(),
|
995 |
+
// 'tr' => array(),
|
996 |
+
// 'th' => array(),
|
997 |
+
// 'thead' => array(),
|
998 |
+
// 'tbody' => array(),
|
999 |
+
);
|
1000 |
+
|
1001 |
+
$plugins_section_titles = array(
|
1002 |
+
'description' => fs_text_x_inline( 'Description', 'Plugin installer section title', 'description', $api->slug ),
|
1003 |
+
'installation' => fs_text_x_inline( 'Installation', 'Plugin installer section title', 'installation', $api->slug ),
|
1004 |
+
'faq' => fs_text_x_inline( 'FAQ', 'Plugin installer section title', 'faq', $api->slug ),
|
1005 |
+
'screenshots' => fs_text_inline( 'Screenshots', 'screenshots', $api->slug ),
|
1006 |
+
'changelog' => fs_text_x_inline( 'Changelog', 'Plugin installer section title', 'changelog', $api->slug ),
|
1007 |
+
'reviews' => fs_text_x_inline( 'Reviews', 'Plugin installer section title', 'reviews', $api->slug ),
|
1008 |
+
'other_notes' => fs_text_x_inline( 'Other Notes', 'Plugin installer section title', 'other-notes', $api->slug ),
|
1009 |
+
);
|
1010 |
+
|
1011 |
+
// Sanitize HTML
|
1012 |
+
// foreach ( (array) $api->sections as $section_name => $content ) {
|
1013 |
+
// $api->sections[$section_name] = wp_kses( $content, $plugins_allowedtags );
|
1014 |
+
// }
|
1015 |
+
|
1016 |
+
foreach ( array( 'version', 'author', 'requires', 'tested', 'homepage', 'downloaded', 'slug' ) as $key ) {
|
1017 |
+
if ( isset( $api->$key ) ) {
|
1018 |
+
$api->$key = wp_kses( $api->$key, $plugins_allowedtags );
|
1019 |
+
}
|
1020 |
+
}
|
1021 |
+
|
1022 |
+
// Add after $api->slug is ready.
|
1023 |
+
$plugins_section_titles['features'] = fs_text_x_inline( 'Features & Pricing', 'Plugin installer section title', 'features-and-pricing', $api->slug );
|
1024 |
+
|
1025 |
+
$_tab = esc_attr( $tab );
|
1026 |
+
|
1027 |
+
$section = isset( $_REQUEST['section'] ) ? wp_unslash( $_REQUEST['section'] ) : 'description'; // Default to the Description tab, Do not translate, API returns English.
|
1028 |
+
if ( empty( $section ) || ! isset( $api->sections[ $section ] ) ) {
|
1029 |
+
$section_titles = array_keys( (array) $api->sections );
|
1030 |
+
$section = array_shift( $section_titles );
|
1031 |
+
}
|
1032 |
+
|
1033 |
+
iframe_header( fs_text_inline( 'Plugin Install', 'plugin-install', $api->slug ) );
|
1034 |
+
|
1035 |
+
$_with_banner = '';
|
1036 |
+
|
1037 |
+
// var_dump($api->banners);
|
1038 |
+
if ( ! empty( $api->banners ) && ( ! empty( $api->banners['low'] ) || ! empty( $api->banners['high'] ) ) ) {
|
1039 |
+
$_with_banner = 'with-banner';
|
1040 |
+
$low = empty( $api->banners['low'] ) ? $api->banners['high'] : $api->banners['low'];
|
1041 |
+
$high = empty( $api->banners['high'] ) ? $api->banners['low'] : $api->banners['high'];
|
1042 |
+
?>
|
1043 |
+
<style type="text/css">
|
1044 |
+
#plugin-information-title.with-banner
|
1045 |
+
{
|
1046 |
+
background-image: url( <?php echo esc_url( $low ); ?> );
|
1047 |
+
}
|
1048 |
+
|
1049 |
+
@media only screen and ( -webkit-min-device-pixel-ratio: 1.5 )
|
1050 |
+
{
|
1051 |
+
#plugin-information-title.with-banner
|
1052 |
+
{
|
1053 |
+
background-image: url( <?php echo esc_url( $high ); ?> );
|
1054 |
+
}
|
1055 |
+
}
|
1056 |
+
</style>
|
1057 |
+
<?php
|
1058 |
+
}
|
1059 |
+
|
1060 |
+
echo '<div id="plugin-information-scrollable">';
|
1061 |
+
echo "<div id='{$_tab}-title' class='{$_with_banner}'><div class='vignette'></div><h2>{$api->name}</h2></div>";
|
1062 |
+
echo "<div id='{$_tab}-tabs' class='{$_with_banner}'>\n";
|
1063 |
+
|
1064 |
+
foreach ( (array) $api->sections as $section_name => $content ) {
|
1065 |
+
if ( 'reviews' === $section_name && ( empty( $api->ratings ) || 0 === array_sum( (array) $api->ratings ) ) ) {
|
1066 |
+
continue;
|
1067 |
+
}
|
1068 |
+
|
1069 |
+
if ( isset( $plugins_section_titles[ $section_name ] ) ) {
|
1070 |
+
$title = $plugins_section_titles[ $section_name ];
|
1071 |
+
} else {
|
1072 |
+
$title = ucwords( str_replace( '_', ' ', $section_name ) );
|
1073 |
+
}
|
1074 |
+
|
1075 |
+
$class = ( $section_name === $section ) ? ' class="current"' : '';
|
1076 |
+
$href = add_query_arg( array( 'tab' => $tab, 'section' => $section_name ) );
|
1077 |
+
$href = esc_url( $href );
|
1078 |
+
$san_section = esc_attr( $section_name );
|
1079 |
+
echo "\t<a name='$san_section' href='$href' $class>" . esc_html( $title ) . "</a>\n";
|
1080 |
+
}
|
1081 |
+
|
1082 |
+
echo "</div>\n";
|
1083 |
+
|
1084 |
+
?>
|
1085 |
+
<div id="<?php echo $_tab; ?>-content" class='<?php echo $_with_banner; ?>'>
|
1086 |
+
<div class="fyi">
|
1087 |
+
<?php if ( $api->is_paid ) : ?>
|
1088 |
+
<?php if ( isset( $api->plans ) ) : ?>
|
1089 |
+
<div class="plugin-information-pricing">
|
1090 |
+
<?php foreach ( $api->plans as $plan ) : ?>
|
1091 |
+
<?php
|
1092 |
+
if ( empty( $plan->pricing ) ) {
|
1093 |
+
continue;
|
1094 |
+
}
|
1095 |
+
|
1096 |
+
/**
|
1097 |
+
* @var FS_Plugin_Plan $plan
|
1098 |
+
*/
|
1099 |
+
?>
|
1100 |
+
<?php $first_pricing = $plan->pricing[0] ?>
|
1101 |
+
<?php $is_multi_cycle = $first_pricing->is_multi_cycle() ?>
|
1102 |
+
<div class="fs-plan<?php if ( ! $is_multi_cycle ) {
|
1103 |
+
echo ' fs-single-cycle';
|
1104 |
+
} ?>" data-plan-id="<?php echo $plan->id ?>">
|
1105 |
+
<h3 data-plan="<?php echo $plan->id ?>"><?php echo esc_html( sprintf( fs_text_x_inline( '%s Plan', 'e.g. Professional Plan', 'x-plan', $api->slug ), $plan->title ) ) ?></h3>
|
1106 |
+
<?php $has_annual = $first_pricing->has_annual() ?>
|
1107 |
+
<?php $has_monthly = $first_pricing->has_monthly() ?>
|
1108 |
+
<div class="nav-tab-wrapper">
|
1109 |
+
<?php $billing_cycles = array( 'monthly', 'annual', 'lifetime' ) ?>
|
1110 |
+
<?php $i = 0;
|
1111 |
+
foreach ( $billing_cycles as $cycle ) : ?>
|
1112 |
+
<?php $prop = "{$cycle}_price";
|
1113 |
+
if ( isset( $first_pricing->{$prop} ) ) : ?>
|
1114 |
+
<?php $is_featured = ( 'annual' === $cycle && $is_multi_cycle ) ?>
|
1115 |
+
<?php
|
1116 |
+
$prices = array();
|
1117 |
+
foreach ( $plan->pricing as $pricing ) {
|
1118 |
+
if ( isset( $pricing->{$prop} ) ) {
|
1119 |
+
$prices[] = array(
|
1120 |
+
'id' => $pricing->id,
|
1121 |
+
'licenses' => $pricing->licenses,
|
1122 |
+
'price' => $pricing->{$prop}
|
1123 |
+
);
|
1124 |
+
}
|
1125 |
+
}
|
1126 |
+
?>
|
1127 |
+
<a class="nav-tab" data-billing-cycle="<?php echo $cycle ?>"
|
1128 |
+
data-pricing="<?php echo esc_attr( json_encode( $prices ) ) ?>">
|
1129 |
+
<?php if ( $is_featured ) : ?>
|
1130 |
+
<label>
|
1131 |
+
★ <?php fs_esc_html_echo_x_inline( 'Best', 'e.g. the best product', 'best', $api->slug ) ?>
|
1132 |
+
★</label>
|
1133 |
+
<?php endif ?>
|
1134 |
+
<?php
|
1135 |
+
switch ( $cycle ) {
|
1136 |
+
case 'monthly':
|
1137 |
+
fs_esc_html_echo_x_inline( 'Monthly', 'as every month', 'monthly', $api->slug );
|
1138 |
+
break;
|
1139 |
+
case 'annual':
|
1140 |
+
fs_esc_html_echo_x_inline( 'Annual', 'as once a year', 'annual', $api->slug );
|
1141 |
+
break;
|
1142 |
+
case 'lifetime':
|
1143 |
+
fs_esc_html_echo_inline( 'Lifetime', 'lifetime', $api->slug );
|
1144 |
+
break;
|
1145 |
+
}
|
1146 |
+
?>
|
1147 |
+
</a>
|
1148 |
+
<?php endif ?>
|
1149 |
+
<?php $i ++; endforeach ?>
|
1150 |
+
<?php wp_enqueue_script( 'jquery' ) ?>
|
1151 |
+
<script type="text/javascript">
|
1152 |
+
(function ($, undef) {
|
1153 |
+
var
|
1154 |
+
_formatBillingFrequency = function (cycle) {
|
1155 |
+
switch (cycle) {
|
1156 |
+
case 'monthly':
|
1157 |
+
return '<?php printf( fs_text_x_inline( 'Billed %s', 'e.g. billed monthly', 'billed-x', $api->slug ), fs_text_x_inline( 'Monthly', 'as every month', 'monthly', $api->slug ) ) ?>';
|
1158 |
+
case 'annual':
|
1159 |
+
return '<?php printf( fs_text_x_inline( 'Billed %s', 'e.g. billed monthly', 'billed-x', $api->slug ), fs_text_x_inline( 'Annually', 'as once a year', 'annually', $api->slug ) ) ?>';
|
1160 |
+
case 'lifetime':
|
1161 |
+
return '<?php printf( fs_text_x_inline( 'Billed %s', 'e.g. billed monthly', 'billed-x', $api->slug ), fs_text_x_inline( 'Once', 'as once a year', 'once', $api->slug ) ) ?>';
|
1162 |
+
}
|
1163 |
+
},
|
1164 |
+
_formatLicensesTitle = function (pricing) {
|
1165 |
+
switch (pricing.licenses) {
|
1166 |
+
case 1:
|
1167 |
+
return '<?php fs_esc_attr_echo_inline( 'Single Site License', 'license-single-site', $api->slug ) ?>';
|
1168 |
+
case null:
|
1169 |
+
return '<?php fs_esc_attr_echo_inline( 'Unlimited Licenses', 'license-unlimited', $api->slug ) ?>';
|
1170 |
+
default:
|
1171 |
+
return '<?php fs_esc_attr_echo_inline( 'Up to %s Sites', 'license-x-sites', $api->slug ) ?>'.replace('%s', pricing.licenses);
|
1172 |
+
}
|
1173 |
+
},
|
1174 |
+
_formatPrice = function (pricing, cycle, multipleLicenses) {
|
1175 |
+
if (undef === multipleLicenses)
|
1176 |
+
multipleLicenses = true;
|
1177 |
+
|
1178 |
+
var priceCycle;
|
1179 |
+
switch (cycle) {
|
1180 |
+
case 'monthly':
|
1181 |
+
priceCycle = ' / <?php fs_echo_x_inline( 'mo', 'as monthly period', 'mo', $api->slug ) ?>';
|
1182 |
+
break;
|
1183 |
+
case 'lifetime':
|
1184 |
+
priceCycle = '';
|
1185 |
+
break;
|
1186 |
+
case 'annual':
|
1187 |
+
default:
|
1188 |
+
priceCycle = ' / <?php fs_echo_x_inline( 'year', 'as annual period', 'year', $api->slug ) ?>';
|
1189 |
+
break;
|
1190 |
+
}
|
1191 |
+
|
1192 |
+
if (!multipleLicenses && 1 == pricing.licenses) {
|
1193 |
+
return '$' + pricing.price + priceCycle;
|
1194 |
+
}
|
1195 |
+
|
1196 |
+
return _formatLicensesTitle(pricing) + ' - <var class="fs-price">$' + pricing.price + priceCycle + '</var>';
|
1197 |
+
},
|
1198 |
+
_checkoutUrl = function (plan, pricing, cycle) {
|
1199 |
+
return '<?php echo esc_url_raw( remove_query_arg( 'billing_cycle', add_query_arg( array( 'plugin_id' => $plan->plugin_id ), $api->checkout_link ) ) ) ?>' +
|
1200 |
+
'&plan_id=' + plan +
|
1201 |
+
'&pricing_id=' + pricing +
|
1202 |
+
'&billing_cycle=' + cycle<?php if ( $plan->has_trial() ) {
|
1203 |
+
echo " + '&trial=true'";
|
1204 |
+
}?>;
|
1205 |
+
},
|
1206 |
+
_updateCtaUrl = function (plan, pricing, cycle) {
|
1207 |
+
$('.plugin-information-pricing .fs-checkout-button, #plugin-information-footer .fs-checkout-button').attr('href', _checkoutUrl(plan, pricing, cycle));
|
1208 |
+
};
|
1209 |
+
|
1210 |
+
$(document).ready(function () {
|
1211 |
+
var $plan = $('.plugin-information-pricing .fs-plan[data-plan-id=<?php echo $plan->id ?>]');
|
1212 |
+
$plan.find('input[type=radio]').on('click', function () {
|
1213 |
+
_updateCtaUrl(
|
1214 |
+
$plan.attr('data-plan-id'),
|
1215 |
+
$(this).val(),
|
1216 |
+
$plan.find('.nav-tab-active').attr('data-billing-cycle')
|
1217 |
+
);
|
1218 |
+
|
1219 |
+
$plan.find('.fs-trial-terms .fs-price').html(
|
1220 |
+
$(this).parents('label').find('.fs-price').html()
|
1221 |
+
);
|
1222 |
+
});
|
1223 |
+
|
1224 |
+
$plan.find('.nav-tab').click(function () {
|
1225 |
+
if ($(this).hasClass('nav-tab-active'))
|
1226 |
+
return;
|
1227 |
+
|
1228 |
+
var $this = $(this),
|
1229 |
+
billingCycle = $this.attr('data-billing-cycle'),
|
1230 |
+
pricing = JSON.parse($this.attr('data-pricing')),
|
1231 |
+
$pricesList = $this.parents('.fs-plan').find('.fs-pricing-body .fs-licenses'),
|
1232 |
+
html = '';
|
1233 |
+
|
1234 |
+
// Un-select previously selected tab.
|
1235 |
+
$plan.find('.nav-tab').removeClass('nav-tab-active');
|
1236 |
+
|
1237 |
+
// Select current tab.
|
1238 |
+
$this.addClass('nav-tab-active');
|
1239 |
+
|
1240 |
+
// Render licenses prices.
|
1241 |
+
if (1 == pricing.length) {
|
1242 |
+
html = '<li><label><?php echo fs_esc_attr_x_inline( 'Price', 'noun', 'price', $api->slug ) ?>: ' + _formatPrice(pricing[0], billingCycle, false) + '</label></li>';
|
1243 |
+
} else {
|
1244 |
+
for (var i = 0; i < pricing.length; i++) {
|
1245 |
+
html += '<li><label><input name="pricing-<?php echo $plan->id ?>" type="radio" value="' + pricing[i].id + '">' + _formatPrice(pricing[i], billingCycle) + '</label></li>';
|
1246 |
+
}
|
1247 |
+
}
|
1248 |
+
$pricesList.html(html);
|
1249 |
+
|
1250 |
+
if (1 < pricing.length) {
|
1251 |
+
// Select first license option.
|
1252 |
+
$pricesList.find('li:first input').click();
|
1253 |
+
}
|
1254 |
+
else {
|
1255 |
+
_updateCtaUrl(
|
1256 |
+
$plan.attr('data-plan-id'),
|
1257 |
+
pricing[0].id,
|
1258 |
+
billingCycle
|
1259 |
+
);
|
1260 |
+
}
|
1261 |
+
|
1262 |
+
// Update billing frequency.
|
1263 |
+
$plan.find('.fs-billing-frequency').html(_formatBillingFrequency(billingCycle));
|
1264 |
+
|
1265 |
+
if ('annual' === billingCycle) {
|
1266 |
+
$plan.find('.fs-annual-discount').show();
|
1267 |
+
} else {
|
1268 |
+
$plan.find('.fs-annual-discount').hide();
|
1269 |
+
}
|
1270 |
+
});
|
1271 |
+
|
1272 |
+
<?php if ( $has_annual ) : ?>
|
1273 |
+
// Select annual by default.
|
1274 |
+
$plan.find('.nav-tab[data-billing-cycle=annual]').click();
|
1275 |
+
<?php else : ?>
|
1276 |
+
// Select first tab.
|
1277 |
+
$plan.find('.nav-tab:first').click();
|
1278 |
+
<?php endif ?>
|
1279 |
+
});
|
1280 |
+
}(jQuery));
|
1281 |
+
</script>
|
1282 |
+
</div>
|
1283 |
+
<div class="fs-pricing-body">
|
1284 |
+
<span class="fs-billing-frequency"></span>
|
1285 |
+
<?php $annual_discount = ( $has_annual && $has_monthly ) ? $plan->pricing[0]->annual_discount_percentage() : 0 ?>
|
1286 |
+
<?php if ( $annual_discount > 0 ) : ?>
|
1287 |
+
<span
|
1288 |
+
class="fs-annual-discount"><?php printf(
|
1289 |
+
/* translators: %s: Discount (e.g. discount of $5 or 10%) */
|
1290 |
+
fs_esc_html_inline( 'Save %s', 'save-x', $api->slug ), $annual_discount . '%' ) ?></span>
|
1291 |
+
<?php endif ?>
|
1292 |
+
<ul class="fs-licenses">
|
1293 |
+
</ul>
|
1294 |
+
<?php echo $this->get_actions_dropdown( $api, $plan ) ?>
|
1295 |
+
<div style="clear:both"></div>
|
1296 |
+
<?php if ( $plan->has_trial() ) : ?>
|
1297 |
+
<?php $trial_period = $this->get_trial_period( $plan ) ?>
|
1298 |
+
<ul class="fs-trial-terms">
|
1299 |
+
<li>
|
1300 |
+
<i class="dashicons dashicons-yes"></i><?php echo esc_html( sprintf( fs_text_inline( 'No commitment for %s - cancel anytime', 'no-commitment-x', $api->slug ), $trial_period ) ) ?>
|
1301 |
+
</li>
|
1302 |
+
<li>
|
1303 |
+
<i class="dashicons dashicons-yes"></i><?php printf( esc_html( fs_text_inline( 'After your free %s, pay as little as %s', 'after-x-pay-as-little-y', $api->slug ) ), $trial_period, '<var class="fs-price">' . $this->get_price_tag( $plan, $plan->pricing[0] ) . '</var>' ) ?>
|
1304 |
+
</li>
|
1305 |
+
</ul>
|
1306 |
+
<?php endif ?>
|
1307 |
+
</div>
|
1308 |
+
</div>
|
1309 |
+
</div>
|
1310 |
+
<?php endforeach ?>
|
1311 |
+
<?php endif ?>
|
1312 |
+
<?php endif ?>
|
1313 |
+
<div>
|
1314 |
+
<h3><?php fs_echo_inline( 'Details', 'details', $api->slug ) ?></h3>
|
1315 |
+
<ul>
|
1316 |
+
<?php if ( ! empty( $api->version ) ) { ?>
|
1317 |
+
<li>
|
1318 |
+
<strong><?php fs_esc_html_echo_x_inline( 'Version', 'product version', 'version', $api->slug ); ?>
|
1319 |
+
:</strong> <?php echo $api->version; ?></li>
|
1320 |
+
<?php
|
1321 |
+
}
|
1322 |
+
if ( ! empty( $api->author ) ) {
|
1323 |
+
?>
|
1324 |
+
<li>
|
1325 |
+
<strong><?php fs_echo_x_inline( 'Author', 'as the plugin author', 'author', $api->slug ); ?>
|
1326 |
+
:</strong> <?php echo links_add_target( $api->author, '_blank' ); ?>
|
1327 |
+
</li>
|
1328 |
+
<?php
|
1329 |
+
}
|
1330 |
+
if ( ! empty( $api->last_updated ) ) {
|
1331 |
+
?>
|
1332 |
+
<li><strong><?php fs_echo_inline( 'Last Updated', 'last-updated', $api->slug ); ?>
|
1333 |
+
:</strong> <span
|
1334 |
+
title="<?php echo $api->last_updated; ?>">
|
1335 |
+
<?php echo esc_html( sprintf(
|
1336 |
+
/* translators: %s: time period (e.g. "2 hours" ago) */
|
1337 |
+
fs_text_x_inline( '%s ago', 'x-ago', $api->slug ),
|
1338 |
+
human_time_diff( strtotime( $api->last_updated ) )
|
1339 |
+
) ) ?>
|
1340 |
+
</span></li>
|
1341 |
+
<?php
|
1342 |
+
}
|
1343 |
+
if ( ! empty( $api->requires ) ) {
|
1344 |
+
?>
|
1345 |
+
<li>
|
1346 |
+
<strong><?php fs_esc_html_echo_inline( 'Requires WordPress Version', 'requires-wordpress-version', $api->slug ) ?>
|
1347 |
+
:</strong> <?php echo esc_html( sprintf( fs_text_inline( '%s or higher', 'x-or-higher', $api->slug ), $api->requires ) ) ?>
|
1348 |
+
</li>
|
1349 |
+
<?php
|
1350 |
+
}
|
1351 |
+
if ( ! empty( $api->tested ) ) {
|
1352 |
+
?>
|
1353 |
+
<li>
|
1354 |
+
<strong><?php fs_esc_html_echo_inline( 'Compatible up to', 'compatible-up-to', $api->slug ); ?>
|
1355 |
+
:</strong> <?php echo $api->tested; ?>
|
1356 |
+
</li>
|
1357 |
+
<?php
|
1358 |
+
}
|
1359 |
+
if ( ! empty( $api->downloaded ) ) {
|
1360 |
+
?>
|
1361 |
+
<li>
|
1362 |
+
<strong><?php fs_esc_html_echo_inline( 'Downloaded', 'downloaded', $api->slug ) ?>
|
1363 |
+
:</strong> <?php echo esc_html( sprintf(
|
1364 |
+
( ( 1 == $api->downloaded ) ?
|
1365 |
+
/* translators: %s: 1 or One (Number of times downloaded) */
|
1366 |
+
fs_text_inline( '%s time', 'x-time', $api->slug ) :
|
1367 |
+
/* translators: %s: Number of times downloaded */
|
1368 |
+
fs_text_inline( '%s times', 'x-times', $api->slug )
|
1369 |
+
),
|
1370 |
+
number_format_i18n( $api->downloaded )
|
1371 |
+
) ); ?>
|
1372 |
+
</li>
|
1373 |
+
<?php
|
1374 |
+
}
|
1375 |
+
if ( ! empty( $api->slug ) && true == $api->is_wp_org_compliant ) {
|
1376 |
+
?>
|
1377 |
+
<li><a target="_blank"
|
1378 |
+
rel="noopener noreferrer"
|
1379 |
+
href="https://wordpress.org/plugins/<?php echo $api->slug; ?>/"><?php fs_esc_html_echo_inline( 'WordPress.org Plugin Page', 'wp-org-plugin-page', $api->slug ) ?>
|
1380 |
+
»</a>
|
1381 |
+
</li>
|
1382 |
+
<?php
|
1383 |
+
}
|
1384 |
+
if ( ! empty( $api->homepage ) ) {
|
1385 |
+
?>
|
1386 |
+
<li><a target="_blank"
|
1387 |
+
rel="noopener noreferrer"
|
1388 |
+
href="<?php echo esc_url( $api->homepage ); ?>"><?php fs_esc_html_echo_inline( 'Plugin Homepage', 'plugin-homepage', $api->slug ) ?>
|
1389 |
+
»</a>
|
1390 |
+
</li>
|
1391 |
+
<?php
|
1392 |
+
}
|
1393 |
+
if ( ! empty( $api->donate_link ) && empty( $api->contributors ) ) {
|
1394 |
+
?>
|
1395 |
+
<li><a target="_blank"
|
1396 |
+
rel="noopener noreferrer"
|
1397 |
+
href="<?php echo esc_url( $api->donate_link ); ?>"><?php fs_esc_html_echo_inline( 'Donate to this plugin', 'donate-to-plugin', $api->slug ) ?>
|
1398 |
+
»</a>
|
1399 |
+
</li>
|
1400 |
+
<?php } ?>
|
1401 |
+
</ul>
|
1402 |
+
</div>
|
1403 |
+
<?php if ( ! empty( $api->rating ) ) { ?>
|
1404 |
+
<h3><?php fs_echo_inline( 'Average Rating', 'average-rating', $api->slug ); ?></h3>
|
1405 |
+
<?php wp_star_rating( array(
|
1406 |
+
'rating' => $api->rating,
|
1407 |
+
'type' => 'percent',
|
1408 |
+
'number' => $api->num_ratings
|
1409 |
+
) ); ?>
|
1410 |
+
<small>(<?php echo esc_html( sprintf(
|
1411 |
+
fs_text_inline( 'based on %s', 'based-on-x', $api->slug ),
|
1412 |
+
sprintf(
|
1413 |
+
( ( 1 == $api->num_ratings ) ?
|
1414 |
+
/* translators: %s: 1 or One */
|
1415 |
+
fs_text_inline( '%s rating', 'x-rating', $api->slug ) :
|
1416 |
+
/* translators: %s: Number larger than 1 */
|
1417 |
+
fs_text_inline( '%s ratings', 'x-ratings', $api->slug )
|
1418 |
+
),
|
1419 |
+
number_format_i18n( $api->num_ratings )
|
1420 |
+
) ) ) ?>)
|
1421 |
+
</small>
|
1422 |
+
<?php
|
1423 |
+
}
|
1424 |
+
|
1425 |
+
if ( ! empty( $api->ratings ) && array_sum( (array) $api->ratings ) > 0 ) {
|
1426 |
+
foreach ( $api->ratings as $key => $ratecount ) {
|
1427 |
+
// Avoid div-by-zero.
|
1428 |
+
$_rating = $api->num_ratings ? ( $ratecount / $api->num_ratings ) : 0;
|
1429 |
+
$stars_label = sprintf(
|
1430 |
+
( ( 1 == $key ) ?
|
1431 |
+
/* translators: %s: 1 or One */
|
1432 |
+
fs_text_inline( '%s star', 'x-star', $api->slug ) :
|
1433 |
+
/* translators: %s: Number larger than 1 */
|
1434 |
+
fs_text_inline( '%s stars', 'x-stars', $api->slug )
|
1435 |
+
),
|
1436 |
+
number_format_i18n( $key )
|
1437 |
+
);
|
1438 |
+
?>
|
1439 |
+
<div class="counter-container">
|
1440 |
+
<span class="counter-label"><a
|
1441 |
+
href="https://wordpress.org/support/view/plugin-reviews/<?php echo $api->slug; ?>?filter=<?php echo $key; ?>"
|
1442 |
+
target="_blank"
|
1443 |
+
rel="noopener noreferrer"
|
1444 |
+
title="<?php echo esc_attr( sprintf(
|
1445 |
+
/* translators: %s: # of stars (e.g. 5 stars) */
|
1446 |
+
fs_text_inline( 'Click to see reviews that provided a rating of %s', 'click-to-reviews', $api->slug ),
|
1447 |
+
$stars_label
|
1448 |
+
) ) ?>"><?php echo $stars_label ?></a></span>
|
1449 |
+
<span class="counter-back">
|
1450 |
+
<span class="counter-bar" style="width: <?php echo absint(92 * $_rating); ?>px;"></span>
|
1451 |
+
</span>
|
1452 |
+
<span class="counter-count"><?php echo number_format_i18n( $ratecount ); ?></span>
|
1453 |
+
</div>
|
1454 |
+
<?php
|
1455 |
+
}
|
1456 |
+
}
|
1457 |
+
if ( ! empty( $api->contributors ) ) {
|
1458 |
+
?>
|
1459 |
+
<h3><?php fs_echo_inline( 'Contributors', 'contributors', $api->slug ); ?></h3>
|
1460 |
+
<ul class="contributors">
|
1461 |
+
<?php
|
1462 |
+
foreach ( (array) $api->contributors as $contrib_username => $contrib_profile ) {
|
1463 |
+
if ( empty( $contrib_username ) && empty( $contrib_profile ) ) {
|
1464 |
+
continue;
|
1465 |
+
}
|
1466 |
+
if ( empty( $contrib_username ) ) {
|
1467 |
+
$contrib_username = preg_replace( '/^.+\/(.+)\/?$/', '\1', $contrib_profile );
|
1468 |
+
}
|
1469 |
+
$contrib_username = sanitize_user( $contrib_username );
|
1470 |
+
if ( empty( $contrib_profile ) ) {
|
1471 |
+
echo "<li><img src='https://wordpress.org/grav-redirect.php?user={$contrib_username}&s=36' width='18' height='18' />{$contrib_username}</li>";
|
1472 |
+
} else {
|
1473 |
+
echo "<li><a href='{$contrib_profile}' target='_blank' rel='noopener noreferrer'><img src='https://wordpress.org/grav-redirect.php?user={$contrib_username}&s=36' width='18' height='18' />{$contrib_username}</a></li>";
|
1474 |
+
}
|
1475 |
+
}
|
1476 |
+
?>
|
1477 |
+
</ul>
|
1478 |
+
<?php if ( ! empty( $api->donate_link ) ) { ?>
|
1479 |
+
<a target="_blank"
|
1480 |
+
rel="noopener noreferrer"
|
1481 |
+
href="<?php echo esc_url( $api->donate_link ); ?>"><?php fs_echo_inline( 'Donate to this plugin', 'donate-to-plugin', $api->slug ) ?>
|
1482 |
+
»</a>
|
1483 |
+
<?php } ?>
|
1484 |
+
<?php } ?>
|
1485 |
+
</div>
|
1486 |
+
<div id="section-holder" class="wrap">
|
1487 |
+
<?php
|
1488 |
+
if ( ! empty( $api->tested ) && version_compare( substr( $GLOBALS['wp_version'], 0, strlen( $api->tested ) ), $api->tested, '>' ) ) {
|
1489 |
+
echo '<div class="notice notice-warning"><p>' . '<strong>' . fs_text_inline( 'Warning', 'warning', $api->slug ) . ':</strong> ' . fs_text_inline( 'This plugin has not been tested with your current version of WordPress.', 'not-tested-warning', $api->slug ) . '</p></div>';
|
1490 |
+
} else if ( ! empty( $api->requires ) && version_compare( substr( $GLOBALS['wp_version'], 0, strlen( $api->requires ) ), $api->requires, '<' ) ) {
|
1491 |
+
echo '<div class="notice notice-warning"><p>' . '<strong>' . fs_text_inline( 'Warning', 'warning', $api->slug ) . ':</strong> ' . fs_text_inline( 'This plugin has not been marked as compatible with your version of WordPress.', 'not-compatible-warning', $api->slug ) . '</p></div>';
|
1492 |
+
}
|
1493 |
+
|
1494 |
+
foreach ( (array) $api->sections as $section_name => $content ) {
|
1495 |
+
$content = links_add_base_url( $content, 'https://wordpress.org/plugins/' . $api->slug . '/' );
|
1496 |
+
$content = links_add_target( $content, '_blank' );
|
1497 |
+
|
1498 |
+
$san_section = esc_attr( $section_name );
|
1499 |
+
|
1500 |
+
$display = ( $section_name === $section ) ? 'block' : 'none';
|
1501 |
+
|
1502 |
+
if ( 'description' === $section_name &&
|
1503 |
+
( ( $api->is_wp_org_compliant && $api->wp_org_missing ) ||
|
1504 |
+
( ! $api->is_wp_org_compliant && $api->fs_missing ) )
|
1505 |
+
) {
|
1506 |
+
$missing_notice = array(
|
1507 |
+
'type' => 'error',
|
1508 |
+
'id' => md5( microtime() ),
|
1509 |
+
'message' => $api->is_paid ?
|
1510 |
+
fs_text_inline( 'Paid add-on must be deployed to Freemius.', 'paid-addon-not-deployed', $api->slug ) :
|
1511 |
+
fs_text_inline( 'Add-on must be deployed to WordPress.org or Freemius.', 'free-addon-not-deployed', $api->slug ),
|
1512 |
+
);
|
1513 |
+
fs_require_template( 'admin-notice.php', $missing_notice );
|
1514 |
+
}
|
1515 |
+
echo "\t<div id='section-{$san_section}' class='section' style='display: {$display};'>\n";
|
1516 |
+
echo $content;
|
1517 |
+
echo "\t</div>\n";
|
1518 |
+
}
|
1519 |
+
echo "</div>\n";
|
1520 |
+
echo "</div>\n";
|
1521 |
+
echo "</div>\n"; // #plugin-information-scrollable
|
1522 |
+
echo "<div id='$tab-footer'>\n";
|
1523 |
+
|
1524 |
+
if (
|
1525 |
+
! empty( $api->download_link ) &&
|
1526 |
+
! empty( $this->status ) &&
|
1527 |
+
in_array( $this->status['status'], array( 'newer_installed', 'latest_installed' ) )
|
1528 |
+
) {
|
1529 |
+
if ( 'newer_installed' === $this->status['status'] ) {
|
1530 |
+
echo $this->get_cta(
|
1531 |
+
( $this->status['is_premium_installed'] ?
|
1532 |
+
esc_html( sprintf( fs_text_inline( 'Newer Version (%s) Installed', 'newer-installed', $api->slug ), $this->status['version'] ) ) :
|
1533 |
+
esc_html( sprintf( fs_text_inline( 'Newer Free Version (%s) Installed', 'newer-free-installed', $api->slug ), $this->status['version'] ) ) ),
|
1534 |
+
false,
|
1535 |
+
true
|
1536 |
+
);
|
1537 |
+
} else {
|
1538 |
+
echo $this->get_cta(
|
1539 |
+
( $this->status['is_premium_installed'] ?
|
1540 |
+
fs_esc_html_inline( 'Latest Version Installed', 'latest-installed', $api->slug ) :
|
1541 |
+
fs_esc_html_inline( 'Latest Free Version Installed', 'latest-free-installed', $api->slug ) ),
|
1542 |
+
false,
|
1543 |
+
true
|
1544 |
+
);
|
1545 |
+
}
|
1546 |
+
}
|
1547 |
+
|
1548 |
+
echo $this->get_actions_dropdown( $api, null );
|
1549 |
+
|
1550 |
+
echo "</div>\n";
|
1551 |
+
?>
|
1552 |
+
<script type="text/javascript">
|
1553 |
+
( function( $, undef ) {
|
1554 |
+
var $dropdowns = $( '.fs-dropdown' );
|
1555 |
+
|
1556 |
+
$( '#plugin-information' )
|
1557 |
+
.click( function( evt ) {
|
1558 |
+
var $target = $( evt.target );
|
1559 |
+
|
1560 |
+
if (
|
1561 |
+
$target.hasClass( 'fs-dropdown-arrow-button' ) ||
|
1562 |
+
( 0 !== $target.parents( '.fs-dropdown-arrow-button' ).length )
|
1563 |
+
) {
|
1564 |
+
var $dropdown = $target.parents( '.fs-dropdown' ),
|
1565 |
+
isActive = $dropdown.hasClass( 'active' );
|
1566 |
+
|
1567 |
+
if ( ! isActive ) {
|
1568 |
+
/**
|
1569 |
+
* Close the other dropdown if it's active.
|
1570 |
+
*
|
1571 |
+
* @author Leo Fajardo (@leorw)
|
1572 |
+
* @since 2.3.0
|
1573 |
+
*/
|
1574 |
+
$( '.fs-dropdown.active' ).each( function() {
|
1575 |
+
toggleDropdown( $( this ), false );
|
1576 |
+
} );
|
1577 |
+
}
|
1578 |
+
|
1579 |
+
/**
|
1580 |
+
* Toggle the current dropdown.
|
1581 |
+
*
|
1582 |
+
* @author Leo Fajardo (@leorw)
|
1583 |
+
* @since 2.3.0
|
1584 |
+
*/
|
1585 |
+
toggleDropdown( $dropdown, ! isActive );
|
1586 |
+
|
1587 |
+
return true;
|
1588 |
+
}
|
1589 |
+
|
1590 |
+
/**
|
1591 |
+
* Close all dropdowns.
|
1592 |
+
*
|
1593 |
+
* @author Leo Fajardo (@leorw)
|
1594 |
+
* @since 2.3.0
|
1595 |
+
*/
|
1596 |
+
toggleDropdown( $( this ).find( '.fs-dropdown' ), false );
|
1597 |
+
});
|
1598 |
+
|
1599 |
+
if ( 0 !== $dropdowns.length ) {
|
1600 |
+
/**
|
1601 |
+
* Add the `up` class so that the bottom dropdown's content will be shown above its buttons.
|
1602 |
+
*
|
1603 |
+
* @author Leo Fajardo (@leorw)
|
1604 |
+
* @since 2.3.0
|
1605 |
+
*/
|
1606 |
+
$( '#plugin-information-footer' ).find( '.fs-dropdown' ).addClass( 'up' );
|
1607 |
+
}
|
1608 |
+
|
1609 |
+
/**
|
1610 |
+
* Returns the default state of the dropdown arrow button and hides the dropdown list.
|
1611 |
+
*
|
1612 |
+
* @author Leo Fajardo (@leorw)
|
1613 |
+
* @since 2.3.0
|
1614 |
+
*
|
1615 |
+
* @param {Object} [$dropdown]
|
1616 |
+
* @param {Boolean} [state]
|
1617 |
+
*/
|
1618 |
+
function toggleDropdown( $dropdown, state ) {
|
1619 |
+
if ( undef === $dropdown ) {
|
1620 |
+
var $activeDropdown = $dropdowns.find( '.active' );
|
1621 |
+
if ( 0 !== $activeDropdown.length ) {
|
1622 |
+
$dropdown = $activeDropdown;
|
1623 |
+
}
|
1624 |
+
}
|
1625 |
+
|
1626 |
+
if ( undef === $dropdown ) {
|
1627 |
+
return;
|
1628 |
+
}
|
1629 |
+
|
1630 |
+
if ( undef === state ) {
|
1631 |
+
state = false;
|
1632 |
+
}
|
1633 |
+
|
1634 |
+
$dropdown.toggleClass( 'active', state );
|
1635 |
+
$dropdown.find( '.fs-dropdown-list' ).toggle( state );
|
1636 |
+
$dropdown.find( '.fs-dropdown-arrow-button' ).toggleClass( 'active', state );
|
1637 |
+
}
|
1638 |
+
} )( jQuery );
|
1639 |
+
</script>
|
1640 |
+
<?php
|
1641 |
+
iframe_footer();
|
1642 |
+
exit;
|
1643 |
+
}
|
1644 |
+
}
|
freemius/includes/i18n.php
CHANGED
@@ -1,605 +1,605 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* @package Freemius
|
4 |
-
* @copyright Copyright (c) 2015, Freemius, Inc.
|
5 |
-
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
|
6 |
-
* @since 1.1.4
|
7 |
-
*
|
8 |
-
* @deprecated This file is no longer in use. It's still in the project for backward compatibility.
|
9 |
-
*/
|
10 |
-
|
11 |
-
if ( ! defined( 'ABSPATH' ) ) {
|
12 |
-
exit;
|
13 |
-
}
|
14 |
-
|
15 |
-
require_once dirname( __FILE__ ) . '/l10n.php';
|
16 |
-
|
17 |
-
/**
|
18 |
-
* All strings can now be overridden.
|
19 |
-
*
|
20 |
-
* For example, if we want to override:
|
21 |
-
* 'you-are-step-away' => 'You are just one step away - %s',
|
22 |
-
*
|
23 |
-
* We can use the filter:
|
24 |
-
* fs_override_i18n( array(
|
25 |
-
* 'opt-in-connect' => __( "Yes - I'm in!", '{your-text_domain}' ),
|
26 |
-
* 'skip' => __( 'Not today', '{your-text_domain}' ),
|
27 |
-
* ), '{plugin_slug}' );
|
28 |
-
*
|
29 |
-
* Or with the Freemius instance:
|
30 |
-
*
|
31 |
-
* my_freemius->override_i18n( array(
|
32 |
-
* 'opt-in-connect' => __( "Yes - I'm in!", '{your-text_domain}' ),
|
33 |
-
* 'skip' => __( 'Not today', '{your-text_domain}' ),
|
34 |
-
* ) );
|
35 |
-
*/
|
36 |
-
global $fs_text;
|
37 |
-
|
38 |
-
$fs_text = array(
|
39 |
-
'account' => _fs_text( 'Account' ),
|
40 |
-
'addon' => _fs_text( 'Add-On' ),
|
41 |
-
'contact-us' => _fs_text( 'Contact Us' ),
|
42 |
-
'contact-support' => _fs_text( 'Contact Support' ),
|
43 |
-
'change-ownership' => _fs_text( 'Change Ownership' ),
|
44 |
-
'support' => _fs_text( 'Support' ),
|
45 |
-
'support-forum' => _fs_text( 'Support Forum' ),
|
46 |
-
'add-ons' => _fs_text( 'Add-Ons' ),
|
47 |
-
'upgrade' => _fs_x( 'Upgrade', 'verb' ),
|
48 |
-
'awesome' => _fs_text( 'Awesome' ),
|
49 |
-
'pricing' => _fs_x( 'Pricing', 'noun' ),
|
50 |
-
'price' => _fs_x( 'Price', 'noun' ),
|
51 |
-
'unlimited-updates' => _fs_text( 'Unlimited Updates' ),
|
52 |
-
'downgrade' => _fs_x( 'Downgrade', 'verb' ),
|
53 |
-
'cancel-subscription' => _fs_x( 'Cancel Subscription', 'verb' ),
|
54 |
-
'cancel-trial' => _fs_text( 'Cancel Trial' ),
|
55 |
-
'free-trial' => _fs_text( 'Free Trial' ),
|
56 |
-
'start-free-x' => _fs_text( 'Start my free %s' ),
|
57 |
-
'no-commitment-x' => _fs_text( 'No commitment for %s - cancel anytime' ),
|
58 |
-
'after-x-pay-as-little-y' => _fs_text( 'After your free %s, pay as little as %s' ),
|
59 |
-
'details' => _fs_text( 'Details' ),
|
60 |
-
'account-details' => _fs_text( 'Account Details' ),
|
61 |
-
'delete' => _fs_x( 'Delete', 'verb' ),
|
62 |
-
'show' => _fs_x( 'Show', 'verb' ),
|
63 |
-
'hide' => _fs_x( 'Hide', 'verb' ),
|
64 |
-
'edit' => _fs_x( 'Edit', 'verb' ),
|
65 |
-
'update' => _fs_x( 'Update', 'verb' ),
|
66 |
-
'date' => _fs_text( 'Date' ),
|
67 |
-
'amount' => _fs_text( 'Amount' ),
|
68 |
-
'invoice' => _fs_text( 'Invoice' ),
|
69 |
-
'billing' => _fs_text( 'Billing' ),
|
70 |
-
'payments' => _fs_text( 'Payments' ),
|
71 |
-
'delete-account' => _fs_text( 'Delete Account' ),
|
72 |
-
'dismiss' => _fs_x( 'Dismiss', 'as close a window' ),
|
73 |
-
'plan' => _fs_x( 'Plan', 'as product pricing plan' ),
|
74 |
-
'change-plan' => _fs_text( 'Change Plan' ),
|
75 |
-
'download-x-version' => _fs_x( 'Download %s Version', 'as download professional version' ),
|
76 |
-
'download-x-version-now' => _fs_x( 'Download %s version now', 'as download professional version now' ),
|
77 |
-
'download-latest' => _fs_x( 'Download Latest', 'as download latest version' ),
|
78 |
-
'you-have-x-license' => _fs_x( 'You have a %s license.', 'E.g. you have a professional license.' ),
|
79 |
-
'new' => _fs_text( 'New' ),
|
80 |
-
'free' => _fs_text( 'Free' ),
|
81 |
-
'trial' => _fs_x( 'Trial', 'as trial plan' ),
|
82 |
-
'start-trial' => _fs_x( 'Start Trial', 'as starting a trial plan' ),
|
83 |
-
'purchase' => _fs_x( 'Purchase', 'verb' ),
|
84 |
-
'purchase-license' => _fs_text( 'Purchase License' ),
|
85 |
-
'buy' => _fs_x( 'Buy', 'verb' ),
|
86 |
-
'buy-license' => _fs_text( 'Buy License' ),
|
87 |
-
'license-single-site' => _fs_text( 'Single Site License' ),
|
88 |
-
'license-unlimited' => _fs_text( 'Unlimited Licenses' ),
|
89 |
-
'license-x-sites' => _fs_text( 'Up to %s Sites' ),
|
90 |
-
'renew-license-now' => _fs_text( '%sRenew your license now%s to access version %s security & feature updates, and support.' ),
|
91 |
-
'ask-for-upgrade-email-address' => _fs_text( "Enter the email address you've used for the upgrade below and we will resend you the license key." ),
|
92 |
-
'x-plan' => _fs_x( '%s Plan', 'e.g. Professional Plan' ),
|
93 |
-
'you-are-step-away' => _fs_text( 'You are just one step away - %s' ),
|
94 |
-
'activate-x-now' => _fs_x( 'Complete "%s" Activation Now',
|
95 |
-
'%s - plugin name. As complete "Jetpack" activation now' ),
|
96 |
-
'few-plugin-tweaks' => _fs_text( 'We made a few tweaks to the %s, %s' ),
|
97 |
-
'optin-x-now' => _fs_text( 'Opt in to make "%s" better!' ),
|
98 |
-
'error' => _fs_text( 'Error' ),
|
99 |
-
'failed-finding-main-path' => _fs_text( 'Freemius SDK couldn\'t find the plugin\'s main file. Please contact sdk@freemius.com with the current error.' ),
|
100 |
-
'learn-more' => _fs_text( 'Learn more' ),
|
101 |
-
'license_not_whitelabeled' => _fs_text( "Is this your client's site? %s if you wish to hide sensitive info like your billing address and invoices from the WP Admin."),
|
102 |
-
'license_whitelabeled' => _fs_text( 'Your %s license was flagged as white-labeled to hide sensitive information from the WP Admin (e.g. your billing address and invoices). If you ever wish to revert it back, you can easily do it through your %s. If this was a mistake you can also %s.'),
|
103 |
-
|
104 |
-
#region Affiliation
|
105 |
-
'affiliation' => _fs_text( 'Affiliation' ),
|
106 |
-
'affiliate' => _fs_text( 'Affiliate' ),
|
107 |
-
'affiliate-tracking' => _fs_text( '%s tracking cookie after the first visit to maximize earnings potential.' ),
|
108 |
-
'renewals-commission' => _fs_text( 'Get commission for automated subscription renewals.' ),
|
109 |
-
'affiliate-application-accepted' => _fs_text( "Your affiliate application for %s has been accepted! Log in to your affiliate area at: %s." ),
|
110 |
-
'affiliate-application-thank-you' => _fs_text( "Thank you for applying for our affiliate program, we'll review your details during the next 14 days and will get back to you with further information." ),
|
111 |
-
'affiliate-application-rejected' => _fs_text( "Thank you for applying for our affiliate program, unfortunately, we've decided at this point to reject your application. Please try again in 30 days." ),
|
112 |
-
'affiliate-account-suspended' => _fs_text( 'Your affiliation account was temporarily suspended.' ),
|
113 |
-
'affiliate-account-blocked' => _fs_text( 'Due to violation of our affiliation terms, we decided to temporarily block your affiliation account. If you have any questions, please contact support.' ),
|
114 |
-
'become-an-ambassador' => _fs_text( 'Like the %s? Become our ambassador and earn cash ;-)' ),
|
115 |
-
'become-an-ambassador-admin-notice' => _fs_text( 'Hey there, did you know that %s has an affiliate program? If you like the %s you can become our ambassador and earn some cash!' ),
|
116 |
-
'refer-new-customers' => _fs_text( 'Refer new customers to our %s and earn %s commission on each successful sale you refer!' ),
|
117 |
-
'program-summary' => _fs_text( 'Program Summary' ),
|
118 |
-
'commission-on-new-license-purchase' => _fs_text( '%s commission when a customer purchases a new license.' ),
|
119 |
-
'unlimited-commissions' => _fs_text( 'Unlimited commissions.' ),
|
120 |
-
'minimum-payout-amount' => _fs_text( '%s minimum payout amount.' ),
|
121 |
-
'payouts-unit-and-processing' => _fs_text( 'Payouts are in USD and processed monthly via PayPal.' ),
|
122 |
-
'commission-payment' => _fs_text( 'As we reserve 30 days for potential refunds, we only pay commissions that are older than 30 days.' ),
|
123 |
-
'become-an-affiliate' => _fs_text( 'Become an affiliate' ),
|
124 |
-
'apply-to-become-an-affiliate' => _fs_text( 'Apply to become an affiliate' ),
|
125 |
-
'full-name' => _fs_text( 'Full name' ),
|
126 |
-
'paypal-account-email-address' => _fs_text( 'PayPal account email address' ),
|
127 |
-
'promotion-methods' => _fs_text( 'Promotion methods' ),
|
128 |
-
'social-media' => _fs_text( 'Social media (Facebook, Twitter, etc.)' ),
|
129 |
-
'mobile-apps' => _fs_text( 'Mobile apps' ),
|
130 |
-
'statistics-information-field-label' => _fs_text( 'Website, email, and social media statistics (optional)' ),
|
131 |
-
'statistics-information-field-desc' => _fs_text( 'Please feel free to provide any relevant website or social media statistics, e.g. monthly unique site visits, number of email subscribers, followers, etc. (we will keep this information confidential).' ),
|
132 |
-
'promotion-method-desc-field-label' => _fs_text( 'How will you promote us?' ),
|
133 |
-
'promotion-method-desc-field-desc' => _fs_text( 'Please provide details on how you intend to promote %s (please be as specific as possible).' ),
|
134 |
-
'domain-field-label' => _fs_text( 'Where are you going to promote the %s?' ),
|
135 |
-
'domain-field-desc' => _fs_text( 'Enter the domain of your website or other websites from where you plan to promote the %s.' ),
|
136 |
-
'extra-domain-fields-label' => _fs_text( 'Extra Domains' ),
|
137 |
-
'extra-domain-fields-desc' => _fs_text( 'Extra domains where you will be marketing the product from.' ),
|
138 |
-
'add-another-domain' => _fs_text( 'Add another domain' ),
|
139 |
-
'remove' => _fs_x( 'Remove', 'Remove domain' ),
|
140 |
-
'email-address-is-required' => _fs_text( 'Email address is required.' ),
|
141 |
-
'domain-is-required' => _fs_text( 'Domain is required.' ),
|
142 |
-
'invalid-domain' => _fs_text( 'Invalid domain' ),
|
143 |
-
'paypal-email-address-is-required' => _fs_text( 'PayPal email address is required.' ),
|
144 |
-
'processing' => _fs_text( 'Processing...' ),
|
145 |
-
'non-expiring' => _fs_text( 'Non-expiring' ),
|
146 |
-
'account-is-pending-activation' => _fs_text( 'Account is pending activation.' ),
|
147 |
-
#endregion Affiliation
|
148 |
-
|
149 |
-
#region Account
|
150 |
-
'expiration' => _fs_x( 'Expiration', 'as expiration date' ),
|
151 |
-
'license' => _fs_x( 'License', 'as software license' ),
|
152 |
-
'not-verified' => _fs_text( 'not verified' ),
|
153 |
-
'verify-email' => _fs_text( 'Verify Email' ),
|
154 |
-
'expires-in' => _fs_x( 'Expires in %s', 'e.g. expires in 2 months' ),
|
155 |
-
'renews-in' => _fs_x( 'Auto renews in %s', 'e.g. auto renews in 2 months' ),
|
156 |
-
'no-expiration' => _fs_text( 'No expiration' ),
|
157 |
-
'expired' => _fs_text( 'Expired' ),
|
158 |
-
'cancelled' => _fs_text( 'Cancelled' ),
|
159 |
-
'in-x' => _fs_x( 'In %s', 'e.g. In 2 hours' ),
|
160 |
-
'x-ago' => _fs_x( '%s ago', 'e.g. 2 min ago' ),
|
161 |
-
/* translators: %s: Version number (e.g. 4.6 or higher) */
|
162 |
-
'x-or-higher' => _fs_text( '%s or higher' ),
|
163 |
-
'version' => _fs_x( 'Version', 'as plugin version' ),
|
164 |
-
'name' => _fs_text( 'Name' ),
|
165 |
-
'email' => _fs_text( 'Email' ),
|
166 |
-
'email-address' => _fs_text( 'Email address' ),
|
167 |
-
'verified' => _fs_text( 'Verified' ),
|
168 |
-
'module' => _fs_text( 'Module' ),
|
169 |
-
'module-type' => _fs_text( 'Module Type' ),
|
170 |
-
'plugin' => _fs_text( 'Plugin' ),
|
171 |
-
'plugins' => _fs_text( 'Plugins' ),
|
172 |
-
'theme' => _fs_text( 'Theme' ),
|
173 |
-
'themes' => _fs_text( 'Themes' ),
|
174 |
-
'path' => _fs_x( 'Path', 'as file/folder path' ),
|
175 |
-
'title' => _fs_text( 'Title' ),
|
176 |
-
'free-version' => _fs_text( 'Free version' ),
|
177 |
-
'premium-version' => _fs_text( 'Premium version' ),
|
178 |
-
'slug' => _fs_x( 'Slug', 'as WP plugin slug' ),
|
179 |
-
'id' => _fs_text( 'ID' ),
|
180 |
-
'users' => _fs_text( 'Users' ),
|
181 |
-
'module-installs' => _fs_text( '%s Installs' ),
|
182 |
-
'sites' => _fs_x( 'Sites', 'like websites' ),
|
183 |
-
'user-id' => _fs_text( 'User ID' ),
|
184 |
-
'site-id' => _fs_text( 'Site ID' ),
|
185 |
-
'public-key' => _fs_text( 'Public Key' ),
|
186 |
-
'secret-key' => _fs_text( 'Secret Key' ),
|
187 |
-
'no-secret' => _fs_x( 'No Secret', 'as secret encryption key missing' ),
|
188 |
-
'no-id' => _fs_text( 'No ID' ),
|
189 |
-
'sync-license' => _fs_x( 'Sync License', 'as synchronize license' ),
|
190 |
-
'sync' => _fs_x( 'Sync', 'as synchronize' ),
|
191 |
-
'activate-license' => _fs_text( 'Activate License' ),
|
192 |
-
'activate-free-version' => _fs_text( 'Activate Free Version' ),
|
193 |
-
'activate-license-message' => _fs_text( 'Please enter the license key that you received in the email right after the purchase:' ),
|
194 |
-
'activating-license' => _fs_text( 'Activating license...' ),
|
195 |
-
'change-license' => _fs_text( 'Change License' ),
|
196 |
-
'update-license' => _fs_text( 'Update License' ),
|
197 |
-
'deactivate-license' => _fs_text( 'Deactivate License' ),
|
198 |
-
'activate' => _fs_text( 'Activate' ),
|
199 |
-
'deactivate' => _fs_text( 'Deactivate' ),
|
200 |
-
'skip-deactivate' => _fs_text( 'Skip & Deactivate' ),
|
201 |
-
'skip-and-x' => _fs_text( 'Skip & %s' ),
|
202 |
-
'no-deactivate' => _fs_text( 'No - just deactivate' ),
|
203 |
-
'yes-do-your-thing' => _fs_text( 'Yes - do your thing' ),
|
204 |
-
'active' => _fs_x( 'Active', 'active mode' ),
|
205 |
-
'is-active' => _fs_x( 'Is Active', 'is active mode?' ),
|
206 |
-
'install-now' => _fs_text( 'Install Now' ),
|
207 |
-
'install-update-now' => _fs_text( 'Install Update Now' ),
|
208 |
-
'more-information-about-x' => _fs_text( 'More information about %s' ),
|
209 |
-
'localhost' => _fs_text( 'Localhost' ),
|
210 |
-
'activate-x-plan' => _fs_x( 'Activate %s Plan', 'as activate Professional plan' ),
|
211 |
-
'x-left' => _fs_x( '%s left', 'as 5 licenses left' ),
|
212 |
-
'last-license' => _fs_text( 'Last license' ),
|
213 |
-
'what-is-your-x' => _fs_text( 'What is your %s?' ),
|
214 |
-
'activate-this-addon' => _fs_text( 'Activate this add-on' ),
|
215 |
-
'deactivate-license-confirm' => _fs_text( 'Deactivating your license will block all premium features, but will enable you to activate the license on another site. Are you sure you want to proceed?' ),
|
216 |
-
'delete-account-x-confirm' => _fs_text( 'Deleting the account will automatically deactivate your %s plan license so you can use it on other sites. If you want to terminate the recurring payments as well, click the "Cancel" button, and first "Downgrade" your account. Are you sure you would like to continue with the deletion?' ),
|
217 |
-
'delete-account-confirm' => _fs_text( 'Deletion is not temporary. Only delete if you no longer want to use this %s anymore. Are you sure you would like to continue with the deletion?' ),
|
218 |
-
'downgrade-x-confirm' => _fs_text( 'Downgrading your plan will immediately stop all future recurring payments and your %s plan license will expire in %s.' ),
|
219 |
-
'cancel-trial-confirm' => _fs_text( 'Cancelling the trial will immediately block access to all premium features. Are you sure?' ),
|
220 |
-
'after-downgrade-non-blocking' => _fs_text( 'You can still enjoy all %s features but you will not have access to %s security & feature updates, nor support.' ),
|
221 |
-
'after-downgrade-blocking' => _fs_text( 'Once your license expires you can still use the Free version but you will NOT have access to the %s features.' ),
|
222 |
-
'proceed-confirmation' => _fs_text( 'Are you sure you want to proceed?' ),
|
223 |
-
#endregion Account
|
224 |
-
|
225 |
-
'add-ons-for-x' => _fs_text( 'Add Ons for %s' ),
|
226 |
-
'add-ons-missing' => _fs_text( 'We could\'nt load the add-ons list. It\'s probably an issue on our side, please try to come back in few minutes.' ),
|
227 |
-
#region Plugin Deactivation
|
228 |
-
'anonymous-feedback' => _fs_text( 'Anonymous feedback' ),
|
229 |
-
'quick-feedback' => _fs_text( 'Quick feedback' ),
|
230 |
-
'deactivation-share-reason' => _fs_text( 'If you have a moment, please let us know why you are %s' ),
|
231 |
-
'deactivating' => _fs_text( 'deactivating' ),
|
232 |
-
'deactivation' => _fs_text( 'Deactivation' ),
|
233 |
-
'theme-switch' => _fs_text( 'Theme Switch' ),
|
234 |
-
'switching' => _fs_text( 'switching' ),
|
235 |
-
'switch' => _fs_text( 'Switch' ),
|
236 |
-
'activate-x' => _fs_text( 'Activate %s' ),
|
237 |
-
'deactivation-modal-button-confirm' => _fs_text( 'Yes - %s' ),
|
238 |
-
'deactivation-modal-button-submit' => _fs_text( 'Submit & %s' ),
|
239 |
-
'cancel' => _fs_text( 'Cancel' ),
|
240 |
-
'reason-no-longer-needed' => _fs_text( 'I no longer need the %s' ),
|
241 |
-
'reason-found-a-better-plugin' => _fs_text( 'I found a better %s' ),
|
242 |
-
'reason-needed-for-a-short-period' => _fs_text( 'I only needed the %s for a short period' ),
|
243 |
-
'reason-broke-my-site' => _fs_text( 'The %s broke my site' ),
|
244 |
-
'reason-suddenly-stopped-working' => _fs_text( 'The %s suddenly stopped working' ),
|
245 |
-
'reason-cant-pay-anymore' => _fs_text( "I can't pay for it anymore" ),
|
246 |
-
'reason-temporary-deactivation' => _fs_text( "It's a temporary deactivation. I'm just debugging an issue." ),
|
247 |
-
'reason-temporary-x' => _fs_text( "It's a temporary %s. I'm just debugging an issue." ),
|
248 |
-
'reason-other' => _fs_x( 'Other',
|
249 |
-
'the text of the "other" reason for deactivating the module that is shown in the modal box.' ),
|
250 |
-
'ask-for-reason-message' => _fs_text( 'Kindly tell us the reason so we can improve.' ),
|
251 |
-
'placeholder-plugin-name' => _fs_text( "What's the %s's name?" ),
|
252 |
-
'placeholder-comfortable-price' => _fs_text( 'What price would you feel comfortable paying?' ),
|
253 |
-
'reason-couldnt-make-it-work' => _fs_text( "I couldn't understand how to make it work" ),
|
254 |
-
'reason-great-but-need-specific-feature' => _fs_text( "The %s is great, but I need specific feature that you don't support" ),
|
255 |
-
'reason-not-working' => _fs_text( 'The %s is not working' ),
|
256 |
-
'reason-not-what-i-was-looking-for' => _fs_text( "It's not what I was looking for" ),
|
257 |
-
'reason-didnt-work-as-expected' => _fs_text( "The %s didn't work as expected" ),
|
258 |
-
'placeholder-feature' => _fs_text( 'What feature?' ),
|
259 |
-
'placeholder-share-what-didnt-work' => _fs_text( "Kindly share what didn't work so we can fix it for future users..." ),
|
260 |
-
'placeholder-what-youve-been-looking-for' => _fs_text( "What you've been looking for?" ),
|
261 |
-
'placeholder-what-did-you-expect' => _fs_text( "What did you expect?" ),
|
262 |
-
'reason-didnt-work' => _fs_text( "The %s didn't work" ),
|
263 |
-
'reason-dont-like-to-share-my-information' => _fs_text( "I don't like to share my information with you" ),
|
264 |
-
'dont-have-to-share-any-data' => _fs_text( "You might have missed it, but you don't have to share any data and can just %s the opt-in." ),
|
265 |
-
#endregion Plugin Deactivation
|
266 |
-
|
267 |
-
#region Connect
|
268 |
-
'hey-x' => _fs_x( 'Hey %s,', 'greeting' ),
|
269 |
-
'thanks-x' => _fs_x( 'Thanks %s!', 'a greeting. E.g. Thanks John!' ),
|
270 |
-
'connect-message' => _fs_text( 'Never miss an important update - opt in to our security and feature updates notifications, and non-sensitive diagnostic tracking with %4$s.' ),
|
271 |
-
'connect-message_on-update' => _fs_text( 'Please help us improve %1$s! If you opt in, some data about your usage of %1$s will be sent to %4$s. If you skip this, that\'s okay! %1$s will still work just fine.' ),
|
272 |
-
'pending-activation-message' => _fs_text( 'You should receive an activation email for %s to your mailbox at %s. Please make sure you click the activation button in that email to %s.' ),
|
273 |
-
'complete-the-install' => _fs_text( 'complete the install' ),
|
274 |
-
'start-the-trial' => _fs_text( 'start the trial' ),
|
275 |
-
'thanks-for-purchasing' => _fs_text( 'Thanks for purchasing %s! To get started, please enter your license key:' ),
|
276 |
-
'license-sync-disclaimer' => _fs_text( 'The %1$s will be periodically sending data to %2$s to check for security and feature updates, and verify the validity of your license.' ),
|
277 |
-
'what-permissions' => _fs_text( 'What permissions are being granted?' ),
|
278 |
-
'permissions-profile' => _fs_text( 'Your Profile Overview' ),
|
279 |
-
'permissions-profile_desc' => _fs_text( 'Name and email address' ),
|
280 |
-
'permissions-site' => _fs_text( 'Your Site Overview' ),
|
281 |
-
'permissions-site_desc' => _fs_text( 'Site URL, WP version, PHP info, plugins & themes' ),
|
282 |
-
'permissions-events' => _fs_text( 'Current %s Events' ),
|
283 |
-
'permissions-events_desc' => _fs_text( 'Activation, deactivation and uninstall' ),
|
284 |
-
'permissions-plugins_themes' => _fs_text( 'Plugins & Themes' ),
|
285 |
-
'permissions-plugins_themes_desc' => _fs_text( 'Titles, versions and state.' ),
|
286 |
-
'permissions-admin-notices' => _fs_text( 'Admin Notices' ),
|
287 |
-
'permissions-newsletter' => _fs_text( 'Newsletter' ),
|
288 |
-
'permissions-newsletter_desc' => _fs_text( 'Updates, announcements, marketing, no spam' ),
|
289 |
-
'privacy-policy' => _fs_text( 'Privacy Policy' ),
|
290 |
-
'tos' => _fs_text( 'Terms of Service' ),
|
291 |
-
'activating' => _fs_x( 'Activating', 'as activating plugin' ),
|
292 |
-
'sending-email' => _fs_x( 'Sending email', 'as in the process of sending an email' ),
|
293 |
-
'opt-in-connect' => _fs_x( 'Allow & Continue', 'button label' ),
|
294 |
-
'agree-activate-license' => _fs_x( 'Agree & Activate License', 'button label' ),
|
295 |
-
'skip' => _fs_x( 'Skip', 'verb' ),
|
296 |
-
'click-here-to-use-plugin-anonymously' => _fs_text( 'Click here to use the plugin anonymously' ),
|
297 |
-
'resend-activation-email' => _fs_text( 'Re-send activation email' ),
|
298 |
-
'license-key' => _fs_text( 'License key' ),
|
299 |
-
'send-license-key' => _fs_text( 'Send License Key' ),
|
300 |
-
'sending-license-key' => _fs_text( 'Sending license key' ),
|
301 |
-
'have-license-key' => _fs_text( 'Have a license key?' ),
|
302 |
-
'dont-have-license-key' => _fs_text( 'Don\'t have a license key?' ),
|
303 |
-
'cant-find-license-key' => _fs_text( "Can't find your license key?" ),
|
304 |
-
'email-not-found' => _fs_text( "We couldn't find your email address in the system, are you sure it's the right address?" ),
|
305 |
-
'no-active-licenses' => _fs_text( "We can't see any active licenses associated with that email address, are you sure it's the right address?" ),
|
306 |
-
'opt-in' => _fs_text( 'Opt In' ),
|
307 |
-
'opt-out' => _fs_text( 'Opt Out' ),
|
308 |
-
'opt-out-cancel' => _fs_text( 'On second thought - I want to continue helping' ),
|
309 |
-
'opting-out' => _fs_text( 'Opting out...' ),
|
310 |
-
'opting-in' => _fs_text( 'Opting in...' ),
|
311 |
-
'opt-out-message-appreciation' => _fs_text( 'We appreciate your help in making the %s better by letting us track some usage data.' ),
|
312 |
-
'opt-out-message-usage-tracking' => _fs_text( "Usage tracking is done in the name of making %s better. Making a better user experience, prioritizing new features, and more good things. We'd really appreciate if you'll reconsider letting us continue with the tracking." ),
|
313 |
-
'opt-out-message-clicking-opt-out' => _fs_text( 'By clicking "Opt Out", we will no longer be sending any data from %s to %s.' ),
|
314 |
-
'apply-on-all-sites-in-the-network' => _fs_text( 'Apply on all sites in the network.' ),
|
315 |
-
'delegate-to-site-admins' => _fs_text( 'Delegate to Site Admins' ),
|
316 |
-
'delegate-to-site-admins-and-continue' => _fs_text( 'Delegate to Site Admins & Continue' ),
|
317 |
-
'continue' => _fs_text( 'Continue' ),
|
318 |
-
'allow' => _fs_text( 'allow' ),
|
319 |
-
'delegate' => _fs_text( 'delegate' ),
|
320 |
-
#endregion Connect
|
321 |
-
|
322 |
-
#region Screenshots
|
323 |
-
'screenshots' => _fs_text( 'Screenshots' ),
|
324 |
-
'view-full-size-x' => _fs_text( 'Click to view full-size screenshot %d' ),
|
325 |
-
#endregion Screenshots
|
326 |
-
|
327 |
-
#region Debug
|
328 |
-
'freemius-debug' => _fs_text( 'Freemius Debug' ),
|
329 |
-
'on' => _fs_x( 'On', 'as turned on' ),
|
330 |
-
'off' => _fs_x( 'Off', 'as turned off' ),
|
331 |
-
'debugging' => _fs_x( 'Debugging', 'as code debugging' ),
|
332 |
-
'freemius-state' => _fs_text( 'Freemius State' ),
|
333 |
-
'connected' => _fs_x( 'Connected', 'as connection was successful' ),
|
334 |
-
'blocked' => _fs_x( 'Blocked', 'as connection blocked' ),
|
335 |
-
'api' => _fs_x( 'API', 'as application program interface' ),
|
336 |
-
'sdk' => _fs_x( 'SDK', 'as software development kit versions' ),
|
337 |
-
'sdk-versions' => _fs_x( 'SDK Versions', 'as software development kit versions' ),
|
338 |
-
'plugin-path' => _fs_x( 'Plugin Path', 'as plugin folder path' ),
|
339 |
-
'sdk-path' => _fs_x( 'SDK Path', 'as sdk path' ),
|
340 |
-
'addons-of-x' => _fs_text( 'Add Ons of Plugin %s' ),
|
341 |
-
'delete-all-confirm' => _fs_text( 'Are you sure you want to delete all Freemius data?' ),
|
342 |
-
'actions' => _fs_text( 'Actions' ),
|
343 |
-
'delete-all-accounts' => _fs_text( 'Delete All Accounts' ),
|
344 |
-
'start-fresh' => _fs_text( 'Start Fresh' ),
|
345 |
-
'clear-api-cache' => _fs_text( 'Clear API Cache' ),
|
346 |
-
'sync-data-from-server' => _fs_text( 'Sync Data From Server' ),
|
347 |
-
'scheduled-crons' => _fs_text( 'Scheduled Crons' ),
|
348 |
-
'cron-type' => _fs_text( 'Cron Type' ),
|
349 |
-
'plugins-themes-sync' => _fs_text( 'Plugins & Themes Sync' ),
|
350 |
-
'module-licenses' => _fs_text( '%s Licenses' ),
|
351 |
-
'debug-log' => _fs_text( 'Debug Log' ),
|
352 |
-
'all' => _fs_text( 'All' ),
|
353 |
-
'file' => _fs_text( 'File' ),
|
354 |
-
'function' => _fs_text( 'Function' ),
|
355 |
-
'process-id' => _fs_text( 'Process ID' ),
|
356 |
-
'logger' => _fs_text( 'Logger' ),
|
357 |
-
'message' => _fs_text( 'Message' ),
|
358 |
-
'download' => _fs_text( 'Download' ),
|
359 |
-
'filter' => _fs_text( 'Filter' ),
|
360 |
-
'type' => _fs_text( 'Type' ),
|
361 |
-
'all-types' => _fs_text( 'All Types' ),
|
362 |
-
'all-requests' => _fs_text( 'All Requests' ),
|
363 |
-
#endregion Debug
|
364 |
-
|
365 |
-
#region Expressions
|
366 |
-
'congrats' => _fs_x( 'Congrats', 'as congratulations' ),
|
367 |
-
'oops' => _fs_x( 'Oops', 'exclamation' ),
|
368 |
-
'yee-haw' => _fs_x( 'Yee-haw', 'interjection expressing joy or exuberance' ),
|
369 |
-
'woot' => _fs_x( 'W00t',
|
370 |
-
'(especially in electronic communication) used to express elation, enthusiasm, or triumph.' ),
|
371 |
-
'right-on' => _fs_x( 'Right on', 'a positive response' ),
|
372 |
-
'hmm' => _fs_x( 'Hmm',
|
373 |
-
'something somebody says when they are thinking about what you have just said. ' ),
|
374 |
-
'ok' => _fs_text( 'O.K' ),
|
375 |
-
'hey' => _fs_x( 'Hey', 'exclamation' ),
|
376 |
-
'heads-up' => _fs_x( 'Heads up',
|
377 |
-
'advance notice of something that will need attention.' ),
|
378 |
-
#endregion Expressions
|
379 |
-
|
380 |
-
#region Admin Notices
|
381 |
-
'you-have-latest' => _fs_text( 'Seems like you got the latest release.' ),
|
382 |
-
'you-are-good' => _fs_text( 'You are all good!' ),
|
383 |
-
'user-exist-message' => _fs_text( 'Sorry, we could not complete the email update. Another user with the same email is already registered.' ),
|
384 |
-
'user-exist-message_ownership' => _fs_text( 'If you would like to give up the ownership of the %s\'s account to %s click the Change Ownership button.' ),
|
385 |
-
'email-updated-message' => _fs_text( 'Your email was successfully updated. You should receive an email with confirmation instructions in few moments.' ),
|
386 |
-
'name-updated-message' => _fs_text( 'Your name was successfully updated.' ),
|
387 |
-
'x-updated' => _fs_text( 'You have successfully updated your %s.' ),
|
388 |
-
'name-update-failed-message' => _fs_text( 'Please provide your full name.' ),
|
389 |
-
'verification-email-sent-message' => _fs_text( 'Verification mail was just sent to %s. If you can\'t find it after 5 min, please check your spam box.' ),
|
390 |
-
'addons-info-external-message' => _fs_text( 'Just letting you know that the add-ons information of %s is being pulled from an external server.' ),
|
391 |
-
'no-cc-required' => _fs_text( 'No credit card required' ),
|
392 |
-
'premium-activated-message' => _fs_text( 'Premium %s version was successfully activated.' ),
|
393 |
-
'successful-version-upgrade-message' => _fs_text( 'The upgrade of %s was successfully completed.' ),
|
394 |
-
'activation-with-plan-x-message' => _fs_text( 'Your account was successfully activated with the %s plan.' ),
|
395 |
-
'download-latest-x-version-now' => _fs_text( 'Download the latest %s version now' ),
|
396 |
-
'follow-steps-to-complete-upgrade' => _fs_text( 'Please follow these steps to complete the upgrade' ),
|
397 |
-
'download-latest-x-version' => _fs_text( 'Download the latest %s version' ),
|
398 |
-
'download-latest-version' => _fs_text( 'Download the latest version' ),
|
399 |
-
'deactivate-free-version' => _fs_text( 'Deactivate the free version' ),
|
400 |
-
'upload-and-activate' => _fs_text( 'Upload and activate the downloaded version' ),
|
401 |
-
'howto-upload-activate' => _fs_text( 'How to upload and activate?' ),
|
402 |
-
'addon-successfully-purchased-message' => _fs_x( '%s Add-on was successfully purchased.',
|
403 |
-
'%s - product name, e.g. Facebook add-on was successfully...' ),
|
404 |
-
'addon-successfully-upgraded-message' => _fs_text( 'Your %s Add-on plan was successfully upgraded.' ),
|
405 |
-
'email-verified-message' => _fs_text( 'Your email has been successfully verified - you are AWESOME!' ),
|
406 |
-
'plan-upgraded-message' => _fs_text( 'Your plan was successfully upgraded.' ),
|
407 |
-
'plan-changed-to-x-message' => _fs_text( 'Your plan was successfully changed to %s.' ),
|
408 |
-
'license-expired-blocking-message' => _fs_text( 'Your license has expired. You can still continue using the free %s forever.' ),
|
409 |
-
'license-cancelled' => _fs_text( 'Your license has been cancelled. If you think it\'s a mistake, please contact support.' ),
|
410 |
-
'trial-started-message' => _fs_text( 'Your trial has been successfully started.' ),
|
411 |
-
'license-activated-message' => _fs_text( 'Your license was successfully activated.' ),
|
412 |
-
'no-active-license-message' => _fs_text( 'It looks like your site currently doesn\'t have an active license.' ),
|
413 |
-
'license-deactivation-message' => _fs_text( 'Your license was successfully deactivated, you are back to the %s plan.' ),
|
414 |
-
'license-deactivation-failed-message' => _fs_text( 'It looks like the license deactivation failed.' ),
|
415 |
-
'license-activation-failed-message' => _fs_text( 'It looks like the license could not be activated.' ),
|
416 |
-
'server-error-message' => _fs_text( 'Error received from the server:' ),
|
417 |
-
'trial-expired-message' => _fs_text( 'Your trial has expired. You can still continue using all our free features.' ),
|
418 |
-
'plan-x-downgraded-message' => _fs_text( 'Your plan was successfully downgraded. Your %s plan license will expire in %s.' ),
|
419 |
-
'plan-downgraded-failure-message' => _fs_text( 'Seems like we are having some temporary issue with your plan downgrade. Please try again in few minutes.' ),
|
420 |
-
'trial-cancel-no-trial-message' => _fs_text( 'It looks like you are not in trial mode anymore so there\'s nothing to cancel :)' ),
|
421 |
-
'trial-cancel-message' => _fs_text( 'Your %s free trial was successfully cancelled.' ),
|
422 |
-
'version-x-released' => _fs_x( 'Version %s was released.', '%s - numeric version number' ),
|
423 |
-
'please-download-x' => _fs_text( 'Please download %s.' ),
|
424 |
-
'latest-x-version' => _fs_x( 'the latest %s version here',
|
425 |
-
'%s - plan name, as the latest professional version here' ),
|
426 |
-
'trial-x-promotion-message' => _fs_text( 'How do you like %s so far? Test all our %s premium features with a %d-day free trial.' ),
|
427 |
-
'start-free-trial' => _fs_x( 'Start free trial', 'call to action' ),
|
428 |
-
'starting-trial' => _fs_text( 'Starting trial' ),
|
429 |
-
'please-wait' => _fs_text( 'Please wait' ),
|
430 |
-
'trial-cancel-failure-message' => _fs_text( 'Seems like we are having some temporary issue with your trial cancellation. Please try again in few minutes.' ),
|
431 |
-
'trial-utilized' => _fs_text( 'You already utilized a trial before.' ),
|
432 |
-
'in-trial-mode' => _fs_text( 'You are already running the %s in a trial mode.' ),
|
433 |
-
'trial-plan-x-not-exist' => _fs_text( 'Plan %s do not exist, therefore, can\'t start a trial.' ),
|
434 |
-
'plan-x-no-trial' => _fs_text( 'Plan %s does not support a trial period.' ),
|
435 |
-
'no-trials' => _fs_text( 'None of the %s\'s plans supports a trial period.' ),
|
436 |
-
'unexpected-api-error' => _fs_text( 'Unexpected API error. Please contact the %s\'s author with the following error.' ),
|
437 |
-
'no-commitment-for-x-days' => _fs_text( 'No commitment for %s days - cancel anytime!' ),
|
438 |
-
'license-expired-non-blocking-message' => _fs_text( 'Your license has expired. You can still continue using all the %s features, but you\'ll need to renew your license to continue getting updates and support.' ),
|
439 |
-
'could-not-activate-x' => _fs_text( 'Couldn\'t activate %s.' ),
|
440 |
-
'contact-us-with-error-message' => _fs_text( 'Please contact us with the following message:' ),
|
441 |
-
'plan-did-not-change-message' => _fs_text( 'It looks like you are still on the %s plan. If you did upgrade or change your plan, it\'s probably an issue on our side - sorry.' ),
|
442 |
-
'contact-us-here' => _fs_text( 'Please contact us here' ),
|
443 |
-
'plan-did-not-change-email-message' => _fs_text( 'I have upgraded my account but when I try to Sync the License, the plan remains %s.' ),
|
444 |
-
#endregion Admin Notices
|
445 |
-
#region Connectivity Issues
|
446 |
-
'connectivity-test-fails-message' => _fs_text( 'From unknown reason, the API connectivity test failed.' ),
|
447 |
-
'connectivity-test-maybe-temporary' => _fs_text( 'It\'s probably a temporary issue on our end. Just to be sure, with your permission, would it be o.k to run another connectivity test?' ),
|
448 |
-
'curl-missing-message' => _fs_text( 'We use PHP cURL library for the API calls, which is a very common library and usually installed and activated out of the box. Unfortunately, cURL is not activated (or disabled) on your server.' ),
|
449 |
-
'curl-disabled-methods' => _fs_text( 'Disabled method(s):' ),
|
450 |
-
'cloudflare-blocks-connection-message' => _fs_text( 'From unknown reason, CloudFlare, the firewall we use, blocks the connection.' ),
|
451 |
-
'x-requires-access-to-api' => _fs_x( '%s requires an access to our API.',
|
452 |
-
'as pluginX requires an access to our API' ),
|
453 |
-
'squid-blocks-connection-message' => _fs_text( 'It looks like your server is using Squid ACL (access control lists), which blocks the connection.' ),
|
454 |
-
'squid-no-clue-title' => _fs_text( 'I don\'t know what is Squid or ACL, help me!' ),
|
455 |
-
'squid-no-clue-desc' => _fs_text( 'We\'ll make sure to contact your hosting company and resolve the issue. You will get a follow-up email to %s once we have an update.' ),
|
456 |
-
'sysadmin-title' => _fs_text( 'I\'m a system administrator' ),
|
457 |
-
'squid-sysadmin-desc' => _fs_text( 'Great, please whitelist the following domains: %s. Once you are done, deactivate the %s and activate it again.' ),
|
458 |
-
'curl-missing-no-clue-title' => _fs_text( 'I don\'t know what is cURL or how to install it, help me!' ),
|
459 |
-
'curl-missing-no-clue-desc' => _fs_text( 'We\'ll make sure to contact your hosting company and resolve the issue. You will get a follow-up email to %s once we have an update.' ),
|
460 |
-
'curl-missing-sysadmin-desc' => _fs_text( 'Great, please install cURL and enable it in your php.ini file. In addition, search for the \'disable_functions\' directive in your php.ini file and remove any disabled methods starting with \'curl_\'. To make sure it was successfully activated, use \'phpinfo()\'. Once activated, deactivate the %s and reactivate it back again.' ),
|
461 |
-
'happy-to-resolve-issue-asap' => _fs_text( 'We are sure it\'s an issue on our side and more than happy to resolve it for you ASAP if you give us a chance.' ),
|
462 |
-
'contact-support-before-deactivation' => _fs_text( 'Sorry for the inconvenience and we are here to help if you give us a chance.' ),
|
463 |
-
'fix-issue-title' => _fs_text( 'Yes - I\'m giving you a chance to fix it' ),
|
464 |
-
'fix-issue-desc' => _fs_text( 'We will do our best to whitelist your server and resolve this issue ASAP. You will get a follow-up email to %s once we have an update.' ),
|
465 |
-
'install-previous-title' => _fs_text( 'Let\'s try your previous version' ),
|
466 |
-
'install-previous-desc' => _fs_text( 'Uninstall this version and install the previous one.' ),
|
467 |
-
'deactivate-plugin-title' => _fs_text( 'That\'s exhausting, please deactivate' ),
|
468 |
-
'deactivate-plugin-desc' => _fs_text( 'We feel your frustration and sincerely apologize for the inconvenience. Hope to see you again in the future.' ),
|
469 |
-
'fix-request-sent-message' => _fs_text( 'Thank for giving us the chance to fix it! A message was just sent to our technical staff. We will get back to you as soon as we have an update to %s. Appreciate your patience.' ),
|
470 |
-
'server-blocking-access' => _fs_x( 'Your server is blocking the access to Freemius\' API, which is crucial for %1$s synchronization. Please contact your host to whitelist %2$s',
|
471 |
-
'%1$s - plugin title, %2$s - API domain' ),
|
472 |
-
'wrong-authentication-param-message' => _fs_text( 'It seems like one of the authentication parameters is wrong. Update your Public Key, Secret Key & User ID, and try again.' ),
|
473 |
-
#endregion Connectivity Issues
|
474 |
-
#region Change Owner
|
475 |
-
'change-owner-request-sent-x' => _fs_text( 'Please check your mailbox, you should receive an email via %s to confirm the ownership change. From security reasons, you must confirm the change within the next 15 min. If you cannot find the email, please check your spam folder.' ),
|
476 |
-
'change-owner-request_owner-confirmed' => _fs_text( 'Thanks for confirming the ownership change. An email was just sent to %s for final approval.' ),
|
477 |
-
'change-owner-request_candidate-confirmed' => _fs_text( '%s is the new owner of the account.' ),
|
478 |
-
#endregion Change Owner
|
479 |
-
'addon-x-cannot-run-without-y' => _fs_x( '%s cannot run without %s.',
|
480 |
-
'addonX cannot run without pluginY' ),
|
481 |
-
'addon-x-cannot-run-without-parent' => _fs_x( '%s cannot run without the plugin.', 'addonX cannot run...' ),
|
482 |
-
'plugin-x-activation-message' => _fs_x( '%s activation was successfully completed.',
|
483 |
-
'pluginX activation was successfully...' ),
|
484 |
-
'features-and-pricing' => _fs_x( 'Features & Pricing', 'Plugin installer section title' ),
|
485 |
-
'free-addon-not-deployed' => _fs_text( 'Add-on must be deployed to WordPress.org or Freemius.' ),
|
486 |
-
'paid-addon-not-deployed' => _fs_text( 'Paid add-on must be deployed to Freemius.' ),
|
487 |
-
#--------------------------------------------------------------------------------
|
488 |
-
#region Add-On Licensing
|
489 |
-
#--------------------------------------------------------------------------------
|
490 |
-
'addon-no-license-message' => _fs_text( '%s is a premium only add-on. You have to purchase a license first before activating the plugin.' ),
|
491 |
-
'addon-trial-cancelled-message' => _fs_text( '%s free trial was successfully cancelled. Since the add-on is premium only it was automatically deactivated. If you like to use it in the future, you\'ll have to purchase a license.' ),
|
492 |
-
#endregion
|
493 |
-
#--------------------------------------------------------------------------------
|
494 |
-
#region Billing Cycles
|
495 |
-
#--------------------------------------------------------------------------------
|
496 |
-
'monthly' => _fs_x( 'Monthly', 'as every month' ),
|
497 |
-
'mo' => _fs_x( 'mo', 'as monthly period' ),
|
498 |
-
'annual' => _fs_x( 'Annual', 'as once a year' ),
|
499 |
-
'annually' => _fs_x( 'Annually', 'as once a year' ),
|
500 |
-
'once' => _fs_x( 'Once', 'as once a year' ),
|
501 |
-
'year' => _fs_x( 'year', 'as annual period' ),
|
502 |
-
'lifetime' => _fs_text( 'Lifetime' ),
|
503 |
-
'best' => _fs_x( 'Best', 'e.g. the best product' ),
|
504 |
-
'billed-x' => _fs_x( 'Billed %s', 'e.g. billed monthly' ),
|
505 |
-
'save-x' => _fs_x( 'Save %s', 'as a discount of $5 or 10%' ),
|
506 |
-
#endregion Billing Cycles
|
507 |
-
'view-details' => _fs_text( 'View details' ),
|
508 |
-
#--------------------------------------------------------------------------------
|
509 |
-
#region Trial
|
510 |
-
#--------------------------------------------------------------------------------
|
511 |
-
'approve-start-trial' => _fs_x( 'Approve & Start Trial', 'button label' ),
|
512 |
-
/* translators: %1$s: Number of trial days; %2$s: Plan name; */
|
513 |
-
'start-trial-prompt-header' => _fs_text( 'You are 1-click away from starting your %1$s-day free trial of the %2$s plan.' ),
|
514 |
-
/* translators: %s: Link to freemius.com */
|
515 |
-
'start-trial-prompt-message' => _fs_text( 'For compliance with the WordPress.org guidelines, before we start the trial we ask that you opt in with your user and non-sensitive site information, allowing the %s to periodically send data to %s to check for version updates and to validate your trial.' ),
|
516 |
-
|
517 |
-
#endregion
|
518 |
-
#--------------------------------------------------------------------------------
|
519 |
-
#region Billing Details
|
520 |
-
#--------------------------------------------------------------------------------
|
521 |
-
'business-name' => _fs_text( 'Business name' ),
|
522 |
-
'tax-vat-id' => _fs_text( 'Tax / VAT ID' ),
|
523 |
-
'address-line-n' => _fs_text( 'Address Line %d' ),
|
524 |
-
'country' => _fs_text( 'Country' ),
|
525 |
-
'select-country' => _fs_text( 'Select Country' ),
|
526 |
-
'city' => _fs_text( 'City' ),
|
527 |
-
'town' => _fs_text( 'Town' ),
|
528 |
-
'state' => _fs_text( 'State' ),
|
529 |
-
'province' => _fs_text( 'Province' ),
|
530 |
-
'zip-postal-code' => _fs_text( 'ZIP / Postal Code' ),
|
531 |
-
#endregion
|
532 |
-
#--------------------------------------------------------------------------------
|
533 |
-
#region Module Installation
|
534 |
-
#--------------------------------------------------------------------------------
|
535 |
-
'installing-plugin-x' => _fs_text( 'Installing plugin: %s' ),
|
536 |
-
'auto-installation' => _fs_text( 'Automatic Installation' ),
|
537 |
-
/* translators: %s: Number of seconds */
|
538 |
-
'x-sec' => _fs_text( '%s sec' ),
|
539 |
-
'installing-in-n' => _fs_text( 'An automated download and installation of %s (paid version) from %s will start in %s. If you would like to do it manually - click the cancellation button now.' ),
|
540 |
-
'installing-module-x' => _fs_text( 'The installation process has started and may take a few minutes to complete. Please wait until it is done - do not refresh this page.' ),
|
541 |
-
'cancel-installation' => _fs_text( 'Cancel Installation' ),
|
542 |
-
'module-package-rename-failure' => _fs_text( 'The remote plugin package does not contain a folder with the desired slug and renaming did not work.' ),
|
543 |
-
'auto-install-error-invalid-id' => _fs_text( 'Invalid module ID.' ),
|
544 |
-
'auto-install-error-not-opted-in' => _fs_text( 'Auto installation only works for opted-in users.' ),
|
545 |
-
'auto-install-error-premium-activated' => _fs_text( 'Premium version already active.' ),
|
546 |
-
'auto-install-error-premium-addon-activated' => _fs_text( 'Premium add-on version already installed.' ),
|
547 |
-
'auto-install-error-invalid-license' => _fs_text( 'You do not have a valid license to access the premium version.' ),
|
548 |
-
'auto-install-error-serviceware' => _fs_text( 'Plugin is a "Serviceware" which means it does not have a premium code version.' ),
|
549 |
-
#endregion
|
550 |
-
|
551 |
-
/* translators: %s: Page name */
|
552 |
-
'secure-x-page-header' => _fs_text( 'Secure HTTPS %s page, running from an external domain' ),
|
553 |
-
'pci-compliant' => _fs_text( 'PCI compliant' ),
|
554 |
-
'view-paid-features' => _fs_text( 'View paid features' ),
|
555 |
-
);
|
556 |
-
|
557 |
-
/**
|
558 |
-
* Localization of the strings in the plugin/theme info dialog box.
|
559 |
-
*
|
560 |
-
* $fs_module_info_text should ONLY include strings that are not located in $fs_text.
|
561 |
-
*
|
562 |
-
* @author Vova Feldman (@svovaf)
|
563 |
-
* @since 1.2.2
|
564 |
-
*/
|
565 |
-
global $fs_module_info_text;
|
566 |
-
|
567 |
-
$fs_module_info_text = array(
|
568 |
-
'description' => _fs_x( 'Description', 'Plugin installer section title' ),
|
569 |
-
'installation' => _fs_x( 'Installation', 'Plugin installer section title' ),
|
570 |
-
'faq' => _fs_x( 'FAQ', 'Plugin installer section title' ),
|
571 |
-
'changelog' => _fs_x( 'Changelog', 'Plugin installer section title' ),
|
572 |
-
'reviews' => _fs_x( 'Reviews', 'Plugin installer section title' ),
|
573 |
-
'other_notes' => _fs_x( 'Other Notes', 'Plugin installer section title' ),
|
574 |
-
/* translators: %s: 1 or One */
|
575 |
-
'x-star' => _fs_text( '%s star' ),
|
576 |
-
/* translators: %s: Number larger than 1 */
|
577 |
-
'x-stars' => _fs_text( '%s stars' ),
|
578 |
-
/* translators: %s: 1 or One */
|
579 |
-
'x-rating' => _fs_text( '%s rating' ),
|
580 |
-
/* translators: %s: Number larger than 1 */
|
581 |
-
'x-ratings' => _fs_text( '%s ratings' ),
|
582 |
-
/* translators: %s: 1 or One (Number of times downloaded) */
|
583 |
-
'x-time' => _fs_text( '%s time' ),
|
584 |
-
/* translators: %s: Number of times downloaded */
|
585 |
-
'x-times' => _fs_text( '%s times' ),
|
586 |
-
/* translators: %s: # of stars (e.g. 5 stars) */
|
587 |
-
'click-to-reviews' => _fs_text( 'Click to see reviews that provided a rating of %s' ),
|
588 |
-
'last-updated:' => _fs_text( 'Last Updated' ),
|
589 |
-
'requires-wordpress-version:' => _fs_text( 'Requires WordPress Version:' ),
|
590 |
-
'author:' => _fs_x( 'Author:', 'as the plugin author' ),
|
591 |
-
'compatible-up-to:' => _fs_text( 'Compatible up to:' ),
|
592 |
-
'downloaded:' => _fs_text( 'Downloaded:' ),
|
593 |
-
'wp-org-plugin-page' => _fs_text( 'WordPress.org Plugin Page' ),
|
594 |
-
'plugin-homepage' => _fs_text( 'Plugin Homepage' ),
|
595 |
-
'donate-to-plugin' => _fs_text( 'Donate to this plugin' ),
|
596 |
-
'average-rating' => _fs_text( 'Average Rating' ),
|
597 |
-
'based-on-x' => _fs_text( 'based on %s' ),
|
598 |
-
'warning:' => _fs_text( 'Warning:' ),
|
599 |
-
'contributors' => _fs_text( 'Contributors' ),
|
600 |
-
'plugin-install' => _fs_text( 'Plugin Install' ),
|
601 |
-
'not-tested-warning' => _fs_text( 'This plugin has not been tested with your current version of WordPress.' ),
|
602 |
-
'not-compatible-warning' => _fs_text( 'This plugin has not been marked as compatible with your version of WordPress.' ),
|
603 |
-
'newer-installed' => _fs_text( 'Newer Version (%s) Installed' ),
|
604 |
-
'latest-installed' => _fs_text( 'Latest Version Installed' ),
|
605 |
-
);
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package Freemius
|
4 |
+
* @copyright Copyright (c) 2015, Freemius, Inc.
|
5 |
+
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
|
6 |
+
* @since 1.1.4
|
7 |
+
*
|
8 |
+
* @deprecated This file is no longer in use. It's still in the project for backward compatibility.
|
9 |
+
*/
|
10 |
+
|
11 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
12 |
+
exit;
|
13 |
+
}
|
14 |
+
|
15 |
+
require_once dirname( __FILE__ ) . '/l10n.php';
|
16 |
+
|
17 |
+
/**
|
18 |
+
* All strings can now be overridden.
|
19 |
+
*
|
20 |
+
* For example, if we want to override:
|
21 |
+
* 'you-are-step-away' => 'You are just one step away - %s',
|
22 |
+
*
|
23 |
+
* We can use the filter:
|
24 |
+
* fs_override_i18n( array(
|
25 |
+
* 'opt-in-connect' => __( "Yes - I'm in!", '{your-text_domain}' ),
|
26 |
+
* 'skip' => __( 'Not today', '{your-text_domain}' ),
|
27 |
+
* ), '{plugin_slug}' );
|
28 |
+
*
|
29 |
+
* Or with the Freemius instance:
|
30 |
+
*
|
31 |
+
* my_freemius->override_i18n( array(
|
32 |
+
* 'opt-in-connect' => __( "Yes - I'm in!", '{your-text_domain}' ),
|
33 |
+
* 'skip' => __( 'Not today', '{your-text_domain}' ),
|
34 |
+
* ) );
|
35 |
+
*/
|
36 |
+
global $fs_text;
|
37 |
+
|
38 |
+
$fs_text = array(
|
39 |
+
'account' => _fs_text( 'Account' ),
|
40 |
+
'addon' => _fs_text( 'Add-On' ),
|
41 |
+
'contact-us' => _fs_text( 'Contact Us' ),
|
42 |
+
'contact-support' => _fs_text( 'Contact Support' ),
|
43 |
+
'change-ownership' => _fs_text( 'Change Ownership' ),
|
44 |
+
'support' => _fs_text( 'Support' ),
|
45 |
+
'support-forum' => _fs_text( 'Support Forum' ),
|
46 |
+
'add-ons' => _fs_text( 'Add-Ons' ),
|
47 |
+
'upgrade' => _fs_x( 'Upgrade', 'verb' ),
|
48 |
+
'awesome' => _fs_text( 'Awesome' ),
|
49 |
+
'pricing' => _fs_x( 'Pricing', 'noun' ),
|
50 |
+
'price' => _fs_x( 'Price', 'noun' ),
|
51 |
+
'unlimited-updates' => _fs_text( 'Unlimited Updates' ),
|
52 |
+
'downgrade' => _fs_x( 'Downgrade', 'verb' ),
|
53 |
+
'cancel-subscription' => _fs_x( 'Cancel Subscription', 'verb' ),
|
54 |
+
'cancel-trial' => _fs_text( 'Cancel Trial' ),
|
55 |
+
'free-trial' => _fs_text( 'Free Trial' ),
|
56 |
+
'start-free-x' => _fs_text( 'Start my free %s' ),
|
57 |
+
'no-commitment-x' => _fs_text( 'No commitment for %s - cancel anytime' ),
|
58 |
+
'after-x-pay-as-little-y' => _fs_text( 'After your free %s, pay as little as %s' ),
|
59 |
+
'details' => _fs_text( 'Details' ),
|
60 |
+
'account-details' => _fs_text( 'Account Details' ),
|
61 |
+
'delete' => _fs_x( 'Delete', 'verb' ),
|
62 |
+
'show' => _fs_x( 'Show', 'verb' ),
|
63 |
+
'hide' => _fs_x( 'Hide', 'verb' ),
|
64 |
+
'edit' => _fs_x( 'Edit', 'verb' ),
|
65 |
+
'update' => _fs_x( 'Update', 'verb' ),
|
66 |
+
'date' => _fs_text( 'Date' ),
|
67 |
+
'amount' => _fs_text( 'Amount' ),
|
68 |
+
'invoice' => _fs_text( 'Invoice' ),
|
69 |
+
'billing' => _fs_text( 'Billing' ),
|
70 |
+
'payments' => _fs_text( 'Payments' ),
|
71 |
+
'delete-account' => _fs_text( 'Delete Account' ),
|
72 |
+
'dismiss' => _fs_x( 'Dismiss', 'as close a window' ),
|
73 |
+
'plan' => _fs_x( 'Plan', 'as product pricing plan' ),
|
74 |
+
'change-plan' => _fs_text( 'Change Plan' ),
|
75 |
+
'download-x-version' => _fs_x( 'Download %s Version', 'as download professional version' ),
|
76 |
+
'download-x-version-now' => _fs_x( 'Download %s version now', 'as download professional version now' ),
|
77 |
+
'download-latest' => _fs_x( 'Download Latest', 'as download latest version' ),
|
78 |
+
'you-have-x-license' => _fs_x( 'You have a %s license.', 'E.g. you have a professional license.' ),
|
79 |
+
'new' => _fs_text( 'New' ),
|
80 |
+
'free' => _fs_text( 'Free' ),
|
81 |
+
'trial' => _fs_x( 'Trial', 'as trial plan' ),
|
82 |
+
'start-trial' => _fs_x( 'Start Trial', 'as starting a trial plan' ),
|
83 |
+
'purchase' => _fs_x( 'Purchase', 'verb' ),
|
84 |
+
'purchase-license' => _fs_text( 'Purchase License' ),
|
85 |
+
'buy' => _fs_x( 'Buy', 'verb' ),
|
86 |
+
'buy-license' => _fs_text( 'Buy License' ),
|
87 |
+
'license-single-site' => _fs_text( 'Single Site License' ),
|
88 |
+
'license-unlimited' => _fs_text( 'Unlimited Licenses' ),
|
89 |
+
'license-x-sites' => _fs_text( 'Up to %s Sites' ),
|
90 |
+
'renew-license-now' => _fs_text( '%sRenew your license now%s to access version %s security & feature updates, and support.' ),
|
91 |
+
'ask-for-upgrade-email-address' => _fs_text( "Enter the email address you've used for the upgrade below and we will resend you the license key." ),
|
92 |
+
'x-plan' => _fs_x( '%s Plan', 'e.g. Professional Plan' ),
|
93 |
+
'you-are-step-away' => _fs_text( 'You are just one step away - %s' ),
|
94 |
+
'activate-x-now' => _fs_x( 'Complete "%s" Activation Now',
|
95 |
+
'%s - plugin name. As complete "Jetpack" activation now' ),
|
96 |
+
'few-plugin-tweaks' => _fs_text( 'We made a few tweaks to the %s, %s' ),
|
97 |
+
'optin-x-now' => _fs_text( 'Opt in to make "%s" better!' ),
|
98 |
+
'error' => _fs_text( 'Error' ),
|
99 |
+
'failed-finding-main-path' => _fs_text( 'Freemius SDK couldn\'t find the plugin\'s main file. Please contact sdk@freemius.com with the current error.' ),
|
100 |
+
'learn-more' => _fs_text( 'Learn more' ),
|
101 |
+
'license_not_whitelabeled' => _fs_text( "Is this your client's site? %s if you wish to hide sensitive info like your billing address and invoices from the WP Admin."),
|
102 |
+
'license_whitelabeled' => _fs_text( 'Your %s license was flagged as white-labeled to hide sensitive information from the WP Admin (e.g. your billing address and invoices). If you ever wish to revert it back, you can easily do it through your %s. If this was a mistake you can also %s.'),
|
103 |
+
|
104 |
+
#region Affiliation
|
105 |
+
'affiliation' => _fs_text( 'Affiliation' ),
|
106 |
+
'affiliate' => _fs_text( 'Affiliate' ),
|
107 |
+
'affiliate-tracking' => _fs_text( '%s tracking cookie after the first visit to maximize earnings potential.' ),
|
108 |
+
'renewals-commission' => _fs_text( 'Get commission for automated subscription renewals.' ),
|
109 |
+
'affiliate-application-accepted' => _fs_text( "Your affiliate application for %s has been accepted! Log in to your affiliate area at: %s." ),
|
110 |
+
'affiliate-application-thank-you' => _fs_text( "Thank you for applying for our affiliate program, we'll review your details during the next 14 days and will get back to you with further information." ),
|
111 |
+
'affiliate-application-rejected' => _fs_text( "Thank you for applying for our affiliate program, unfortunately, we've decided at this point to reject your application. Please try again in 30 days." ),
|
112 |
+
'affiliate-account-suspended' => _fs_text( 'Your affiliation account was temporarily suspended.' ),
|
113 |
+
'affiliate-account-blocked' => _fs_text( 'Due to violation of our affiliation terms, we decided to temporarily block your affiliation account. If you have any questions, please contact support.' ),
|
114 |
+
'become-an-ambassador' => _fs_text( 'Like the %s? Become our ambassador and earn cash ;-)' ),
|
115 |
+
'become-an-ambassador-admin-notice' => _fs_text( 'Hey there, did you know that %s has an affiliate program? If you like the %s you can become our ambassador and earn some cash!' ),
|
116 |
+
'refer-new-customers' => _fs_text( 'Refer new customers to our %s and earn %s commission on each successful sale you refer!' ),
|
117 |
+
'program-summary' => _fs_text( 'Program Summary' ),
|
118 |
+
'commission-on-new-license-purchase' => _fs_text( '%s commission when a customer purchases a new license.' ),
|
119 |
+
'unlimited-commissions' => _fs_text( 'Unlimited commissions.' ),
|
120 |
+
'minimum-payout-amount' => _fs_text( '%s minimum payout amount.' ),
|
121 |
+
'payouts-unit-and-processing' => _fs_text( 'Payouts are in USD and processed monthly via PayPal.' ),
|
122 |
+
'commission-payment' => _fs_text( 'As we reserve 30 days for potential refunds, we only pay commissions that are older than 30 days.' ),
|
123 |
+
'become-an-affiliate' => _fs_text( 'Become an affiliate' ),
|
124 |
+
'apply-to-become-an-affiliate' => _fs_text( 'Apply to become an affiliate' ),
|
125 |
+
'full-name' => _fs_text( 'Full name' ),
|
126 |
+
'paypal-account-email-address' => _fs_text( 'PayPal account email address' ),
|
127 |
+
'promotion-methods' => _fs_text( 'Promotion methods' ),
|
128 |
+
'social-media' => _fs_text( 'Social media (Facebook, Twitter, etc.)' ),
|
129 |
+
'mobile-apps' => _fs_text( 'Mobile apps' ),
|
130 |
+
'statistics-information-field-label' => _fs_text( 'Website, email, and social media statistics (optional)' ),
|
131 |
+
'statistics-information-field-desc' => _fs_text( 'Please feel free to provide any relevant website or social media statistics, e.g. monthly unique site visits, number of email subscribers, followers, etc. (we will keep this information confidential).' ),
|
132 |
+
'promotion-method-desc-field-label' => _fs_text( 'How will you promote us?' ),
|
133 |
+
'promotion-method-desc-field-desc' => _fs_text( 'Please provide details on how you intend to promote %s (please be as specific as possible).' ),
|
134 |
+
'domain-field-label' => _fs_text( 'Where are you going to promote the %s?' ),
|
135 |
+
'domain-field-desc' => _fs_text( 'Enter the domain of your website or other websites from where you plan to promote the %s.' ),
|
136 |
+
'extra-domain-fields-label' => _fs_text( 'Extra Domains' ),
|
137 |
+
'extra-domain-fields-desc' => _fs_text( 'Extra domains where you will be marketing the product from.' ),
|
138 |
+
'add-another-domain' => _fs_text( 'Add another domain' ),
|
139 |
+
'remove' => _fs_x( 'Remove', 'Remove domain' ),
|
140 |
+
'email-address-is-required' => _fs_text( 'Email address is required.' ),
|
141 |
+
'domain-is-required' => _fs_text( 'Domain is required.' ),
|
142 |
+
'invalid-domain' => _fs_text( 'Invalid domain' ),
|
143 |
+
'paypal-email-address-is-required' => _fs_text( 'PayPal email address is required.' ),
|
144 |
+
'processing' => _fs_text( 'Processing...' ),
|
145 |
+
'non-expiring' => _fs_text( 'Non-expiring' ),
|
146 |
+
'account-is-pending-activation' => _fs_text( 'Account is pending activation.' ),
|
147 |
+
#endregion Affiliation
|
148 |
+
|
149 |
+
#region Account
|
150 |
+
'expiration' => _fs_x( 'Expiration', 'as expiration date' ),
|
151 |
+
'license' => _fs_x( 'License', 'as software license' ),
|
152 |
+
'not-verified' => _fs_text( 'not verified' ),
|
153 |
+
'verify-email' => _fs_text( 'Verify Email' ),
|
154 |
+
'expires-in' => _fs_x( 'Expires in %s', 'e.g. expires in 2 months' ),
|
155 |
+
'renews-in' => _fs_x( 'Auto renews in %s', 'e.g. auto renews in 2 months' ),
|
156 |
+
'no-expiration' => _fs_text( 'No expiration' ),
|
157 |
+
'expired' => _fs_text( 'Expired' ),
|
158 |
+
'cancelled' => _fs_text( 'Cancelled' ),
|
159 |
+
'in-x' => _fs_x( 'In %s', 'e.g. In 2 hours' ),
|
160 |
+
'x-ago' => _fs_x( '%s ago', 'e.g. 2 min ago' ),
|
161 |
+
/* translators: %s: Version number (e.g. 4.6 or higher) */
|
162 |
+
'x-or-higher' => _fs_text( '%s or higher' ),
|
163 |
+
'version' => _fs_x( 'Version', 'as plugin version' ),
|
164 |
+
'name' => _fs_text( 'Name' ),
|
165 |
+
'email' => _fs_text( 'Email' ),
|
166 |
+
'email-address' => _fs_text( 'Email address' ),
|
167 |
+
'verified' => _fs_text( 'Verified' ),
|
168 |
+
'module' => _fs_text( 'Module' ),
|
169 |
+
'module-type' => _fs_text( 'Module Type' ),
|
170 |
+
'plugin' => _fs_text( 'Plugin' ),
|
171 |
+
'plugins' => _fs_text( 'Plugins' ),
|
172 |
+
'theme' => _fs_text( 'Theme' ),
|
173 |
+
'themes' => _fs_text( 'Themes' ),
|
174 |
+
'path' => _fs_x( 'Path', 'as file/folder path' ),
|
175 |
+
'title' => _fs_text( 'Title' ),
|
176 |
+
'free-version' => _fs_text( 'Free version' ),
|
177 |
+
'premium-version' => _fs_text( 'Premium version' ),
|
178 |
+
'slug' => _fs_x( 'Slug', 'as WP plugin slug' ),
|
179 |
+
'id' => _fs_text( 'ID' ),
|
180 |
+
'users' => _fs_text( 'Users' ),
|
181 |
+
'module-installs' => _fs_text( '%s Installs' ),
|
182 |
+
'sites' => _fs_x( 'Sites', 'like websites' ),
|
183 |
+
'user-id' => _fs_text( 'User ID' ),
|
184 |
+
'site-id' => _fs_text( 'Site ID' ),
|
185 |
+
'public-key' => _fs_text( 'Public Key' ),
|
186 |
+
'secret-key' => _fs_text( 'Secret Key' ),
|
187 |
+
'no-secret' => _fs_x( 'No Secret', 'as secret encryption key missing' ),
|
188 |
+
'no-id' => _fs_text( 'No ID' ),
|
189 |
+
'sync-license' => _fs_x( 'Sync License', 'as synchronize license' ),
|
190 |
+
'sync' => _fs_x( 'Sync', 'as synchronize' ),
|
191 |
+
'activate-license' => _fs_text( 'Activate License' ),
|
192 |
+
'activate-free-version' => _fs_text( 'Activate Free Version' ),
|
193 |
+
'activate-license-message' => _fs_text( 'Please enter the license key that you received in the email right after the purchase:' ),
|
194 |
+
'activating-license' => _fs_text( 'Activating license...' ),
|
195 |
+
'change-license' => _fs_text( 'Change License' ),
|
196 |
+
'update-license' => _fs_text( 'Update License' ),
|
197 |
+
'deactivate-license' => _fs_text( 'Deactivate License' ),
|
198 |
+
'activate' => _fs_text( 'Activate' ),
|
199 |
+
'deactivate' => _fs_text( 'Deactivate' ),
|
200 |
+
'skip-deactivate' => _fs_text( 'Skip & Deactivate' ),
|
201 |
+
'skip-and-x' => _fs_text( 'Skip & %s' ),
|
202 |
+
'no-deactivate' => _fs_text( 'No - just deactivate' ),
|
203 |
+
'yes-do-your-thing' => _fs_text( 'Yes - do your thing' ),
|
204 |
+
'active' => _fs_x( 'Active', 'active mode' ),
|
205 |
+
'is-active' => _fs_x( 'Is Active', 'is active mode?' ),
|
206 |
+
'install-now' => _fs_text( 'Install Now' ),
|
207 |
+
'install-update-now' => _fs_text( 'Install Update Now' ),
|
208 |
+
'more-information-about-x' => _fs_text( 'More information about %s' ),
|
209 |
+
'localhost' => _fs_text( 'Localhost' ),
|
210 |
+
'activate-x-plan' => _fs_x( 'Activate %s Plan', 'as activate Professional plan' ),
|
211 |
+
'x-left' => _fs_x( '%s left', 'as 5 licenses left' ),
|
212 |
+
'last-license' => _fs_text( 'Last license' ),
|
213 |
+
'what-is-your-x' => _fs_text( 'What is your %s?' ),
|
214 |
+
'activate-this-addon' => _fs_text( 'Activate this add-on' ),
|
215 |
+
'deactivate-license-confirm' => _fs_text( 'Deactivating your license will block all premium features, but will enable you to activate the license on another site. Are you sure you want to proceed?' ),
|
216 |
+
'delete-account-x-confirm' => _fs_text( 'Deleting the account will automatically deactivate your %s plan license so you can use it on other sites. If you want to terminate the recurring payments as well, click the "Cancel" button, and first "Downgrade" your account. Are you sure you would like to continue with the deletion?' ),
|
217 |
+
'delete-account-confirm' => _fs_text( 'Deletion is not temporary. Only delete if you no longer want to use this %s anymore. Are you sure you would like to continue with the deletion?' ),
|
218 |
+
'downgrade-x-confirm' => _fs_text( 'Downgrading your plan will immediately stop all future recurring payments and your %s plan license will expire in %s.' ),
|
219 |
+
'cancel-trial-confirm' => _fs_text( 'Cancelling the trial will immediately block access to all premium features. Are you sure?' ),
|
220 |
+
'after-downgrade-non-blocking' => _fs_text( 'You can still enjoy all %s features but you will not have access to %s security & feature updates, nor support.' ),
|
221 |
+
'after-downgrade-blocking' => _fs_text( 'Once your license expires you can still use the Free version but you will NOT have access to the %s features.' ),
|
222 |
+
'proceed-confirmation' => _fs_text( 'Are you sure you want to proceed?' ),
|
223 |
+
#endregion Account
|
224 |
+
|
225 |
+
'add-ons-for-x' => _fs_text( 'Add Ons for %s' ),
|
226 |
+
'add-ons-missing' => _fs_text( 'We could\'nt load the add-ons list. It\'s probably an issue on our side, please try to come back in few minutes.' ),
|
227 |
+
#region Plugin Deactivation
|
228 |
+
'anonymous-feedback' => _fs_text( 'Anonymous feedback' ),
|
229 |
+
'quick-feedback' => _fs_text( 'Quick feedback' ),
|
230 |
+
'deactivation-share-reason' => _fs_text( 'If you have a moment, please let us know why you are %s' ),
|
231 |
+
'deactivating' => _fs_text( 'deactivating' ),
|
232 |
+
'deactivation' => _fs_text( 'Deactivation' ),
|
233 |
+
'theme-switch' => _fs_text( 'Theme Switch' ),
|
234 |
+
'switching' => _fs_text( 'switching' ),
|
235 |
+
'switch' => _fs_text( 'Switch' ),
|
236 |
+
'activate-x' => _fs_text( 'Activate %s' ),
|
237 |
+
'deactivation-modal-button-confirm' => _fs_text( 'Yes - %s' ),
|
238 |
+
'deactivation-modal-button-submit' => _fs_text( 'Submit & %s' ),
|
239 |
+
'cancel' => _fs_text( 'Cancel' ),
|
240 |
+
'reason-no-longer-needed' => _fs_text( 'I no longer need the %s' ),
|
241 |
+
'reason-found-a-better-plugin' => _fs_text( 'I found a better %s' ),
|
242 |
+
'reason-needed-for-a-short-period' => _fs_text( 'I only needed the %s for a short period' ),
|
243 |
+
'reason-broke-my-site' => _fs_text( 'The %s broke my site' ),
|
244 |
+
'reason-suddenly-stopped-working' => _fs_text( 'The %s suddenly stopped working' ),
|
245 |
+
'reason-cant-pay-anymore' => _fs_text( "I can't pay for it anymore" ),
|
246 |
+
'reason-temporary-deactivation' => _fs_text( "It's a temporary deactivation. I'm just debugging an issue." ),
|
247 |
+
'reason-temporary-x' => _fs_text( "It's a temporary %s. I'm just debugging an issue." ),
|
248 |
+
'reason-other' => _fs_x( 'Other',
|
249 |
+
'the text of the "other" reason for deactivating the module that is shown in the modal box.' ),
|
250 |
+
'ask-for-reason-message' => _fs_text( 'Kindly tell us the reason so we can improve.' ),
|
251 |
+
'placeholder-plugin-name' => _fs_text( "What's the %s's name?" ),
|
252 |
+
'placeholder-comfortable-price' => _fs_text( 'What price would you feel comfortable paying?' ),
|
253 |
+
'reason-couldnt-make-it-work' => _fs_text( "I couldn't understand how to make it work" ),
|
254 |
+
'reason-great-but-need-specific-feature' => _fs_text( "The %s is great, but I need specific feature that you don't support" ),
|
255 |
+
'reason-not-working' => _fs_text( 'The %s is not working' ),
|
256 |
+
'reason-not-what-i-was-looking-for' => _fs_text( "It's not what I was looking for" ),
|
257 |
+
'reason-didnt-work-as-expected' => _fs_text( "The %s didn't work as expected" ),
|
258 |
+
'placeholder-feature' => _fs_text( 'What feature?' ),
|
259 |
+
'placeholder-share-what-didnt-work' => _fs_text( "Kindly share what didn't work so we can fix it for future users..." ),
|
260 |
+
'placeholder-what-youve-been-looking-for' => _fs_text( "What you've been looking for?" ),
|
261 |
+
'placeholder-what-did-you-expect' => _fs_text( "What did you expect?" ),
|
262 |
+
'reason-didnt-work' => _fs_text( "The %s didn't work" ),
|
263 |
+
'reason-dont-like-to-share-my-information' => _fs_text( "I don't like to share my information with you" ),
|
264 |
+
'dont-have-to-share-any-data' => _fs_text( "You might have missed it, but you don't have to share any data and can just %s the opt-in." ),
|
265 |
+
#endregion Plugin Deactivation
|
266 |
+
|
267 |
+
#region Connect
|
268 |
+
'hey-x' => _fs_x( 'Hey %s,', 'greeting' ),
|
269 |
+
'thanks-x' => _fs_x( 'Thanks %s!', 'a greeting. E.g. Thanks John!' ),
|
270 |
+
'connect-message' => _fs_text( 'Never miss an important update - opt in to our security and feature updates notifications, and non-sensitive diagnostic tracking with %4$s.' ),
|
271 |
+
'connect-message_on-update' => _fs_text( 'Please help us improve %1$s! If you opt in, some data about your usage of %1$s will be sent to %4$s. If you skip this, that\'s okay! %1$s will still work just fine.' ),
|
272 |
+
'pending-activation-message' => _fs_text( 'You should receive an activation email for %s to your mailbox at %s. Please make sure you click the activation button in that email to %s.' ),
|
273 |
+
'complete-the-install' => _fs_text( 'complete the install' ),
|
274 |
+
'start-the-trial' => _fs_text( 'start the trial' ),
|
275 |
+
'thanks-for-purchasing' => _fs_text( 'Thanks for purchasing %s! To get started, please enter your license key:' ),
|
276 |
+
'license-sync-disclaimer' => _fs_text( 'The %1$s will be periodically sending data to %2$s to check for security and feature updates, and verify the validity of your license.' ),
|
277 |
+
'what-permissions' => _fs_text( 'What permissions are being granted?' ),
|
278 |
+
'permissions-profile' => _fs_text( 'Your Profile Overview' ),
|
279 |
+
'permissions-profile_desc' => _fs_text( 'Name and email address' ),
|
280 |
+
'permissions-site' => _fs_text( 'Your Site Overview' ),
|
281 |
+
'permissions-site_desc' => _fs_text( 'Site URL, WP version, PHP info, plugins & themes' ),
|
282 |
+
'permissions-events' => _fs_text( 'Current %s Events' ),
|
283 |
+
'permissions-events_desc' => _fs_text( 'Activation, deactivation and uninstall' ),
|
284 |
+
'permissions-plugins_themes' => _fs_text( 'Plugins & Themes' ),
|
285 |
+
'permissions-plugins_themes_desc' => _fs_text( 'Titles, versions and state.' ),
|
286 |
+
'permissions-admin-notices' => _fs_text( 'Admin Notices' ),
|
287 |
+
'permissions-newsletter' => _fs_text( 'Newsletter' ),
|
288 |
+
'permissions-newsletter_desc' => _fs_text( 'Updates, announcements, marketing, no spam' ),
|
289 |
+
'privacy-policy' => _fs_text( 'Privacy Policy' ),
|
290 |
+
'tos' => _fs_text( 'Terms of Service' ),
|
291 |
+
'activating' => _fs_x( 'Activating', 'as activating plugin' ),
|
292 |
+
'sending-email' => _fs_x( 'Sending email', 'as in the process of sending an email' ),
|
293 |
+
'opt-in-connect' => _fs_x( 'Allow & Continue', 'button label' ),
|
294 |
+
'agree-activate-license' => _fs_x( 'Agree & Activate License', 'button label' ),
|
295 |
+
'skip' => _fs_x( 'Skip', 'verb' ),
|
296 |
+
'click-here-to-use-plugin-anonymously' => _fs_text( 'Click here to use the plugin anonymously' ),
|
297 |
+
'resend-activation-email' => _fs_text( 'Re-send activation email' ),
|
298 |
+
'license-key' => _fs_text( 'License key' ),
|
299 |
+
'send-license-key' => _fs_text( 'Send License Key' ),
|
300 |
+
'sending-license-key' => _fs_text( 'Sending license key' ),
|
301 |
+
'have-license-key' => _fs_text( 'Have a license key?' ),
|
302 |
+
'dont-have-license-key' => _fs_text( 'Don\'t have a license key?' ),
|
303 |
+
'cant-find-license-key' => _fs_text( "Can't find your license key?" ),
|
304 |
+
'email-not-found' => _fs_text( "We couldn't find your email address in the system, are you sure it's the right address?" ),
|
305 |
+
'no-active-licenses' => _fs_text( "We can't see any active licenses associated with that email address, are you sure it's the right address?" ),
|
306 |
+
'opt-in' => _fs_text( 'Opt In' ),
|
307 |
+
'opt-out' => _fs_text( 'Opt Out' ),
|
308 |
+
'opt-out-cancel' => _fs_text( 'On second thought - I want to continue helping' ),
|
309 |
+
'opting-out' => _fs_text( 'Opting out...' ),
|
310 |
+
'opting-in' => _fs_text( 'Opting in...' ),
|
311 |
+
'opt-out-message-appreciation' => _fs_text( 'We appreciate your help in making the %s better by letting us track some usage data.' ),
|
312 |
+
'opt-out-message-usage-tracking' => _fs_text( "Usage tracking is done in the name of making %s better. Making a better user experience, prioritizing new features, and more good things. We'd really appreciate if you'll reconsider letting us continue with the tracking." ),
|
313 |
+
'opt-out-message-clicking-opt-out' => _fs_text( 'By clicking "Opt Out", we will no longer be sending any data from %s to %s.' ),
|
314 |
+
'apply-on-all-sites-in-the-network' => _fs_text( 'Apply on all sites in the network.' ),
|
315 |
+
'delegate-to-site-admins' => _fs_text( 'Delegate to Site Admins' ),
|
316 |
+
'delegate-to-site-admins-and-continue' => _fs_text( 'Delegate to Site Admins & Continue' ),
|
317 |
+
'continue' => _fs_text( 'Continue' ),
|
318 |
+
'allow' => _fs_text( 'allow' ),
|
319 |
+
'delegate' => _fs_text( 'delegate' ),
|
320 |
+
#endregion Connect
|
321 |
+
|
322 |
+
#region Screenshots
|
323 |
+
'screenshots' => _fs_text( 'Screenshots' ),
|
324 |
+
'view-full-size-x' => _fs_text( 'Click to view full-size screenshot %d' ),
|
325 |
+
#endregion Screenshots
|
326 |
+
|
327 |
+
#region Debug
|
328 |
+
'freemius-debug' => _fs_text( 'Freemius Debug' ),
|
329 |
+
'on' => _fs_x( 'On', 'as turned on' ),
|
330 |
+
'off' => _fs_x( 'Off', 'as turned off' ),
|
331 |
+
'debugging' => _fs_x( 'Debugging', 'as code debugging' ),
|
332 |
+
'freemius-state' => _fs_text( 'Freemius State' ),
|
333 |
+
'connected' => _fs_x( 'Connected', 'as connection was successful' ),
|
334 |
+
'blocked' => _fs_x( 'Blocked', 'as connection blocked' ),
|
335 |
+
'api' => _fs_x( 'API', 'as application program interface' ),
|
336 |
+
'sdk' => _fs_x( 'SDK', 'as software development kit versions' ),
|
337 |
+
'sdk-versions' => _fs_x( 'SDK Versions', 'as software development kit versions' ),
|
338 |
+
'plugin-path' => _fs_x( 'Plugin Path', 'as plugin folder path' ),
|
339 |
+
'sdk-path' => _fs_x( 'SDK Path', 'as sdk path' ),
|
340 |
+
'addons-of-x' => _fs_text( 'Add Ons of Plugin %s' ),
|
341 |
+
'delete-all-confirm' => _fs_text( 'Are you sure you want to delete all Freemius data?' ),
|
342 |
+
'actions' => _fs_text( 'Actions' ),
|
343 |
+
'delete-all-accounts' => _fs_text( 'Delete All Accounts' ),
|
344 |
+
'start-fresh' => _fs_text( 'Start Fresh' ),
|
345 |
+
'clear-api-cache' => _fs_text( 'Clear API Cache' ),
|
346 |
+
'sync-data-from-server' => _fs_text( 'Sync Data From Server' ),
|
347 |
+
'scheduled-crons' => _fs_text( 'Scheduled Crons' ),
|
348 |
+
'cron-type' => _fs_text( 'Cron Type' ),
|
349 |
+
'plugins-themes-sync' => _fs_text( 'Plugins & Themes Sync' ),
|
350 |
+
'module-licenses' => _fs_text( '%s Licenses' ),
|
351 |
+
'debug-log' => _fs_text( 'Debug Log' ),
|
352 |
+
'all' => _fs_text( 'All' ),
|
353 |
+
'file' => _fs_text( 'File' ),
|
354 |
+
'function' => _fs_text( 'Function' ),
|
355 |
+
'process-id' => _fs_text( 'Process ID' ),
|
356 |
+
'logger' => _fs_text( 'Logger' ),
|
357 |
+
'message' => _fs_text( 'Message' ),
|
358 |
+
'download' => _fs_text( 'Download' ),
|
359 |
+
'filter' => _fs_text( 'Filter' ),
|
360 |
+
'type' => _fs_text( 'Type' ),
|
361 |
+
'all-types' => _fs_text( 'All Types' ),
|
362 |
+
'all-requests' => _fs_text( 'All Requests' ),
|
363 |
+
#endregion Debug
|
364 |
+
|
365 |
+
#region Expressions
|
366 |
+
'congrats' => _fs_x( 'Congrats', 'as congratulations' ),
|
367 |
+
'oops' => _fs_x( 'Oops', 'exclamation' ),
|
368 |
+
'yee-haw' => _fs_x( 'Yee-haw', 'interjection expressing joy or exuberance' ),
|
369 |
+
'woot' => _fs_x( 'W00t',
|
370 |
+
'(especially in electronic communication) used to express elation, enthusiasm, or triumph.' ),
|
371 |
+
'right-on' => _fs_x( 'Right on', 'a positive response' ),
|
372 |
+
'hmm' => _fs_x( 'Hmm',
|
373 |
+
'something somebody says when they are thinking about what you have just said. ' ),
|
374 |
+
'ok' => _fs_text( 'O.K' ),
|
375 |
+
'hey' => _fs_x( 'Hey', 'exclamation' ),
|
376 |
+
'heads-up' => _fs_x( 'Heads up',
|
377 |
+
'advance notice of something that will need attention.' ),
|
378 |
+
#endregion Expressions
|
379 |
+
|
380 |
+
#region Admin Notices
|
381 |
+
'you-have-latest' => _fs_text( 'Seems like you got the latest release.' ),
|
382 |
+
'you-are-good' => _fs_text( 'You are all good!' ),
|
383 |
+
'user-exist-message' => _fs_text( 'Sorry, we could not complete the email update. Another user with the same email is already registered.' ),
|
384 |
+
'user-exist-message_ownership' => _fs_text( 'If you would like to give up the ownership of the %s\'s account to %s click the Change Ownership button.' ),
|
385 |
+
'email-updated-message' => _fs_text( 'Your email was successfully updated. You should receive an email with confirmation instructions in few moments.' ),
|
386 |
+
'name-updated-message' => _fs_text( 'Your name was successfully updated.' ),
|
387 |
+
'x-updated' => _fs_text( 'You have successfully updated your %s.' ),
|
388 |
+
'name-update-failed-message' => _fs_text( 'Please provide your full name.' ),
|
389 |
+
'verification-email-sent-message' => _fs_text( 'Verification mail was just sent to %s. If you can\'t find it after 5 min, please check your spam box.' ),
|
390 |
+
'addons-info-external-message' => _fs_text( 'Just letting you know that the add-ons information of %s is being pulled from an external server.' ),
|
391 |
+
'no-cc-required' => _fs_text( 'No credit card required' ),
|
392 |
+
'premium-activated-message' => _fs_text( 'Premium %s version was successfully activated.' ),
|
393 |
+
'successful-version-upgrade-message' => _fs_text( 'The upgrade of %s was successfully completed.' ),
|
394 |
+
'activation-with-plan-x-message' => _fs_text( 'Your account was successfully activated with the %s plan.' ),
|
395 |
+
'download-latest-x-version-now' => _fs_text( 'Download the latest %s version now' ),
|
396 |
+
'follow-steps-to-complete-upgrade' => _fs_text( 'Please follow these steps to complete the upgrade' ),
|
397 |
+
'download-latest-x-version' => _fs_text( 'Download the latest %s version' ),
|
398 |
+
'download-latest-version' => _fs_text( 'Download the latest version' ),
|
399 |
+
'deactivate-free-version' => _fs_text( 'Deactivate the free version' ),
|
400 |
+
'upload-and-activate' => _fs_text( 'Upload and activate the downloaded version' ),
|
401 |
+
'howto-upload-activate' => _fs_text( 'How to upload and activate?' ),
|
402 |
+
'addon-successfully-purchased-message' => _fs_x( '%s Add-on was successfully purchased.',
|
403 |
+
'%s - product name, e.g. Facebook add-on was successfully...' ),
|
404 |
+
'addon-successfully-upgraded-message' => _fs_text( 'Your %s Add-on plan was successfully upgraded.' ),
|
405 |
+
'email-verified-message' => _fs_text( 'Your email has been successfully verified - you are AWESOME!' ),
|
406 |
+
'plan-upgraded-message' => _fs_text( 'Your plan was successfully upgraded.' ),
|
407 |
+
'plan-changed-to-x-message' => _fs_text( 'Your plan was successfully changed to %s.' ),
|
408 |
+
'license-expired-blocking-message' => _fs_text( 'Your license has expired. You can still continue using the free %s forever.' ),
|
409 |
+
'license-cancelled' => _fs_text( 'Your license has been cancelled. If you think it\'s a mistake, please contact support.' ),
|
410 |
+
'trial-started-message' => _fs_text( 'Your trial has been successfully started.' ),
|
411 |
+
'license-activated-message' => _fs_text( 'Your license was successfully activated.' ),
|
412 |
+
'no-active-license-message' => _fs_text( 'It looks like your site currently doesn\'t have an active license.' ),
|
413 |
+
'license-deactivation-message' => _fs_text( 'Your license was successfully deactivated, you are back to the %s plan.' ),
|
414 |
+
'license-deactivation-failed-message' => _fs_text( 'It looks like the license deactivation failed.' ),
|
415 |
+
'license-activation-failed-message' => _fs_text( 'It looks like the license could not be activated.' ),
|
416 |
+
'server-error-message' => _fs_text( 'Error received from the server:' ),
|
417 |
+
'trial-expired-message' => _fs_text( 'Your trial has expired. You can still continue using all our free features.' ),
|
418 |
+
'plan-x-downgraded-message' => _fs_text( 'Your plan was successfully downgraded. Your %s plan license will expire in %s.' ),
|
419 |
+
'plan-downgraded-failure-message' => _fs_text( 'Seems like we are having some temporary issue with your plan downgrade. Please try again in few minutes.' ),
|
420 |
+
'trial-cancel-no-trial-message' => _fs_text( 'It looks like you are not in trial mode anymore so there\'s nothing to cancel :)' ),
|
421 |
+
'trial-cancel-message' => _fs_text( 'Your %s free trial was successfully cancelled.' ),
|
422 |
+
'version-x-released' => _fs_x( 'Version %s was released.', '%s - numeric version number' ),
|
423 |
+
'please-download-x' => _fs_text( 'Please download %s.' ),
|
424 |
+
'latest-x-version' => _fs_x( 'the latest %s version here',
|
425 |
+
'%s - plan name, as the latest professional version here' ),
|
426 |
+
'trial-x-promotion-message' => _fs_text( 'How do you like %s so far? Test all our %s premium features with a %d-day free trial.' ),
|
427 |
+
'start-free-trial' => _fs_x( 'Start free trial', 'call to action' ),
|
428 |
+
'starting-trial' => _fs_text( 'Starting trial' ),
|
429 |
+
'please-wait' => _fs_text( 'Please wait' ),
|
430 |
+
'trial-cancel-failure-message' => _fs_text( 'Seems like we are having some temporary issue with your trial cancellation. Please try again in few minutes.' ),
|
431 |
+
'trial-utilized' => _fs_text( 'You already utilized a trial before.' ),
|
432 |
+
'in-trial-mode' => _fs_text( 'You are already running the %s in a trial mode.' ),
|
433 |
+
'trial-plan-x-not-exist' => _fs_text( 'Plan %s do not exist, therefore, can\'t start a trial.' ),
|
434 |
+
'plan-x-no-trial' => _fs_text( 'Plan %s does not support a trial period.' ),
|
435 |
+
'no-trials' => _fs_text( 'None of the %s\'s plans supports a trial period.' ),
|
436 |
+
'unexpected-api-error' => _fs_text( 'Unexpected API error. Please contact the %s\'s author with the following error.' ),
|
437 |
+
'no-commitment-for-x-days' => _fs_text( 'No commitment for %s days - cancel anytime!' ),
|
438 |
+
'license-expired-non-blocking-message' => _fs_text( 'Your license has expired. You can still continue using all the %s features, but you\'ll need to renew your license to continue getting updates and support.' ),
|
439 |
+
'could-not-activate-x' => _fs_text( 'Couldn\'t activate %s.' ),
|
440 |
+
'contact-us-with-error-message' => _fs_text( 'Please contact us with the following message:' ),
|
441 |
+
'plan-did-not-change-message' => _fs_text( 'It looks like you are still on the %s plan. If you did upgrade or change your plan, it\'s probably an issue on our side - sorry.' ),
|
442 |
+
'contact-us-here' => _fs_text( 'Please contact us here' ),
|
443 |
+
'plan-did-not-change-email-message' => _fs_text( 'I have upgraded my account but when I try to Sync the License, the plan remains %s.' ),
|
444 |
+
#endregion Admin Notices
|
445 |
+
#region Connectivity Issues
|
446 |
+
'connectivity-test-fails-message' => _fs_text( 'From unknown reason, the API connectivity test failed.' ),
|
447 |
+
'connectivity-test-maybe-temporary' => _fs_text( 'It\'s probably a temporary issue on our end. Just to be sure, with your permission, would it be o.k to run another connectivity test?' ),
|
448 |
+
'curl-missing-message' => _fs_text( 'We use PHP cURL library for the API calls, which is a very common library and usually installed and activated out of the box. Unfortunately, cURL is not activated (or disabled) on your server.' ),
|
449 |
+
'curl-disabled-methods' => _fs_text( 'Disabled method(s):' ),
|
450 |
+
'cloudflare-blocks-connection-message' => _fs_text( 'From unknown reason, CloudFlare, the firewall we use, blocks the connection.' ),
|
451 |
+
'x-requires-access-to-api' => _fs_x( '%s requires an access to our API.',
|
452 |
+
'as pluginX requires an access to our API' ),
|
453 |
+
'squid-blocks-connection-message' => _fs_text( 'It looks like your server is using Squid ACL (access control lists), which blocks the connection.' ),
|
454 |
+
'squid-no-clue-title' => _fs_text( 'I don\'t know what is Squid or ACL, help me!' ),
|
455 |
+
'squid-no-clue-desc' => _fs_text( 'We\'ll make sure to contact your hosting company and resolve the issue. You will get a follow-up email to %s once we have an update.' ),
|
456 |
+
'sysadmin-title' => _fs_text( 'I\'m a system administrator' ),
|
457 |
+
'squid-sysadmin-desc' => _fs_text( 'Great, please whitelist the following domains: %s. Once you are done, deactivate the %s and activate it again.' ),
|
458 |
+
'curl-missing-no-clue-title' => _fs_text( 'I don\'t know what is cURL or how to install it, help me!' ),
|
459 |
+
'curl-missing-no-clue-desc' => _fs_text( 'We\'ll make sure to contact your hosting company and resolve the issue. You will get a follow-up email to %s once we have an update.' ),
|
460 |
+
'curl-missing-sysadmin-desc' => _fs_text( 'Great, please install cURL and enable it in your php.ini file. In addition, search for the \'disable_functions\' directive in your php.ini file and remove any disabled methods starting with \'curl_\'. To make sure it was successfully activated, use \'phpinfo()\'. Once activated, deactivate the %s and reactivate it back again.' ),
|
461 |
+
'happy-to-resolve-issue-asap' => _fs_text( 'We are sure it\'s an issue on our side and more than happy to resolve it for you ASAP if you give us a chance.' ),
|
462 |
+
'contact-support-before-deactivation' => _fs_text( 'Sorry for the inconvenience and we are here to help if you give us a chance.' ),
|
463 |
+
'fix-issue-title' => _fs_text( 'Yes - I\'m giving you a chance to fix it' ),
|
464 |
+
'fix-issue-desc' => _fs_text( 'We will do our best to whitelist your server and resolve this issue ASAP. You will get a follow-up email to %s once we have an update.' ),
|
465 |
+
'install-previous-title' => _fs_text( 'Let\'s try your previous version' ),
|
466 |
+
'install-previous-desc' => _fs_text( 'Uninstall this version and install the previous one.' ),
|
467 |
+
'deactivate-plugin-title' => _fs_text( 'That\'s exhausting, please deactivate' ),
|
468 |
+
'deactivate-plugin-desc' => _fs_text( 'We feel your frustration and sincerely apologize for the inconvenience. Hope to see you again in the future.' ),
|
469 |
+
'fix-request-sent-message' => _fs_text( 'Thank for giving us the chance to fix it! A message was just sent to our technical staff. We will get back to you as soon as we have an update to %s. Appreciate your patience.' ),
|
470 |
+
'server-blocking-access' => _fs_x( 'Your server is blocking the access to Freemius\' API, which is crucial for %1$s synchronization. Please contact your host to whitelist %2$s',
|
471 |
+
'%1$s - plugin title, %2$s - API domain' ),
|
472 |
+
'wrong-authentication-param-message' => _fs_text( 'It seems like one of the authentication parameters is wrong. Update your Public Key, Secret Key & User ID, and try again.' ),
|
473 |
+
#endregion Connectivity Issues
|
474 |
+
#region Change Owner
|
475 |
+
'change-owner-request-sent-x' => _fs_text( 'Please check your mailbox, you should receive an email via %s to confirm the ownership change. From security reasons, you must confirm the change within the next 15 min. If you cannot find the email, please check your spam folder.' ),
|
476 |
+
'change-owner-request_owner-confirmed' => _fs_text( 'Thanks for confirming the ownership change. An email was just sent to %s for final approval.' ),
|
477 |
+
'change-owner-request_candidate-confirmed' => _fs_text( '%s is the new owner of the account.' ),
|
478 |
+
#endregion Change Owner
|
479 |
+
'addon-x-cannot-run-without-y' => _fs_x( '%s cannot run without %s.',
|
480 |
+
'addonX cannot run without pluginY' ),
|
481 |
+
'addon-x-cannot-run-without-parent' => _fs_x( '%s cannot run without the plugin.', 'addonX cannot run...' ),
|
482 |
+
'plugin-x-activation-message' => _fs_x( '%s activation was successfully completed.',
|
483 |
+
'pluginX activation was successfully...' ),
|
484 |
+
'features-and-pricing' => _fs_x( 'Features & Pricing', 'Plugin installer section title' ),
|
485 |
+
'free-addon-not-deployed' => _fs_text( 'Add-on must be deployed to WordPress.org or Freemius.' ),
|
486 |
+
'paid-addon-not-deployed' => _fs_text( 'Paid add-on must be deployed to Freemius.' ),
|
487 |
+
#--------------------------------------------------------------------------------
|
488 |
+
#region Add-On Licensing
|
489 |
+
#--------------------------------------------------------------------------------
|
490 |
+
'addon-no-license-message' => _fs_text( '%s is a premium only add-on. You have to purchase a license first before activating the plugin.' ),
|
491 |
+
'addon-trial-cancelled-message' => _fs_text( '%s free trial was successfully cancelled. Since the add-on is premium only it was automatically deactivated. If you like to use it in the future, you\'ll have to purchase a license.' ),
|
492 |
+
#endregion
|
493 |
+
#--------------------------------------------------------------------------------
|
494 |
+
#region Billing Cycles
|
495 |
+
#--------------------------------------------------------------------------------
|
496 |
+
'monthly' => _fs_x( 'Monthly', 'as every month' ),
|
497 |
+
'mo' => _fs_x( 'mo', 'as monthly period' ),
|
498 |
+
'annual' => _fs_x( 'Annual', 'as once a year' ),
|
499 |
+
'annually' => _fs_x( 'Annually', 'as once a year' ),
|
500 |
+
'once' => _fs_x( 'Once', 'as once a year' ),
|
501 |
+
'year' => _fs_x( 'year', 'as annual period' ),
|
502 |
+
'lifetime' => _fs_text( 'Lifetime' ),
|
503 |
+
'best' => _fs_x( 'Best', 'e.g. the best product' ),
|
504 |
+
'billed-x' => _fs_x( 'Billed %s', 'e.g. billed monthly' ),
|
505 |
+
'save-x' => _fs_x( 'Save %s', 'as a discount of $5 or 10%' ),
|
506 |
+
#endregion Billing Cycles
|
507 |
+
'view-details' => _fs_text( 'View details' ),
|
508 |
+
#--------------------------------------------------------------------------------
|
509 |
+
#region Trial
|
510 |
+
#--------------------------------------------------------------------------------
|
511 |
+
'approve-start-trial' => _fs_x( 'Approve & Start Trial', 'button label' ),
|
512 |
+
/* translators: %1$s: Number of trial days; %2$s: Plan name; */
|
513 |
+
'start-trial-prompt-header' => _fs_text( 'You are 1-click away from starting your %1$s-day free trial of the %2$s plan.' ),
|
514 |
+
/* translators: %s: Link to freemius.com */
|
515 |
+
'start-trial-prompt-message' => _fs_text( 'For compliance with the WordPress.org guidelines, before we start the trial we ask that you opt in with your user and non-sensitive site information, allowing the %s to periodically send data to %s to check for version updates and to validate your trial.' ),
|
516 |
+
|
517 |
+
#endregion
|
518 |
+
#--------------------------------------------------------------------------------
|
519 |
+
#region Billing Details
|
520 |
+
#--------------------------------------------------------------------------------
|
521 |
+
'business-name' => _fs_text( 'Business name' ),
|
522 |
+
'tax-vat-id' => _fs_text( 'Tax / VAT ID' ),
|
523 |
+
'address-line-n' => _fs_text( 'Address Line %d' ),
|
524 |
+
'country' => _fs_text( 'Country' ),
|
525 |
+
'select-country' => _fs_text( 'Select Country' ),
|
526 |
+
'city' => _fs_text( 'City' ),
|
527 |
+
'town' => _fs_text( 'Town' ),
|
528 |
+
'state' => _fs_text( 'State' ),
|
529 |
+
'province' => _fs_text( 'Province' ),
|
530 |
+
'zip-postal-code' => _fs_text( 'ZIP / Postal Code' ),
|
531 |
+
#endregion
|
532 |
+
#--------------------------------------------------------------------------------
|
533 |
+
#region Module Installation
|
534 |
+
#--------------------------------------------------------------------------------
|
535 |
+
'installing-plugin-x' => _fs_text( 'Installing plugin: %s' ),
|
536 |
+
'auto-installation' => _fs_text( 'Automatic Installation' ),
|
537 |
+
/* translators: %s: Number of seconds */
|
538 |
+
'x-sec' => _fs_text( '%s sec' ),
|
539 |
+
'installing-in-n' => _fs_text( 'An automated download and installation of %s (paid version) from %s will start in %s. If you would like to do it manually - click the cancellation button now.' ),
|
540 |
+
'installing-module-x' => _fs_text( 'The installation process has started and may take a few minutes to complete. Please wait until it is done - do not refresh this page.' ),
|
541 |
+
'cancel-installation' => _fs_text( 'Cancel Installation' ),
|
542 |
+
'module-package-rename-failure' => _fs_text( 'The remote plugin package does not contain a folder with the desired slug and renaming did not work.' ),
|
543 |
+
'auto-install-error-invalid-id' => _fs_text( 'Invalid module ID.' ),
|
544 |
+
'auto-install-error-not-opted-in' => _fs_text( 'Auto installation only works for opted-in users.' ),
|
545 |
+
'auto-install-error-premium-activated' => _fs_text( 'Premium version already active.' ),
|
546 |
+
'auto-install-error-premium-addon-activated' => _fs_text( 'Premium add-on version already installed.' ),
|
547 |
+
'auto-install-error-invalid-license' => _fs_text( 'You do not have a valid license to access the premium version.' ),
|
548 |
+
'auto-install-error-serviceware' => _fs_text( 'Plugin is a "Serviceware" which means it does not have a premium code version.' ),
|
549 |
+
#endregion
|
550 |
+
|
551 |
+
/* translators: %s: Page name */
|
552 |
+
'secure-x-page-header' => _fs_text( 'Secure HTTPS %s page, running from an external domain' ),
|
553 |
+
'pci-compliant' => _fs_text( 'PCI compliant' ),
|
554 |
+
'view-paid-features' => _fs_text( 'View paid features' ),
|
555 |
+
);
|
556 |
+
|
557 |
+
/**
|
558 |
+
* Localization of the strings in the plugin/theme info dialog box.
|
559 |
+
*
|
560 |
+
* $fs_module_info_text should ONLY include strings that are not located in $fs_text.
|
561 |
+
*
|
562 |
+
* @author Vova Feldman (@svovaf)
|
563 |
+
* @since 1.2.2
|
564 |
+
*/
|
565 |
+
global $fs_module_info_text;
|
566 |
+
|
567 |
+
$fs_module_info_text = array(
|
568 |
+
'description' => _fs_x( 'Description', 'Plugin installer section title' ),
|
569 |
+
'installation' => _fs_x( 'Installation', 'Plugin installer section title' ),
|
570 |
+
'faq' => _fs_x( 'FAQ', 'Plugin installer section title' ),
|
571 |
+
'changelog' => _fs_x( 'Changelog', 'Plugin installer section title' ),
|
572 |
+
'reviews' => _fs_x( 'Reviews', 'Plugin installer section title' ),
|
573 |
+
'other_notes' => _fs_x( 'Other Notes', 'Plugin installer section title' ),
|
574 |
+
/* translators: %s: 1 or One */
|
575 |
+
'x-star' => _fs_text( '%s star' ),
|
576 |
+
/* translators: %s: Number larger than 1 */
|
577 |
+
'x-stars' => _fs_text( '%s stars' ),
|
578 |
+
/* translators: %s: 1 or One */
|
579 |
+
'x-rating' => _fs_text( '%s rating' ),
|
580 |
+
/* translators: %s: Number larger than 1 */
|
581 |
+
'x-ratings' => _fs_text( '%s ratings' ),
|
582 |
+
/* translators: %s: 1 or One (Number of times downloaded) */
|
583 |
+
'x-time' => _fs_text( '%s time' ),
|
584 |
+
/* translators: %s: Number of times downloaded */
|
585 |
+
'x-times' => _fs_text( '%s times' ),
|
586 |
+
/* translators: %s: # of stars (e.g. 5 stars) */
|
587 |
+
'click-to-reviews' => _fs_text( 'Click to see reviews that provided a rating of %s' ),
|
588 |
+
'last-updated:' => _fs_text( 'Last Updated' ),
|
589 |
+
'requires-wordpress-version:' => _fs_text( 'Requires WordPress Version:' ),
|
590 |
+
'author:' => _fs_x( 'Author:', 'as the plugin author' ),
|
591 |
+
'compatible-up-to:' => _fs_text( 'Compatible up to:' ),
|
592 |
+
'downloaded:' => _fs_text( 'Downloaded:' ),
|
593 |
+
'wp-org-plugin-page' => _fs_text( 'WordPress.org Plugin Page' ),
|
594 |
+
'plugin-homepage' => _fs_text( 'Plugin Homepage' ),
|
595 |
+
'donate-to-plugin' => _fs_text( 'Donate to this plugin' ),
|
596 |
+
'average-rating' => _fs_text( 'Average Rating' ),
|
597 |
+
'based-on-x' => _fs_text( 'based on %s' ),
|
598 |
+
'warning:' => _fs_text( 'Warning:' ),
|
599 |
+
'contributors' => _fs_text( 'Contributors' ),
|
600 |
+
'plugin-install' => _fs_text( 'Plugin Install' ),
|
601 |
+
'not-tested-warning' => _fs_text( 'This plugin has not been tested with your current version of WordPress.' ),
|
602 |
+
'not-compatible-warning' => _fs_text( 'This plugin has not been marked as compatible with your version of WordPress.' ),
|
603 |
+
'newer-installed' => _fs_text( 'Newer Version (%s) Installed' ),
|
604 |
+
'latest-installed' => _fs_text( 'Latest Version Installed' ),
|
605 |
+
);
|
freemius/includes/managers/class-fs-admin-notice-manager.php
CHANGED
@@ -175,7 +175,12 @@
|
|
175 |
*
|
176 |
*/
|
177 |
function dismiss_notice_ajax_callback() {
|
178 |
-
|
|
|
|
|
|
|
|
|
|
|
179 |
wp_die();
|
180 |
}
|
181 |
|
@@ -469,4 +474,4 @@
|
|
469 |
}
|
470 |
|
471 |
#endregion
|
472 |
-
}
|
175 |
*
|
176 |
*/
|
177 |
function dismiss_notice_ajax_callback() {
|
178 |
+
check_admin_referer( 'fs_dismiss_notice_action' );
|
179 |
+
|
180 |
+
if ( ! is_numeric( $_POST['message_id'] ) ) {
|
181 |
+
$this->_sticky_storage->remove( $_POST['message_id'] );
|
182 |
+
}
|
183 |
+
|
184 |
wp_die();
|
185 |
}
|
186 |
|
474 |
}
|
475 |
|
476 |
#endregion
|
477 |
+
}
|
freemius/includes/managers/class-fs-plugin-manager.php
CHANGED
@@ -1,220 +1,220 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* @package Freemius
|
4 |
-
* @copyright Copyright (c) 2015, Freemius, Inc.
|
5 |
-
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
|
6 |
-
* @since 1.0.6
|
7 |
-
*/
|
8 |
-
|
9 |
-
if ( ! defined( 'ABSPATH' ) ) {
|
10 |
-
exit;
|
11 |
-
}
|
12 |
-
|
13 |
-
class FS_Plugin_Manager {
|
14 |
-
/**
|
15 |
-
* @since 1.2.2
|
16 |
-
*
|
17 |
-
* @var string|number
|
18 |
-
*/
|
19 |
-
protected $_module_id;
|
20 |
-
/**
|
21 |
-
* @since 1.2.2
|
22 |
-
*
|
23 |
-
* @var FS_Plugin
|
24 |
-
*/
|
25 |
-
protected $_module;
|
26 |
-
|
27 |
-
/**
|
28 |
-
* @var FS_Plugin_Manager[]
|
29 |
-
*/
|
30 |
-
private static $_instances = array();
|
31 |
-
/**
|
32 |
-
* @var FS_Logger
|
33 |
-
*/
|
34 |
-
protected $_logger;
|
35 |
-
|
36 |
-
/**
|
37 |
-
* Option names
|
38 |
-
*
|
39 |
-
* @author Leo Fajardo (@leorw)
|
40 |
-
* @since 1.2.2
|
41 |
-
*/
|
42 |
-
const OPTION_NAME_PLUGINS = 'plugins';
|
43 |
-
const OPTION_NAME_THEMES = 'themes';
|
44 |
-
|
45 |
-
/**
|
46 |
-
* @param string|number $module_id
|
47 |
-
*
|
48 |
-
* @return FS_Plugin_Manager
|
49 |
-
*/
|
50 |
-
static function instance( $module_id ) {
|
51 |
-
$key = 'm_' . $module_id;
|
52 |
-
|
53 |
-
if ( ! isset( self::$_instances[ $key ] ) ) {
|
54 |
-
self::$_instances[ $key ] = new FS_Plugin_Manager( $module_id );
|
55 |
-
}
|
56 |
-
|
57 |
-
return self::$_instances[ $key ];
|
58 |
-
}
|
59 |
-
|
60 |
-
/**
|
61 |
-
* @param string|number $module_id
|
62 |
-
*/
|
63 |
-
protected function __construct( $module_id ) {
|
64 |
-
$this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $module_id . '_' . 'plugins', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
|
65 |
-
$this->_module_id = $module_id;
|
66 |
-
|
67 |
-
$this->load();
|
68 |
-
}
|
69 |
-
|
70 |
-
protected function get_option_manager() {
|
71 |
-
return FS_Option_Manager::get_manager( WP_FS__ACCOUNTS_OPTION_NAME, true, true );
|
72 |
-
}
|
73 |
-
|
74 |
-
/**
|
75 |
-
* @author Leo Fajardo (@leorw)
|
76 |
-
* @since 1.2.2
|
77 |
-
*
|
78 |
-
* @param string|bool $module_type "plugin", "theme", or "false" for all modules.
|
79 |
-
*
|
80 |
-
* @return array
|
81 |
-
*/
|
82 |
-
protected function get_all_modules( $module_type = false ) {
|
83 |
-
$option_manager = $this->get_option_manager();
|
84 |
-
|
85 |
-
if ( false !== $module_type ) {
|
86 |
-
return fs_get_entities( $option_manager->get_option( $module_type . 's', array() ), FS_Plugin::get_class_name() );
|
87 |
-
}
|
88 |
-
|
89 |
-
return array(
|
90 |
-
self::OPTION_NAME_PLUGINS => fs_get_entities( $option_manager->get_option( self::OPTION_NAME_PLUGINS, array() ), FS_Plugin::get_class_name() ),
|
91 |
-
self::OPTION_NAME_THEMES => fs_get_entities( $option_manager->get_option( self::OPTION_NAME_THEMES, array() ), FS_Plugin::get_class_name() ),
|
92 |
-
);
|
93 |
-
}
|
94 |
-
|
95 |
-
/**
|
96 |
-
* Load plugin data from local DB.
|
97 |
-
*
|
98 |
-
* @author Vova Feldman (@svovaf)
|
99 |
-
* @since 1.0.6
|
100 |
-
*/
|
101 |
-
function load() {
|
102 |
-
$all_modules = $this->get_all_modules();
|
103 |
-
|
104 |
-
if ( ! is_numeric( $this->_module_id ) ) {
|
105 |
-
unset( $all_modules[ self::OPTION_NAME_THEMES ] );
|
106 |
-
}
|
107 |
-
|
108 |
-
foreach ( $all_modules as $modules ) {
|
109 |
-
/**
|
110 |
-
* @since 1.2.2
|
111 |
-
*
|
112 |
-
* @var $modules FS_Plugin[]
|
113 |
-
*/
|
114 |
-
foreach ( $modules as $module ) {
|
115 |
-
$found_module = false;
|
116 |
-
|
117 |
-
/**
|
118 |
-
* If module ID is not numeric, it must be a plugin's slug.
|
119 |
-
*
|
120 |
-
* @author Leo Fajardo (@leorw)
|
121 |
-
* @since 1.2.2
|
122 |
-
*/
|
123 |
-
if ( ! is_numeric( $this->_module_id ) ) {
|
124 |
-
if ( $this->_module_id === $module->slug ) {
|
125 |
-
$this->_module_id = $module->id;
|
126 |
-
$found_module = true;
|
127 |
-
}
|
128 |
-
} else if ( $this->_module_id == $module->id ) {
|
129 |
-
$found_module = true;
|
130 |
-
}
|
131 |
-
|
132 |
-
if ( $found_module ) {
|
133 |
-
$this->_module = $module;
|
134 |
-
break;
|
135 |
-
}
|
136 |
-
}
|
137 |
-
}
|
138 |
-
}
|
139 |
-
|
140 |
-
/**
|
141 |
-
* Store plugin on local DB.
|
142 |
-
*
|
143 |
-
* @author Vova Feldman (@svovaf)
|
144 |
-
* @since 1.0.6
|
145 |
-
*
|
146 |
-
* @param bool|FS_Plugin $module
|
147 |
-
* @param bool $flush
|
148 |
-
*
|
149 |
-
* @return bool|\FS_Plugin
|
150 |
-
*/
|
151 |
-
function store( $module = false, $flush = true ) {
|
152 |
-
if ( false !== $module ) {
|
153 |
-
$this->_module = $module;
|
154 |
-
}
|
155 |
-
|
156 |
-
$all_modules = $this->get_all_modules( $this->_module->type );
|
157 |
-
$all_modules[ $this->_module->slug ] = $this->_module;
|
158 |
-
|
159 |
-
$options_manager = $this->get_option_manager();
|
160 |
-
$options_manager->set_option( $this->_module->type . 's', $all_modules, $flush );
|
161 |
-
|
162 |
-
return $this->_module;
|
163 |
-
}
|
164 |
-
|
165 |
-
/**
|
166 |
-
* Update local plugin data if different.
|
167 |
-
*
|
168 |
-
* @author Vova Feldman (@svovaf)
|
169 |
-
* @since 1.0.6
|
170 |
-
*
|
171 |
-
* @param \FS_Plugin $plugin
|
172 |
-
* @param bool $store
|
173 |
-
*
|
174 |
-
* @return bool True if plugin was updated.
|
175 |
-
*/
|
176 |
-
function update( FS_Plugin $plugin, $store = true ) {
|
177 |
-
if ( ! ($this->_module instanceof FS_Plugin ) ||
|
178 |
-
$this->_module->slug != $plugin->slug ||
|
179 |
-
$this->_module->public_key != $plugin->public_key ||
|
180 |
-
$this->_module->secret_key != $plugin->secret_key ||
|
181 |
-
$this->_module->parent_plugin_id != $plugin->parent_plugin_id ||
|
182 |
-
$this->_module->title != $plugin->title
|
183 |
-
) {
|
184 |
-
$this->store( $plugin, $store );
|
185 |
-
|
186 |
-
return true;
|
187 |
-
}
|
188 |
-
|
189 |
-
return false;
|
190 |
-
}
|
191 |
-
|
192 |
-
/**
|
193 |
-
* @author Vova Feldman (@svovaf)
|
194 |
-
* @since 1.0.6
|
195 |
-
*
|
196 |
-
* @param FS_Plugin $plugin
|
197 |
-
* @param bool $store
|
198 |
-
*/
|
199 |
-
function set( FS_Plugin $plugin, $store = false ) {
|
200 |
-
$this->_module = $plugin;
|
201 |
-
|
202 |
-
if ( $store ) {
|
203 |
-
$this->store();
|
204 |
-
}
|
205 |
-
}
|
206 |
-
|
207 |
-
/**
|
208 |
-
* @author Vova Feldman (@svovaf)
|
209 |
-
* @since 1.0.6
|
210 |
-
*
|
211 |
-
* @return bool|\FS_Plugin
|
212 |
-
*/
|
213 |
-
function get() {
|
214 |
-
return isset( $this->_module ) ?
|
215 |
-
$this->_module :
|
216 |
-
false;
|
217 |
-
}
|
218 |
-
|
219 |
-
|
220 |
}
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package Freemius
|
4 |
+
* @copyright Copyright (c) 2015, Freemius, Inc.
|
5 |
+
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
|
6 |
+
* @since 1.0.6
|
7 |
+
*/
|
8 |
+
|
9 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
10 |
+
exit;
|
11 |
+
}
|
12 |
+
|
13 |
+
class FS_Plugin_Manager {
|
14 |
+
/**
|
15 |
+
* @since 1.2.2
|
16 |
+
*
|
17 |
+
* @var string|number
|
18 |
+
*/
|
19 |
+
protected $_module_id;
|
20 |
+
/**
|
21 |
+
* @since 1.2.2
|
22 |
+
*
|
23 |
+
* @var FS_Plugin
|
24 |
+
*/
|
25 |
+
protected $_module;
|
26 |
+
|
27 |
+
/**
|
28 |
+
* @var FS_Plugin_Manager[]
|
29 |
+
*/
|
30 |
+
private static $_instances = array();
|
31 |
+
/**
|
32 |
+
* @var FS_Logger
|
33 |
+
*/
|
34 |
+
protected $_logger;
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Option names
|
38 |
+
*
|
39 |
+
* @author Leo Fajardo (@leorw)
|
40 |
+
* @since 1.2.2
|
41 |
+
*/
|
42 |
+
const OPTION_NAME_PLUGINS = 'plugins';
|
43 |
+
const OPTION_NAME_THEMES = 'themes';
|
44 |
+
|
45 |
+
/**
|
46 |
+
* @param string|number $module_id
|
47 |
+
*
|
48 |
+
* @return FS_Plugin_Manager
|
49 |
+
*/
|
50 |
+
static function instance( $module_id ) {
|
51 |
+
$key = 'm_' . $module_id;
|
52 |
+
|
53 |
+
if ( ! isset( self::$_instances[ $key ] ) ) {
|
54 |
+
self::$_instances[ $key ] = new FS_Plugin_Manager( $module_id );
|
55 |
+
}
|
56 |
+
|
57 |
+
return self::$_instances[ $key ];
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* @param string|number $module_id
|
62 |
+
*/
|
63 |
+
protected function __construct( $module_id ) {
|
64 |
+
$this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $module_id . '_' . 'plugins', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
|
65 |
+
$this->_module_id = $module_id;
|
66 |
+
|
67 |
+
$this->load();
|
68 |
+
}
|
69 |
+
|
70 |
+
protected function get_option_manager() {
|
71 |
+
return FS_Option_Manager::get_manager( WP_FS__ACCOUNTS_OPTION_NAME, true, true );
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* @author Leo Fajardo (@leorw)
|
76 |
+
* @since 1.2.2
|
77 |
+
*
|
78 |
+
* @param string|bool $module_type "plugin", "theme", or "false" for all modules.
|
79 |
+
*
|
80 |
+
* @return array
|
81 |
+
*/
|
82 |
+
protected function get_all_modules( $module_type = false ) {
|
83 |
+
$option_manager = $this->get_option_manager();
|
84 |
+
|
85 |
+
if ( false !== $module_type ) {
|
86 |
+
return fs_get_entities( $option_manager->get_option( $module_type . 's', array() ), FS_Plugin::get_class_name() );
|
87 |
+
}
|
88 |
+
|
89 |
+
return array(
|
90 |
+
self::OPTION_NAME_PLUGINS => fs_get_entities( $option_manager->get_option( self::OPTION_NAME_PLUGINS, array() ), FS_Plugin::get_class_name() ),
|
91 |
+
self::OPTION_NAME_THEMES => fs_get_entities( $option_manager->get_option( self::OPTION_NAME_THEMES, array() ), FS_Plugin::get_class_name() ),
|
92 |
+
);
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* Load plugin data from local DB.
|
97 |
+
*
|
98 |
+
* @author Vova Feldman (@svovaf)
|
99 |
+
* @since 1.0.6
|
100 |
+
*/
|
101 |
+
function load() {
|
102 |
+
$all_modules = $this->get_all_modules();
|
103 |
+
|
104 |
+
if ( ! is_numeric( $this->_module_id ) ) {
|
105 |
+
unset( $all_modules[ self::OPTION_NAME_THEMES ] );
|
106 |
+
}
|
107 |
+
|
108 |
+
foreach ( $all_modules as $modules ) {
|
109 |
+
/**
|
110 |
+
* @since 1.2.2
|
111 |
+
*
|
112 |
+
* @var $modules FS_Plugin[]
|
113 |
+
*/
|
114 |
+
foreach ( $modules as $module ) {
|
115 |
+
$found_module = false;
|
116 |
+
|
117 |
+
/**
|
118 |
+
* If module ID is not numeric, it must be a plugin's slug.
|
119 |
+
*
|
120 |
+
* @author Leo Fajardo (@leorw)
|
121 |
+
* @since 1.2.2
|
122 |
+
*/
|
123 |
+
if ( ! is_numeric( $this->_module_id ) ) {
|
124 |
+
if ( $this->_module_id === $module->slug ) {
|
125 |
+
$this->_module_id = $module->id;
|
126 |
+
$found_module = true;
|
127 |
+
}
|
128 |
+
} else if ( $this->_module_id == $module->id ) {
|
129 |
+
$found_module = true;
|
130 |
+
}
|
131 |
+
|
132 |
+
if ( $found_module ) {
|
133 |
+
$this->_module = $module;
|
134 |
+
break;
|
135 |
+
}
|
136 |
+
}
|
137 |
+
}
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Store plugin on local DB.
|
142 |
+
*
|
143 |
+
* @author Vova Feldman (@svovaf)
|
144 |
+
* @since 1.0.6
|
145 |
+
*
|
146 |
+
* @param bool|FS_Plugin $module
|
147 |
+
* @param bool $flush
|
148 |
+
*
|
149 |
+
* @return bool|\FS_Plugin
|
150 |
+
*/
|
151 |
+
function store( $module = false, $flush = true ) {
|
152 |
+
if ( false !== $module ) {
|
153 |
+
$this->_module = $module;
|
154 |
+
}
|
155 |
+
|
156 |
+
$all_modules = $this->get_all_modules( $this->_module->type );
|
157 |
+
$all_modules[ $this->_module->slug ] = $this->_module;
|
158 |
+
|
159 |
+
$options_manager = $this->get_option_manager();
|
160 |
+
$options_manager->set_option( $this->_module->type . 's', $all_modules, $flush );
|
161 |
+
|
162 |
+
return $this->_module;
|
163 |
+
}
|
164 |
+
|
165 |
+
/**
|
166 |
+
* Update local plugin data if different.
|
167 |
+
*
|
168 |
+
* @author Vova Feldman (@svovaf)
|
169 |
+
* @since 1.0.6
|
170 |
+
*
|
171 |
+
* @param \FS_Plugin $plugin
|
172 |
+
* @param bool $store
|
173 |
+
*
|
174 |
+
* @return bool True if plugin was updated.
|
175 |
+
*/
|
176 |
+
function update( FS_Plugin $plugin, $store = true ) {
|
177 |
+
if ( ! ($this->_module instanceof FS_Plugin ) ||
|
178 |
+
$this->_module->slug != $plugin->slug ||
|
179 |
+
$this->_module->public_key != $plugin->public_key ||
|
180 |
+
$this->_module->secret_key != $plugin->secret_key ||
|
181 |
+
$this->_module->parent_plugin_id != $plugin->parent_plugin_id ||
|
182 |
+
$this->_module->title != $plugin->title
|
183 |
+
) {
|
184 |
+
$this->store( $plugin, $store );
|
185 |
+
|
186 |
+
return true;
|
187 |
+
}
|
188 |
+
|
189 |
+
return false;
|
190 |
+
}
|
191 |
+
|
192 |
+
/**
|
193 |
+
* @author Vova Feldman (@svovaf)
|
194 |
+
* @since 1.0.6
|
195 |
+
*
|
196 |
+
* @param FS_Plugin $plugin
|
197 |
+
* @param bool $store
|
198 |
+
*/
|
199 |
+
function set( FS_Plugin $plugin, $store = false ) {
|
200 |
+
$this->_module = $plugin;
|
201 |
+
|
202 |
+
if ( $store ) {
|
203 |
+
$this->store();
|
204 |
+
}
|
205 |
+
}
|
206 |
+
|
207 |
+
/**
|
208 |
+
* @author Vova Feldman (@svovaf)
|
209 |
+
* @since 1.0.6
|
210 |
+
*
|
211 |
+
* @return bool|\FS_Plugin
|
212 |
+
*/
|
213 |
+
function get() {
|
214 |
+
return isset( $this->_module ) ?
|
215 |
+
$this->_module :
|
216 |
+
false;
|
217 |
+
}
|
218 |
+
|
219 |
+
|
220 |
}
|
freemius/includes/sdk/Exceptions/ArgumentNotExistException.php
CHANGED
@@ -1,4 +1,8 @@
|
|
1 |
<?php
|
|
|
|
|
|
|
|
|
2 |
if ( ! class_exists( 'Freemius_InvalidArgumentException' ) ) {
|
3 |
exit;
|
4 |
}
|
@@ -6,4 +10,4 @@
|
|
6 |
if ( ! class_exists( 'Freemius_ArgumentNotExistException' ) ) {
|
7 |
class Freemius_ArgumentNotExistException extends Freemius_InvalidArgumentException {
|
8 |
}
|
9 |
-
}
|
1 |
<?php
|
2 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
3 |
+
exit;
|
4 |
+
}
|
5 |
+
|
6 |
if ( ! class_exists( 'Freemius_InvalidArgumentException' ) ) {
|
7 |
exit;
|
8 |
}
|
10 |
if ( ! class_exists( 'Freemius_ArgumentNotExistException' ) ) {
|
11 |
class Freemius_ArgumentNotExistException extends Freemius_InvalidArgumentException {
|
12 |
}
|
13 |
+
}
|
freemius/includes/sdk/Exceptions/EmptyArgumentException.php
CHANGED
@@ -1,4 +1,8 @@
|
|
1 |
<?php
|
|
|
|
|
|
|
|
|
2 |
if ( ! class_exists( 'Freemius_InvalidArgumentException' ) ) {
|
3 |
exit;
|
4 |
}
|
@@ -6,4 +10,4 @@
|
|
6 |
if ( ! class_exists( 'Freemius_EmptyArgumentException' ) ) {
|
7 |
class Freemius_EmptyArgumentException extends Freemius_InvalidArgumentException {
|
8 |
}
|
9 |
-
}
|
1 |
<?php
|
2 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
3 |
+
exit;
|
4 |
+
}
|
5 |
+
|
6 |
if ( ! class_exists( 'Freemius_InvalidArgumentException' ) ) {
|
7 |
exit;
|
8 |
}
|
10 |
if ( ! class_exists( 'Freemius_EmptyArgumentException' ) ) {
|
11 |
class Freemius_EmptyArgumentException extends Freemius_InvalidArgumentException {
|
12 |
}
|
13 |
+
}
|
freemius/includes/sdk/Exceptions/Exception.php
CHANGED
@@ -1,4 +1,8 @@
|
|
1 |
<?php
|
|
|
|
|
|
|
|
|
2 |
if ( ! class_exists( 'Freemius_Exception' ) ) {
|
3 |
/**
|
4 |
* Thrown when an API call returns an exception.
|
@@ -71,4 +75,4 @@
|
|
71 |
return $str . $this->getMessage();
|
72 |
}
|
73 |
}
|
74 |
-
}
|
1 |
<?php
|
2 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
3 |
+
exit;
|
4 |
+
}
|
5 |
+
|
6 |
if ( ! class_exists( 'Freemius_Exception' ) ) {
|
7 |
/**
|
8 |
* Thrown when an API call returns an exception.
|
75 |
return $str . $this->getMessage();
|
76 |
}
|
77 |
}
|
78 |
+
}
|
freemius/includes/sdk/Exceptions/InvalidArgumentException.php
CHANGED
@@ -1,8 +1,12 @@
|
|
1 |
<?php
|
|
|
|
|
|
|
|
|
2 |
if ( ! class_exists( 'Freemius_Exception' ) ) {
|
3 |
exit;
|
4 |
}
|
5 |
|
6 |
if ( ! class_exists( 'Freemius_InvalidArgumentException' ) ) {
|
7 |
class Freemius_InvalidArgumentException extends Freemius_Exception { }
|
8 |
-
}
|
1 |
<?php
|
2 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
3 |
+
exit;
|
4 |
+
}
|
5 |
+
|
6 |
if ( ! class_exists( 'Freemius_Exception' ) ) {
|
7 |
exit;
|
8 |
}
|
9 |
|
10 |
if ( ! class_exists( 'Freemius_InvalidArgumentException' ) ) {
|
11 |
class Freemius_InvalidArgumentException extends Freemius_Exception { }
|
12 |
+
}
|
freemius/includes/sdk/Exceptions/OAuthException.php
CHANGED
@@ -1,4 +1,8 @@
|
|
1 |
<?php
|
|
|
|
|
|
|
|
|
2 |
if ( ! class_exists( 'Freemius_Exception' ) ) {
|
3 |
exit;
|
4 |
}
|
@@ -9,4 +13,4 @@
|
|
9 |
parent::__construct( $pResult );
|
10 |
}
|
11 |
}
|
12 |
-
}
|
1 |
<?php
|
2 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
3 |
+
exit;
|
4 |
+
}
|
5 |
+
|
6 |
if ( ! class_exists( 'Freemius_Exception' ) ) {
|
7 |
exit;
|
8 |
}
|
13 |
parent::__construct( $pResult );
|
14 |
}
|
15 |
}
|
16 |
+
}
|
freemius/includes/sdk/FreemiusBase.php
CHANGED
@@ -15,6 +15,10 @@
|
|
15 |
* under the License.
|
16 |
*/
|
17 |
|
|
|
|
|
|
|
|
|
18 |
if ( ! defined( 'FS_API__VERSION' ) ) {
|
19 |
define( 'FS_API__VERSION', '1' );
|
20 |
}
|
15 |
* under the License.
|
16 |
*/
|
17 |
|
18 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
19 |
+
exit;
|
20 |
+
}
|
21 |
+
|
22 |
if ( ! defined( 'FS_API__VERSION' ) ) {
|
23 |
define( 'FS_API__VERSION', '1' );
|
24 |
}
|
freemius/includes/sdk/FreemiusWordPress.php
CHANGED
@@ -14,6 +14,9 @@
|
|
14 |
* License for the specific language governing permissions and limitations
|
15 |
* under the License.
|
16 |
*/
|
|
|
|
|
|
|
17 |
|
18 |
require_once dirname( __FILE__ ) . '/FreemiusBase.php';
|
19 |
|
@@ -709,4 +712,4 @@
|
|
709 |
}
|
710 |
|
711 |
#endregion
|
712 |
-
}
|
14 |
* License for the specific language governing permissions and limitations
|
15 |
* under the License.
|
16 |
*/
|
17 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
18 |
+
exit;
|
19 |
+
}
|
20 |
|
21 |
require_once dirname( __FILE__ ) . '/FreemiusBase.php';
|
22 |
|
712 |
}
|
713 |
|
714 |
#endregion
|
715 |
+
}
|
freemius/languages/freemius-cs_CZ.mo
CHANGED
Binary file
|
freemius/languages/freemius-da_DK.mo
CHANGED
Binary file
|
freemius/languages/freemius-en.mo
CHANGED
Binary file
|
freemius/languages/freemius-es_ES.mo
CHANGED
Binary file
|
freemius/languages/freemius-fr_FR.mo
CHANGED
Binary file
|
freemius/languages/freemius-he_IL.mo
CHANGED
Binary file
|
freemius/languages/freemius-hu_HU.mo
CHANGED
Binary file
|
freemius/languages/freemius-it_IT.mo
CHANGED
Binary file
|
freemius/languages/freemius-ja.mo
CHANGED
Binary file
|
freemius/languages/freemius-nl_NL.mo
CHANGED
Binary file
|
freemius/languages/freemius-ru_RU.mo
CHANGED
Binary file
|
freemius/languages/freemius-ta.mo
CHANGED
Binary file
|
freemius/languages/freemius-zh_CN.mo
CHANGED
Binary file
|
freemius/languages/freemius.pot
CHANGED
@@ -32,787 +32,779 @@ msgstr ""
|
|
32 |
msgid "Error"
|
33 |
msgstr ""
|
34 |
|
35 |
-
#: includes/class-freemius.php:
|
36 |
msgid "I found a better %s"
|
37 |
msgstr ""
|
38 |
|
39 |
-
#: includes/class-freemius.php:
|
40 |
msgid "What's the %s's name?"
|
41 |
msgstr ""
|
42 |
|
43 |
-
#: includes/class-freemius.php:
|
44 |
msgid "It's a temporary %s. I'm just debugging an issue."
|
45 |
msgstr ""
|
46 |
|
47 |
-
#: includes/class-freemius.php:
|
48 |
msgid "Deactivation"
|
49 |
msgstr ""
|
50 |
|
51 |
-
#: includes/class-freemius.php:
|
52 |
msgid "Theme Switch"
|
53 |
msgstr ""
|
54 |
|
55 |
-
#: includes/class-freemius.php:
|
56 |
msgid "Other"
|
57 |
msgstr ""
|
58 |
|
59 |
-
#: includes/class-freemius.php:
|
60 |
msgid "I no longer need the %s"
|
61 |
msgstr ""
|
62 |
|
63 |
-
#: includes/class-freemius.php:
|
64 |
msgid "I only needed the %s for a short period"
|
65 |
msgstr ""
|
66 |
|
67 |
-
#: includes/class-freemius.php:
|
68 |
msgid "The %s broke my site"
|
69 |
msgstr ""
|
70 |
|
71 |
-
#: includes/class-freemius.php:
|
72 |
msgid "The %s suddenly stopped working"
|
73 |
msgstr ""
|
74 |
|
75 |
-
#: includes/class-freemius.php:
|
76 |
msgid "I can't pay for it anymore"
|
77 |
msgstr ""
|
78 |
|
79 |
-
#: includes/class-freemius.php:
|
80 |
msgid "What price would you feel comfortable paying?"
|
81 |
msgstr ""
|
82 |
|
83 |
-
#: includes/class-freemius.php:
|
84 |
msgid "I don't like to share my information with you"
|
85 |
msgstr ""
|
86 |
|
87 |
-
#: includes/class-freemius.php:
|
88 |
msgid "The %s didn't work"
|
89 |
msgstr ""
|
90 |
|
91 |
-
#: includes/class-freemius.php:
|
92 |
msgid "I couldn't understand how to make it work"
|
93 |
msgstr ""
|
94 |
|
95 |
-
#: includes/class-freemius.php:
|
96 |
msgid "The %s is great, but I need specific feature that you don't support"
|
97 |
msgstr ""
|
98 |
|
99 |
-
#: includes/class-freemius.php:
|
100 |
msgid "What feature?"
|
101 |
msgstr ""
|
102 |
|
103 |
-
#: includes/class-freemius.php:
|
104 |
msgid "The %s is not working"
|
105 |
msgstr ""
|
106 |
|
107 |
-
#: includes/class-freemius.php:
|
108 |
msgid "Kindly share what didn't work so we can fix it for future users..."
|
109 |
msgstr ""
|
110 |
|
111 |
-
#: includes/class-freemius.php:
|
112 |
msgid "It's not what I was looking for"
|
113 |
msgstr ""
|
114 |
|
115 |
-
#: includes/class-freemius.php:
|
116 |
msgid "What you've been looking for?"
|
117 |
msgstr ""
|
118 |
|
119 |
-
#: includes/class-freemius.php:
|
120 |
msgid "The %s didn't work as expected"
|
121 |
msgstr ""
|
122 |
|
123 |
-
#: includes/class-freemius.php:
|
124 |
msgid "What did you expect?"
|
125 |
msgstr ""
|
126 |
|
127 |
-
#: includes/class-freemius.php:
|
128 |
msgid "Freemius Debug"
|
129 |
msgstr ""
|
130 |
|
131 |
-
#: includes/class-freemius.php:
|
132 |
msgid "I don't know what is cURL or how to install it, help me!"
|
133 |
msgstr ""
|
134 |
|
135 |
-
#: includes/class-freemius.php:
|
136 |
msgid "We'll make sure to contact your hosting company and resolve the issue. You will get a follow-up email to %s once we have an update."
|
137 |
msgstr ""
|
138 |
|
139 |
-
#: includes/class-freemius.php:
|
140 |
msgid "Great, please install cURL and enable it in your php.ini file. In addition, search for the 'disable_functions' directive in your php.ini file and remove any disabled methods starting with 'curl_'. To make sure it was successfully activated, use 'phpinfo()'. Once activated, deactivate the %s and reactivate it back again."
|
141 |
msgstr ""
|
142 |
|
143 |
-
#: includes/class-freemius.php:
|
144 |
msgid "Yes - do your thing"
|
145 |
msgstr ""
|
146 |
|
147 |
-
#: includes/class-freemius.php:
|
148 |
msgid "No - just deactivate"
|
149 |
msgstr ""
|
150 |
|
151 |
-
#: includes/class-freemius.php:
|
152 |
msgctxt "exclamation"
|
153 |
msgid "Oops"
|
154 |
msgstr ""
|
155 |
|
156 |
-
#: includes/class-freemius.php:
|
157 |
msgid "Thank for giving us the chance to fix it! A message was just sent to our technical staff. We will get back to you as soon as we have an update to %s. Appreciate your patience."
|
158 |
msgstr ""
|
159 |
|
160 |
-
#: includes/class-freemius.php:
|
161 |
-
msgid "You have purchased a %s license."
|
162 |
-
msgstr ""
|
163 |
-
|
164 |
-
#: includes/class-freemius.php:4929
|
165 |
-
msgid " The %s's %sdownload link%s, license key, and installation instructions have been sent to %s. If you can't find the email after 5 min, please check your spam box."
|
166 |
-
msgstr ""
|
167 |
-
|
168 |
-
#: includes/class-freemius.php:4939, includes/class-freemius.php:5914, includes/class-freemius.php:16976, includes/class-freemius.php:16987, includes/class-freemius.php:20354, includes/class-freemius.php:20718, includes/class-freemius.php:20787, includes/class-freemius.php:20959
|
169 |
-
msgctxt "interjection expressing joy or exuberance"
|
170 |
-
msgid "Yee-haw"
|
171 |
-
msgstr ""
|
172 |
-
|
173 |
-
#: includes/class-freemius.php:4953
|
174 |
msgctxt "addonX cannot run without pluginY"
|
175 |
msgid "%s cannot run without %s."
|
176 |
msgstr ""
|
177 |
|
178 |
-
#: includes/class-freemius.php:
|
179 |
msgctxt "addonX cannot run..."
|
180 |
msgid "%s cannot run without the plugin."
|
181 |
msgstr ""
|
182 |
|
183 |
-
#: includes/class-freemius.php:
|
184 |
msgid "Unexpected API error. Please contact the %s's author with the following error."
|
185 |
msgstr ""
|
186 |
|
187 |
-
#: includes/class-freemius.php:
|
188 |
msgid "Premium %s version was successfully activated."
|
189 |
msgstr ""
|
190 |
|
191 |
-
#: includes/class-freemius.php:
|
192 |
msgctxt "Used to express elation, enthusiasm, or triumph (especially in electronic communication)."
|
193 |
msgid "W00t"
|
194 |
msgstr ""
|
195 |
|
196 |
-
#: includes/class-freemius.php:
|
197 |
msgid "You have a %s license."
|
198 |
msgstr ""
|
199 |
|
200 |
-
#: includes/class-freemius.php:
|
|
|
|
|
|
|
|
|
|
|
201 |
msgid "%s free trial was successfully cancelled. Since the add-on is premium only it was automatically deactivated. If you like to use it in the future, you'll have to purchase a license."
|
202 |
msgstr ""
|
203 |
|
204 |
-
#: includes/class-freemius.php:
|
205 |
msgid "%s is a premium only add-on. You have to purchase a license first before activating the plugin."
|
206 |
msgstr ""
|
207 |
|
208 |
-
#: includes/class-freemius.php:
|
209 |
msgid "More information about %s"
|
210 |
msgstr ""
|
211 |
|
212 |
-
#: includes/class-freemius.php:
|
213 |
msgid "Purchase License"
|
214 |
msgstr ""
|
215 |
|
216 |
-
#: includes/class-freemius.php:
|
217 |
msgid "You should receive an activation email for %s to your mailbox at %s. Please make sure you click the activation button in that email to %s."
|
218 |
msgstr ""
|
219 |
|
220 |
-
#: includes/class-freemius.php:
|
221 |
msgid "start the trial"
|
222 |
msgstr ""
|
223 |
|
224 |
-
#: includes/class-freemius.php:
|
225 |
msgid "complete the install"
|
226 |
msgstr ""
|
227 |
|
228 |
-
#: includes/class-freemius.php:
|
229 |
msgid "You are just one step away - %s"
|
230 |
msgstr ""
|
231 |
|
232 |
-
#: includes/class-freemius.php:
|
233 |
msgctxt "%s - plugin name. As complete \"PluginX\" activation now"
|
234 |
msgid "Complete \"%s\" Activation Now"
|
235 |
msgstr ""
|
236 |
|
237 |
-
#: includes/class-freemius.php:
|
238 |
msgid "We made a few tweaks to the %s, %s"
|
239 |
msgstr ""
|
240 |
|
241 |
-
#: includes/class-freemius.php:
|
242 |
msgid "Opt in to make \"%s\" better!"
|
243 |
msgstr ""
|
244 |
|
245 |
-
#: includes/class-freemius.php:
|
246 |
msgid "The upgrade of %s was successfully completed."
|
247 |
msgstr ""
|
248 |
|
249 |
-
#: includes/class-freemius.php:
|
250 |
msgid "Add-On"
|
251 |
msgstr ""
|
252 |
|
253 |
-
#: includes/class-freemius.php:
|
254 |
msgid "Plugin"
|
255 |
msgstr ""
|
256 |
|
257 |
-
#: includes/class-freemius.php:
|
258 |
msgid "Theme"
|
259 |
msgstr ""
|
260 |
|
261 |
-
#: includes/class-freemius.php:
|
262 |
msgid "An unknown error has occurred while trying to toggle the license's white-label mode."
|
263 |
msgstr ""
|
264 |
|
265 |
-
#: includes/class-freemius.php:
|
266 |
msgid "Your %s license was flagged as white-labeled to hide sensitive information from the WP Admin (e.g. your email, license key, prices, billing address & invoices). If you ever wish to revert it back, you can easily do it through your %s. If this was a mistake you can also %s."
|
267 |
msgstr ""
|
268 |
|
269 |
-
#: includes/class-freemius.php:
|
270 |
msgid "User Dashboard"
|
271 |
msgstr ""
|
272 |
|
273 |
-
#: includes/class-freemius.php:
|
274 |
msgid "revert it now"
|
275 |
msgstr ""
|
276 |
|
277 |
-
#: includes/class-freemius.php:
|
278 |
msgid "An unknown error has occurred while trying to set the user's beta mode."
|
279 |
msgstr ""
|
280 |
|
281 |
-
#: includes/class-freemius.php:
|
282 |
msgid "Invalid new user ID or email address."
|
283 |
msgstr ""
|
284 |
|
285 |
-
#: includes/class-freemius.php:
|
286 |
msgid "Sorry, we could not complete the email update. Another user with the same email is already registered."
|
287 |
msgstr ""
|
288 |
|
289 |
-
#: includes/class-freemius.php:
|
290 |
msgid "If you would like to give up the ownership of the %s's account to %s click the Change Ownership button."
|
291 |
msgstr ""
|
292 |
|
293 |
-
#: includes/class-freemius.php:
|
294 |
msgid "Change Ownership"
|
295 |
msgstr ""
|
296 |
|
297 |
-
#: includes/class-freemius.php:
|
298 |
msgid "Invalid site details collection."
|
299 |
msgstr ""
|
300 |
|
301 |
-
#: includes/class-freemius.php:
|
302 |
msgid "We couldn't find your email address in the system, are you sure it's the right address?"
|
303 |
msgstr ""
|
304 |
|
305 |
-
#: includes/class-freemius.php:
|
306 |
msgid "We can't see any active licenses associated with that email address, are you sure it's the right address?"
|
307 |
msgstr ""
|
308 |
|
309 |
-
#: includes/class-freemius.php:
|
310 |
msgid "Account is pending activation."
|
311 |
msgstr ""
|
312 |
|
313 |
-
#: includes/class-freemius.php:
|
314 |
msgid "Buy a license now"
|
315 |
msgstr ""
|
316 |
|
317 |
-
#: includes/class-freemius.php:
|
318 |
msgid "Renew your license now"
|
319 |
msgstr ""
|
320 |
|
321 |
-
#: includes/class-freemius.php:
|
322 |
msgid "%s to access version %s security & feature updates, and support."
|
323 |
msgstr ""
|
324 |
|
325 |
-
#: includes/class-freemius.php:
|
326 |
msgid "%s activation was successfully completed."
|
327 |
msgstr ""
|
328 |
|
329 |
-
#: includes/class-freemius.php:
|
330 |
msgid "Your account was successfully activated with the %s plan."
|
331 |
msgstr ""
|
332 |
|
333 |
-
#: includes/class-freemius.php:
|
334 |
msgid "Your trial has been successfully started."
|
335 |
msgstr ""
|
336 |
|
337 |
-
#: includes/class-freemius.php:
|
338 |
msgid "Couldn't activate %s."
|
339 |
msgstr ""
|
340 |
|
341 |
-
#: includes/class-freemius.php:
|
342 |
msgid "Please contact us with the following message:"
|
343 |
msgstr ""
|
344 |
|
345 |
-
#: includes/class-freemius.php:
|
346 |
msgid "An unknown error has occurred."
|
347 |
msgstr ""
|
348 |
|
349 |
-
#: includes/class-freemius.php:
|
350 |
msgid "Upgrade"
|
351 |
msgstr ""
|
352 |
|
353 |
-
#: includes/class-freemius.php:
|
354 |
msgid "Start Trial"
|
355 |
msgstr ""
|
356 |
|
357 |
-
#: includes/class-freemius.php:
|
358 |
msgid "Pricing"
|
359 |
msgstr ""
|
360 |
|
361 |
-
#: includes/class-freemius.php:
|
362 |
msgid "Affiliation"
|
363 |
msgstr ""
|
364 |
|
365 |
-
#: includes/class-freemius.php:
|
366 |
msgid "Account"
|
367 |
msgstr ""
|
368 |
|
369 |
-
#: includes/class-freemius.php:
|
370 |
msgid "Contact Us"
|
371 |
msgstr ""
|
372 |
|
373 |
-
#: includes/class-freemius.php:
|
374 |
msgid "Add-Ons"
|
375 |
msgstr ""
|
376 |
|
377 |
-
#: includes/class-freemius.php:
|
378 |
msgctxt "ASCII arrow left icon"
|
379 |
msgid "←"
|
380 |
msgstr ""
|
381 |
|
382 |
-
#: includes/class-freemius.php:
|
383 |
msgctxt "ASCII arrow right icon"
|
384 |
msgid "➤"
|
385 |
msgstr ""
|
386 |
|
387 |
-
#: includes/class-freemius.php:
|
388 |
msgctxt "noun"
|
389 |
msgid "Pricing"
|
390 |
msgstr ""
|
391 |
|
392 |
-
#: includes/class-freemius.php:
|
393 |
msgid "Support Forum"
|
394 |
msgstr ""
|
395 |
|
396 |
-
#: includes/class-freemius.php:
|
397 |
msgid "Your email has been successfully verified - you are AWESOME!"
|
398 |
msgstr ""
|
399 |
|
400 |
-
#: includes/class-freemius.php:
|
401 |
msgctxt "a positive response"
|
402 |
msgid "Right on"
|
403 |
msgstr ""
|
404 |
|
405 |
-
#: includes/class-freemius.php:
|
406 |
msgid "seems like the key you entered doesn't match our records."
|
407 |
msgstr ""
|
408 |
|
409 |
-
#: includes/class-freemius.php:
|
410 |
msgid "Debug mode was successfully enabled and will be automatically disabled in 60 min. You can also disable it earlier by clicking the \"Stop Debug\" link."
|
411 |
msgstr ""
|
412 |
|
413 |
-
#: includes/class-freemius.php:
|
414 |
msgid "Your %s Add-on plan was successfully upgraded."
|
415 |
msgstr ""
|
416 |
|
417 |
-
#: includes/class-freemius.php:
|
418 |
msgid "%s Add-on was successfully purchased."
|
419 |
msgstr ""
|
420 |
|
421 |
-
#: includes/class-freemius.php:
|
422 |
msgid "Download the latest version"
|
423 |
msgstr ""
|
424 |
|
425 |
-
#: includes/class-freemius.php:
|
426 |
msgid "Your server is blocking the access to Freemius' API, which is crucial for %1$s synchronization. Please contact your host to whitelist %2$s"
|
427 |
msgstr ""
|
428 |
|
429 |
-
#: includes/class-freemius.php:
|
430 |
msgid "Error received from the server:"
|
431 |
msgstr ""
|
432 |
|
433 |
-
#: includes/class-freemius.php:
|
434 |
msgid "It seems like one of the authentication parameters is wrong. Update your Public Key, Secret Key & User ID, and try again."
|
435 |
msgstr ""
|
436 |
|
437 |
-
#: includes/class-freemius.php:
|
438 |
msgctxt "something somebody says when they are thinking about what you have just said."
|
439 |
msgid "Hmm"
|
440 |
msgstr ""
|
441 |
|
442 |
-
#: includes/class-freemius.php:
|
443 |
msgid "It looks like you are still on the %s plan. If you did upgrade or change your plan, it's probably an issue on our side - sorry."
|
444 |
msgstr ""
|
445 |
|
446 |
-
#: includes/class-freemius.php:
|
447 |
msgctxt "trial period"
|
448 |
msgid "Trial"
|
449 |
msgstr ""
|
450 |
|
451 |
-
#: includes/class-freemius.php:
|
452 |
msgid "I have upgraded my account but when I try to Sync the License, the plan remains %s."
|
453 |
msgstr ""
|
454 |
|
455 |
-
#: includes/class-freemius.php:
|
456 |
msgid "Please contact us here"
|
457 |
msgstr ""
|
458 |
|
459 |
-
#: includes/class-freemius.php:
|
460 |
msgid "Your plan was successfully activated."
|
461 |
msgstr ""
|
462 |
|
463 |
-
#: includes/class-freemius.php:
|
464 |
msgid "Your plan was successfully upgraded."
|
465 |
msgstr ""
|
466 |
|
467 |
-
#: includes/class-freemius.php:
|
468 |
msgid "Your plan was successfully changed to %s."
|
469 |
msgstr ""
|
470 |
|
471 |
-
#: includes/class-freemius.php:
|
472 |
msgid "Your license has expired. You can still continue using the free %s forever."
|
473 |
msgstr ""
|
474 |
|
475 |
-
#: includes/class-freemius.php:
|
476 |
msgid "Your license has expired. %1$sUpgrade now%2$s to continue using the %3$s without interruptions."
|
477 |
msgstr ""
|
478 |
|
479 |
-
#: includes/class-freemius.php:
|
480 |
msgid "Your license has been cancelled. If you think it's a mistake, please contact support."
|
481 |
msgstr ""
|
482 |
|
483 |
-
#: includes/class-freemius.php:
|
484 |
msgid "Your license has expired. You can still continue using all the %s features, but you'll need to renew your license to continue getting updates and support."
|
485 |
msgstr ""
|
486 |
|
487 |
-
#: includes/class-freemius.php:
|
488 |
msgid "Your free trial has expired. You can still continue using all our free features."
|
489 |
msgstr ""
|
490 |
|
491 |
-
#: includes/class-freemius.php:
|
492 |
msgid "Your free trial has expired. %1$sUpgrade now%2$s to continue using the %3$s without interruptions."
|
493 |
msgstr ""
|
494 |
|
495 |
-
#: includes/class-freemius.php:
|
496 |
msgid "It looks like the license could not be activated."
|
497 |
msgstr ""
|
498 |
|
499 |
-
#: includes/class-freemius.php:
|
500 |
msgid "Your license was successfully activated."
|
501 |
msgstr ""
|
502 |
|
503 |
-
#: includes/class-freemius.php:
|
504 |
msgid "It looks like your site currently doesn't have an active license."
|
505 |
msgstr ""
|
506 |
|
507 |
-
#: includes/class-freemius.php:
|
508 |
msgid "It looks like the license deactivation failed."
|
509 |
msgstr ""
|
510 |
|
511 |
-
#: includes/class-freemius.php:
|
512 |
msgid "Your %s license was successfully deactivated."
|
513 |
msgstr ""
|
514 |
|
515 |
-
#: includes/class-freemius.php:
|
516 |
msgid "Your license was successfully deactivated, you are back to the %s plan."
|
517 |
msgstr ""
|
518 |
|
519 |
-
#: includes/class-freemius.php:
|
520 |
msgid "O.K"
|
521 |
msgstr ""
|
522 |
|
523 |
-
#: includes/class-freemius.php:
|
524 |
msgid "Seems like we are having some temporary issue with your subscription cancellation. Please try again in few minutes."
|
525 |
msgstr ""
|
526 |
|
527 |
-
#: includes/class-freemius.php:
|
528 |
msgid "Your subscription was successfully cancelled. Your %s plan license will expire in %s."
|
529 |
msgstr ""
|
530 |
|
531 |
-
#: includes/class-freemius.php:
|
532 |
msgid "You are already running the %s in a trial mode."
|
533 |
msgstr ""
|
534 |
|
535 |
-
#: includes/class-freemius.php:
|
536 |
msgid "You already utilized a trial before."
|
537 |
msgstr ""
|
538 |
|
539 |
-
#: includes/class-freemius.php:
|
540 |
msgid "Plan %s do not exist, therefore, can't start a trial."
|
541 |
msgstr ""
|
542 |
|
543 |
-
#: includes/class-freemius.php:
|
544 |
msgid "Plan %s does not support a trial period."
|
545 |
msgstr ""
|
546 |
|
547 |
-
#: includes/class-freemius.php:
|
548 |
msgid "None of the %s's plans supports a trial period."
|
549 |
msgstr ""
|
550 |
|
551 |
-
#: includes/class-freemius.php:
|
552 |
msgid "It looks like you are not in trial mode anymore so there's nothing to cancel :)"
|
553 |
msgstr ""
|
554 |
|
555 |
-
#: includes/class-freemius.php:
|
556 |
msgid "Seems like we are having some temporary issue with your trial cancellation. Please try again in few minutes."
|
557 |
msgstr ""
|
558 |
|
559 |
-
#: includes/class-freemius.php:
|
560 |
msgid "Your %s free trial was successfully cancelled."
|
561 |
msgstr ""
|
562 |
|
563 |
-
#: includes/class-freemius.php:
|
564 |
msgid "Version %s was released."
|
565 |
msgstr ""
|
566 |
|
567 |
-
#: includes/class-freemius.php:
|
568 |
msgid "Please download %s."
|
569 |
msgstr ""
|
570 |
|
571 |
-
#: includes/class-freemius.php:
|
572 |
msgid "the latest %s version here"
|
573 |
msgstr ""
|
574 |
|
575 |
-
#: includes/class-freemius.php:
|
576 |
msgid "New"
|
577 |
msgstr ""
|
578 |
|
579 |
-
#: includes/class-freemius.php:
|
580 |
msgid "Seems like you got the latest release."
|
581 |
msgstr ""
|
582 |
|
583 |
-
#: includes/class-freemius.php:
|
584 |
msgid "You are all good!"
|
585 |
msgstr ""
|
586 |
|
587 |
-
#: includes/class-freemius.php:
|
588 |
msgid "Verification mail was just sent to %s. If you can't find it after 5 min, please check your spam box."
|
589 |
msgstr ""
|
590 |
|
591 |
-
#: includes/class-freemius.php:
|
592 |
msgid "Site successfully opted in."
|
593 |
msgstr ""
|
594 |
|
595 |
-
#: includes/class-freemius.php:
|
596 |
msgid "Awesome"
|
597 |
msgstr ""
|
598 |
|
599 |
-
#: includes/class-freemius.php:
|
600 |
msgid "We appreciate your help in making the %s better by letting us track some usage data."
|
601 |
msgstr ""
|
602 |
|
603 |
-
#: includes/class-freemius.php:
|
604 |
msgid "Thank you!"
|
605 |
msgstr ""
|
606 |
|
607 |
-
#: includes/class-freemius.php:
|
608 |
msgid "We will no longer be sending any usage data of %s on %s to %s."
|
609 |
msgstr ""
|
610 |
|
611 |
-
#: includes/class-freemius.php:
|
612 |
msgid "Please check your mailbox, you should receive an email via %s to confirm the ownership change. From security reasons, you must confirm the change within the next 15 min. If you cannot find the email, please check your spam folder."
|
613 |
msgstr ""
|
614 |
|
615 |
-
#: includes/class-freemius.php:
|
616 |
msgid "Thanks for confirming the ownership change. An email was just sent to %s for final approval."
|
617 |
msgstr ""
|
618 |
|
619 |
-
#: includes/class-freemius.php:
|
620 |
msgid "%s is the new owner of the account."
|
621 |
msgstr ""
|
622 |
|
623 |
-
#: includes/class-freemius.php:
|
624 |
msgctxt "as congratulations"
|
625 |
msgid "Congrats"
|
626 |
msgstr ""
|
627 |
|
628 |
-
#: includes/class-freemius.php:
|
629 |
msgid "Your email was successfully updated. You should receive an email with confirmation instructions in few moments."
|
630 |
msgstr ""
|
631 |
|
632 |
-
#: includes/class-freemius.php:
|
633 |
msgid "Please provide your full name."
|
634 |
msgstr ""
|
635 |
|
636 |
-
#: includes/class-freemius.php:
|
637 |
msgid "Your name was successfully updated."
|
638 |
msgstr ""
|
639 |
|
640 |
-
#: includes/class-freemius.php:
|
641 |
msgid "You have successfully updated your %s."
|
642 |
msgstr ""
|
643 |
|
644 |
-
#: includes/class-freemius.php:
|
645 |
msgid "Is this your client's site? %s if you wish to hide sensitive info like your email, license key, prices, billing address & invoices from the WP Admin."
|
646 |
msgstr ""
|
647 |
|
648 |
-
#: includes/class-freemius.php:
|
649 |
msgid "Click here"
|
650 |
msgstr ""
|
651 |
|
652 |
-
#: includes/class-freemius.php:
|
653 |
msgid "Just letting you know that the add-ons information of %s is being pulled from an external server."
|
654 |
msgstr ""
|
655 |
|
656 |
-
#: includes/class-freemius.php:
|
657 |
msgctxt "advance notice of something that will need attention."
|
658 |
msgid "Heads up"
|
659 |
msgstr ""
|
660 |
|
661 |
-
#: includes/class-freemius.php:
|
662 |
msgctxt "exclamation"
|
663 |
msgid "Hey"
|
664 |
msgstr ""
|
665 |
|
666 |
-
#: includes/class-freemius.php:
|
667 |
msgid "How do you like %s so far? Test all our %s premium features with a %d-day free trial."
|
668 |
msgstr ""
|
669 |
|
670 |
-
#: includes/class-freemius.php:
|
671 |
msgid "No commitment for %s days - cancel anytime!"
|
672 |
msgstr ""
|
673 |
|
674 |
-
#: includes/class-freemius.php:
|
675 |
msgid "No credit card required"
|
676 |
msgstr ""
|
677 |
|
678 |
-
#: includes/class-freemius.php:
|
679 |
msgctxt "call to action"
|
680 |
msgid "Start free trial"
|
681 |
msgstr ""
|
682 |
|
683 |
-
#: includes/class-freemius.php:
|
684 |
msgid "Hey there, did you know that %s has an affiliate program? If you like the %s you can become our ambassador and earn some cash!"
|
685 |
msgstr ""
|
686 |
|
687 |
-
#: includes/class-freemius.php:
|
688 |
msgid "Learn more"
|
689 |
msgstr ""
|
690 |
|
691 |
-
#: includes/class-freemius.php:
|
692 |
msgid "Activate License"
|
693 |
msgstr ""
|
694 |
|
695 |
-
#: includes/class-freemius.php:
|
696 |
msgid "Change License"
|
697 |
msgstr ""
|
698 |
|
699 |
-
#: includes/class-freemius.php:
|
700 |
msgid "Opt Out"
|
701 |
msgstr ""
|
702 |
|
703 |
-
#: includes/class-freemius.php:
|
704 |
msgid "Opt In"
|
705 |
msgstr ""
|
706 |
|
707 |
-
#: includes/class-freemius.php:
|
708 |
msgid " The paid version of %1$s is already installed. Please activate it to start benefiting the %2$s features. %3$s"
|
709 |
msgstr ""
|
710 |
|
711 |
-
#: includes/class-freemius.php:
|
712 |
msgid "Activate %s features"
|
713 |
msgstr ""
|
714 |
|
715 |
-
#: includes/class-freemius.php:
|
716 |
msgid "Please follow these steps to complete the upgrade"
|
717 |
msgstr ""
|
718 |
|
719 |
-
#: includes/class-freemius.php:
|
720 |
msgid "Download the latest %s version"
|
721 |
msgstr ""
|
722 |
|
723 |
-
#: includes/class-freemius.php:
|
724 |
msgid "Upload and activate the downloaded version"
|
725 |
msgstr ""
|
726 |
|
727 |
-
#: includes/class-freemius.php:
|
728 |
msgid "How to upload and activate?"
|
729 |
msgstr ""
|
730 |
|
731 |
-
#: includes/class-freemius.php:
|
732 |
msgid "%sClick here%s to choose the sites where you'd like to activate the license on."
|
733 |
msgstr ""
|
734 |
|
735 |
-
#: includes/class-freemius.php:
|
736 |
msgid "Auto installation only works for opted-in users."
|
737 |
msgstr ""
|
738 |
|
739 |
-
#: includes/class-freemius.php:
|
740 |
msgid "Invalid module ID."
|
741 |
msgstr ""
|
742 |
|
743 |
-
#: includes/class-freemius.php:
|
744 |
msgid "Premium version already active."
|
745 |
msgstr ""
|
746 |
|
747 |
-
#: includes/class-freemius.php:
|
748 |
msgid "You do not have a valid license to access the premium version."
|
749 |
msgstr ""
|
750 |
|
751 |
-
#: includes/class-freemius.php:
|
752 |
msgid "Plugin is a \"Serviceware\" which means it does not have a premium code version."
|
753 |
msgstr ""
|
754 |
|
755 |
-
#: includes/class-freemius.php:
|
756 |
msgid "Premium add-on version already installed."
|
757 |
msgstr ""
|
758 |
|
759 |
-
#: includes/class-freemius.php:
|
760 |
msgid "View paid features"
|
761 |
msgstr ""
|
762 |
|
763 |
-
#: includes/class-freemius.php:
|
764 |
msgid "Thank you so much for using %s and its add-ons!"
|
765 |
msgstr ""
|
766 |
|
767 |
-
#: includes/class-freemius.php:
|
768 |
msgid "Thank you so much for using %s!"
|
769 |
msgstr ""
|
770 |
|
771 |
-
#: includes/class-freemius.php:
|
772 |
msgid "You've already opted-in to our usage-tracking, which helps us keep improving the %s."
|
773 |
msgstr ""
|
774 |
|
775 |
-
#: includes/class-freemius.php:
|
776 |
msgid "Thank you so much for using our products!"
|
777 |
msgstr ""
|
778 |
|
779 |
-
#: includes/class-freemius.php:
|
780 |
msgid "You've already opted-in to our usage-tracking, which helps us keep improving them."
|
781 |
msgstr ""
|
782 |
|
783 |
-
#: includes/class-freemius.php:
|
784 |
msgid "%s and its add-ons"
|
785 |
msgstr ""
|
786 |
|
787 |
-
#: includes/class-freemius.php:
|
788 |
msgid "Products"
|
789 |
msgstr ""
|
790 |
|
791 |
-
#: includes/class-freemius.php:
|
792 |
msgid "Yes"
|
793 |
msgstr ""
|
794 |
|
795 |
-
#: includes/class-freemius.php:
|
796 |
msgid "send me security & feature updates, educational content and offers."
|
797 |
msgstr ""
|
798 |
|
799 |
-
#: includes/class-freemius.php:
|
800 |
msgid "No"
|
801 |
msgstr ""
|
802 |
|
803 |
-
#: includes/class-freemius.php:
|
804 |
msgid "do %sNOT%s send me security & feature updates, educational content and offers."
|
805 |
msgstr ""
|
806 |
|
807 |
-
#: includes/class-freemius.php:
|
808 |
msgid "Due to the new %sEU General Data Protection Regulation (GDPR)%s compliance requirements it is required that you provide your explicit consent, again, confirming that you are onboard :-)"
|
809 |
msgstr ""
|
810 |
|
811 |
-
#: includes/class-freemius.php:
|
812 |
msgid "Please let us know if you'd like us to contact you for security & feature updates, educational content, and occasional offers:"
|
813 |
msgstr ""
|
814 |
|
815 |
-
#: includes/class-freemius.php:
|
816 |
msgid "License key is empty."
|
817 |
msgstr ""
|
818 |
|
@@ -824,31 +816,31 @@ msgstr ""
|
|
824 |
msgid "Buy license"
|
825 |
msgstr ""
|
826 |
|
827 |
-
#: includes/class-fs-plugin-updater.php:
|
828 |
msgid "There is a %s of %s available."
|
829 |
msgstr ""
|
830 |
|
831 |
-
#: includes/class-fs-plugin-updater.php:
|
832 |
msgid "new Beta version"
|
833 |
msgstr ""
|
834 |
|
835 |
-
#: includes/class-fs-plugin-updater.php:
|
836 |
msgid "new version"
|
837 |
msgstr ""
|
838 |
|
839 |
-
#: includes/class-fs-plugin-updater.php:
|
840 |
msgid "Important Upgrade Notice:"
|
841 |
msgstr ""
|
842 |
|
843 |
-
#: includes/class-fs-plugin-updater.php:
|
844 |
msgid "Installing plugin: %s"
|
845 |
msgstr ""
|
846 |
|
847 |
-
#: includes/class-fs-plugin-updater.php:
|
848 |
msgid "Unable to connect to the filesystem. Please confirm your credentials."
|
849 |
msgstr ""
|
850 |
|
851 |
-
#: includes/class-fs-plugin-updater.php:
|
852 |
msgid "The remote plugin package does not contain a folder with the desired slug and renaming did not work."
|
853 |
msgstr ""
|
854 |
|
@@ -895,7 +887,7 @@ msgstr ""
|
|
895 |
msgid "Activate this add-on"
|
896 |
msgstr ""
|
897 |
|
898 |
-
#: includes/fs-plugin-info-dialog.php:789, templates/connect.php:
|
899 |
msgid "Activate Free Version"
|
900 |
msgstr ""
|
901 |
|
@@ -1497,198 +1489,198 @@ msgid "PCI compliant"
|
|
1497 |
msgstr ""
|
1498 |
|
1499 |
#. translators: %s: name (e.g. Hey John,)
|
1500 |
-
#: templates/connect.php:
|
1501 |
msgctxt "greeting"
|
1502 |
msgid "Hey %s,"
|
1503 |
msgstr ""
|
1504 |
|
1505 |
-
#: templates/connect.php:
|
1506 |
msgid "Allow & Continue"
|
1507 |
msgstr ""
|
1508 |
|
1509 |
-
#: templates/connect.php:
|
1510 |
msgid "Re-send activation email"
|
1511 |
msgstr ""
|
1512 |
|
1513 |
-
#: templates/connect.php:
|
1514 |
msgid "Thanks %s!"
|
1515 |
msgstr ""
|
1516 |
|
1517 |
-
#: templates/connect.php:
|
1518 |
msgid "Agree & Activate License"
|
1519 |
msgstr ""
|
1520 |
|
1521 |
-
#: templates/connect.php:
|
1522 |
msgid "Welcome to %s! To get started, please enter your license key:"
|
1523 |
msgstr ""
|
1524 |
|
1525 |
-
#: templates/connect.php:
|
1526 |
msgid "Never miss an important update - opt in to our security & feature updates notifications, educational content, offers, and non-sensitive diagnostic tracking with %4$s."
|
1527 |
msgstr ""
|
1528 |
|
1529 |
-
#: templates/connect.php:
|
1530 |
msgid "Never miss an important update - opt in to our security and feature updates notifications, and non-sensitive diagnostic tracking with %4$s."
|
1531 |
msgstr ""
|
1532 |
|
1533 |
-
#: templates/connect.php:
|
1534 |
msgid "Never miss an important update - opt in to our security & feature updates notifications, educational content, offers, and non-sensitive diagnostic tracking with %4$s. If you skip this, that's okay! %1$s will still work just fine."
|
1535 |
msgstr ""
|
1536 |
|
1537 |
-
#: templates/connect.php:
|
1538 |
msgid "Never miss an important update - opt in to our security & feature updates notifications, and non-sensitive diagnostic tracking with %4$s. If you skip this, that's okay! %1$s will still work just fine."
|
1539 |
msgstr ""
|
1540 |
|
1541 |
-
#: templates/connect.php:
|
1542 |
msgid "We're excited to introduce the Freemius network-level integration."
|
1543 |
msgstr ""
|
1544 |
|
1545 |
-
#: templates/connect.php:
|
1546 |
msgid "During the update process we detected %d site(s) that are still pending license activation."
|
1547 |
msgstr ""
|
1548 |
|
1549 |
-
#: templates/connect.php:
|
1550 |
msgid "If you'd like to use the %s on those sites, please enter your license key below and click the activation button."
|
1551 |
msgstr ""
|
1552 |
|
1553 |
-
#: templates/connect.php:
|
1554 |
msgid "%s's paid features"
|
1555 |
msgstr ""
|
1556 |
|
1557 |
-
#: templates/connect.php:
|
1558 |
msgid "Alternatively, you can skip it for now and activate the license later, in your %s's network-level Account page."
|
1559 |
msgstr ""
|
1560 |
|
1561 |
-
#: templates/connect.php:
|
1562 |
msgid "During the update process we detected %s site(s) in the network that are still pending your attention."
|
1563 |
msgstr ""
|
1564 |
|
1565 |
-
#: templates/connect.php:
|
1566 |
msgid "License key"
|
1567 |
msgstr ""
|
1568 |
|
1569 |
-
#: templates/connect.php:
|
1570 |
msgid "Can't find your license key?"
|
1571 |
msgstr ""
|
1572 |
|
1573 |
-
#: templates/connect.php:
|
1574 |
msgctxt "verb"
|
1575 |
msgid "Skip"
|
1576 |
msgstr ""
|
1577 |
|
1578 |
-
#: templates/connect.php:
|
1579 |
msgid "Delegate to Site Admins"
|
1580 |
msgstr ""
|
1581 |
|
1582 |
-
#: templates/connect.php:
|
1583 |
msgid "If you click it, this decision will be delegated to the sites administrators."
|
1584 |
msgstr ""
|
1585 |
|
1586 |
-
#: templates/connect.php:
|
1587 |
msgid "License issues?"
|
1588 |
msgstr ""
|
1589 |
|
1590 |
-
#: templates/connect.php:
|
1591 |
msgid "Your Profile Overview"
|
1592 |
msgstr ""
|
1593 |
|
1594 |
-
#: templates/connect.php:
|
1595 |
msgid "Name and email address"
|
1596 |
msgstr ""
|
1597 |
|
1598 |
-
#: templates/connect.php:
|
1599 |
msgid "So you can manage and control your license remotely from the User Dashboard."
|
1600 |
msgstr ""
|
1601 |
|
1602 |
-
#: templates/connect.php:
|
1603 |
msgid "Your Site Overview"
|
1604 |
msgstr ""
|
1605 |
|
1606 |
-
#: templates/connect.php:
|
1607 |
msgid "Site URL, WP version, PHP info"
|
1608 |
msgstr ""
|
1609 |
|
1610 |
-
#: templates/connect.php:
|
1611 |
msgid "Admin Notices"
|
1612 |
msgstr ""
|
1613 |
|
1614 |
-
#: templates/connect.php:
|
1615 |
msgid "Updates, announcements, marketing, no spam"
|
1616 |
msgstr ""
|
1617 |
|
1618 |
-
#: templates/connect.php:
|
1619 |
msgid "So you can reuse the license when the %s is no longer active."
|
1620 |
msgstr ""
|
1621 |
|
1622 |
-
#: templates/connect.php:
|
1623 |
msgid "Current %s Status"
|
1624 |
msgstr ""
|
1625 |
|
1626 |
-
#: templates/connect.php:
|
1627 |
msgid "Active, deactivated, or uninstalled"
|
1628 |
msgstr ""
|
1629 |
|
1630 |
-
#: templates/connect.php:
|
1631 |
msgid "Newsletter"
|
1632 |
msgstr ""
|
1633 |
|
1634 |
-
#: templates/connect.php:
|
1635 |
msgid "Plugins & Themes"
|
1636 |
msgstr ""
|
1637 |
|
1638 |
-
#: templates/connect.php:
|
1639 |
msgid "optional"
|
1640 |
msgstr ""
|
1641 |
|
1642 |
-
#: templates/connect.php:
|
1643 |
msgid "To help us troubleshoot any potential issues that may arise from other plugin or theme conflicts."
|
1644 |
msgstr ""
|
1645 |
|
1646 |
-
#: templates/connect.php:
|
1647 |
msgid "Title, slug, version, and is active"
|
1648 |
msgstr ""
|
1649 |
|
1650 |
-
#: templates/connect.php:
|
1651 |
msgid "The %1$s will periodically send %2$s to %3$s for security & feature updates delivery, and license management."
|
1652 |
msgstr ""
|
1653 |
|
1654 |
-
#: templates/connect.php:
|
1655 |
msgid "diagnostic data"
|
1656 |
msgstr ""
|
1657 |
|
1658 |
-
#: templates/connect.php:
|
1659 |
msgid "Freemius is our licensing and software updates engine"
|
1660 |
msgstr ""
|
1661 |
|
1662 |
-
#: templates/connect.php:
|
1663 |
msgid "What permissions are being granted?"
|
1664 |
msgstr ""
|
1665 |
|
1666 |
-
#: templates/connect.php:
|
1667 |
msgid "Don't have a license key?"
|
1668 |
msgstr ""
|
1669 |
|
1670 |
-
#: templates/connect.php:
|
1671 |
msgid "Have a license key?"
|
1672 |
msgstr ""
|
1673 |
|
1674 |
-
#: templates/connect.php:
|
1675 |
msgid "Privacy Policy"
|
1676 |
msgstr ""
|
1677 |
|
1678 |
-
#: templates/connect.php:
|
1679 |
msgid "License Agreement"
|
1680 |
msgstr ""
|
1681 |
|
1682 |
-
#: templates/connect.php:
|
1683 |
msgid "Terms of Service"
|
1684 |
msgstr ""
|
1685 |
|
1686 |
-
#: templates/connect.php:
|
1687 |
msgctxt "as in the process of sending an email"
|
1688 |
msgid "Sending email"
|
1689 |
msgstr ""
|
1690 |
|
1691 |
-
#: templates/connect.php:
|
1692 |
msgctxt "as activating plugin"
|
1693 |
msgid "Activating"
|
1694 |
msgstr ""
|
32 |
msgid "Error"
|
33 |
msgstr ""
|
34 |
|
35 |
+
#: includes/class-freemius.php:2540
|
36 |
msgid "I found a better %s"
|
37 |
msgstr ""
|
38 |
|
39 |
+
#: includes/class-freemius.php:2542
|
40 |
msgid "What's the %s's name?"
|
41 |
msgstr ""
|
42 |
|
43 |
+
#: includes/class-freemius.php:2548
|
44 |
msgid "It's a temporary %s. I'm just debugging an issue."
|
45 |
msgstr ""
|
46 |
|
47 |
+
#: includes/class-freemius.php:2550
|
48 |
msgid "Deactivation"
|
49 |
msgstr ""
|
50 |
|
51 |
+
#: includes/class-freemius.php:2551
|
52 |
msgid "Theme Switch"
|
53 |
msgstr ""
|
54 |
|
55 |
+
#: includes/class-freemius.php:2560, templates/forms/resend-key.php:24, templates/forms/user-change.php:29
|
56 |
msgid "Other"
|
57 |
msgstr ""
|
58 |
|
59 |
+
#: includes/class-freemius.php:2568
|
60 |
msgid "I no longer need the %s"
|
61 |
msgstr ""
|
62 |
|
63 |
+
#: includes/class-freemius.php:2575
|
64 |
msgid "I only needed the %s for a short period"
|
65 |
msgstr ""
|
66 |
|
67 |
+
#: includes/class-freemius.php:2581
|
68 |
msgid "The %s broke my site"
|
69 |
msgstr ""
|
70 |
|
71 |
+
#: includes/class-freemius.php:2588
|
72 |
msgid "The %s suddenly stopped working"
|
73 |
msgstr ""
|
74 |
|
75 |
+
#: includes/class-freemius.php:2598
|
76 |
msgid "I can't pay for it anymore"
|
77 |
msgstr ""
|
78 |
|
79 |
+
#: includes/class-freemius.php:2600
|
80 |
msgid "What price would you feel comfortable paying?"
|
81 |
msgstr ""
|
82 |
|
83 |
+
#: includes/class-freemius.php:2606
|
84 |
msgid "I don't like to share my information with you"
|
85 |
msgstr ""
|
86 |
|
87 |
+
#: includes/class-freemius.php:2627
|
88 |
msgid "The %s didn't work"
|
89 |
msgstr ""
|
90 |
|
91 |
+
#: includes/class-freemius.php:2637
|
92 |
msgid "I couldn't understand how to make it work"
|
93 |
msgstr ""
|
94 |
|
95 |
+
#: includes/class-freemius.php:2645
|
96 |
msgid "The %s is great, but I need specific feature that you don't support"
|
97 |
msgstr ""
|
98 |
|
99 |
+
#: includes/class-freemius.php:2647
|
100 |
msgid "What feature?"
|
101 |
msgstr ""
|
102 |
|
103 |
+
#: includes/class-freemius.php:2651
|
104 |
msgid "The %s is not working"
|
105 |
msgstr ""
|
106 |
|
107 |
+
#: includes/class-freemius.php:2653
|
108 |
msgid "Kindly share what didn't work so we can fix it for future users..."
|
109 |
msgstr ""
|
110 |
|
111 |
+
#: includes/class-freemius.php:2657
|
112 |
msgid "It's not what I was looking for"
|
113 |
msgstr ""
|
114 |
|
115 |
+
#: includes/class-freemius.php:2659
|
116 |
msgid "What you've been looking for?"
|
117 |
msgstr ""
|
118 |
|
119 |
+
#: includes/class-freemius.php:2663
|
120 |
msgid "The %s didn't work as expected"
|
121 |
msgstr ""
|
122 |
|
123 |
+
#: includes/class-freemius.php:2665
|
124 |
msgid "What did you expect?"
|
125 |
msgstr ""
|
126 |
|
127 |
+
#: includes/class-freemius.php:3520, templates/debug.php:20
|
128 |
msgid "Freemius Debug"
|
129 |
msgstr ""
|
130 |
|
131 |
+
#: includes/class-freemius.php:4272
|
132 |
msgid "I don't know what is cURL or how to install it, help me!"
|
133 |
msgstr ""
|
134 |
|
135 |
+
#: includes/class-freemius.php:4274
|
136 |
msgid "We'll make sure to contact your hosting company and resolve the issue. You will get a follow-up email to %s once we have an update."
|
137 |
msgstr ""
|
138 |
|
139 |
+
#: includes/class-freemius.php:4281
|
140 |
msgid "Great, please install cURL and enable it in your php.ini file. In addition, search for the 'disable_functions' directive in your php.ini file and remove any disabled methods starting with 'curl_'. To make sure it was successfully activated, use 'phpinfo()'. Once activated, deactivate the %s and reactivate it back again."
|
141 |
msgstr ""
|
142 |
|
143 |
+
#: includes/class-freemius.php:4386
|
144 |
msgid "Yes - do your thing"
|
145 |
msgstr ""
|
146 |
|
147 |
+
#: includes/class-freemius.php:4391
|
148 |
msgid "No - just deactivate"
|
149 |
msgstr ""
|
150 |
|
151 |
+
#: includes/class-freemius.php:4436, includes/class-freemius.php:4930, includes/class-freemius.php:6191, includes/class-freemius.php:13368, includes/class-freemius.php:14110, includes/class-freemius.php:17542, includes/class-freemius.php:17647, includes/class-freemius.php:17822, includes/class-freemius.php:20056, includes/class-freemius.php:20414, includes/class-freemius.php:20424, includes/class-freemius.php:21109, includes/class-freemius.php:22015, includes/class-freemius.php:22148, includes/class-freemius.php:22304, templates/add-ons.php:57
|
152 |
msgctxt "exclamation"
|
153 |
msgid "Oops"
|
154 |
msgstr ""
|
155 |
|
156 |
+
#: includes/class-freemius.php:4505
|
157 |
msgid "Thank for giving us the chance to fix it! A message was just sent to our technical staff. We will get back to you as soon as we have an update to %s. Appreciate your patience."
|
158 |
msgstr ""
|
159 |
|
160 |
+
#: includes/class-freemius.php:4927
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
161 |
msgctxt "addonX cannot run without pluginY"
|
162 |
msgid "%s cannot run without %s."
|
163 |
msgstr ""
|
164 |
|
165 |
+
#: includes/class-freemius.php:4928
|
166 |
msgctxt "addonX cannot run..."
|
167 |
msgid "%s cannot run without the plugin."
|
168 |
msgstr ""
|
169 |
|
170 |
+
#: includes/class-freemius.php:5127, includes/class-freemius.php:5152, includes/class-freemius.php:21180
|
171 |
msgid "Unexpected API error. Please contact the %s's author with the following error."
|
172 |
msgstr ""
|
173 |
|
174 |
+
#: includes/class-freemius.php:5857
|
175 |
msgid "Premium %s version was successfully activated."
|
176 |
msgstr ""
|
177 |
|
178 |
+
#: includes/class-freemius.php:5869, includes/class-freemius.php:7774
|
179 |
msgctxt "Used to express elation, enthusiasm, or triumph (especially in electronic communication)."
|
180 |
msgid "W00t"
|
181 |
msgstr ""
|
182 |
|
183 |
+
#: includes/class-freemius.php:5884
|
184 |
msgid "You have a %s license."
|
185 |
msgstr ""
|
186 |
|
187 |
+
#: includes/class-freemius.php:5888, includes/class-freemius.php:16947, includes/class-freemius.php:16958, includes/class-freemius.php:20325, includes/class-freemius.php:20689, includes/class-freemius.php:20758, includes/class-freemius.php:20930
|
188 |
+
msgctxt "interjection expressing joy or exuberance"
|
189 |
+
msgid "Yee-haw"
|
190 |
+
msgstr ""
|
191 |
+
|
192 |
+
#: includes/class-freemius.php:6174
|
193 |
msgid "%s free trial was successfully cancelled. Since the add-on is premium only it was automatically deactivated. If you like to use it in the future, you'll have to purchase a license."
|
194 |
msgstr ""
|
195 |
|
196 |
+
#: includes/class-freemius.php:6178
|
197 |
msgid "%s is a premium only add-on. You have to purchase a license first before activating the plugin."
|
198 |
msgstr ""
|
199 |
|
200 |
+
#: includes/class-freemius.php:6187, templates/add-ons.php:186, templates/account/partials/addon.php:381
|
201 |
msgid "More information about %s"
|
202 |
msgstr ""
|
203 |
|
204 |
+
#: includes/class-freemius.php:6188
|
205 |
msgid "Purchase License"
|
206 |
msgstr ""
|
207 |
|
208 |
+
#: includes/class-freemius.php:7125, templates/connect.php:171
|
209 |
msgid "You should receive an activation email for %s to your mailbox at %s. Please make sure you click the activation button in that email to %s."
|
210 |
msgstr ""
|
211 |
|
212 |
+
#: includes/class-freemius.php:7129
|
213 |
msgid "start the trial"
|
214 |
msgstr ""
|
215 |
|
216 |
+
#: includes/class-freemius.php:7130, templates/connect.php:175
|
217 |
msgid "complete the install"
|
218 |
msgstr ""
|
219 |
|
220 |
+
#: includes/class-freemius.php:7249
|
221 |
msgid "You are just one step away - %s"
|
222 |
msgstr ""
|
223 |
|
224 |
+
#: includes/class-freemius.php:7252
|
225 |
msgctxt "%s - plugin name. As complete \"PluginX\" activation now"
|
226 |
msgid "Complete \"%s\" Activation Now"
|
227 |
msgstr ""
|
228 |
|
229 |
+
#: includes/class-freemius.php:7334
|
230 |
msgid "We made a few tweaks to the %s, %s"
|
231 |
msgstr ""
|
232 |
|
233 |
+
#: includes/class-freemius.php:7338
|
234 |
msgid "Opt in to make \"%s\" better!"
|
235 |
msgstr ""
|
236 |
|
237 |
+
#: includes/class-freemius.php:7773
|
238 |
msgid "The upgrade of %s was successfully completed."
|
239 |
msgstr ""
|
240 |
|
241 |
+
#: includes/class-freemius.php:10255, includes/class-fs-plugin-updater.php:1087, includes/class-fs-plugin-updater.php:1282, includes/class-fs-plugin-updater.php:1289, templates/auto-installation.php:32
|
242 |
msgid "Add-On"
|
243 |
msgstr ""
|
244 |
|
245 |
+
#: includes/class-freemius.php:10257, templates/account.php:394, templates/account.php:402, templates/debug.php:358, templates/debug.php:549
|
246 |
msgid "Plugin"
|
247 |
msgstr ""
|
248 |
|
249 |
+
#: includes/class-freemius.php:10258, templates/account.php:395, templates/account.php:403, templates/debug.php:358, templates/debug.php:549, templates/forms/deactivation/form.php:71
|
250 |
msgid "Theme"
|
251 |
msgstr ""
|
252 |
|
253 |
+
#: includes/class-freemius.php:13188
|
254 |
msgid "An unknown error has occurred while trying to toggle the license's white-label mode."
|
255 |
msgstr ""
|
256 |
|
257 |
+
#: includes/class-freemius.php:13202
|
258 |
msgid "Your %s license was flagged as white-labeled to hide sensitive information from the WP Admin (e.g. your email, license key, prices, billing address & invoices). If you ever wish to revert it back, you can easily do it through your %s. If this was a mistake you can also %s."
|
259 |
msgstr ""
|
260 |
|
261 |
+
#: includes/class-freemius.php:13207
|
262 |
msgid "User Dashboard"
|
263 |
msgstr ""
|
264 |
|
265 |
+
#: includes/class-freemius.php:13208
|
266 |
msgid "revert it now"
|
267 |
msgstr ""
|
268 |
|
269 |
+
#: includes/class-freemius.php:13266
|
270 |
msgid "An unknown error has occurred while trying to set the user's beta mode."
|
271 |
msgstr ""
|
272 |
|
273 |
+
#: includes/class-freemius.php:13339
|
274 |
msgid "Invalid new user ID or email address."
|
275 |
msgstr ""
|
276 |
|
277 |
+
#: includes/class-freemius.php:13369, includes/class-freemius.php:22259
|
278 |
msgid "Sorry, we could not complete the email update. Another user with the same email is already registered."
|
279 |
msgstr ""
|
280 |
|
281 |
+
#: includes/class-freemius.php:13370, includes/class-freemius.php:22260
|
282 |
msgid "If you would like to give up the ownership of the %s's account to %s click the Change Ownership button."
|
283 |
msgstr ""
|
284 |
|
285 |
+
#: includes/class-freemius.php:13377, includes/class-freemius.php:22267
|
286 |
msgid "Change Ownership"
|
287 |
msgstr ""
|
288 |
|
289 |
+
#: includes/class-freemius.php:13977
|
290 |
msgid "Invalid site details collection."
|
291 |
msgstr ""
|
292 |
|
293 |
+
#: includes/class-freemius.php:14097
|
294 |
msgid "We couldn't find your email address in the system, are you sure it's the right address?"
|
295 |
msgstr ""
|
296 |
|
297 |
+
#: includes/class-freemius.php:14099
|
298 |
msgid "We can't see any active licenses associated with that email address, are you sure it's the right address?"
|
299 |
msgstr ""
|
300 |
|
301 |
+
#: includes/class-freemius.php:14373
|
302 |
msgid "Account is pending activation."
|
303 |
msgstr ""
|
304 |
|
305 |
+
#: includes/class-freemius.php:14485, templates/forms/premium-versions-upgrade-handler.php:47
|
306 |
msgid "Buy a license now"
|
307 |
msgstr ""
|
308 |
|
309 |
+
#: includes/class-freemius.php:14497, templates/forms/premium-versions-upgrade-handler.php:46
|
310 |
msgid "Renew your license now"
|
311 |
msgstr ""
|
312 |
|
313 |
+
#: includes/class-freemius.php:14501
|
314 |
msgid "%s to access version %s security & feature updates, and support."
|
315 |
msgstr ""
|
316 |
|
317 |
+
#: includes/class-freemius.php:16929
|
318 |
msgid "%s activation was successfully completed."
|
319 |
msgstr ""
|
320 |
|
321 |
+
#: includes/class-freemius.php:16943
|
322 |
msgid "Your account was successfully activated with the %s plan."
|
323 |
msgstr ""
|
324 |
|
325 |
+
#: includes/class-freemius.php:16954, includes/class-freemius.php:20754
|
326 |
msgid "Your trial has been successfully started."
|
327 |
msgstr ""
|
328 |
|
329 |
+
#: includes/class-freemius.php:17540, includes/class-freemius.php:17645, includes/class-freemius.php:17820
|
330 |
msgid "Couldn't activate %s."
|
331 |
msgstr ""
|
332 |
|
333 |
+
#: includes/class-freemius.php:17541, includes/class-freemius.php:17646, includes/class-freemius.php:17821
|
334 |
msgid "Please contact us with the following message:"
|
335 |
msgstr ""
|
336 |
|
337 |
+
#: includes/class-freemius.php:17642, templates/forms/data-debug-mode.php:162
|
338 |
msgid "An unknown error has occurred."
|
339 |
msgstr ""
|
340 |
|
341 |
+
#: includes/class-freemius.php:18178, includes/class-freemius.php:23340
|
342 |
msgid "Upgrade"
|
343 |
msgstr ""
|
344 |
|
345 |
+
#: includes/class-freemius.php:18184
|
346 |
msgid "Start Trial"
|
347 |
msgstr ""
|
348 |
|
349 |
+
#: includes/class-freemius.php:18186
|
350 |
msgid "Pricing"
|
351 |
msgstr ""
|
352 |
|
353 |
+
#: includes/class-freemius.php:18266, includes/class-freemius.php:18268
|
354 |
msgid "Affiliation"
|
355 |
msgstr ""
|
356 |
|
357 |
+
#: includes/class-freemius.php:18296, includes/class-freemius.php:18298, templates/account.php:242, templates/debug.php:324
|
358 |
msgid "Account"
|
359 |
msgstr ""
|
360 |
|
361 |
+
#: includes/class-freemius.php:18312, includes/class-freemius.php:18314, includes/customizer/class-fs-customizer-support-section.php:60
|
362 |
msgid "Contact Us"
|
363 |
msgstr ""
|
364 |
|
365 |
+
#: includes/class-freemius.php:18325, includes/class-freemius.php:18327, includes/class-freemius.php:23354, templates/account.php:121, templates/account/partials/addon.php:44
|
366 |
msgid "Add-Ons"
|
367 |
msgstr ""
|
368 |
|
369 |
+
#: includes/class-freemius.php:18361
|
370 |
msgctxt "ASCII arrow left icon"
|
371 |
msgid "←"
|
372 |
msgstr ""
|
373 |
|
374 |
+
#: includes/class-freemius.php:18361
|
375 |
msgctxt "ASCII arrow right icon"
|
376 |
msgid "➤"
|
377 |
msgstr ""
|
378 |
|
379 |
+
#: includes/class-freemius.php:18363, templates/pricing.php:109
|
380 |
msgctxt "noun"
|
381 |
msgid "Pricing"
|
382 |
msgstr ""
|
383 |
|
384 |
+
#: includes/class-freemius.php:18576, includes/customizer/class-fs-customizer-support-section.php:67
|
385 |
msgid "Support Forum"
|
386 |
msgstr ""
|
387 |
|
388 |
+
#: includes/class-freemius.php:19550
|
389 |
msgid "Your email has been successfully verified - you are AWESOME!"
|
390 |
msgstr ""
|
391 |
|
392 |
+
#: includes/class-freemius.php:19551
|
393 |
msgctxt "a positive response"
|
394 |
msgid "Right on"
|
395 |
msgstr ""
|
396 |
|
397 |
+
#: includes/class-freemius.php:20057
|
398 |
msgid "seems like the key you entered doesn't match our records."
|
399 |
msgstr ""
|
400 |
|
401 |
+
#: includes/class-freemius.php:20081
|
402 |
msgid "Debug mode was successfully enabled and will be automatically disabled in 60 min. You can also disable it earlier by clicking the \"Stop Debug\" link."
|
403 |
msgstr ""
|
404 |
|
405 |
+
#: includes/class-freemius.php:20316
|
406 |
msgid "Your %s Add-on plan was successfully upgraded."
|
407 |
msgstr ""
|
408 |
|
409 |
+
#: includes/class-freemius.php:20318
|
410 |
msgid "%s Add-on was successfully purchased."
|
411 |
msgstr ""
|
412 |
|
413 |
+
#: includes/class-freemius.php:20321
|
414 |
msgid "Download the latest version"
|
415 |
msgstr ""
|
416 |
|
417 |
+
#: includes/class-freemius.php:20407
|
418 |
msgid "Your server is blocking the access to Freemius' API, which is crucial for %1$s synchronization. Please contact your host to whitelist %2$s"
|
419 |
msgstr ""
|
420 |
|
421 |
+
#: includes/class-freemius.php:20413, includes/class-freemius.php:20423, includes/class-freemius.php:20889, includes/class-freemius.php:20978
|
422 |
msgid "Error received from the server:"
|
423 |
msgstr ""
|
424 |
|
425 |
+
#: includes/class-freemius.php:20423
|
426 |
msgid "It seems like one of the authentication parameters is wrong. Update your Public Key, Secret Key & User ID, and try again."
|
427 |
msgstr ""
|
428 |
|
429 |
+
#: includes/class-freemius.php:20651, includes/class-freemius.php:20894, includes/class-freemius.php:20949, includes/class-freemius.php:21056
|
430 |
msgctxt "something somebody says when they are thinking about what you have just said."
|
431 |
msgid "Hmm"
|
432 |
msgstr ""
|
433 |
|
434 |
+
#: includes/class-freemius.php:20664
|
435 |
msgid "It looks like you are still on the %s plan. If you did upgrade or change your plan, it's probably an issue on our side - sorry."
|
436 |
msgstr ""
|
437 |
|
438 |
+
#: includes/class-freemius.php:20665, templates/account.php:123, templates/add-ons.php:250, templates/account/partials/addon.php:46
|
439 |
msgctxt "trial period"
|
440 |
msgid "Trial"
|
441 |
msgstr ""
|
442 |
|
443 |
+
#: includes/class-freemius.php:20670
|
444 |
msgid "I have upgraded my account but when I try to Sync the License, the plan remains %s."
|
445 |
msgstr ""
|
446 |
|
447 |
+
#: includes/class-freemius.php:20674, includes/class-freemius.php:20733
|
448 |
msgid "Please contact us here"
|
449 |
msgstr ""
|
450 |
|
451 |
+
#: includes/class-freemius.php:20685
|
452 |
msgid "Your plan was successfully activated."
|
453 |
msgstr ""
|
454 |
|
455 |
+
#: includes/class-freemius.php:20686
|
456 |
msgid "Your plan was successfully upgraded."
|
457 |
msgstr ""
|
458 |
|
459 |
+
#: includes/class-freemius.php:20703
|
460 |
msgid "Your plan was successfully changed to %s."
|
461 |
msgstr ""
|
462 |
|
463 |
+
#: includes/class-freemius.php:20719
|
464 |
msgid "Your license has expired. You can still continue using the free %s forever."
|
465 |
msgstr ""
|
466 |
|
467 |
+
#: includes/class-freemius.php:20721
|
468 |
msgid "Your license has expired. %1$sUpgrade now%2$s to continue using the %3$s without interruptions."
|
469 |
msgstr ""
|
470 |
|
471 |
+
#: includes/class-freemius.php:20729
|
472 |
msgid "Your license has been cancelled. If you think it's a mistake, please contact support."
|
473 |
msgstr ""
|
474 |
|
475 |
+
#: includes/class-freemius.php:20742
|
476 |
msgid "Your license has expired. You can still continue using all the %s features, but you'll need to renew your license to continue getting updates and support."
|
477 |
msgstr ""
|
478 |
|
479 |
+
#: includes/class-freemius.php:20768
|
480 |
msgid "Your free trial has expired. You can still continue using all our free features."
|
481 |
msgstr ""
|
482 |
|
483 |
+
#: includes/class-freemius.php:20770
|
484 |
msgid "Your free trial has expired. %1$sUpgrade now%2$s to continue using the %3$s without interruptions."
|
485 |
msgstr ""
|
486 |
|
487 |
+
#: includes/class-freemius.php:20885
|
488 |
msgid "It looks like the license could not be activated."
|
489 |
msgstr ""
|
490 |
|
491 |
+
#: includes/class-freemius.php:20927
|
492 |
msgid "Your license was successfully activated."
|
493 |
msgstr ""
|
494 |
|
495 |
+
#: includes/class-freemius.php:20953
|
496 |
msgid "It looks like your site currently doesn't have an active license."
|
497 |
msgstr ""
|
498 |
|
499 |
+
#: includes/class-freemius.php:20977
|
500 |
msgid "It looks like the license deactivation failed."
|
501 |
msgstr ""
|
502 |
|
503 |
+
#: includes/class-freemius.php:21006
|
504 |
msgid "Your %s license was successfully deactivated."
|
505 |
msgstr ""
|
506 |
|
507 |
+
#: includes/class-freemius.php:21007
|
508 |
msgid "Your license was successfully deactivated, you are back to the %s plan."
|
509 |
msgstr ""
|
510 |
|
511 |
+
#: includes/class-freemius.php:21010
|
512 |
msgid "O.K"
|
513 |
msgstr ""
|
514 |
|
515 |
+
#: includes/class-freemius.php:21063
|
516 |
msgid "Seems like we are having some temporary issue with your subscription cancellation. Please try again in few minutes."
|
517 |
msgstr ""
|
518 |
|
519 |
+
#: includes/class-freemius.php:21072
|
520 |
msgid "Your subscription was successfully cancelled. Your %s plan license will expire in %s."
|
521 |
msgstr ""
|
522 |
|
523 |
+
#: includes/class-freemius.php:21114
|
524 |
msgid "You are already running the %s in a trial mode."
|
525 |
msgstr ""
|
526 |
|
527 |
+
#: includes/class-freemius.php:21125
|
528 |
msgid "You already utilized a trial before."
|
529 |
msgstr ""
|
530 |
|
531 |
+
#: includes/class-freemius.php:21139
|
532 |
msgid "Plan %s do not exist, therefore, can't start a trial."
|
533 |
msgstr ""
|
534 |
|
535 |
+
#: includes/class-freemius.php:21150
|
536 |
msgid "Plan %s does not support a trial period."
|
537 |
msgstr ""
|
538 |
|
539 |
+
#: includes/class-freemius.php:21161
|
540 |
msgid "None of the %s's plans supports a trial period."
|
541 |
msgstr ""
|
542 |
|
543 |
+
#: includes/class-freemius.php:21211
|
544 |
msgid "It looks like you are not in trial mode anymore so there's nothing to cancel :)"
|
545 |
msgstr ""
|
546 |
|
547 |
+
#: includes/class-freemius.php:21247
|
548 |
msgid "Seems like we are having some temporary issue with your trial cancellation. Please try again in few minutes."
|
549 |
msgstr ""
|
550 |
|
551 |
+
#: includes/class-freemius.php:21266
|
552 |
msgid "Your %s free trial was successfully cancelled."
|
553 |
msgstr ""
|
554 |
|
555 |
+
#: includes/class-freemius.php:21582
|
556 |
msgid "Version %s was released."
|
557 |
msgstr ""
|
558 |
|
559 |
+
#: includes/class-freemius.php:21582
|
560 |
msgid "Please download %s."
|
561 |
msgstr ""
|
562 |
|
563 |
+
#: includes/class-freemius.php:21589
|
564 |
msgid "the latest %s version here"
|
565 |
msgstr ""
|
566 |
|
567 |
+
#: includes/class-freemius.php:21594
|
568 |
msgid "New"
|
569 |
msgstr ""
|
570 |
|
571 |
+
#: includes/class-freemius.php:21599
|
572 |
msgid "Seems like you got the latest release."
|
573 |
msgstr ""
|
574 |
|
575 |
+
#: includes/class-freemius.php:21600
|
576 |
msgid "You are all good!"
|
577 |
msgstr ""
|
578 |
|
579 |
+
#: includes/class-freemius.php:21903
|
580 |
msgid "Verification mail was just sent to %s. If you can't find it after 5 min, please check your spam box."
|
581 |
msgstr ""
|
582 |
|
583 |
+
#: includes/class-freemius.php:22043
|
584 |
msgid "Site successfully opted in."
|
585 |
msgstr ""
|
586 |
|
587 |
+
#: includes/class-freemius.php:22044, includes/class-freemius.php:23050
|
588 |
msgid "Awesome"
|
589 |
msgstr ""
|
590 |
|
591 |
+
#: includes/class-freemius.php:22060, templates/forms/optout.php:41
|
592 |
msgid "We appreciate your help in making the %s better by letting us track some usage data."
|
593 |
msgstr ""
|
594 |
|
595 |
+
#: includes/class-freemius.php:22061
|
596 |
msgid "Thank you!"
|
597 |
msgstr ""
|
598 |
|
599 |
+
#: includes/class-freemius.php:22068
|
600 |
msgid "We will no longer be sending any usage data of %s on %s to %s."
|
601 |
msgstr ""
|
602 |
|
603 |
+
#: includes/class-freemius.php:22226
|
604 |
msgid "Please check your mailbox, you should receive an email via %s to confirm the ownership change. From security reasons, you must confirm the change within the next 15 min. If you cannot find the email, please check your spam folder."
|
605 |
msgstr ""
|
606 |
|
607 |
+
#: includes/class-freemius.php:22232
|
608 |
msgid "Thanks for confirming the ownership change. An email was just sent to %s for final approval."
|
609 |
msgstr ""
|
610 |
|
611 |
+
#: includes/class-freemius.php:22237
|
612 |
msgid "%s is the new owner of the account."
|
613 |
msgstr ""
|
614 |
|
615 |
+
#: includes/class-freemius.php:22239
|
616 |
msgctxt "as congratulations"
|
617 |
msgid "Congrats"
|
618 |
msgstr ""
|
619 |
|
620 |
+
#: includes/class-freemius.php:22275
|
621 |
msgid "Your email was successfully updated. You should receive an email with confirmation instructions in few moments."
|
622 |
msgstr ""
|
623 |
|
624 |
+
#: includes/class-freemius.php:22287
|
625 |
msgid "Please provide your full name."
|
626 |
msgstr ""
|
627 |
|
628 |
+
#: includes/class-freemius.php:22292
|
629 |
msgid "Your name was successfully updated."
|
630 |
msgstr ""
|
631 |
|
632 |
+
#: includes/class-freemius.php:22353
|
633 |
msgid "You have successfully updated your %s."
|
634 |
msgstr ""
|
635 |
|
636 |
+
#: includes/class-freemius.php:22412
|
637 |
msgid "Is this your client's site? %s if you wish to hide sensitive info like your email, license key, prices, billing address & invoices from the WP Admin."
|
638 |
msgstr ""
|
639 |
|
640 |
+
#: includes/class-freemius.php:22415
|
641 |
msgid "Click here"
|
642 |
msgstr ""
|
643 |
|
644 |
+
#: includes/class-freemius.php:22513
|
645 |
msgid "Just letting you know that the add-ons information of %s is being pulled from an external server."
|
646 |
msgstr ""
|
647 |
|
648 |
+
#: includes/class-freemius.php:22514
|
649 |
msgctxt "advance notice of something that will need attention."
|
650 |
msgid "Heads up"
|
651 |
msgstr ""
|
652 |
|
653 |
+
#: includes/class-freemius.php:23090
|
654 |
msgctxt "exclamation"
|
655 |
msgid "Hey"
|
656 |
msgstr ""
|
657 |
|
658 |
+
#: includes/class-freemius.php:23090
|
659 |
msgid "How do you like %s so far? Test all our %s premium features with a %d-day free trial."
|
660 |
msgstr ""
|
661 |
|
662 |
+
#: includes/class-freemius.php:23098
|
663 |
msgid "No commitment for %s days - cancel anytime!"
|
664 |
msgstr ""
|
665 |
|
666 |
+
#: includes/class-freemius.php:23099
|
667 |
msgid "No credit card required"
|
668 |
msgstr ""
|
669 |
|
670 |
+
#: includes/class-freemius.php:23106, templates/forms/trial-start.php:53
|
671 |
msgctxt "call to action"
|
672 |
msgid "Start free trial"
|
673 |
msgstr ""
|
674 |
|
675 |
+
#: includes/class-freemius.php:23183
|
676 |
msgid "Hey there, did you know that %s has an affiliate program? If you like the %s you can become our ambassador and earn some cash!"
|
677 |
msgstr ""
|
678 |
|
679 |
+
#: includes/class-freemius.php:23192
|
680 |
msgid "Learn more"
|
681 |
msgstr ""
|
682 |
|
683 |
+
#: includes/class-freemius.php:23378, templates/account.php:558, templates/account.php:708, templates/connect.php:179, templates/connect.php:461, templates/forms/license-activation.php:27, templates/account/partials/addon.php:321
|
684 |
msgid "Activate License"
|
685 |
msgstr ""
|
686 |
|
687 |
+
#: includes/class-freemius.php:23379, templates/account.php:652, templates/account.php:707, templates/account/partials/addon.php:322, templates/account/partials/site.php:271
|
688 |
msgid "Change License"
|
689 |
msgstr ""
|
690 |
|
691 |
+
#: includes/class-freemius.php:23500, templates/account/partials/site.php:169
|
692 |
msgid "Opt Out"
|
693 |
msgstr ""
|
694 |
|
695 |
+
#: includes/class-freemius.php:23502, includes/class-freemius.php:23508, templates/account/partials/site.php:49, templates/account/partials/site.php:169
|
696 |
msgid "Opt In"
|
697 |
msgstr ""
|
698 |
|
699 |
+
#: includes/class-freemius.php:23738
|
700 |
msgid " The paid version of %1$s is already installed. Please activate it to start benefiting the %2$s features. %3$s"
|
701 |
msgstr ""
|
702 |
|
703 |
+
#: includes/class-freemius.php:23746
|
704 |
msgid "Activate %s features"
|
705 |
msgstr ""
|
706 |
|
707 |
+
#: includes/class-freemius.php:23759
|
708 |
msgid "Please follow these steps to complete the upgrade"
|
709 |
msgstr ""
|
710 |
|
711 |
+
#: includes/class-freemius.php:23763
|
712 |
msgid "Download the latest %s version"
|
713 |
msgstr ""
|
714 |
|
715 |
+
#: includes/class-freemius.php:23767
|
716 |
msgid "Upload and activate the downloaded version"
|
717 |
msgstr ""
|
718 |
|
719 |
+
#: includes/class-freemius.php:23769
|
720 |
msgid "How to upload and activate?"
|
721 |
msgstr ""
|
722 |
|
723 |
+
#: includes/class-freemius.php:23903
|
724 |
msgid "%sClick here%s to choose the sites where you'd like to activate the license on."
|
725 |
msgstr ""
|
726 |
|
727 |
+
#: includes/class-freemius.php:24072
|
728 |
msgid "Auto installation only works for opted-in users."
|
729 |
msgstr ""
|
730 |
|
731 |
+
#: includes/class-freemius.php:24082, includes/class-freemius.php:24115, includes/class-fs-plugin-updater.php:1261, includes/class-fs-plugin-updater.php:1275
|
732 |
msgid "Invalid module ID."
|
733 |
msgstr ""
|
734 |
|
735 |
+
#: includes/class-freemius.php:24091, includes/class-fs-plugin-updater.php:1297
|
736 |
msgid "Premium version already active."
|
737 |
msgstr ""
|
738 |
|
739 |
+
#: includes/class-freemius.php:24098
|
740 |
msgid "You do not have a valid license to access the premium version."
|
741 |
msgstr ""
|
742 |
|
743 |
+
#: includes/class-freemius.php:24105
|
744 |
msgid "Plugin is a \"Serviceware\" which means it does not have a premium code version."
|
745 |
msgstr ""
|
746 |
|
747 |
+
#: includes/class-freemius.php:24123, includes/class-fs-plugin-updater.php:1296
|
748 |
msgid "Premium add-on version already installed."
|
749 |
msgstr ""
|
750 |
|
751 |
+
#: includes/class-freemius.php:24473
|
752 |
msgid "View paid features"
|
753 |
msgstr ""
|
754 |
|
755 |
+
#: includes/class-freemius.php:24795
|
756 |
msgid "Thank you so much for using %s and its add-ons!"
|
757 |
msgstr ""
|
758 |
|
759 |
+
#: includes/class-freemius.php:24796
|
760 |
msgid "Thank you so much for using %s!"
|
761 |
msgstr ""
|
762 |
|
763 |
+
#: includes/class-freemius.php:24802
|
764 |
msgid "You've already opted-in to our usage-tracking, which helps us keep improving the %s."
|
765 |
msgstr ""
|
766 |
|
767 |
+
#: includes/class-freemius.php:24806
|
768 |
msgid "Thank you so much for using our products!"
|
769 |
msgstr ""
|
770 |
|
771 |
+
#: includes/class-freemius.php:24807
|
772 |
msgid "You've already opted-in to our usage-tracking, which helps us keep improving them."
|
773 |
msgstr ""
|
774 |
|
775 |
+
#: includes/class-freemius.php:24826
|
776 |
msgid "%s and its add-ons"
|
777 |
msgstr ""
|
778 |
|
779 |
+
#: includes/class-freemius.php:24835
|
780 |
msgid "Products"
|
781 |
msgstr ""
|
782 |
|
783 |
+
#: includes/class-freemius.php:24842, templates/connect.php:275
|
784 |
msgid "Yes"
|
785 |
msgstr ""
|
786 |
|
787 |
+
#: includes/class-freemius.php:24843, templates/connect.php:276
|
788 |
msgid "send me security & feature updates, educational content and offers."
|
789 |
msgstr ""
|
790 |
|
791 |
+
#: includes/class-freemius.php:24844, templates/connect.php:281
|
792 |
msgid "No"
|
793 |
msgstr ""
|
794 |
|
795 |
+
#: includes/class-freemius.php:24846, templates/connect.php:283
|
796 |
msgid "do %sNOT%s send me security & feature updates, educational content and offers."
|
797 |
msgstr ""
|
798 |
|
799 |
+
#: includes/class-freemius.php:24856
|
800 |
msgid "Due to the new %sEU General Data Protection Regulation (GDPR)%s compliance requirements it is required that you provide your explicit consent, again, confirming that you are onboard :-)"
|
801 |
msgstr ""
|
802 |
|
803 |
+
#: includes/class-freemius.php:24858, templates/connect.php:290
|
804 |
msgid "Please let us know if you'd like us to contact you for security & feature updates, educational content, and occasional offers:"
|
805 |
msgstr ""
|
806 |
|
807 |
+
#: includes/class-freemius.php:25140
|
808 |
msgid "License key is empty."
|
809 |
msgstr ""
|
810 |
|
816 |
msgid "Buy license"
|
817 |
msgstr ""
|
818 |
|
819 |
+
#: includes/class-fs-plugin-updater.php:327, includes/class-fs-plugin-updater.php:360
|
820 |
msgid "There is a %s of %s available."
|
821 |
msgstr ""
|
822 |
|
823 |
+
#: includes/class-fs-plugin-updater.php:329, includes/class-fs-plugin-updater.php:365
|
824 |
msgid "new Beta version"
|
825 |
msgstr ""
|
826 |
|
827 |
+
#: includes/class-fs-plugin-updater.php:330, includes/class-fs-plugin-updater.php:366
|
828 |
msgid "new version"
|
829 |
msgstr ""
|
830 |
|
831 |
+
#: includes/class-fs-plugin-updater.php:389
|
832 |
msgid "Important Upgrade Notice:"
|
833 |
msgstr ""
|
834 |
|
835 |
+
#: includes/class-fs-plugin-updater.php:1326
|
836 |
msgid "Installing plugin: %s"
|
837 |
msgstr ""
|
838 |
|
839 |
+
#: includes/class-fs-plugin-updater.php:1367
|
840 |
msgid "Unable to connect to the filesystem. Please confirm your credentials."
|
841 |
msgstr ""
|
842 |
|
843 |
+
#: includes/class-fs-plugin-updater.php:1549
|
844 |
msgid "The remote plugin package does not contain a folder with the desired slug and renaming did not work."
|
845 |
msgstr ""
|
846 |
|
887 |
msgid "Activate this add-on"
|
888 |
msgstr ""
|
889 |
|
890 |
+
#: includes/fs-plugin-info-dialog.php:789, templates/connect.php:458
|
891 |
msgid "Activate Free Version"
|
892 |
msgstr ""
|
893 |
|
1489 |
msgstr ""
|
1490 |
|
1491 |
#. translators: %s: name (e.g. Hey John,)
|
1492 |
+
#: templates/connect.php:112
|
1493 |
msgctxt "greeting"
|
1494 |
msgid "Hey %s,"
|
1495 |
msgstr ""
|
1496 |
|
1497 |
+
#: templates/connect.php:162
|
1498 |
msgid "Allow & Continue"
|
1499 |
msgstr ""
|
1500 |
|
1501 |
+
#: templates/connect.php:166
|
1502 |
msgid "Re-send activation email"
|
1503 |
msgstr ""
|
1504 |
|
1505 |
+
#: templates/connect.php:170
|
1506 |
msgid "Thanks %s!"
|
1507 |
msgstr ""
|
1508 |
|
1509 |
+
#: templates/connect.php:180, templates/forms/license-activation.php:46
|
1510 |
msgid "Agree & Activate License"
|
1511 |
msgstr ""
|
1512 |
|
1513 |
+
#: templates/connect.php:184
|
1514 |
msgid "Welcome to %s! To get started, please enter your license key:"
|
1515 |
msgstr ""
|
1516 |
|
1517 |
+
#: templates/connect.php:191
|
1518 |
msgid "Never miss an important update - opt in to our security & feature updates notifications, educational content, offers, and non-sensitive diagnostic tracking with %4$s."
|
1519 |
msgstr ""
|
1520 |
|
1521 |
+
#: templates/connect.php:192
|
1522 |
msgid "Never miss an important update - opt in to our security and feature updates notifications, and non-sensitive diagnostic tracking with %4$s."
|
1523 |
msgstr ""
|
1524 |
|
1525 |
+
#: templates/connect.php:198
|
1526 |
msgid "Never miss an important update - opt in to our security & feature updates notifications, educational content, offers, and non-sensitive diagnostic tracking with %4$s. If you skip this, that's okay! %1$s will still work just fine."
|
1527 |
msgstr ""
|
1528 |
|
1529 |
+
#: templates/connect.php:199
|
1530 |
msgid "Never miss an important update - opt in to our security & feature updates notifications, and non-sensitive diagnostic tracking with %4$s. If you skip this, that's okay! %1$s will still work just fine."
|
1531 |
msgstr ""
|
1532 |
|
1533 |
+
#: templates/connect.php:233
|
1534 |
msgid "We're excited to introduce the Freemius network-level integration."
|
1535 |
msgstr ""
|
1536 |
|
1537 |
+
#: templates/connect.php:236
|
1538 |
msgid "During the update process we detected %d site(s) that are still pending license activation."
|
1539 |
msgstr ""
|
1540 |
|
1541 |
+
#: templates/connect.php:238
|
1542 |
msgid "If you'd like to use the %s on those sites, please enter your license key below and click the activation button."
|
1543 |
msgstr ""
|
1544 |
|
1545 |
+
#: templates/connect.php:240
|
1546 |
msgid "%s's paid features"
|
1547 |
msgstr ""
|
1548 |
|
1549 |
+
#: templates/connect.php:245
|
1550 |
msgid "Alternatively, you can skip it for now and activate the license later, in your %s's network-level Account page."
|
1551 |
msgstr ""
|
1552 |
|
1553 |
+
#: templates/connect.php:247
|
1554 |
msgid "During the update process we detected %s site(s) in the network that are still pending your attention."
|
1555 |
msgstr ""
|
1556 |
|
1557 |
+
#: templates/connect.php:256, templates/forms/data-debug-mode.php:35, templates/forms/license-activation.php:49
|
1558 |
msgid "License key"
|
1559 |
msgstr ""
|
1560 |
|
1561 |
+
#: templates/connect.php:259, templates/forms/license-activation.php:22
|
1562 |
msgid "Can't find your license key?"
|
1563 |
msgstr ""
|
1564 |
|
1565 |
+
#: templates/connect.php:318, templates/connect.php:700, templates/forms/deactivation/retry-skip.php:20
|
1566 |
msgctxt "verb"
|
1567 |
msgid "Skip"
|
1568 |
msgstr ""
|
1569 |
|
1570 |
+
#: templates/connect.php:321
|
1571 |
msgid "Delegate to Site Admins"
|
1572 |
msgstr ""
|
1573 |
|
1574 |
+
#: templates/connect.php:321
|
1575 |
msgid "If you click it, this decision will be delegated to the sites administrators."
|
1576 |
msgstr ""
|
1577 |
|
1578 |
+
#: templates/connect.php:346
|
1579 |
msgid "License issues?"
|
1580 |
msgstr ""
|
1581 |
|
1582 |
+
#: templates/connect.php:362
|
1583 |
msgid "Your Profile Overview"
|
1584 |
msgstr ""
|
1585 |
|
1586 |
+
#: templates/connect.php:363
|
1587 |
msgid "Name and email address"
|
1588 |
msgstr ""
|
1589 |
|
1590 |
+
#: templates/connect.php:370
|
1591 |
msgid "So you can manage and control your license remotely from the User Dashboard."
|
1592 |
msgstr ""
|
1593 |
|
1594 |
+
#: templates/connect.php:371
|
1595 |
msgid "Your Site Overview"
|
1596 |
msgstr ""
|
1597 |
|
1598 |
+
#: templates/connect.php:372
|
1599 |
msgid "Site URL, WP version, PHP info"
|
1600 |
msgstr ""
|
1601 |
|
1602 |
+
#: templates/connect.php:379
|
1603 |
msgid "Admin Notices"
|
1604 |
msgstr ""
|
1605 |
|
1606 |
+
#: templates/connect.php:380, templates/connect.php:398
|
1607 |
msgid "Updates, announcements, marketing, no spam"
|
1608 |
msgstr ""
|
1609 |
|
1610 |
+
#: templates/connect.php:387
|
1611 |
msgid "So you can reuse the license when the %s is no longer active."
|
1612 |
msgstr ""
|
1613 |
|
1614 |
+
#: templates/connect.php:388
|
1615 |
msgid "Current %s Status"
|
1616 |
msgstr ""
|
1617 |
|
1618 |
+
#: templates/connect.php:389
|
1619 |
msgid "Active, deactivated, or uninstalled"
|
1620 |
msgstr ""
|
1621 |
|
1622 |
+
#: templates/connect.php:397
|
1623 |
msgid "Newsletter"
|
1624 |
msgstr ""
|
1625 |
|
1626 |
+
#: templates/connect.php:405
|
1627 |
msgid "Plugins & Themes"
|
1628 |
msgstr ""
|
1629 |
|
1630 |
+
#: templates/connect.php:405
|
1631 |
msgid "optional"
|
1632 |
msgstr ""
|
1633 |
|
1634 |
+
#: templates/connect.php:406
|
1635 |
msgid "To help us troubleshoot any potential issues that may arise from other plugin or theme conflicts."
|
1636 |
msgstr ""
|
1637 |
|
1638 |
+
#: templates/connect.php:407
|
1639 |
msgid "Title, slug, version, and is active"
|
1640 |
msgstr ""
|
1641 |
|
1642 |
+
#: templates/connect.php:424
|
1643 |
msgid "The %1$s will periodically send %2$s to %3$s for security & feature updates delivery, and license management."
|
1644 |
msgstr ""
|
1645 |
|
1646 |
+
#: templates/connect.php:426
|
1647 |
msgid "diagnostic data"
|
1648 |
msgstr ""
|
1649 |
|
1650 |
+
#: templates/connect.php:427
|
1651 |
msgid "Freemius is our licensing and software updates engine"
|
1652 |
msgstr ""
|
1653 |
|
1654 |
+
#: templates/connect.php:430
|
1655 |
msgid "What permissions are being granted?"
|
1656 |
msgstr ""
|
1657 |
|
1658 |
+
#: templates/connect.php:457
|
1659 |
msgid "Don't have a license key?"
|
1660 |
msgstr ""
|
1661 |
|
1662 |
+
#: templates/connect.php:460
|
1663 |
msgid "Have a license key?"
|
1664 |
msgstr ""
|
1665 |
|
1666 |
+
#: templates/connect.php:468
|
1667 |
msgid "Privacy Policy"
|
1668 |
msgstr ""
|
1669 |
|
1670 |
+
#: templates/connect.php:470
|
1671 |
msgid "License Agreement"
|
1672 |
msgstr ""
|
1673 |
|
1674 |
+
#: templates/connect.php:470
|
1675 |
msgid "Terms of Service"
|
1676 |
msgstr ""
|
1677 |
|
1678 |
+
#: templates/connect.php:866
|
1679 |
msgctxt "as in the process of sending an email"
|
1680 |
msgid "Sending email"
|
1681 |
msgstr ""
|
1682 |
|
1683 |
+
#: templates/connect.php:867
|
1684 |
msgctxt "as activating plugin"
|
1685 |
msgid "Activating"
|
1686 |
msgstr ""
|
freemius/require.php
CHANGED
@@ -6,6 +6,10 @@
|
|
6 |
* @since 1.1.9
|
7 |
*/
|
8 |
|
|
|
|
|
|
|
|
|
9 |
// Configuration should be loaded first.
|
10 |
require_once dirname( __FILE__ ) . '/config.php';
|
11 |
require_once WP_FS__DIR_INCLUDES . '/fs-core-functions.php';
|
@@ -46,4 +50,4 @@
|
|
46 |
require_once WP_FS__DIR_INCLUDES . '/class-fs-admin-notices.php';
|
47 |
require_once WP_FS__DIR_INCLUDES . '/class-freemius-abstract.php';
|
48 |
require_once WP_FS__DIR_INCLUDES . '/sdk/Exceptions/Exception.php';
|
49 |
-
require_once WP_FS__DIR_INCLUDES . '/class-freemius.php';
|
6 |
* @since 1.1.9
|
7 |
*/
|
8 |
|
9 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
10 |
+
exit;
|
11 |
+
}
|
12 |
+
|
13 |
// Configuration should be loaded first.
|
14 |
require_once dirname( __FILE__ ) . '/config.php';
|
15 |
require_once WP_FS__DIR_INCLUDES . '/fs-core-functions.php';
|
50 |
require_once WP_FS__DIR_INCLUDES . '/class-fs-admin-notices.php';
|
51 |
require_once WP_FS__DIR_INCLUDES . '/class-freemius-abstract.php';
|
52 |
require_once WP_FS__DIR_INCLUDES . '/sdk/Exceptions/Exception.php';
|
53 |
+
require_once WP_FS__DIR_INCLUDES . '/class-freemius.php';
|
freemius/start.php
CHANGED
@@ -15,7 +15,7 @@
|
|
15 |
*
|
16 |
* @var string
|
17 |
*/
|
18 |
-
$this_sdk_version = '2.4.
|
19 |
|
20 |
#region SDK Selection Logic --------------------------------------------------------------------
|
21 |
|
15 |
*
|
16 |
* @var string
|
17 |
*/
|
18 |
+
$this_sdk_version = '2.4.3';
|
19 |
|
20 |
#region SDK Selection Logic --------------------------------------------------------------------
|
21 |
|
freemius/templates/account/partials/addon.php
CHANGED
@@ -1,4 +1,9 @@
|
|
1 |
<?php
|
|
|
|
|
|
|
|
|
|
|
2 |
/**
|
3 |
* @var array $VARS
|
4 |
* @var Freemius $fs
|
@@ -443,4 +448,4 @@
|
|
443 |
</td>
|
444 |
<!--/ Optional Delete Action -->
|
445 |
<?php endif ?>
|
446 |
-
</tr>
|
1 |
<?php
|
2 |
+
|
3 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
4 |
+
exit;
|
5 |
+
}
|
6 |
+
|
7 |
/**
|
8 |
* @var array $VARS
|
9 |
* @var Freemius $fs
|
448 |
</td>
|
449 |
<!--/ Optional Delete Action -->
|
450 |
<?php endif ?>
|
451 |
+
</tr>
|
freemius/templates/account/partials/site.php
CHANGED
@@ -1,352 +1,352 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* @package Freemius
|
4 |
-
* @copyright Copyright (c) 2015, Freemius, Inc.
|
5 |
-
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
|
6 |
-
* @since 2.0.0
|
7 |
-
*/
|
8 |
-
|
9 |
-
if ( ! defined( 'ABSPATH' ) ) {
|
10 |
-
exit;
|
11 |
-
}
|
12 |
-
|
13 |
-
/**
|
14 |
-
* @var array $VARS
|
15 |
-
* @var Freemius $fs
|
16 |
-
* @var FS_Plugin_License $main_license
|
17 |
-
*/
|
18 |
-
$fs = $VARS['freemius'];
|
19 |
-
$slug = $fs->get_slug();
|
20 |
-
$site = $VARS['site'];
|
21 |
-
$main_license = $VARS['license'];
|
22 |
-
$is_data_debug_mode = $fs->is_data_debug_mode();
|
23 |
-
$is_whitelabeled = $fs->is_whitelabeled();
|
24 |
-
$has_paid_plan = $fs->has_paid_plan();
|
25 |
-
$is_premium = $fs->is_premium();
|
26 |
-
$main_user = $fs->get_user();
|
27 |
-
$blog_id = $site['blog_id'];
|
28 |
-
|
29 |
-
$install = $VARS['install'];
|
30 |
-
$is_registered = ! empty( $install );
|
31 |
-
$license = null;
|
32 |
-
$trial_plan = $fs->get_trial_plan();
|
33 |
-
$free_text = fs_text_inline( 'Free', 'free', $slug );
|
34 |
-
|
35 |
-
if ( $is_whitelabeled && $fs->is_delegated_connection( $blog_id ) ) {
|
36 |
-
$is_whitelabeled = $fs->is_whitelabeled( true, $blog_id );
|
37 |
-
}
|
38 |
-
?>
|
39 |
-
<tr class="fs-site-details" data-blog-id="<?php echo $blog_id ?>"<?php if ( $is_registered ) : ?> data-install-id="<?php echo $install->id ?>"<?php endif ?>>
|
40 |
-
<!-- Install ID or Opt-in option -->
|
41 |
-
<td><?php if ( $is_registered ) : ?>
|
42 |
-
<?php echo $install->id ?>
|
43 |
-
<?php else : ?>
|
44 |
-
<?php $action = 'opt_in' ?>
|
45 |
-
<form action="<?php echo $fs->_get_admin_page_url( 'account' ) ?>" method="POST">
|
46 |
-
<input type="hidden" name="fs_action" value="<?php echo $action ?>">
|
47 |
-
<?php wp_nonce_field( trim( "{$action}:{$blog_id}", ':' ) ) ?>
|
48 |
-
<input type="hidden" name="blog_id" value="<?php echo $blog_id ?>">
|
49 |
-
<button class="fs-opt-in button button-small"><?php fs_esc_html_echo_inline( 'Opt In', 'opt-in', $slug ) ?></button>
|
50 |
-
</form>
|
51 |
-
<?php endif ?>
|
52 |
-
</td>
|
53 |
-
<!--/ Install ID or Opt-in option -->
|
54 |
-
|
55 |
-
<!-- Site URL -->
|
56 |
-
<td class="fs-field-url fs-main-column"><?php echo fs_strip_url_protocol( $site['url'] ) ?></td>
|
57 |
-
<!--/ Site URL -->
|
58 |
-
|
59 |
-
<!-- License Activation / Deactivation -->
|
60 |
-
<td><?php if ( $has_paid_plan ) {
|
61 |
-
$view_params = array(
|
62 |
-
'freemius' => $fs,
|
63 |
-
'slug' => $slug,
|
64 |
-
'blog_id' => $blog_id,
|
65 |
-
'class' => 'button-small',
|
66 |
-
);
|
67 |
-
|
68 |
-
$license = null;
|
69 |
-
if ( $is_registered ) {
|
70 |
-
$view_params['install_id'] = $install->id;
|
71 |
-
$view_params['is_localhost'] = $install->is_localhost();
|
72 |
-
|
73 |
-
$has_license = FS_Plugin_License::is_valid_id( $install->license_id );
|
74 |
-
$license = $has_license ?
|
75 |
-
$fs->_get_license_by_id( $install->license_id ) :
|
76 |
-
null;
|
77 |
-
} else {
|
78 |
-
$view_params['is_localhost'] = FS_Site::is_localhost_by_address( $site['url'] );
|
79 |
-
}
|
80 |
-
|
81 |
-
if ( ! $is_whitelabeled ) {
|
82 |
-
if ( is_object( $license ) ) {
|
83 |
-
$view_params['license'] = $license;
|
84 |
-
|
85 |
-
// Show license deactivation button.
|
86 |
-
fs_require_template( 'account/partials/deactivate-license-button.php', $view_params );
|
87 |
-
} else {
|
88 |
-
if ( is_object( $main_license ) && $main_license->can_activate( $view_params['is_localhost'] ) ) {
|
89 |
-
// Main license is available for activation.
|
90 |
-
$available_license = $main_license;
|
91 |
-
} else {
|
92 |
-
// Try to find any available license for activation.
|
93 |
-
$available_license = $fs->_get_available_premium_license( $view_params['is_localhost'] );
|
94 |
-
}
|
95 |
-
|
96 |
-
if ( is_object( $available_license ) ) {
|
97 |
-
$premium_plan = $fs->_get_plan_by_id( $available_license->plan_id );
|
98 |
-
|
99 |
-
$view_params['license'] = $available_license;
|
100 |
-
$view_params['class'] .= ' button-primary';
|
101 |
-
$view_params['plan'] = $premium_plan;
|
102 |
-
|
103 |
-
fs_require_template( 'account/partials/activate-license-button.php', $view_params );
|
104 |
-
}
|
105 |
-
}
|
106 |
-
}
|
107 |
-
} ?></td>
|
108 |
-
<!--/ License Activation / Deactivation -->
|
109 |
-
|
110 |
-
<!-- Plan -->
|
111 |
-
<td><?php if ( $is_registered ) : ?>
|
112 |
-
<?php
|
113 |
-
if ( ! $has_paid_plan ) {
|
114 |
-
$plan_title = $free_text;
|
115 |
-
} else {
|
116 |
-
if ( $install->is_trial() ) {
|
117 |
-
if ( is_object( $trial_plan ) && $trial_plan->id == $install->trial_plan_id ) {
|
118 |
-
$plan_title = is_string( $trial_plan->name ) ?
|
119 |
-
strtoupper( $trial_plan->title ) :
|
120 |
-
fs_text_inline( 'Trial', 'trial', $slug );
|
121 |
-
} else {
|
122 |
-
$plan_title = fs_text_inline( 'Trial', 'trial', $slug );
|
123 |
-
}
|
124 |
-
} else {
|
125 |
-
$plan = $fs->_get_plan_by_id( $install->plan_id );
|
126 |
-
$plan_title = strtoupper( is_string( $plan->title ) ?
|
127 |
-
$plan->title :
|
128 |
-
strtoupper( $free_text )
|
129 |
-
);
|
130 |
-
}
|
131 |
-
}
|
132 |
-
?>
|
133 |
-
<code><?php echo $plan_title ?></code>
|
134 |
-
<?php endif ?></td>
|
135 |
-
<!--/ Plan -->
|
136 |
-
|
137 |
-
<!-- More details button -->
|
138 |
-
<td><?php if ( $is_registered ) : ?>
|
139 |
-
<button class="fs-show-install-details button button-small">More details <i
|
140 |
-
class="dashicons dashicons-arrow-right-alt2"></i>
|
141 |
-
</button><?php endif ?></td>
|
142 |
-
<!--/ More details button -->
|
143 |
-
</tr>
|
144 |
-
<?php if ( $is_registered ) : ?>
|
145 |
-
<!-- More details -->
|
146 |
-
<tr class="fs-install-details" data-install-id="<?php echo $install->id ?>" style="display: none">
|
147 |
-
<td colspan="5">
|
148 |
-
<table class="widefat fs-key-value-table">
|
149 |
-
<tbody>
|
150 |
-
<?php $row_index = 0 ?>
|
151 |
-
<!-- Blog ID -->
|
152 |
-
<tr <?php if ( 1 == $row_index % 2 ) {
|
153 |
-
echo ' class="alternate"';
|
154 |
-
} ?>>
|
155 |
-
<td>
|
156 |
-
<nobr><?php fs_esc_html_echo_inline( 'Blog ID', 'blog-id', $slug ) ?>:</nobr>
|
157 |
-
</td>
|
158 |
-
<td><code><?php echo $blog_id ?></code></td>
|
159 |
-
<td><?php if ( ! FS_Plugin_License::is_valid_id( $install->license_id ) ) : ?>
|
160 |
-
<!-- Toggle Usage Tracking -->
|
161 |
-
<?php $action = 'toggle_tracking' ?>
|
162 |
-
<form action="<?php echo $fs->_get_admin_page_url( 'account' ) ?>" method="POST">
|
163 |
-
<input type="hidden" name="fs_action" value="<?php echo $action ?>">
|
164 |
-
<?php wp_nonce_field( trim( "{$action}:{$blog_id}:{$install->id}", ':' ) ) ?>
|
165 |
-
<input type="hidden" name="install_id" value="<?php echo $install->id ?>">
|
166 |
-
<input type="hidden" name="blog_id" value="<?php echo $blog_id ?>">
|
167 |
-
<button class="fs-toggle-tracking button button-small<?php if ( $install->is_disconnected ) {
|
168 |
-
echo ' button-primary';
|
169 |
-
} ?>" data-is-disconnected="<?php echo $install->is_disconnected ? 'true' : 'false' ?>"><?php $install->is_disconnected ? fs_esc_html_echo_inline( 'Opt In', 'opt-in', $slug ) : fs_esc_html_echo_inline( 'Opt Out', 'opt-out', $slug ) ?></button>
|
170 |
-
</form>
|
171 |
-
<!--/ Toggle Usage Tracking -->
|
172 |
-
<?php endif ?></td>
|
173 |
-
</tr>
|
174 |
-
<?php $row_index ++ ?>
|
175 |
-
<!--/ Blog ID -->
|
176 |
-
|
177 |
-
<?php if ( $is_registered && $install->user_id != $main_user->id ) : ?>
|
178 |
-
<?php
|
179 |
-
/**
|
180 |
-
* @var FS_User $user
|
181 |
-
*/
|
182 |
-
$user = Freemius::_get_user_by_id( $install->user_id ) ?>
|
183 |
-
<?php if ( is_object( $user ) ) : ?>
|
184 |
-
<!-- User Name -->
|
185 |
-
<tr <?php if ( 1 == $row_index % 2 ) {
|
186 |
-
echo ' class="alternate"';
|
187 |
-
} ?>>
|
188 |
-
<td>
|
189 |
-
<nobr><?php fs_esc_html_echo_inline( 'Owner Name', 'owner-name', $slug ) ?>:</nobr>
|
190 |
-
</td>
|
191 |
-
<td colspan="2"><code><?php echo htmlspecialchars( $user->get_name() ) ?></code></td>
|
192 |
-
</tr>
|
193 |
-
<?php $row_index ++ ?>
|
194 |
-
<!--/ User Name -->
|
195 |
-
|
196 |
-
<!-- User Email -->
|
197 |
-
<tr <?php if ( 1 == $row_index % 2 ) {
|
198 |
-
echo ' class="alternate"';
|
199 |
-
} ?>>
|
200 |
-
<td>
|
201 |
-
<nobr><?php fs_esc_html_echo_inline( 'Owner Email', 'owner-email', $slug ) ?>:</nobr>
|
202 |
-
</td>
|
203 |
-
<td colspan="2"><code><?php echo htmlspecialchars( $user->email ) ?></code></td>
|
204 |
-
</tr>
|
205 |
-
<?php $row_index ++ ?>
|
206 |
-
<!--/ User Email -->
|
207 |
-
|
208 |
-
<!-- User ID -->
|
209 |
-
<tr <?php if ( 1 == $row_index % 2 ) {
|
210 |
-
echo ' class="alternate"';
|
211 |
-
} ?>>
|
212 |
-
<td>
|
213 |
-
<nobr><?php fs_esc_html_echo_inline( 'Owner ID', 'owner-id', $slug ) ?>:</nobr>
|
214 |
-
</td>
|
215 |
-
<td colspan="2"><code><?php echo $user->id ?></code></td>
|
216 |
-
</tr>
|
217 |
-
<?php $row_index ++ ?>
|
218 |
-
<!--/ User ID -->
|
219 |
-
<?php endif ?>
|
220 |
-
<?php endif ?>
|
221 |
-
|
222 |
-
<!-- Public Key -->
|
223 |
-
<tr <?php if ( 1 == $row_index % 2 ) {
|
224 |
-
echo ' class="alternate"';
|
225 |
-
} ?>>
|
226 |
-
<td>
|
227 |
-
<nobr><?php fs_esc_html_echo_inline( 'Public Key', 'public-key', $slug ) ?>:</nobr>
|
228 |
-
</td>
|
229 |
-
<td colspan="2"><code><?php echo htmlspecialchars( $install->public_key ) ?></code></td>
|
230 |
-
</tr>
|
231 |
-
<?php $row_index ++ ?>
|
232 |
-
<!--/ Public Key -->
|
233 |
-
|
234 |
-
<!-- Secret Key -->
|
235 |
-
<tr <?php if ( 1 == $row_index % 2 ) {
|
236 |
-
echo ' class="alternate"';
|
237 |
-
} ?>>
|
238 |
-
<td>
|
239 |
-
<nobr><?php fs_esc_html_echo_inline( 'Secret Key', 'secret-key', $slug ) ?>:</nobr>
|
240 |
-
</td>
|
241 |
-
<td>
|
242 |
-
<code><?php echo FS_Plugin_License::mask_secret_key_for_html( $install->secret_key ) ?></code>
|
243 |
-
<?php if ( ! $is_whitelabeled ) : ?>
|
244 |
-
<input type="text" value="<?php echo htmlspecialchars( $install->secret_key ) ?>"
|
245 |
-
style="display: none" readonly/></td>
|
246 |
-
<?php endif ?>
|
247 |
-
<?php if ( ! $is_whitelabeled ) : ?>
|
248 |
-
<td><button class="button button-small fs-toggle-visibility"><?php fs_esc_html_echo_x_inline( 'Show', 'verb', 'show', $slug ) ?></button></td>
|
249 |
-
<?php endif ?>
|
250 |
-
</tr>
|
251 |
-
<?php $row_index ++ ?>
|
252 |
-
<!--/ Secret Key -->
|
253 |
-
|
254 |
-
<?php if ( is_object( $license ) ) : ?>
|
255 |
-
<!-- License Key -->
|
256 |
-
<tr <?php if ( 1 == $row_index % 2 ) {
|
257 |
-
echo ' class="alternate"';
|
258 |
-
} ?>>
|
259 |
-
<td>
|
260 |
-
<nobr><?php fs_esc_html_echo_inline( 'License Key', 'license-key', $slug ) ?>:</nobr>
|
261 |
-
</td>
|
262 |
-
<td>
|
263 |
-
<code><?php echo $license->get_html_escaped_masked_secret_key() ?></code>
|
264 |
-
<?php if ( ! $is_whitelabeled ) : ?>
|
265 |
-
<input type="text" value="<?php echo htmlspecialchars( $license->secret_key ) ?>"
|
266 |
-
style="display: none" readonly/></td>
|
267 |
-
<?php endif ?>
|
268 |
-
<?php if ( ! $is_whitelabeled ) : ?>
|
269 |
-
<td>
|
270 |
-
<button class="button button-small fs-toggle-visibility"><?php fs_esc_html_echo_x_inline( 'Show', 'verb', 'show', $slug ) ?></button>
|
271 |
-
<button class="button button-small activate-license-trigger <?php echo $fs->get_unique_affix() ?>"><?php fs_esc_html_echo_inline( 'Change License', 'change-license', $slug ) ?></button>
|
272 |
-
</td>
|
273 |
-
<?php endif ?>
|
274 |
-
</tr>
|
275 |
-
<?php $row_index ++ ?>
|
276 |
-
<!--/ License Key -->
|
277 |
-
|
278 |
-
<?php if ( ! is_object( $main_license ) || $main_license->id != $license->id ) : ?>
|
279 |
-
<?php $subscription = $fs->_get_subscription( $license->id ) ?>
|
280 |
-
<?php if ( ! $license->is_lifetime() && is_object( $subscription ) ) : ?>
|
281 |
-
<!-- Subscription -->
|
282 |
-
<tr <?php if ( 1 == $row_index % 2 ) {
|
283 |
-
echo ' class="alternate"';
|
284 |
-
} ?>>
|
285 |
-
<td>
|
286 |
-
<nobr><?php fs_esc_html_echo_inline( 'Subscription', 'subscription', $slug ) ?>:</nobr>
|
287 |
-
</td>
|
288 |
-
<?php
|
289 |
-
$is_active_subscription = $subscription->is_active();
|
290 |
-
|
291 |
-
$renews_in_text = fs_text_inline( 'Auto renews in %s', 'renews-in', $slug );
|
292 |
-
/* translators: %s: Time period (e.g. Expires in "2 months") */
|
293 |
-
$expires_in_text = fs_text_inline( 'Expires in %s', 'expires-in', $slug );
|
294 |
-
?>
|
295 |
-
<td>
|
296 |
-
<code><?php echo $subscription->id ?> - <?php
|
297 |
-
echo ( 12 == $subscription->billing_cycle ?
|
298 |
-
_fs_text_inline( 'Annual', 'annual', $slug ) :
|
299 |
-
_fs_text_inline( 'Monthly', 'monthly', $slug )
|
300 |
-
);
|
301 |
-
?>
|
302 |
-
</code>
|
303 |
-
<?php if ( ! $is_active_subscription && ! $license->is_first_payment_pending() ) : ?>
|
304 |
-
<label class="fs-tag fs-warn"><?php echo esc_html( sprintf( $expires_in_text, human_time_diff( time(), strtotime( $license->expiration ) ) ) ) ?></label>
|
305 |
-
<?php elseif ( $is_active_subscription && ! $subscription->is_first_payment_pending() ) : ?>
|
306 |
-
<label class="fs-tag fs-success"><?php echo esc_html( sprintf( $renews_in_text, human_time_diff( time(), strtotime( $subscription->next_payment ) ) ) ) ?></label>
|
307 |
-
<?php endif ?>
|
308 |
-
</td>
|
309 |
-
<td><?php if ( $is_active_subscription ) : ?>
|
310 |
-
<?php
|
311 |
-
$downgrading_plan_text = fs_text_inline( 'Downgrading your plan', 'downgrading-plan', $slug );
|
312 |
-
$cancelling_subscription_text = fs_text_inline( 'Cancelling the subscription', 'cancelling-subscription', $slug );
|
313 |
-
/* translators: %1$s: Either 'Downgrading your plan' or 'Cancelling the subscription' */
|
314 |
-
$downgrade_x_confirm_text = fs_text_inline( '%1$s will immediately stop all future recurring payments and your %2$s plan license will expire in %3$s.', 'downgrade-x-confirm', $slug );
|
315 |
-
$prices_increase_text = fs_text_inline( 'Please note that we will not be able to grandfather outdated pricing for renewals/new subscriptions after a cancellation. If you choose to renew the subscription manually in the future, after a price increase, which typically occurs once a year, you will be charged the updated price.', 'pricing-increase-warning', $slug );
|
316 |
-
$after_downgrade_non_blocking_text = fs_text_inline( 'You can still enjoy all %s features but you will not have access to %s security & feature updates, nor support.', 'after-downgrade-non-blocking', $slug );
|
317 |
-
$after_downgrade_blocking_text = fs_text_inline( 'Once your license expires you can still use the Free version but you will NOT have access to the %s features.', 'after-downgrade-blocking', $slug );
|
318 |
-
$downgrade_text = fs_text_x_inline( 'Downgrade', 'verb', 'downgrade', $slug );
|
319 |
-
|
320 |
-
$human_readable_license_expiration = human_time_diff( time(), strtotime( $license->expiration ) );
|
321 |
-
$downgrade_confirmation_message = sprintf(
|
322 |
-
$downgrade_x_confirm_text,
|
323 |
-
( $fs->is_only_premium() ? $cancelling_subscription_text : $downgrading_plan_text ),
|
324 |
-
$plan->title,
|
325 |
-
$human_readable_license_expiration
|
326 |
-
);
|
327 |
-
|
328 |
-
$after_downgrade_message = ! $license->is_block_features ?
|
329 |
-
sprintf( $after_downgrade_non_blocking_text, $plan->title, $fs->get_module_label( true ) ) :
|
330 |
-
sprintf( $after_downgrade_blocking_text, $plan->title );
|
331 |
-
?>
|
332 |
-
<?php $action = 'downgrade_account' ?>
|
333 |
-
<form id="fs_downgrade" action="<?php echo $fs->_get_admin_page_url( 'account' ) ?>" method="POST">
|
334 |
-
<input type="hidden" name="fs_action" value="<?php echo $action ?>">
|
335 |
-
<?php wp_nonce_field( trim( "{$action}:{$blog_id}", ':' ) ) ?>
|
336 |
-
<input type="hidden" name="blog_id" value="<?php echo $blog_id ?>">
|
337 |
-
<button class="button button-small" onclick="if (confirm('<?php echo esc_attr( $downgrade_confirmation_message . ' ' . $after_downgrade_message . ' ' . $prices_increase_text ) ?>')) { this.parentNode.submit(); } else { return false; }"><?php echo $downgrade_text ?></button>
|
338 |
-
</form>
|
339 |
-
<?php endif ?></td>
|
340 |
-
</tr>
|
341 |
-
<?php $row_index ++ ?>
|
342 |
-
<?php endif ?>
|
343 |
-
<!--/ Subscription -->
|
344 |
-
<?php endif ?>
|
345 |
-
<?php endif ?>
|
346 |
-
|
347 |
-
</tbody>
|
348 |
-
</table>
|
349 |
-
</td>
|
350 |
-
</tr>
|
351 |
-
<!--/ More details -->
|
352 |
<?php endif ?>
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package Freemius
|
4 |
+
* @copyright Copyright (c) 2015, Freemius, Inc.
|
5 |
+
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
|
6 |
+
* @since 2.0.0
|
7 |
+
*/
|
8 |
+
|
9 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
10 |
+
exit;
|
11 |
+
}
|
12 |
+
|
13 |
+
/**
|
14 |
+
* @var array $VARS
|
15 |
+
* @var Freemius $fs
|
16 |
+
* @var FS_Plugin_License $main_license
|
17 |
+
*/
|
18 |
+
$fs = $VARS['freemius'];
|
19 |
+
$slug = $fs->get_slug();
|
20 |
+
$site = $VARS['site'];
|
21 |
+
$main_license = $VARS['license'];
|
22 |
+
$is_data_debug_mode = $fs->is_data_debug_mode();
|
23 |
+
$is_whitelabeled = $fs->is_whitelabeled();
|
24 |
+
$has_paid_plan = $fs->has_paid_plan();
|
25 |
+
$is_premium = $fs->is_premium();
|
26 |
+
$main_user = $fs->get_user();
|
27 |
+
$blog_id = $site['blog_id'];
|
28 |
+
|
29 |
+
$install = $VARS['install'];
|
30 |
+
$is_registered = ! empty( $install );
|
31 |
+
$license = null;
|
32 |
+
$trial_plan = $fs->get_trial_plan();
|
33 |
+
$free_text = fs_text_inline( 'Free', 'free', $slug );
|
34 |
+
|
35 |
+
if ( $is_whitelabeled && $fs->is_delegated_connection( $blog_id ) ) {
|
36 |
+
$is_whitelabeled = $fs->is_whitelabeled( true, $blog_id );
|
37 |
+
}
|
38 |
+
?>
|
39 |
+
<tr class="fs-site-details" data-blog-id="<?php echo $blog_id ?>"<?php if ( $is_registered ) : ?> data-install-id="<?php echo $install->id ?>"<?php endif ?>>
|
40 |
+
<!-- Install ID or Opt-in option -->
|
41 |
+
<td><?php if ( $is_registered ) : ?>
|
42 |
+
<?php echo $install->id ?>
|
43 |
+
<?php else : ?>
|
44 |
+
<?php $action = 'opt_in' ?>
|
45 |
+
<form action="<?php echo $fs->_get_admin_page_url( 'account' ) ?>" method="POST">
|
46 |
+
<input type="hidden" name="fs_action" value="<?php echo $action ?>">
|
47 |
+
<?php wp_nonce_field( trim( "{$action}:{$blog_id}", ':' ) ) ?>
|
48 |
+
<input type="hidden" name="blog_id" value="<?php echo $blog_id ?>">
|
49 |
+
<button class="fs-opt-in button button-small"><?php fs_esc_html_echo_inline( 'Opt In', 'opt-in', $slug ) ?></button>
|
50 |
+
</form>
|
51 |
+
<?php endif ?>
|
52 |
+
</td>
|
53 |
+
<!--/ Install ID or Opt-in option -->
|
54 |
+
|
55 |
+
<!-- Site URL -->
|
56 |
+
<td class="fs-field-url fs-main-column"><?php echo fs_strip_url_protocol( $site['url'] ) ?></td>
|
57 |
+
<!--/ Site URL -->
|
58 |
+
|
59 |
+
<!-- License Activation / Deactivation -->
|
60 |
+
<td><?php if ( $has_paid_plan ) {
|
61 |
+
$view_params = array(
|
62 |
+
'freemius' => $fs,
|
63 |
+
'slug' => $slug,
|
64 |
+
'blog_id' => $blog_id,
|
65 |
+
'class' => 'button-small',
|
66 |
+
);
|
67 |
+
|
68 |
+
$license = null;
|
69 |
+
if ( $is_registered ) {
|
70 |
+
$view_params['install_id'] = $install->id;
|
71 |
+
$view_params['is_localhost'] = $install->is_localhost();
|
72 |
+
|
73 |
+
$has_license = FS_Plugin_License::is_valid_id( $install->license_id );
|
74 |
+
$license = $has_license ?
|
75 |
+
$fs->_get_license_by_id( $install->license_id ) :
|
76 |
+
null;
|
77 |
+
} else {
|
78 |
+
$view_params['is_localhost'] = FS_Site::is_localhost_by_address( $site['url'] );
|
79 |
+
}
|
80 |
+
|
81 |
+
if ( ! $is_whitelabeled ) {
|
82 |
+
if ( is_object( $license ) ) {
|
83 |
+
$view_params['license'] = $license;
|
84 |
+
|
85 |
+
// Show license deactivation button.
|
86 |
+
fs_require_template( 'account/partials/deactivate-license-button.php', $view_params );
|
87 |
+
} else {
|
88 |
+
if ( is_object( $main_license ) && $main_license->can_activate( $view_params['is_localhost'] ) ) {
|
89 |
+
// Main license is available for activation.
|
90 |
+
$available_license = $main_license;
|
91 |
+
} else {
|
92 |
+
// Try to find any available license for activation.
|
93 |
+
$available_license = $fs->_get_available_premium_license( $view_params['is_localhost'] );
|
94 |
+
}
|
95 |
+
|
96 |
+
if ( is_object( $available_license ) ) {
|
97 |
+
$premium_plan = $fs->_get_plan_by_id( $available_license->plan_id );
|
98 |
+
|
99 |
+
$view_params['license'] = $available_license;
|
100 |
+
$view_params['class'] .= ' button-primary';
|
101 |
+
$view_params['plan'] = $premium_plan;
|
102 |
+
|
103 |
+
fs_require_template( 'account/partials/activate-license-button.php', $view_params );
|
104 |
+
}
|
105 |
+
}
|
106 |
+
}
|
107 |
+
} ?></td>
|
108 |
+
<!--/ License Activation / Deactivation -->
|
109 |
+
|
110 |
+
<!-- Plan -->
|
111 |
+
<td><?php if ( $is_registered ) : ?>
|
112 |
+
<?php
|
113 |
+
if ( ! $has_paid_plan ) {
|
114 |
+
$plan_title = $free_text;
|
115 |
+
} else {
|
116 |
+
if ( $install->is_trial() ) {
|
117 |
+
if ( is_object( $trial_plan ) && $trial_plan->id == $install->trial_plan_id ) {
|
118 |
+
$plan_title = is_string( $trial_plan->name ) ?
|
119 |
+
strtoupper( $trial_plan->title ) :
|
120 |
+
fs_text_inline( 'Trial', 'trial', $slug );
|
121 |
+
} else {
|
122 |
+
$plan_title = fs_text_inline( 'Trial', 'trial', $slug );
|
123 |
+
}
|
124 |
+
} else {
|
125 |
+
$plan = $fs->_get_plan_by_id( $install->plan_id );
|
126 |
+
$plan_title = strtoupper( is_string( $plan->title ) ?
|
127 |
+
$plan->title :
|
128 |
+
strtoupper( $free_text )
|
129 |
+
);
|
130 |
+
}
|
131 |
+
}
|
132 |
+
?>
|
133 |
+
<code><?php echo $plan_title ?></code>
|
134 |
+
<?php endif ?></td>
|
135 |
+
<!--/ Plan -->
|
136 |
+
|
137 |
+
<!-- More details button -->
|
138 |
+
<td><?php if ( $is_registered ) : ?>
|
139 |
+
<button class="fs-show-install-details button button-small">More details <i
|
140 |
+
class="dashicons dashicons-arrow-right-alt2"></i>
|
141 |
+
</button><?php endif ?></td>
|
142 |
+
<!--/ More details button -->
|
143 |
+
</tr>
|
144 |
+
<?php if ( $is_registered ) : ?>
|
145 |
+
<!-- More details -->
|
146 |
+
<tr class="fs-install-details" data-install-id="<?php echo $install->id ?>" style="display: none">
|
147 |
+
<td colspan="5">
|
148 |
+
<table class="widefat fs-key-value-table">
|
149 |
+
<tbody>
|
150 |
+
<?php $row_index = 0 ?>
|
151 |
+
<!-- Blog ID -->
|
152 |
+
<tr <?php if ( 1 == $row_index % 2 ) {
|
153 |
+
echo ' class="alternate"';
|
154 |
+
} ?>>
|
155 |
+
<td>
|
156 |
+
<nobr><?php fs_esc_html_echo_inline( 'Blog ID', 'blog-id', $slug ) ?>:</nobr>
|
157 |
+
</td>
|
158 |
+
<td><code><?php echo $blog_id ?></code></td>
|
159 |
+
<td><?php if ( ! FS_Plugin_License::is_valid_id( $install->license_id ) ) : ?>
|
160 |
+
<!-- Toggle Usage Tracking -->
|
161 |
+
<?php $action = 'toggle_tracking' ?>
|
162 |
+
<form action="<?php echo $fs->_get_admin_page_url( 'account' ) ?>" method="POST">
|
163 |
+
<input type="hidden" name="fs_action" value="<?php echo $action ?>">
|
164 |
+
<?php wp_nonce_field( trim( "{$action}:{$blog_id}:{$install->id}", ':' ) ) ?>
|
165 |
+
<input type="hidden" name="install_id" value="<?php echo $install->id ?>">
|
166 |
+
<input type="hidden" name="blog_id" value="<?php echo $blog_id ?>">
|
167 |
+
<button class="fs-toggle-tracking button button-small<?php if ( $install->is_disconnected ) {
|
168 |
+
echo ' button-primary';
|
169 |
+
} ?>" data-is-disconnected="<?php echo $install->is_disconnected ? 'true' : 'false' ?>"><?php $install->is_disconnected ? fs_esc_html_echo_inline( 'Opt In', 'opt-in', $slug ) : fs_esc_html_echo_inline( 'Opt Out', 'opt-out', $slug ) ?></button>
|
170 |
+
</form>
|
171 |
+
<!--/ Toggle Usage Tracking -->
|
172 |
+
<?php endif ?></td>
|
173 |
+
</tr>
|
174 |
+
<?php $row_index ++ ?>
|
175 |
+
<!--/ Blog ID -->
|
176 |
+
|
177 |
+
<?php if ( $is_registered && $install->user_id != $main_user->id ) : ?>
|
178 |
+
<?php
|
179 |
+
/**
|
180 |
+
* @var FS_User $user
|
181 |
+
*/
|
182 |
+
$user = Freemius::_get_user_by_id( $install->user_id ) ?>
|
183 |
+
<?php if ( is_object( $user ) ) : ?>
|
184 |
+
<!-- User Name -->
|
185 |
+
<tr <?php if ( 1 == $row_index % 2 ) {
|
186 |
+
echo ' class="alternate"';
|
187 |
+
} ?>>
|
188 |
+
<td>
|
189 |
+
<nobr><?php fs_esc_html_echo_inline( 'Owner Name', 'owner-name', $slug ) ?>:</nobr>
|
190 |
+
</td>
|
191 |
+
<td colspan="2"><code><?php echo htmlspecialchars( $user->get_name() ) ?></code></td>
|
192 |
+
</tr>
|
193 |
+
<?php $row_index ++ ?>
|
194 |
+
<!--/ User Name -->
|
195 |
+
|
196 |
+
<!-- User Email -->
|
197 |
+
<tr <?php if ( 1 == $row_index % 2 ) {
|
198 |
+
echo ' class="alternate"';
|
199 |
+
} ?>>
|
200 |
+
<td>
|
201 |
+
<nobr><?php fs_esc_html_echo_inline( 'Owner Email', 'owner-email', $slug ) ?>:</nobr>
|
202 |
+
</td>
|
203 |
+
<td colspan="2"><code><?php echo htmlspecialchars( $user->email ) ?></code></td>
|
204 |
+
</tr>
|
205 |
+
<?php $row_index ++ ?>
|
206 |
+
<!--/ User Email -->
|
207 |
+
|
208 |
+
<!-- User ID -->
|
209 |
+
<tr <?php if ( 1 == $row_index % 2 ) {
|
210 |
+
echo ' class="alternate"';
|
211 |
+
} ?>>
|
212 |
+
<td>
|
213 |
+
<nobr><?php fs_esc_html_echo_inline( 'Owner ID', 'owner-id', $slug ) ?>:</nobr>
|
214 |
+
</td>
|
215 |
+
<td colspan="2"><code><?php echo $user->id ?></code></td>
|
216 |
+
</tr>
|
217 |
+
<?php $row_index ++ ?>
|
218 |
+
<!--/ User ID -->
|
219 |
+
<?php endif ?>
|
220 |
+
<?php endif ?>
|
221 |
+
|
222 |
+
<!-- Public Key -->
|
223 |
+
<tr <?php if ( 1 == $row_index % 2 ) {
|
224 |
+
echo ' class="alternate"';
|
225 |
+
} ?>>
|
226 |
+
<td>
|
227 |
+
<nobr><?php fs_esc_html_echo_inline( 'Public Key', 'public-key', $slug ) ?>:</nobr>
|
228 |
+
</td>
|
229 |
+
<td colspan="2"><code><?php echo htmlspecialchars( $install->public_key ) ?></code></td>
|
230 |
+
</tr>
|
231 |
+
<?php $row_index ++ ?>
|
232 |
+
<!--/ Public Key -->
|
233 |
+
|
234 |
+
<!-- Secret Key -->
|
235 |
+
<tr <?php if ( 1 == $row_index % 2 ) {
|
236 |
+
echo ' class="alternate"';
|
237 |
+
} ?>>
|
238 |
+
<td>
|
239 |
+
<nobr><?php fs_esc_html_echo_inline( 'Secret Key', 'secret-key', $slug ) ?>:</nobr>
|
240 |
+
</td>
|
241 |
+
<td>
|
242 |
+
<code><?php echo FS_Plugin_License::mask_secret_key_for_html( $install->secret_key ) ?></code>
|
243 |
+
<?php if ( ! $is_whitelabeled ) : ?>
|
244 |
+
<input type="text" value="<?php echo htmlspecialchars( $install->secret_key ) ?>"
|
245 |
+
style="display: none" readonly/></td>
|
246 |
+
<?php endif ?>
|
247 |
+
<?php if ( ! $is_whitelabeled ) : ?>
|
248 |
+
<td><button class="button button-small fs-toggle-visibility"><?php fs_esc_html_echo_x_inline( 'Show', 'verb', 'show', $slug ) ?></button></td>
|
249 |
+
<?php endif ?>
|
250 |
+
</tr>
|
251 |
+
<?php $row_index ++ ?>
|
252 |
+
<!--/ Secret Key -->
|
253 |
+
|
254 |
+
<?php if ( is_object( $license ) ) : ?>
|
255 |
+
<!-- License Key -->
|
256 |
+
<tr <?php if ( 1 == $row_index % 2 ) {
|
257 |
+
echo ' class="alternate"';
|
258 |
+
} ?>>
|
259 |
+
<td>
|
260 |
+
<nobr><?php fs_esc_html_echo_inline( 'License Key', 'license-key', $slug ) ?>:</nobr>
|
261 |
+
</td>
|
262 |
+
<td>
|
263 |
+
<code><?php echo $license->get_html_escaped_masked_secret_key() ?></code>
|
264 |
+
<?php if ( ! $is_whitelabeled ) : ?>
|
265 |
+
<input type="text" value="<?php echo htmlspecialchars( $license->secret_key ) ?>"
|
266 |
+
style="display: none" readonly/></td>
|
267 |
+
<?php endif ?>
|
268 |
+
<?php if ( ! $is_whitelabeled ) : ?>
|
269 |
+
<td>
|
270 |
+
<button class="button button-small fs-toggle-visibility"><?php fs_esc_html_echo_x_inline( 'Show', 'verb', 'show', $slug ) ?></button>
|
271 |
+
<button class="button button-small activate-license-trigger <?php echo $fs->get_unique_affix() ?>"><?php fs_esc_html_echo_inline( 'Change License', 'change-license', $slug ) ?></button>
|
272 |
+
</td>
|
273 |
+
<?php endif ?>
|
274 |
+
</tr>
|
275 |
+
<?php $row_index ++ ?>
|
276 |
+
<!--/ License Key -->
|
277 |
+
|
278 |
+
<?php if ( ! is_object( $main_license ) || $main_license->id != $license->id ) : ?>
|
279 |
+
<?php $subscription = $fs->_get_subscription( $license->id ) ?>
|
280 |
+
<?php if ( ! $license->is_lifetime() && is_object( $subscription ) ) : ?>
|
281 |
+
<!-- Subscription -->
|
282 |
+
<tr <?php if ( 1 == $row_index % 2 ) {
|
283 |
+
echo ' class="alternate"';
|
284 |
+
} ?>>
|
285 |
+
<td>
|
286 |
+
<nobr><?php fs_esc_html_echo_inline( 'Subscription', 'subscription', $slug ) ?>:</nobr>
|
287 |
+
</td>
|
288 |
+
<?php
|
289 |
+
$is_active_subscription = $subscription->is_active();
|
290 |
+
|
291 |
+
$renews_in_text = fs_text_inline( 'Auto renews in %s', 'renews-in', $slug );
|
292 |
+
/* translators: %s: Time period (e.g. Expires in "2 months") */
|
293 |
+
$expires_in_text = fs_text_inline( 'Expires in %s', 'expires-in', $slug );
|
294 |
+
?>
|
295 |
+
<td>
|
296 |
+
<code><?php echo $subscription->id ?> - <?php
|
297 |
+
echo ( 12 == $subscription->billing_cycle ?
|
298 |
+
_fs_text_inline( 'Annual', 'annual', $slug ) :
|
299 |
+
_fs_text_inline( 'Monthly', 'monthly', $slug )
|
300 |
+
);
|
301 |
+
?>
|
302 |
+
</code>
|
303 |
+
<?php if ( ! $is_active_subscription && ! $license->is_first_payment_pending() ) : ?>
|
304 |
+
<label class="fs-tag fs-warn"><?php echo esc_html( sprintf( $expires_in_text, human_time_diff( time(), strtotime( $license->expiration ) ) ) ) ?></label>
|
305 |
+
<?php elseif ( $is_active_subscription && ! $subscription->is_first_payment_pending() ) : ?>
|
306 |
+
<label class="fs-tag fs-success"><?php echo esc_html( sprintf( $renews_in_text, human_time_diff( time(), strtotime( $subscription->next_payment ) ) ) ) ?></label>
|
307 |
+
<?php endif ?>
|
308 |
+
</td>
|
309 |
+
<td><?php if ( $is_active_subscription ) : ?>
|
310 |
+
<?php
|
311 |
+
$downgrading_plan_text = fs_text_inline( 'Downgrading your plan', 'downgrading-plan', $slug );
|
312 |
+
$cancelling_subscription_text = fs_text_inline( 'Cancelling the subscription', 'cancelling-subscription', $slug );
|
313 |
+
/* translators: %1$s: Either 'Downgrading your plan' or 'Cancelling the subscription' */
|
314 |
+
$downgrade_x_confirm_text = fs_text_inline( '%1$s will immediately stop all future recurring payments and your %2$s plan license will expire in %3$s.', 'downgrade-x-confirm', $slug );
|
315 |
+
$prices_increase_text = fs_text_inline( 'Please note that we will not be able to grandfather outdated pricing for renewals/new subscriptions after a cancellation. If you choose to renew the subscription manually in the future, after a price increase, which typically occurs once a year, you will be charged the updated price.', 'pricing-increase-warning', $slug );
|
316 |
+
$after_downgrade_non_blocking_text = fs_text_inline( 'You can still enjoy all %s features but you will not have access to %s security & feature updates, nor support.', 'after-downgrade-non-blocking', $slug );
|
317 |
+
$after_downgrade_blocking_text = fs_text_inline( 'Once your license expires you can still use the Free version but you will NOT have access to the %s features.', 'after-downgrade-blocking', $slug );
|
318 |
+
$downgrade_text = fs_text_x_inline( 'Downgrade', 'verb', 'downgrade', $slug );
|
319 |
+
|
320 |
+
$human_readable_license_expiration = human_time_diff( time(), strtotime( $license->expiration ) );
|
321 |
+
$downgrade_confirmation_message = sprintf(
|
322 |
+
$downgrade_x_confirm_text,
|
323 |
+
( $fs->is_only_premium() ? $cancelling_subscription_text : $downgrading_plan_text ),
|
324 |
+
$plan->title,
|
325 |
+
$human_readable_license_expiration
|
326 |
+
);
|
327 |
+
|
328 |
+
$after_downgrade_message = ! $license->is_block_features ?
|
329 |
+
sprintf( $after_downgrade_non_blocking_text, $plan->title, $fs->get_module_label( true ) ) :
|
330 |
+
sprintf( $after_downgrade_blocking_text, $plan->title );
|
331 |
+
?>
|
332 |
+
<?php $action = 'downgrade_account' ?>
|
333 |
+
<form id="fs_downgrade" action="<?php echo $fs->_get_admin_page_url( 'account' ) ?>" method="POST">
|
334 |
+
<input type="hidden" name="fs_action" value="<?php echo $action ?>">
|
335 |
+
<?php wp_nonce_field( trim( "{$action}:{$blog_id}", ':' ) ) ?>
|
336 |
+
<input type="hidden" name="blog_id" value="<?php echo $blog_id ?>">
|
337 |
+
<button class="button button-small" onclick="if (confirm('<?php echo esc_attr( $downgrade_confirmation_message . ' ' . $after_downgrade_message . ' ' . $prices_increase_text ) ?>')) { this.parentNode.submit(); } else { return false; }"><?php echo $downgrade_text ?></button>
|
338 |
+
</form>
|
339 |
+
<?php endif ?></td>
|
340 |
+
</tr>
|
341 |
+
<?php $row_index ++ ?>
|
342 |
+
<?php endif ?>
|
343 |
+
<!--/ Subscription -->
|
344 |
+
<?php endif ?>
|
345 |
+
<?php endif ?>
|
346 |
+
|
347 |
+
</tbody>
|
348 |
+
</table>
|
349 |
+
</td>
|
350 |
+
</tr>
|
351 |
+
<!--/ More details -->
|
352 |
<?php endif ?>
|
freemius/templates/add-ons.php
CHANGED
@@ -1,502 +1,502 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* @package Freemius
|
4 |
-
* @copyright Copyright (c) 2015, Freemius, Inc.
|
5 |
-
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
|
6 |
-
* @since 1.0.3
|
7 |
-
*/
|
8 |
-
|
9 |
-
if ( ! defined( 'ABSPATH' ) ) {
|
10 |
-
exit;
|
11 |
-
}
|
12 |
-
|
13 |
-
/**
|
14 |
-
* @var array $VARS
|
15 |
-
* @var Freemius
|
16 |
-
*/
|
17 |
-
$fs = freemius( $VARS['id'] );
|
18 |
-
|
19 |
-
$slug = $fs->get_slug();
|
20 |
-
|
21 |
-
$open_addon_slug = fs_request_get( 'slug' );
|
22 |
-
|
23 |
-
$open_addon = false;
|
24 |
-
|
25 |
-
$is_data_debug_mode = $fs->is_data_debug_mode();
|
26 |
-
$is_whitelabeled = $fs->is_whitelabeled();
|
27 |
-
|
28 |
-
/**
|
29 |
-
* @var FS_Plugin[]
|
30 |
-
*/
|
31 |
-
$addons = $fs->get_addons();
|
32 |
-
|
33 |
-
$has_addons = ( is_array( $addons ) && 0 < count( $addons ) );
|
34 |
-
|
35 |
-
$account_addon_ids = $fs->get_updated_account_addons();
|
36 |
-
|
37 |
-
$download_latest_text = fs_text_x_inline( 'Download Latest', 'as download latest version', 'download-latest', $slug );
|
38 |
-
$view_details_text = fs_text_inline( 'View details', 'view-details', $slug );
|
39 |
-
|
40 |
-
$has_tabs = $fs->_add_tabs_before_content();
|
41 |
-
|
42 |
-
$fs_blog_id = ( is_multisite() && ! is_network_admin() ) ?
|
43 |
-
get_current_blog_id() :
|
44 |
-
0;
|
45 |
-
?>
|
46 |
-
<div id="fs_addons" class="wrap fs-section">
|
47 |
-
<?php if ( ! $has_tabs ) : ?>
|
48 |
-
<h2><?php echo esc_html( sprintf( fs_text_inline( 'Add Ons for %s', 'add-ons-for-x', $slug ), $fs->get_plugin_name() ) ) ?></h2>
|
49 |
-
<?php endif ?>
|
50 |
-
|
51 |
-
<?php $fs->do_action( 'addons/after_title' ) ?>
|
52 |
-
|
53 |
-
<div id="poststuff">
|
54 |
-
<?php if ( ! $has_addons ) : ?>
|
55 |
-
<h3><?php echo esc_html( sprintf(
|
56 |
-
'%s... %s',
|
57 |
-
fs_text_x_inline( 'Oops', 'exclamation', 'oops', $slug ),
|
58 |
-
fs_text_inline( 'We couldn\'t load the add-ons list. It\'s probably an issue on our side, please try to come back in few minutes.', 'add-ons-missing', $slug )
|
59 |
-
) ) ?></h3>
|
60 |
-
<?php endif ?>
|
61 |
-
<ul class="fs-cards-list">
|
62 |
-
<?php if ( $has_addons ) : ?>
|
63 |
-
<?php
|
64 |
-
$plans_and_pricing_by_addon_id = $fs->_get_addons_plans_and_pricing_map_by_id();
|
65 |
-
|
66 |
-
$active_plugins_directories_map = Freemius::get_active_plugins_directories_map( $fs_blog_id );
|
67 |
-
?>
|
68 |
-
<?php
|
69 |
-
$hide_all_addons_data = false;
|
70 |
-
|
71 |
-
if ( $fs->is_whitelabeled_by_flag() ) {
|
72 |
-
$hide_all_addons_data = true;
|
73 |
-
|
74 |
-
$addon_ids = $fs->get_updated_account_addons();
|
75 |
-
$installed_addons = $fs->get_installed_addons();
|
76 |
-
foreach ( $installed_addons as $fs_addon ) {
|
77 |
-
$addon_ids[] = $fs_addon->get_id();
|
78 |
-
}
|
79 |
-
|
80 |
-
if ( ! empty( $addon_ids ) ) {
|
81 |
-
$addon_ids = array_unique( $addon_ids );
|
82 |
-
}
|
83 |
-
|
84 |
-
foreach ( $addon_ids as $addon_id ) {
|
85 |
-
$addon = $fs->get_addon( $addon_id );
|
86 |
-
|
87 |
-
if ( ! is_object( $addon ) ) {
|
88 |
-
continue;
|
89 |
-
}
|
90 |
-
|
91 |
-
$addon_storage = FS_Storage::instance( WP_FS__MODULE_TYPE_PLUGIN, $addon->slug );
|
92 |
-
|
93 |
-
if ( ! $addon_storage->is_whitelabeled ) {
|
94 |
-
$hide_all_addons_data = false;
|
95 |
-
break;
|
96 |
-
}
|
97 |
-
|
98 |
-
if ( $is_data_debug_mode ) {
|
99 |
-
$is_whitelabeled = false;
|
100 |
-
}
|
101 |
-
}
|
102 |
-
}
|
103 |
-
?>
|
104 |
-
<?php foreach ( $addons as $addon ) : ?>
|
105 |
-
<?php
|
106 |
-
$basename = $fs->get_addon_basename( $addon->id );
|
107 |
-
|
108 |
-
$is_addon_installed = file_exists( fs_normalize_path( WP_PLUGIN_DIR . '/' . $basename ) );
|
109 |
-
|
110 |
-
if ( ! $is_addon_installed && $hide_all_addons_data ) {
|
111 |
-
continue;
|
112 |
-
}
|
113 |
-
|
114 |
-
$is_addon_activated = $is_addon_installed ?
|
115 |
-
$fs->is_addon_activated( $addon->id ) :
|
116 |
-
false;
|
117 |
-
|
118 |
-
$is_plugin_active = (
|
119 |
-
$is_addon_activated ||
|
120 |
-
isset( $active_plugins_directories_map[ dirname( $basename ) ] )
|
121 |
-
);
|
122 |
-
|
123 |
-
$open_addon = ( $open_addon || ( $open_addon_slug === $addon->slug ) );
|
124 |
-
|
125 |
-
$price = 0;
|
126 |
-
$has_trial = false;
|
127 |
-
$has_free_plan = false;
|
128 |
-
$has_paid_plan = false;
|
129 |
-
|
130 |
-
if ( isset( $plans_and_pricing_by_addon_id[$addon->id] ) ) {
|
131 |
-
$plans = $plans_and_pricing_by_addon_id[$addon->id];
|
132 |
-
|
133 |
-
if ( is_array( $plans ) && 0 < count( $plans ) ) {
|
134 |
-
foreach ( $plans as $plan ) {
|
135 |
-
if ( ! isset( $plan->pricing ) ||
|
136 |
-
! is_array( $plan->pricing ) ||
|
137 |
-
0 == count( $plan->pricing )
|
138 |
-
) {
|
139 |
-
// No pricing means a free plan.
|
140 |
-
$has_free_plan = true;
|
141 |
-
continue;
|
142 |
-
}
|
143 |
-
|
144 |
-
|
145 |
-
$has_paid_plan = true;
|
146 |
-
$has_trial = $has_trial || ( is_numeric( $plan->trial_period ) && ( $plan->trial_period > 0 ) );
|
147 |
-
|
148 |
-
$min_price = 999999;
|
149 |
-
foreach ( $plan->pricing as $pricing ) {
|
150 |
-
$pricing = new FS_Pricing( $pricing );
|
151 |
-
|
152 |
-
if ( ! $pricing->is_usd() ) {
|
153 |
-
/**
|
154 |
-
* Skip non-USD pricing.
|
155 |
-
*
|
156 |
-
* @author Leo Fajardo (@leorw)
|
157 |
-
* @since 2.3.1
|
158 |
-
*/
|
159 |
-
continue;
|
160 |
-
}
|
161 |
-
|
162 |
-
if ( $pricing->has_annual() ) {
|
163 |
-
$min_price = min( $min_price, $pricing->annual_price );
|
164 |
-
} else if ( $pricing->has_monthly() ) {
|
165 |
-
$min_price = min( $min_price, 12 * $pricing->monthly_price );
|
166 |
-
}
|
167 |
-
}
|
168 |
-
|
169 |
-
if ( $min_price < 999999 ) {
|
170 |
-
$price = $min_price;
|
171 |
-
}
|
172 |
-
|
173 |
-
}
|
174 |
-
}
|
175 |
-
|
176 |
-
if ( ! $has_paid_plan && ! $has_free_plan ) {
|
177 |
-
continue;
|
178 |
-
}
|
179 |
-
}
|
180 |
-
?>
|
181 |
-
<li class="fs-card fs-addon" data-slug="<?php echo $addon->slug ?>">
|
182 |
-
<?php
|
183 |
-
$view_details_link = sprintf( '<a href="%s" aria-label="%s" data-title="%s"',
|
184 |
-
esc_url( network_admin_url( 'plugin-install.php?fs_allow_updater_and_dialog=true' . ( ! empty( $fs_blog_id ) ? '&fs_blog_id=' . $fs_blog_id : '' ) . '&tab=plugin-information&parent_plugin_id=' . $fs->get_id() . '&plugin=' . $addon->slug .
|
185 |
-
'&TB_iframe=true&width=600&height=550' ) ),
|
186 |
-
esc_attr( sprintf( fs_text_inline( 'More information about %s', 'more-information-about-x', $slug ), $addon->title ) ),
|
187 |
-
esc_attr( $addon->title )
|
188 |
-
) . ' class="thickbox%s">%s</a>';
|
189 |
-
|
190 |
-
echo sprintf(
|
191 |
-
$view_details_link,
|
192 |
-
/**
|
193 |
-
* Additional class.
|
194 |
-
*
|
195 |
-
* @author Leo Fajardo (@leorw)
|
196 |
-
* @since 2.2.4
|
197 |
-
*/
|
198 |
-
' fs-overlay',
|
199 |
-
/**
|
200 |
-
* Set the view details link text to an empty string since it is an overlay that
|
201 |
-
* doesn't really need a text and whose purpose is to open the details dialog when
|
202 |
-
* the card is clicked.
|
203 |
-
*
|
204 |
-
* @author Leo Fajardo (@leorw)
|
205 |
-
* @since 2.2.4
|
206 |
-
*/
|
207 |
-
''
|
208 |
-
);
|
209 |
-
?>
|
210 |
-
<?php
|
211 |
-
if ( is_null( $addon->info ) ) {
|
212 |
-
$addon->info = new stdClass();
|
213 |
-
}
|
214 |
-
if ( ! isset( $addon->info->card_banner_url ) ) {
|
215 |
-
$addon->info->card_banner_url = '//dashboard.freemius.com/assets/img/marketing/blueprint-300x100.jpg';
|
216 |
-
}
|
217 |
-
if ( ! isset( $addon->info->short_description ) ) {
|
218 |
-
$addon->info->short_description = 'What\'s the one thing your add-on does really, really well?';
|
219 |
-
}
|
220 |
-
?>
|
221 |
-
<div class="fs-inner">
|
222 |
-
<ul>
|
223 |
-
<li class="fs-card-banner"
|
224 |
-
style="background-image: url('<?php echo $addon->info->card_banner_url ?>');"><?php
|
225 |
-
if ( $is_plugin_active || $is_addon_installed ) {
|
226 |
-
echo sprintf(
|
227 |
-
'<span class="fs-badge fs-installed-addon-badge">%s</span>',
|
228 |
-
esc_html( $is_plugin_active ?
|
229 |
-
fs_text_x_inline( 'Active', 'active add-on', 'active-addon', $slug ) :
|
230 |
-
fs_text_x_inline( 'Installed', 'installed add-on', 'installed-addon', $slug )
|
231 |
-
)
|
232 |
-
);
|
233 |
-
}
|
234 |
-
?></li>
|
235 |
-
<!-- <li class="fs-tag"></li> -->
|
236 |
-
<li class="fs-title"><?php echo $addon->title ?></li>
|
237 |
-
<li class="fs-offer">
|
238 |
-
<span
|
239 |
-
class="fs-price"><?php
|
240 |
-
if ( $is_whitelabeled ) {
|
241 |
-
echo ' ';
|
242 |
-
} else {
|
243 |
-
$descriptors = array();
|
244 |
-
|
245 |
-
if ($has_free_plan)
|
246 |
-
$descriptors[] = fs_text_inline( 'Free', 'free', $slug );
|
247 |
-
if ($has_paid_plan && $price > 0)
|
248 |
-
$descriptors[] = '$' . number_format( $price, 2 );
|
249 |
-
if ($has_trial)
|
250 |
-
$descriptors[] = fs_text_x_inline( 'Trial', 'trial period', 'trial', $slug );
|
251 |
-
|
252 |
-
echo implode(' - ', $descriptors);
|
253 |
-
|
254 |
-
} ?></span>
|
255 |
-
</li>
|
256 |
-
<li class="fs-description"><?php echo ! empty( $addon->info->short_description ) ? $addon->info->short_description : 'SHORT DESCRIPTION' ?></li>
|
257 |
-
<?php
|
258 |
-
$is_free_only_wp_org_compliant = ( ! $has_paid_plan && $addon->is_wp_org_compliant );
|
259 |
-
|
260 |
-
$is_allowed_to_install = (
|
261 |
-
$fs->is_allowed_to_install() ||
|
262 |
-
$is_free_only_wp_org_compliant
|
263 |
-
);
|
264 |
-
|
265 |
-
$show_premium_activation_or_installation_action = true;
|
266 |
-
|
267 |
-
if ( ! in_array( $addon->id, $account_addon_ids ) ) {
|
268 |
-
$show_premium_activation_or_installation_action = false;
|
269 |
-
} else if ( $is_addon_installed ) {
|
270 |
-
/**
|
271 |
-
* If any add-on's version (free or premium) is installed, check if the
|
272 |
-
* premium version can be activated and show the relevant action. Otherwise,
|
273 |
-
* show the relevant action for the free version.
|
274 |
-
*
|
275 |
-
* @author Leo Fajardo (@leorw)
|
276 |
-
* @since 2.4.5
|
277 |
-
*/
|
278 |
-
$fs_addon = $is_addon_activated ?
|
279 |
-
$fs->get_addon_instance( $addon->id ) :
|
280 |
-
null;
|
281 |
-
|
282 |
-
$premium_plugin_basename = is_object( $fs_addon ) ?
|
283 |
-
$fs_addon->premium_plugin_basename() :
|
284 |
-
"{$addon->premium_slug}/{$addon->slug}.php";
|
285 |
-
|
286 |
-
if (
|
287 |
-
( $is_addon_activated && $fs_addon->is_premium() ) ||
|
288 |
-
file_exists( fs_normalize_path( WP_PLUGIN_DIR . '/' . $premium_plugin_basename ) )
|
289 |
-
) {
|
290 |
-
$basename = $premium_plugin_basename;
|
291 |
-
}
|
292 |
-
|
293 |
-
$show_premium_activation_or_installation_action = (
|
294 |
-
( ! $is_addon_activated || ! $fs_addon->is_premium() ) &&
|
295 |
-
/**
|
296 |
-
* This check is needed for cases when an active add-on doesn't have an
|
297 |
-
* associated Freemius instance.
|
298 |
-
*
|
299 |
-
* @author Leo Fajardo (@leorw)
|
300 |
-
* @since 2.4.5
|
301 |
-
*/
|
302 |
-
( ! $is_plugin_active )
|
303 |
-
);
|
304 |
-
}
|
305 |
-
?>
|
306 |
-
<?php if ( ! $show_premium_activation_or_installation_action ) : ?>
|
307 |
-
<li class="fs-cta"><a class="button"><?php echo esc_html( $view_details_text ) ?></a></li>
|
308 |
-
<?php else : ?>
|
309 |
-
<?php
|
310 |
-
$latest_download_local_url = $is_free_only_wp_org_compliant ?
|
311 |
-
null :
|
312 |
-
$fs->_get_latest_download_local_url( $addon->id );
|
313 |
-
?>
|
314 |
-
|
315 |
-
<li class="fs-cta fs-dropdown">
|
316 |
-
<div class="button-group">
|
317 |
-
<?php if ( $is_allowed_to_install ) : ?>
|
318 |
-
<?php
|
319 |
-
if ( ! $is_addon_installed ) {
|
320 |
-
echo sprintf(
|
321 |
-
'<a class="button button-primary" href="%s">%s</a>',
|
322 |
-
wp_nonce_url( self_admin_url( 'update.php?' . ( ( $has_paid_plan || ! $addon->is_wp_org_compliant ) ? 'fs_allow_updater_and_dialog=true&' : '' ) . 'action=install-plugin&plugin=' . $addon->slug ), 'install-plugin_' . $addon->slug ),
|
323 |
-
fs_esc_html_inline( 'Install Now', 'install-now', $slug )
|
324 |
-
);
|
325 |
-
} else {
|
326 |
-
echo sprintf(
|
327 |
-
'<a class="button button-primary edit" href="%s" title="%s" target="_parent">%s</a>',
|
328 |
-
wp_nonce_url( 'plugins.php?action=activate&plugin=' . $basename, 'activate-plugin_' . $basename ),
|
329 |
-
fs_esc_attr_inline( 'Activate this add-on', 'activate-this-addon', $addon->slug ),
|
330 |
-
fs_text_inline( 'Activate', 'activate', $addon->slug )
|
331 |
-
);
|
332 |
-
}
|
333 |
-
?>
|
334 |
-
<?php else : ?>
|
335 |
-
<a target="_blank" rel="noopener" class="button button-primary" href="<?php echo $latest_download_local_url ?>"><?php echo esc_html( $download_latest_text ) ?></a>
|
336 |
-
<?php endif ?>
|
337 |
-
<div class="button button-primary fs-dropdown-arrow-button"><span class="fs-dropdown-arrow"></span><ul class="fs-dropdown-list" style="display: none">
|
338 |
-
<?php if ( $is_allowed_to_install && ! empty( $latest_download_local_url ) ) : ?>
|
339 |
-
<li><a target="_blank" rel="noopener" href="<?php echo $latest_download_local_url ?>"><?php echo esc_html( $download_latest_text ) ?></a></li>
|
340 |
-
<?php endif ?>
|
341 |
-
<li><?php
|
342 |
-
echo sprintf(
|
343 |
-
$view_details_link,
|
344 |
-
/**
|
345 |
-
* No additional class.
|
346 |
-
*
|
347 |
-
* @author Leo Fajardo (@leorw)
|
348 |
-
* @since 2.2.4
|
349 |
-
*/
|
350 |
-
'',
|
351 |
-
/**
|
352 |
-
* Set the view details link text to a non-empty string since it is an
|
353 |
-
* item in the dropdown list and the text should be visible.
|
354 |
-
*
|
355 |
-
* @author Leo Fajardo (@leorw)
|
356 |
-
* @since 2.2.4
|
357 |
-
*/
|
358 |
-
esc_html( $view_details_text )
|
359 |
-
);
|
360 |
-
?></li>
|
361 |
-
</ul></div>
|
362 |
-
</div>
|
363 |
-
</li>
|
364 |
-
<?php endif ?>
|
365 |
-
</ul>
|
366 |
-
</div>
|
367 |
-
</li>
|
368 |
-
<?php endforeach ?>
|
369 |
-
<?php endif ?>
|
370 |
-
</ul>
|
371 |
-
</div>
|
372 |
-
|
373 |
-
<?php $fs->do_action( 'addons/after_addons' ) ?>
|
374 |
-
</div>
|
375 |
-
<script type="text/javascript">
|
376 |
-
(function( $, undef ) {
|
377 |
-
<?php if ( $open_addon ) : ?>
|
378 |
-
|
379 |
-
var interval = setInterval(function () {
|
380 |
-
// Open add-on information page.
|
381 |
-
<?php
|
382 |
-
/**
|
383 |
-
* @author Vova Feldman
|
384 |
-
*
|
385 |
-
* This code does NOT expose an XSS vulnerability because:
|
386 |
-
* 1. This page only renders for admins, so if an attacker manage to get
|
387 |
-
* admin access, they can do more harm.
|
388 |
-
* 2. This code won't be rendered unless $open_addon_slug matches any of
|
389 |
-
* the plugin's add-ons slugs.
|
390 |
-
*/
|
391 |
-
?>
|
392 |
-
$('.fs-card[data-slug=<?php echo $open_addon_slug ?>] a').click();
|
393 |
-
if ($('#TB_iframeContent').length > 0) {
|
394 |
-
clearInterval(interval);
|
395 |
-
interval = null;
|
396 |
-
}
|
397 |
-
}, 200);
|
398 |
-
|
399 |
-
<?php else : ?>
|
400 |
-
|
401 |
-
$( '.fs-card.fs-addon' )
|
402 |
-
.mouseover(function() {
|
403 |
-
var $this = $( this );
|
404 |
-
|
405 |
-
$this.find( '.fs-cta .button' ).addClass( 'button-primary' );
|
406 |
-
|
407 |
-
if ( 0 === $this.find( '.fs-dropdown-arrow-button.active' ).length ) {
|
408 |
-
/**
|
409 |
-
* When hovering over a card, close the dropdown on any other card.
|
410 |
-
*
|
411 |
-
* @author Leo Fajardo (@leorw)
|
412 |
-
* @since 2.2.4
|
413 |
-
*/
|
414 |
-
toggleDropdown();
|
415 |
-
}
|
416 |
-
}).mouseout(function( evt ) {
|
417 |
-
var $relatedTarget = $( evt.relatedTarget );
|
418 |
-
|
419 |
-
if ( 0 !== $relatedTarget.parents( '.fs-addon' ).length ) {
|
420 |
-
return true;
|
421 |
-
}
|
422 |
-
|
423 |
-
var $this = $( this );
|
424 |
-
|
425 |
-
/**
|
426 |
-
* Set the color of the "View details" button to "secondary".
|
427 |
-
*
|
428 |
-
* @author Leo Fajardo (@leorw)
|
429 |
-
* @since 2.2.4
|
430 |
-
*/
|
431 |
-
$this.find( '.fs-cta .button' ).filter(function() {
|
432 |
-
/**
|
433 |
-
* Keep the "primary" color of the dropdown arrow button, "Install Now" button, and
|
434 |
-
* "Download Latest" button.
|
435 |
-
|
436 |
-
* @author Leo Fajardo (@leorw)
|
437 |
-
* @since 2.2.4
|
438 |
-
*/
|
439 |
-
return $( this ).parent().is( ':not(.button-group)' );
|
440 |
-
}).removeClass('button-primary');
|
441 |
-
|
442 |
-
toggleDropdown( $this.find( '.fs-dropdown' ), false );
|
443 |
-
}).find( 'a.thickbox, .button:not(.fs-dropdown-arrow-button)' ).click(function() {
|
444 |
-
toggleDropdown();
|
445 |
-
});
|
446 |
-
|
447 |
-
<?php endif ?>
|
448 |
-
|
449 |
-
var $dropdowns = $( '.fs-dropdown' );
|
450 |
-
if ( 0 !== $dropdowns.length ) {
|
451 |
-
$dropdowns.find( '.fs-dropdown-arrow-button' ).click(function() {
|
452 |
-
var $this = $( this ),
|
453 |
-
$dropdown = $this.parents( '.fs-dropdown' );
|
454 |
-
|
455 |
-
toggleDropdown( $dropdown, ! $dropdown.hasClass( 'active' ) );
|
456 |
-
});
|
457 |
-
}
|
458 |
-
|
459 |
-
/**
|
460 |
-
* Returns the default state of the dropdown arrow button and hides the dropdown list.
|
461 |
-
*
|
462 |
-
* @author Leo Fajardo (@leorw)
|
463 |
-
* @since 2.2.4
|
464 |
-
*
|
465 |
-
* @param {(Object|undefined)} [$dropdown]
|
466 |
-
* @param {(Boolean|undefined)} [state]
|
467 |
-
*/
|
468 |
-
function toggleDropdown( $dropdown, state ) {
|
469 |
-
if ( undef === $dropdown ) {
|
470 |
-
var $activeDropdown = $dropdowns.find( '.active' );
|
471 |
-
if ( 0 !== $activeDropdown.length ) {
|
472 |
-
$dropdown = $activeDropdown;
|
473 |
-
}
|
474 |
-
}
|
475 |
-
|
476 |
-
if ( undef === $dropdown ) {
|
477 |
-
return;
|
478 |
-
}
|
479 |
-
|
480 |
-
if ( undef === state ) {
|
481 |
-
state = false;
|
482 |
-
}
|
483 |
-
|
484 |
-
$dropdown.toggleClass( 'active', state );
|
485 |
-
$dropdown.find( '.fs-dropdown-list' ).toggle( state );
|
486 |
-
$dropdown.find( '.fs-dropdown-arrow-button' ).toggleClass( 'active', state );
|
487 |
-
}
|
488 |
-
})( jQuery );
|
489 |
-
</script>
|
490 |
-
<?php
|
491 |
-
if ( $has_tabs ) {
|
492 |
-
$fs->_add_tabs_after_content();
|
493 |
-
}
|
494 |
-
|
495 |
-
$params = array(
|
496 |
-
'page' => 'addons',
|
497 |
-
'module_id' => $fs->get_id(),
|
498 |
-
'module_type' => $fs->get_module_type(),
|
499 |
-
'module_slug' => $slug,
|
500 |
-
'module_version' => $fs->get_plugin_version(),
|
501 |
-
);
|
502 |
fs_require_template( 'powered-by.php', $params );
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package Freemius
|
4 |
+
* @copyright Copyright (c) 2015, Freemius, Inc.
|
5 |
+
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
|
6 |
+
* @since 1.0.3
|
7 |
+
*/
|
8 |
+
|
9 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
10 |
+
exit;
|
11 |
+
}
|
12 |
+
|
13 |
+
/**
|
14 |
+
* @var array $VARS
|
15 |
+
* @var Freemius
|
16 |
+
*/
|
17 |
+
$fs = freemius( $VARS['id'] );
|
18 |
+
|
19 |
+
$slug = $fs->get_slug();
|
20 |
+
|
21 |
+
$open_addon_slug = fs_request_get( 'slug' );
|
22 |
+
|
23 |
+
$open_addon = false;
|
24 |
+
|
25 |
+
$is_data_debug_mode = $fs->is_data_debug_mode();
|
26 |
+
$is_whitelabeled = $fs->is_whitelabeled();
|
27 |
+
|
28 |
+
/**
|
29 |
+
* @var FS_Plugin[]
|
30 |
+
*/
|
31 |
+
$addons = $fs->get_addons();
|
32 |
+
|
33 |
+
$has_addons = ( is_array( $addons ) && 0 < count( $addons ) );
|
34 |
+
|
35 |
+
$account_addon_ids = $fs->get_updated_account_addons();
|
36 |
+
|
37 |
+
$download_latest_text = fs_text_x_inline( 'Download Latest', 'as download latest version', 'download-latest', $slug );
|
38 |
+
$view_details_text = fs_text_inline( 'View details', 'view-details', $slug );
|
39 |
+
|
40 |
+
$has_tabs = $fs->_add_tabs_before_content();
|
41 |
+
|
42 |
+
$fs_blog_id = ( is_multisite() && ! is_network_admin() ) ?
|
43 |
+
get_current_blog_id() :
|
44 |
+
0;
|
45 |
+
?>
|
46 |
+
<div id="fs_addons" class="wrap fs-section">
|
47 |
+
<?php if ( ! $has_tabs ) : ?>
|
48 |
+
<h2><?php echo esc_html( sprintf( fs_text_inline( 'Add Ons for %s', 'add-ons-for-x', $slug ), $fs->get_plugin_name() ) ) ?></h2>
|
49 |
+
<?php endif ?>
|
50 |
+
|
51 |
+
<?php $fs->do_action( 'addons/after_title' ) ?>
|
52 |
+
|
53 |
+
<div id="poststuff">
|
54 |
+
<?php if ( ! $has_addons ) : ?>
|
55 |
+
<h3><?php echo esc_html( sprintf(
|
56 |
+
'%s... %s',
|
57 |
+
fs_text_x_inline( 'Oops', 'exclamation', 'oops', $slug ),
|
58 |
+
fs_text_inline( 'We couldn\'t load the add-ons list. It\'s probably an issue on our side, please try to come back in few minutes.', 'add-ons-missing', $slug )
|
59 |
+
) ) ?></h3>
|
60 |
+
<?php endif ?>
|
61 |
+
<ul class="fs-cards-list">
|
62 |
+
<?php if ( $has_addons ) : ?>
|
63 |
+
<?php
|
64 |
+
$plans_and_pricing_by_addon_id = $fs->_get_addons_plans_and_pricing_map_by_id();
|
65 |
+
|
66 |
+
$active_plugins_directories_map = Freemius::get_active_plugins_directories_map( $fs_blog_id );
|
67 |
+
?>
|
68 |
+
<?php
|
69 |
+
$hide_all_addons_data = false;
|
70 |
+
|
71 |
+
if ( $fs->is_whitelabeled_by_flag() ) {
|
72 |
+
$hide_all_addons_data = true;
|
73 |
+
|
74 |
+
$addon_ids = $fs->get_updated_account_addons();
|
75 |
+
$installed_addons = $fs->get_installed_addons();
|
76 |
+
foreach ( $installed_addons as $fs_addon ) {
|
77 |
+
$addon_ids[] = $fs_addon->get_id();
|
78 |
+
}
|
79 |
+
|
80 |
+
if ( ! empty( $addon_ids ) ) {
|
81 |
+
$addon_ids = array_unique( $addon_ids );
|
82 |
+
}
|
83 |
+
|
84 |
+
foreach ( $addon_ids as $addon_id ) {
|
85 |
+
$addon = $fs->get_addon( $addon_id );
|
86 |
+
|
87 |
+
if ( ! is_object( $addon ) ) {
|
88 |
+
continue;
|
89 |
+
}
|
90 |
+
|
91 |
+
$addon_storage = FS_Storage::instance( WP_FS__MODULE_TYPE_PLUGIN, $addon->slug );
|
92 |
+
|
93 |
+
if ( ! $addon_storage->is_whitelabeled ) {
|
94 |
+
$hide_all_addons_data = false;
|
95 |
+
break;
|
96 |
+
}
|
97 |
+
|
98 |
+
if ( $is_data_debug_mode ) {
|
99 |
+
$is_whitelabeled = false;
|
100 |
+
}
|
101 |
+
}
|
102 |
+
}
|
103 |
+
?>
|
104 |
+
<?php foreach ( $addons as $addon ) : ?>
|
105 |
+
<?php
|
106 |
+
$basename = $fs->get_addon_basename( $addon->id );
|
107 |
+
|
108 |
+
$is_addon_installed = file_exists( fs_normalize_path( WP_PLUGIN_DIR . '/' . $basename ) );
|
109 |
+
|
110 |
+
if ( ! $is_addon_installed && $hide_all_addons_data ) {
|
111 |
+
continue;
|
112 |
+
}
|
113 |
+
|
114 |
+
$is_addon_activated = $is_addon_installed ?
|
115 |
+
$fs->is_addon_activated( $addon->id ) :
|
116 |
+
false;
|
117 |
+
|
118 |
+
$is_plugin_active = (
|
119 |
+
$is_addon_activated ||
|
120 |
+
isset( $active_plugins_directories_map[ dirname( $basename ) ] )
|
121 |
+
);
|
122 |
+
|
123 |
+
$open_addon = ( $open_addon || ( $open_addon_slug === $addon->slug ) );
|
124 |
+
|
125 |
+
$price = 0;
|
126 |
+
$has_trial = false;
|
127 |
+
$has_free_plan = false;
|
128 |
+
$has_paid_plan = false;
|
129 |
+
|
130 |
+
if ( isset( $plans_and_pricing_by_addon_id[$addon->id] ) ) {
|
131 |
+
$plans = $plans_and_pricing_by_addon_id[$addon->id];
|
132 |
+
|
133 |
+
if ( is_array( $plans ) && 0 < count( $plans ) ) {
|
134 |
+
foreach ( $plans as $plan ) {
|
135 |
+
if ( ! isset( $plan->pricing ) ||
|
136 |
+
! is_array( $plan->pricing ) ||
|
137 |
+
0 == count( $plan->pricing )
|
138 |
+
) {
|
139 |
+
// No pricing means a free plan.
|
140 |
+
$has_free_plan = true;
|
141 |
+
continue;
|
142 |
+
}
|
143 |
+
|
144 |
+
|
145 |
+
$has_paid_plan = true;
|
146 |
+
$has_trial = $has_trial || ( is_numeric( $plan->trial_period ) && ( $plan->trial_period > 0 ) );
|
147 |
+
|
148 |
+
$min_price = 999999;
|
149 |
+
foreach ( $plan->pricing as $pricing ) {
|
150 |
+
$pricing = new FS_Pricing( $pricing );
|
151 |
+
|
152 |
+
if ( ! $pricing->is_usd() ) {
|
153 |
+
/**
|
154 |
+
* Skip non-USD pricing.
|
155 |
+
*
|
156 |
+
* @author Leo Fajardo (@leorw)
|
157 |
+
* @since 2.3.1
|
158 |
+
*/
|
159 |
+
continue;
|
160 |
+
}
|
161 |
+
|
162 |
+
if ( $pricing->has_annual() ) {
|
163 |
+
$min_price = min( $min_price, $pricing->annual_price );
|
164 |
+
} else if ( $pricing->has_monthly() ) {
|
165 |
+
$min_price = min( $min_price, 12 * $pricing->monthly_price );
|
166 |
+
}
|
167 |
+
}
|
168 |
+
|
169 |
+
if ( $min_price < 999999 ) {
|
170 |
+
$price = $min_price;
|
171 |
+
}
|
172 |
+
|
173 |
+
}
|
174 |
+
}
|
175 |
+
|
176 |
+
if ( ! $has_paid_plan && ! $has_free_plan ) {
|
177 |
+
continue;
|
178 |
+
}
|
179 |
+
}
|
180 |
+
?>
|
181 |
+
<li class="fs-card fs-addon" data-slug="<?php echo $addon->slug ?>">
|
182 |
+
<?php
|
183 |
+
$view_details_link = sprintf( '<a href="%s" aria-label="%s" data-title="%s"',
|
184 |
+
esc_url( network_admin_url( 'plugin-install.php?fs_allow_updater_and_dialog=true' . ( ! empty( $fs_blog_id ) ? '&fs_blog_id=' . $fs_blog_id : '' ) . '&tab=plugin-information&parent_plugin_id=' . $fs->get_id() . '&plugin=' . $addon->slug .
|
185 |
+
'&TB_iframe=true&width=600&height=550' ) ),
|
186 |
+
esc_attr( sprintf( fs_text_inline( 'More information about %s', 'more-information-about-x', $slug ), $addon->title ) ),
|
187 |
+
esc_attr( $addon->title )
|
188 |
+
) . ' class="thickbox%s">%s</a>';
|
189 |
+
|
190 |
+
echo sprintf(
|
191 |
+
$view_details_link,
|
192 |
+
/**
|
193 |
+
* Additional class.
|
194 |
+
*
|
195 |
+
* @author Leo Fajardo (@leorw)
|
196 |
+
* @since 2.2.4
|
197 |
+
*/
|
198 |
+
' fs-overlay',
|
199 |
+
/**
|
200 |
+
* Set the view details link text to an empty string since it is an overlay that
|
201 |
+
* doesn't really need a text and whose purpose is to open the details dialog when
|
202 |
+
* the card is clicked.
|
203 |
+
*
|
204 |
+
* @author Leo Fajardo (@leorw)
|
205 |
+
* @since 2.2.4
|
206 |
+
*/
|
207 |
+
''
|
208 |
+
);
|
209 |
+
?>
|
210 |
+
<?php
|
211 |
+
if ( is_null( $addon->info ) ) {
|
212 |
+
$addon->info = new stdClass();
|
213 |
+
}
|
214 |
+
if ( ! isset( $addon->info->card_banner_url ) ) {
|
215 |
+
$addon->info->card_banner_url = '//dashboard.freemius.com/assets/img/marketing/blueprint-300x100.jpg';
|
216 |
+
}
|
217 |
+
if ( ! isset( $addon->info->short_description ) ) {
|
218 |
+
$addon->info->short_description = 'What\'s the one thing your add-on does really, really well?';
|
219 |
+
}
|
220 |
+
?>
|
221 |
+
<div class="fs-inner">
|
222 |
+
<ul>
|
223 |
+
<li class="fs-card-banner"
|
224 |
+
style="background-image: url('<?php echo $addon->info->card_banner_url ?>');"><?php
|
225 |
+
if ( $is_plugin_active || $is_addon_installed ) {
|
226 |
+
echo sprintf(
|
227 |
+
'<span class="fs-badge fs-installed-addon-badge">%s</span>',
|
228 |
+
esc_html( $is_plugin_active ?
|
229 |
+
fs_text_x_inline( 'Active', 'active add-on', 'active-addon', $slug ) :
|
230 |
+
fs_text_x_inline( 'Installed', 'installed add-on', 'installed-addon', $slug )
|
231 |
+
)
|
232 |
+
);
|
233 |
+
}
|
234 |
+
?></li>
|
235 |
+
<!-- <li class="fs-tag"></li> -->
|
236 |
+
<li class="fs-title"><?php echo $addon->title ?></li>
|
237 |
+
<li class="fs-offer">
|
238 |
+
<span
|
239 |
+
class="fs-price"><?php
|
240 |
+
if ( $is_whitelabeled ) {
|
241 |
+
echo ' ';
|
242 |
+
} else {
|
243 |
+
$descriptors = array();
|
244 |
+
|
245 |
+
if ($has_free_plan)
|
246 |
+
$descriptors[] = fs_text_inline( 'Free', 'free', $slug );
|
247 |
+
if ($has_paid_plan && $price > 0)
|
248 |
+
$descriptors[] = '$' . number_format( $price, 2 );
|
249 |
+
if ($has_trial)
|
250 |
+
$descriptors[] = fs_text_x_inline( 'Trial', 'trial period', 'trial', $slug );
|
251 |
+
|
252 |
+
echo implode(' - ', $descriptors);
|
253 |
+
|
254 |
+
} ?></span>
|
255 |
+
</li>
|
256 |
+
<li class="fs-description"><?php echo ! empty( $addon->info->short_description ) ? $addon->info->short_description : 'SHORT DESCRIPTION' ?></li>
|
257 |
+
<?php
|
258 |
+
$is_free_only_wp_org_compliant = ( ! $has_paid_plan && $addon->is_wp_org_compliant );
|
259 |
+
|
260 |
+
$is_allowed_to_install = (
|
261 |
+
$fs->is_allowed_to_install() ||
|
262 |
+
$is_free_only_wp_org_compliant
|
263 |
+
);
|
264 |
+
|
265 |
+
$show_premium_activation_or_installation_action = true;
|
266 |
+
|
267 |
+
if ( ! in_array( $addon->id, $account_addon_ids ) ) {
|
268 |
+
$show_premium_activation_or_installation_action = false;
|
269 |
+
} else if ( $is_addon_installed ) {
|
270 |
+
/**
|
271 |
+
* If any add-on's version (free or premium) is installed, check if the
|
272 |
+
* premium version can be activated and show the relevant action. Otherwise,
|
273 |
+
* show the relevant action for the free version.
|
274 |
+
*
|
275 |
+
* @author Leo Fajardo (@leorw)
|
276 |
+
* @since 2.4.5
|
277 |
+
*/
|
278 |
+
$fs_addon = $is_addon_activated ?
|
279 |
+
$fs->get_addon_instance( $addon->id ) :
|
280 |
+
null;
|
281 |
+
|
282 |
+
$premium_plugin_basename = is_object( $fs_addon ) ?
|
283 |
+
$fs_addon->premium_plugin_basename() :
|
284 |
+
"{$addon->premium_slug}/{$addon->slug}.php";
|
285 |
+
|
286 |
+
if (
|
287 |
+
( $is_addon_activated && $fs_addon->is_premium() ) ||
|
288 |
+
file_exists( fs_normalize_path( WP_PLUGIN_DIR . '/' . $premium_plugin_basename ) )
|
289 |
+
) {
|
290 |
+
$basename = $premium_plugin_basename;
|
291 |
+
}
|
292 |
+
|
293 |
+
$show_premium_activation_or_installation_action = (
|
294 |
+
( ! $is_addon_activated || ! $fs_addon->is_premium() ) &&
|
295 |
+
/**
|
296 |
+
* This check is needed for cases when an active add-on doesn't have an
|
297 |
+
* associated Freemius instance.
|
298 |
+
*
|
299 |
+
* @author Leo Fajardo (@leorw)
|
300 |
+
* @since 2.4.5
|
301 |
+
*/
|
302 |
+
( ! $is_plugin_active )
|
303 |
+
);
|
304 |
+
}
|
305 |
+
?>
|
306 |
+
<?php if ( ! $show_premium_activation_or_installation_action ) : ?>
|
307 |
+
<li class="fs-cta"><a class="button"><?php echo esc_html( $view_details_text ) ?></a></li>
|
308 |
+
<?php else : ?>
|
309 |
+
<?php
|
310 |
+
$latest_download_local_url = $is_free_only_wp_org_compliant ?
|
311 |
+
null :
|
312 |
+
$fs->_get_latest_download_local_url( $addon->id );
|
313 |
+
?>
|
314 |
+
|
315 |
+
<li class="fs-cta fs-dropdown">
|
316 |
+
<div class="button-group">
|
317 |
+
<?php if ( $is_allowed_to_install ) : ?>
|
318 |
+
<?php
|
319 |
+
if ( ! $is_addon_installed ) {
|
320 |
+
echo sprintf(
|
321 |
+
'<a class="button button-primary" href="%s">%s</a>',
|
322 |
+
wp_nonce_url( self_admin_url( 'update.php?' . ( ( $has_paid_plan || ! $addon->is_wp_org_compliant ) ? 'fs_allow_updater_and_dialog=true&' : '' ) . 'action=install-plugin&plugin=' . $addon->slug ), 'install-plugin_' . $addon->slug ),
|
323 |
+
fs_esc_html_inline( 'Install Now', 'install-now', $slug )
|
324 |
+
);
|
325 |
+
} else {
|
326 |
+
echo sprintf(
|
327 |
+
'<a class="button button-primary edit" href="%s" title="%s" target="_parent">%s</a>',
|
328 |
+
wp_nonce_url( 'plugins.php?action=activate&plugin=' . $basename, 'activate-plugin_' . $basename ),
|
329 |
+
fs_esc_attr_inline( 'Activate this add-on', 'activate-this-addon', $addon->slug ),
|
330 |
+
fs_text_inline( 'Activate', 'activate', $addon->slug )
|
331 |
+
);
|
332 |
+
}
|
333 |
+
?>
|
334 |
+
<?php else : ?>
|
335 |
+
<a target="_blank" rel="noopener" class="button button-primary" href="<?php echo $latest_download_local_url ?>"><?php echo esc_html( $download_latest_text ) ?></a>
|
336 |
+
<?php endif ?>
|
337 |
+
<div class="button button-primary fs-dropdown-arrow-button"><span class="fs-dropdown-arrow"></span><ul class="fs-dropdown-list" style="display: none">
|
338 |
+
<?php if ( $is_allowed_to_install && ! empty( $latest_download_local_url ) ) : ?>
|
339 |
+
<li><a target="_blank" rel="noopener" href="<?php echo $latest_download_local_url ?>"><?php echo esc_html( $download_latest_text ) ?></a></li>
|
340 |
+
<?php endif ?>
|
341 |
+
<li><?php
|
342 |
+
echo sprintf(
|
343 |
+
$view_details_link,
|
344 |
+
/**
|
345 |
+
* No additional class.
|
346 |
+
*
|
347 |
+
* @author Leo Fajardo (@leorw)
|
348 |
+
* @since 2.2.4
|
349 |
+
*/
|
350 |
+
'',
|
351 |
+
/**
|
352 |
+
* Set the view details link text to a non-empty string since it is an
|
353 |
+
* item in the dropdown list and the text should be visible.
|
354 |
+
*
|
355 |
+
* @author Leo Fajardo (@leorw)
|
356 |
+
* @since 2.2.4
|
357 |
+
*/
|
358 |
+
esc_html( $view_details_text )
|
359 |
+
);
|
360 |
+
?></li>
|
361 |
+
</ul></div>
|
362 |
+
</div>
|
363 |
+
</li>
|
364 |
+
<?php endif ?>
|
365 |
+
</ul>
|
366 |
+
</div>
|
367 |
+
</li>
|
368 |
+
<?php endforeach ?>
|
369 |
+
<?php endif ?>
|
370 |
+
</ul>
|
371 |
+
</div>
|
372 |
+
|
373 |
+
<?php $fs->do_action( 'addons/after_addons' ) ?>
|
374 |
+
</div>
|
375 |
+
<script type="text/javascript">
|
376 |
+
(function( $, undef ) {
|
377 |
+
<?php if ( $open_addon ) : ?>
|
378 |
+
|
379 |
+
var interval = setInterval(function () {
|
380 |
+
// Open add-on information page.
|
381 |
+
<?php
|
382 |
+
/**
|
383 |
+
* @author Vova Feldman
|
384 |
+
*
|
385 |
+
* This code does NOT expose an XSS vulnerability because:
|
386 |
+
* 1. This page only renders for admins, so if an attacker manage to get
|
387 |
+
* admin access, they can do more harm.
|
388 |
+
* 2. This code won't be rendered unless $open_addon_slug matches any of
|
389 |
+
* the plugin's add-ons slugs.
|
390 |
+
*/
|
391 |
+
?>
|
392 |
+
$('.fs-card[data-slug=<?php echo $open_addon_slug ?>] a').click();
|
393 |
+
if ($('#TB_iframeContent').length > 0) {
|
394 |
+
clearInterval(interval);
|
395 |
+
interval = null;
|
396 |
+
}
|
397 |
+
}, 200);
|
398 |
+
|
399 |
+
<?php else : ?>
|
400 |
+
|
401 |
+
$( '.fs-card.fs-addon' )
|
402 |
+
.mouseover(function() {
|
403 |
+
var $this = $( this );
|
404 |
+
|
405 |
+
$this.find( '.fs-cta .button' ).addClass( 'button-primary' );
|
406 |
+
|
407 |
+
if ( 0 === $this.find( '.fs-dropdown-arrow-button.active' ).length ) {
|
408 |
+
/**
|
409 |
+
* When hovering over a card, close the dropdown on any other card.
|
410 |
+
*
|
411 |
+
* @author Leo Fajardo (@leorw)
|
412 |
+
* @since 2.2.4
|
413 |
+
*/
|
414 |
+
toggleDropdown();
|
415 |
+
}
|
416 |
+
}).mouseout(function( evt ) {
|
417 |
+
var $relatedTarget = $( evt.relatedTarget );
|
418 |
+
|
419 |
+
if ( 0 !== $relatedTarget.parents( '.fs-addon' ).length ) {
|
420 |
+
return true;
|
421 |
+
}
|
422 |
+
|
423 |
+
var $this = $( this );
|
424 |
+
|
425 |
+
/**
|
426 |
+
* Set the color of the "View details" button to "secondary".
|
427 |
+
*
|
428 |
+
* @author Leo Fajardo (@leorw)
|
429 |
+
* @since 2.2.4
|
430 |
+
*/
|
431 |
+
$this.find( '.fs-cta .button' ).filter(function() {
|
432 |
+
/**
|
433 |
+
* Keep the "primary" color of the dropdown arrow button, "Install Now" button, and
|
434 |
+
* "Download Latest" button.
|
435 |
+
|
436 |
+
* @author Leo Fajardo (@leorw)
|
437 |
+
* @since 2.2.4
|
438 |
+
*/
|
439 |
+
return $( this ).parent().is( ':not(.button-group)' );
|
440 |
+
}).removeClass('button-primary');
|
441 |
+
|
442 |
+
toggleDropdown( $this.find( '.fs-dropdown' ), false );
|
443 |
+
}).find( 'a.thickbox, .button:not(.fs-dropdown-arrow-button)' ).click(function() {
|
444 |
+
toggleDropdown();
|
445 |
+
});
|
446 |
+
|
447 |
+
<?php endif ?>
|
448 |
+
|
449 |
+
var $dropdowns = $( '.fs-dropdown' );
|
450 |
+
if ( 0 !== $dropdowns.length ) {
|
451 |
+
$dropdowns.find( '.fs-dropdown-arrow-button' ).click(function() {
|
452 |
+
var $this = $( this ),
|
453 |
+
$dropdown = $this.parents( '.fs-dropdown' );
|
454 |
+
|
455 |
+
toggleDropdown( $dropdown, ! $dropdown.hasClass( 'active' ) );
|
456 |
+
});
|
457 |
+
}
|
458 |
+
|
459 |
+
/**
|
460 |
+
* Returns the default state of the dropdown arrow button and hides the dropdown list.
|
461 |
+
*
|
462 |
+
* @author Leo Fajardo (@leorw)
|
463 |
+
* @since 2.2.4
|
464 |
+
*
|
465 |
+
* @param {(Object|undefined)} [$dropdown]
|
466 |
+
* @param {(Boolean|undefined)} [state]
|
467 |
+
*/
|
468 |
+
function toggleDropdown( $dropdown, state ) {
|
469 |
+
if ( undef === $dropdown ) {
|
470 |
+
var $activeDropdown = $dropdowns.find( '.active' );
|
471 |
+
if ( 0 !== $activeDropdown.length ) {
|
472 |
+
$dropdown = $activeDropdown;
|
473 |
+
}
|
474 |
+
}
|
475 |
+
|
476 |
+
if ( undef === $dropdown ) {
|
477 |
+
return;
|
478 |
+
}
|
479 |
+
|
480 |
+
if ( undef === state ) {
|
481 |
+
state = false;
|
482 |
+
}
|
483 |
+
|
484 |
+
$dropdown.toggleClass( 'active', state );
|
485 |
+
$dropdown.find( '.fs-dropdown-list' ).toggle( state );
|
486 |
+
$dropdown.find( '.fs-dropdown-arrow-button' ).toggleClass( 'active', state );
|
487 |
+
}
|
488 |
+
})( jQuery );
|
489 |
+
</script>
|
490 |
+
<?php
|
491 |
+
if ( $has_tabs ) {
|
492 |
+
$fs->_add_tabs_after_content();
|
493 |
+
}
|
494 |
+
|
495 |
+
$params = array(
|
496 |
+
'page' => 'addons',
|
497 |
+
'module_id' => $fs->get_id(),
|
498 |
+
'module_type' => $fs->get_module_type(),
|
499 |
+
'module_slug' => $slug,
|
500 |
+
'module_version' => $fs->get_plugin_version(),
|
501 |
+
);
|
502 |
fs_require_template( 'powered-by.php', $params );
|
freemius/templates/ajax-loader.php
CHANGED
@@ -1 +1,6 @@
|
|
1 |
-
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
3 |
+
exit;
|
4 |
+
}
|
5 |
+
?>
|
6 |
+
<div class="fs-ajax-loader" style="display: none"><?php for ( $i = 1; $i <= 8; $i ++ ) : ?><div class="fs-ajax-loader-bar fs-ajax-loader-bar-<?php echo $i ?>"></div><?php endfor ?></div>
|
freemius/templates/debug.php
CHANGED
@@ -37,6 +37,8 @@
|
|
37 |
|
38 |
$.post( ajaxurl, {
|
39 |
action: 'fs_toggle_debug_mode',
|
|
|
|
|
40 |
is_on : ($(this).hasClass( 'fs-on' ) ? 1 : 0)
|
41 |
}, function ( response ) {
|
42 |
if ( 1 == response ) {
|
@@ -111,7 +113,8 @@
|
|
111 |
if (optionName) {
|
112 |
$.post(ajaxurl, {
|
113 |
action : 'fs_get_db_option',
|
114 |
-
|
|
|
115 |
option_name: optionName
|
116 |
}, function (response) {
|
117 |
if (response.data.value)
|
@@ -131,7 +134,8 @@
|
|
131 |
if (optionValue) {
|
132 |
$.post(ajaxurl, {
|
133 |
action : 'fs_set_db_option',
|
134 |
-
|
|
|
135 |
option_name : optionName,
|
136 |
option_value: optionValue
|
137 |
}, function () {
|
@@ -724,6 +728,8 @@
|
|
724 |
|
725 |
$.post(ajaxurl, {
|
726 |
action : 'fs_get_debug_log',
|
|
|
|
|
727 |
filters: filters,
|
728 |
offset : offset,
|
729 |
limit : limit
|
37 |
|
38 |
$.post( ajaxurl, {
|
39 |
action: 'fs_toggle_debug_mode',
|
40 |
+
// As such we don't need to use `wp_json_encode` method but using it to follow wp.org guideline.
|
41 |
+
_wpnonce : <?php echo wp_json_encode( wp_create_nonce( 'fs_toggle_debug_mode' ) ); ?>,
|
42 |
is_on : ($(this).hasClass( 'fs-on' ) ? 1 : 0)
|
43 |
}, function ( response ) {
|
44 |
if ( 1 == response ) {
|
113 |
if (optionName) {
|
114 |
$.post(ajaxurl, {
|
115 |
action : 'fs_get_db_option',
|
116 |
+
// As such we don't need to use `wp_json_encode` method but using it to follow wp.org guideline.
|
117 |
+
_wpnonce : <?php echo wp_json_encode( wp_create_nonce( 'fs_get_db_option' ) ); ?>,
|
118 |
option_name: optionName
|
119 |
}, function (response) {
|
120 |
if (response.data.value)
|
134 |
if (optionValue) {
|
135 |
$.post(ajaxurl, {
|
136 |
action : 'fs_set_db_option',
|
137 |
+
// As such we don't need to use `wp_json_encode` method but using it to follow wp.org guideline.
|
138 |
+
_wpnonce : <?php echo wp_json_encode( wp_create_nonce( 'fs_set_db_option' ) ); ?>,
|
139 |
option_name : optionName,
|
140 |
option_value: optionValue
|
141 |
}, function () {
|
728 |
|
729 |
$.post(ajaxurl, {
|
730 |
action : 'fs_get_debug_log',
|
731 |
+
// As such we don't need to use `wp_json_encode` method but using it to follow wp.org guideline.
|
732 |
+
_wpnonce : <?php echo wp_json_encode( wp_create_nonce( 'fs_get_debug_log' ) ); ?>,
|
733 |
filters: filters,
|
734 |
offset : offset,
|
735 |
limit : limit
|
freemius/templates/firewall-issues-js.php
CHANGED
@@ -22,10 +22,12 @@
|
|
22 |
notice = $( this ).parents( '.fs-notice' ),
|
23 |
ajaxActionSuffix = notice.attr( 'data-manager-id' ).replace( ':', '-' );
|
24 |
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
|
|
|
|
29 |
|
30 |
if ( 'squid' === error_type ) {
|
31 |
data.hosting_company = prompt( 'What is the name or URL of your hosting company?' );
|
@@ -39,7 +41,9 @@
|
|
39 |
}
|
40 |
|
41 |
if ( 'retry_ping' === error_type ) {
|
42 |
-
|
|
|
|
|
43 |
}
|
44 |
|
45 |
$( this ).css({'cursor': 'wait'});
|
@@ -56,4 +60,4 @@
|
|
56 |
});
|
57 |
});
|
58 |
});
|
59 |
-
</script>
|
22 |
notice = $( this ).parents( '.fs-notice' ),
|
23 |
ajaxActionSuffix = notice.attr( 'data-manager-id' ).replace( ':', '-' );
|
24 |
|
25 |
+
var data = {
|
26 |
+
action : 'fs_resolve_firewall_issues_' + ajaxActionSuffix,
|
27 |
+
// As such we don't need to use `wp_json_encode` method but using it to follow wp.org guideline.
|
28 |
+
_wpnonce : <?php echo wp_json_encode( wp_create_nonce( 'fs_resolve_firewall_issues' ) ); ?>,
|
29 |
+
error_type: error_type
|
30 |
+
};
|
31 |
|
32 |
if ( 'squid' === error_type ) {
|
33 |
data.hosting_company = prompt( 'What is the name or URL of your hosting company?' );
|
41 |
}
|
42 |
|
43 |
if ( 'retry_ping' === error_type ) {
|
44 |
+
data.action = 'fs_retry_connectivity_test_' + ajaxActionSuffix;
|
45 |
+
// As such we don't need to use `wp_json_encode` method but using it to follow wp.org guideline.
|
46 |
+
data._wpnonce = <?php echo wp_json_encode( wp_create_nonce( 'fs_retry_connectivity_test' ) ); ?>;
|
47 |
}
|
48 |
|
49 |
$( this ).css({'cursor': 'wait'});
|
60 |
});
|
61 |
});
|
62 |
});
|
63 |
+
</script>
|
freemius/templates/forms/data-debug-mode.php
CHANGED
@@ -1,213 +1,213 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* @package Freemius
|
4 |
-
* @copyright Copyright (c) 2015, Freemius, Inc.
|
5 |
-
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
|
6 |
-
* @since 2.3.1
|
7 |
-
*/
|
8 |
-
|
9 |
-
if ( ! defined( 'ABSPATH' ) ) {
|
10 |
-
exit;
|
11 |
-
}
|
12 |
-
|
13 |
-
/**
|
14 |
-
* @var array $VARS
|
15 |
-
*
|
16 |
-
* @var Freemius $fs
|
17 |
-
*/
|
18 |
-
$fs = freemius( $VARS['id'] );
|
19 |
-
$slug = $fs->get_slug();
|
20 |
-
$unique_affix = $fs->get_unique_affix();
|
21 |
-
$last_license_user_id = $fs->get_last_license_user_id();
|
22 |
-
$has_last_license_user_id = FS_User::is_valid_id( $last_license_user_id );
|
23 |
-
|
24 |
-
$message_above_input_field = ( ! $has_last_license_user_id ) ?
|
25 |
-
fs_text_inline( 'Please enter the license key to enable the debug mode:', 'submit-developer-license-key-message', $slug ) :
|
26 |
-
sprintf(
|
27 |
-
fs_text_inline( 'To enter the debug mode, please enter the secret key of the license owner (UserID = %d), which you can find in your "My Profile" section of your User Dashboard:', 'submit-addon-developer-key-message', $slug ),
|
28 |
-
$last_license_user_id
|
29 |
-
);
|
30 |
-
|
31 |
-
$processing_text = ( fs_esc_js_inline( 'Processing', 'processing', $slug ) . '...' );
|
32 |
-
$submit_button_text = fs_text_inline( 'Submit', 'submit', $slug );
|
33 |
-
$debug_license_link_text = fs_esc_html_inline( 'Start Debug', 'start-debug-license', $slug );
|
34 |
-
$license_or_user_key_text = ( ! $has_last_license_user_id ) ?
|
35 |
-
fs_text_inline( 'License key', 'license-key' , $slug ) :
|
36 |
-
fs_text_inline( 'User key', 'user-key' , $slug );
|
37 |
-
$input_html = "<input class='fs-license-or-user-key' type='password' placeholder='{$license_or_user_key_text}' tabindex='1' />";
|
38 |
-
|
39 |
-
$modal_content_html = <<< HTML
|
40 |
-
<div class="notice notice-error inline license-or-user-key-submission-message"><p></p></div>
|
41 |
-
<p>{$message_above_input_field}</p>
|
42 |
-
{$input_html}
|
43 |
-
HTML;
|
44 |
-
|
45 |
-
fs_enqueue_local_style( 'fs_dialog_boxes', '/admin/dialog-boxes.css' );
|
46 |
-
?>
|
47 |
-
<script type="text/javascript">
|
48 |
-
( function( $ ) {
|
49 |
-
$( document ).ready( function() {
|
50 |
-
var modalContentHtml = <?php echo json_encode( $modal_content_html ) ?>,
|
51 |
-
modalHtml =
|
52 |
-
'<div class="fs-modal fs-modal-developer-license-debug-mode fs-modal-developer-license-debug-mode-<?php echo $unique_affix ?>">'
|
53 |
-
+ ' <div class="fs-modal-dialog">'
|
54 |
-
+ ' <div class="fs-modal-body">'
|
55 |
-
+ ' <div class="fs-modal-panel active">' + modalContentHtml + '</div>'
|
56 |
-
+ ' </div>'
|
57 |
-
+ ' <div class="fs-modal-footer">'
|
58 |
-
+ ' <button class="button button-secondary button-close" tabindex="4"><?php fs_esc_js_echo_inline( 'Cancel', 'cancel', $slug ) ?></button>'
|
59 |
-
+ ' <button class="button button-primary button-submit-license-or-user-key" tabindex="3"><?php echo esc_js( $submit_button_text ) ?></button>'
|
60 |
-
+ ' </div>'
|
61 |
-
+ ' </div>'
|
62 |
-
+ '</div>',
|
63 |
-
$modal = $( modalHtml ),
|
64 |
-
$debugLicenseLink = $( '.debug-license-trigger' ),
|
65 |
-
$submitKeyButton = $modal.find( '.button-submit-license-or-user-key' ),
|
66 |
-
$licenseOrUserKeyInput = $modal.find( 'input.fs-license-or-user-key' ),
|
67 |
-
$licenseOrUserKeySubmissionMessage = $modal.find( '.license-or-user-key-submission-message' ),
|
68 |
-
isDebugMode = <?php echo $fs->is_data_debug_mode() ? 'true' : 'false' ?>;
|
69 |
-
|
70 |
-
$modal.appendTo( $( 'body' ) );
|
71 |
-
|
72 |
-
function registerEventHandlers() {
|
73 |
-
$debugLicenseLink.click(function (evt) {
|
74 |
-
evt.preventDefault();
|
75 |
-
|
76 |
-
if ( isDebugMode ) {
|
77 |
-
setDeveloperLicenseDebugMode();
|
78 |
-
return true;
|
79 |
-
}
|
80 |
-
|
81 |
-
showModal( evt );
|
82 |
-
});
|
83 |
-
|
84 |
-
$modal.on( 'input propertychange', 'input.fs-license-or-user-key', function () {
|
85 |
-
var licenseOrUserKey = $( this ).val().trim();
|
86 |
-
|
87 |
-
/**
|
88 |
-
* If license or user key is not empty, enable the submission button.
|
89 |
-
*/
|
90 |
-
if ( licenseOrUserKey.length > 0 ) {
|
91 |
-
enableSubmitButton();
|
92 |
-
}
|
93 |
-
});
|
94 |
-
|
95 |
-
$modal.on( 'blur', 'input.fs-license-or-user-key', function () {
|
96 |
-
var licenseOrUserKey = $( this ).val().trim();
|
97 |
-
|
98 |
-
/**
|
99 |
-
* If license or user key is empty, disable the submission button.
|
100 |
-
*/
|
101 |
-
if ( 0 === licenseOrUserKey.length ) {
|
102 |
-
disableSubmitButton();
|
103 |
-
}
|
104 |
-
});
|
105 |
-
|
106 |
-
$modal.on( 'click', '.button-submit-license-or-user-key', function ( evt ) {
|
107 |
-
evt.preventDefault();
|
108 |
-
|
109 |
-
if ( $( this ).hasClass( 'disabled' ) ) {
|
110 |
-
return;
|
111 |
-
}
|
112 |
-
|
113 |
-
var licenseOrUserKey = $licenseOrUserKeyInput.val().trim();
|
114 |
-
|
115 |
-
disableSubmitButton();
|
116 |
-
|
117 |
-
if ( 0 === licenseOrUserKey.length ) {
|
118 |
-
return;
|
119 |
-
}
|
120 |
-
|
121 |
-
setDeveloperLicenseDebugMode( licenseOrUserKey );
|
122 |
-
});
|
123 |
-
|
124 |
-
// If the user has clicked outside the window, close the modal.
|
125 |
-
$modal.on( 'click', '.fs-close, .button-secondary', function () {
|
126 |
-
closeModal();
|
127 |
-
return false;
|
128 |
-
} );
|
129 |
-
}
|
130 |
-
|
131 |
-
registerEventHandlers();
|
132 |
-
|
133 |
-
function setDeveloperLicenseDebugMode( licenseOrUserKey ) {
|
134 |
-
var data = {
|
135 |
-
action : '<?php echo $fs->get_ajax_action( 'set_data_debug_mode' ) ?>',
|
136 |
-
security : '<?php echo $fs->get_ajax_security( 'set_data_debug_mode' ) ?>',
|
137 |
-
license_or_user_key: licenseOrUserKey,
|
138 |
-
is_debug_mode : isDebugMode,
|
139 |
-
module_id : '<?php echo $fs->get_id() ?>'
|
140 |
-
};
|
141 |
-
|
142 |
-
$.ajax( {
|
143 |
-
url : ajaxurl,
|
144 |
-
method : 'POST',
|
145 |
-
data : data,
|
146 |
-
beforeSend: function () {
|
147 |
-
$debugLicenseLink.find('span').text( '<?php echo $processing_text ?>' );
|
148 |
-
$submitKeyButton.text( '<?php echo $processing_text ?>' );
|
149 |
-
},
|
150 |
-
success : function ( result ) {
|
151 |
-
if ( result.success ) {
|
152 |
-
closeModal();
|
153 |
-
|
154 |
-
// Reload the "Account" page so that the pricing/upgrade link will be properly hidden/shown.
|
155 |
-
window.location.reload();
|
156 |
-
} else {
|
157 |
-
showError( result.error.message ? result.error.message : result.error );
|
158 |
-
resetButtons();
|
159 |
-
}
|
160 |
-
},
|
161 |
-
error : function () {
|
162 |
-
showError( <?php echo json_encode( fs_text_inline( 'An unknown error has occurred.', 'unknown-error', $slug ) ) ?> );
|
163 |
-
resetButtons();
|
164 |
-
}
|
165 |
-
});
|
166 |
-
}
|
167 |
-
|
168 |
-
function showModal( evt ) {
|
169 |
-
resetModal();
|
170 |
-
|
171 |
-
// Display the dialog box.
|
172 |
-
$modal.addClass( 'active' );
|
173 |
-
$( 'body' ).addClass( 'has-fs-modal' );
|
174 |
-
|
175 |
-
$licenseOrUserKeyInput.val( '' );
|
176 |
-
$licenseOrUserKeyInput.focus();
|
177 |
-
}
|
178 |
-
|
179 |
-
function closeModal() {
|
180 |
-
$modal.removeClass( 'active' );
|
181 |
-
$( 'body' ).removeClass( 'has-fs-modal' );
|
182 |
-
}
|
183 |
-
|
184 |
-
function resetButtons() {
|
185 |
-
enableSubmitButton();
|
186 |
-
$submitKeyButton.text( <?php echo json_encode( $submit_button_text ) ?> );
|
187 |
-
$debugLicenseLink.find('span').text( <?php echo json_encode( $debug_license_link_text ) ?> );
|
188 |
-
}
|
189 |
-
|
190 |
-
function resetModal() {
|
191 |
-
hideError();
|
192 |
-
resetButtons();
|
193 |
-
}
|
194 |
-
|
195 |
-
function enableSubmitButton() {
|
196 |
-
$submitKeyButton.removeClass( 'disabled' );
|
197 |
-
}
|
198 |
-
|
199 |
-
function disableSubmitButton() {
|
200 |
-
$submitKeyButton.addClass( 'disabled' );
|
201 |
-
}
|
202 |
-
|
203 |
-
function hideError() {
|
204 |
-
$licenseOrUserKeySubmissionMessage.hide();
|
205 |
-
}
|
206 |
-
|
207 |
-
function showError( msg ) {
|
208 |
-
$licenseOrUserKeySubmissionMessage.find( ' > p' ).html( msg );
|
209 |
-
$licenseOrUserKeySubmissionMessage.show();
|
210 |
-
}
|
211 |
-
} );
|
212 |
-
} )( jQuery );
|
213 |
</script>
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package Freemius
|
4 |
+
* @copyright Copyright (c) 2015, Freemius, Inc.
|
5 |
+
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
|
6 |
+
* @since 2.3.1
|
7 |
+
*/
|
8 |
+
|
9 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
10 |
+
exit;
|
11 |
+
}
|
12 |
+
|
13 |
+
/**
|
14 |
+
* @var array $VARS
|
15 |
+
*
|
16 |
+
* @var Freemius $fs
|
17 |
+
*/
|
18 |
+
$fs = freemius( $VARS['id'] );
|
19 |
+
$slug = $fs->get_slug();
|
20 |
+
$unique_affix = $fs->get_unique_affix();
|
21 |
+
$last_license_user_id = $fs->get_last_license_user_id();
|
22 |
+
$has_last_license_user_id = FS_User::is_valid_id( $last_license_user_id );
|
23 |
+
|
24 |
+
$message_above_input_field = ( ! $has_last_license_user_id ) ?
|
25 |
+
fs_text_inline( 'Please enter the license key to enable the debug mode:', 'submit-developer-license-key-message', $slug ) :
|
26 |
+
sprintf(
|
27 |
+
fs_text_inline( 'To enter the debug mode, please enter the secret key of the license owner (UserID = %d), which you can find in your "My Profile" section of your User Dashboard:', 'submit-addon-developer-key-message', $slug ),
|
28 |
+
$last_license_user_id
|
29 |
+
);
|
30 |
+
|
31 |
+
$processing_text = ( fs_esc_js_inline( 'Processing', 'processing', $slug ) . '...' );
|
32 |
+
$submit_button_text = fs_text_inline( 'Submit', 'submit', $slug );
|
33 |
+
$debug_license_link_text = fs_esc_html_inline( 'Start Debug', 'start-debug-license', $slug );
|
34 |
+
$license_or_user_key_text = ( ! $has_last_license_user_id ) ?
|
35 |
+
fs_text_inline( 'License key', 'license-key' , $slug ) :
|
36 |
+
fs_text_inline( 'User key', 'user-key' , $slug );
|
37 |
+
$input_html = "<input class='fs-license-or-user-key' type='password' placeholder='{$license_or_user_key_text}' tabindex='1' />";
|
38 |
+
|
39 |
+
$modal_content_html = <<< HTML
|
40 |
+
<div class="notice notice-error inline license-or-user-key-submission-message"><p></p></div>
|
41 |
+
<p>{$message_above_input_field}</p>
|
42 |
+
{$input_html}
|
43 |
+
HTML;
|
44 |
+
|
45 |
+
fs_enqueue_local_style( 'fs_dialog_boxes', '/admin/dialog-boxes.css' );
|
46 |
+
?>
|
47 |
+
<script type="text/javascript">
|
48 |
+
( function( $ ) {
|
49 |
+
$( document ).ready( function() {
|
50 |
+
var modalContentHtml = <?php echo json_encode( $modal_content_html ) ?>,
|
51 |
+
modalHtml =
|
52 |
+
'<div class="fs-modal fs-modal-developer-license-debug-mode fs-modal-developer-license-debug-mode-<?php echo $unique_affix ?>">'
|
53 |
+
+ ' <div class="fs-modal-dialog">'
|
54 |
+
+ ' <div class="fs-modal-body">'
|
55 |
+
+ ' <div class="fs-modal-panel active">' + modalContentHtml + '</div>'
|
56 |
+
+ ' </div>'
|
57 |
+
+ ' <div class="fs-modal-footer">'
|
58 |
+
+ ' <button class="button button-secondary button-close" tabindex="4"><?php fs_esc_js_echo_inline( 'Cancel', 'cancel', $slug ) ?></button>'
|
59 |
+
+ ' <button class="button button-primary button-submit-license-or-user-key" tabindex="3"><?php echo esc_js( $submit_button_text ) ?></button>'
|
60 |
+
+ ' </div>'
|
61 |
+
+ ' </div>'
|
62 |
+
+ '</div>',
|
63 |
+
$modal = $( modalHtml ),
|
64 |
+
$debugLicenseLink = $( '.debug-license-trigger' ),
|
65 |
+
$submitKeyButton = $modal.find( '.button-submit-license-or-user-key' ),
|
66 |
+
$licenseOrUserKeyInput = $modal.find( 'input.fs-license-or-user-key' ),
|
67 |
+
$licenseOrUserKeySubmissionMessage = $modal.find( '.license-or-user-key-submission-message' ),
|
68 |
+
isDebugMode = <?php echo $fs->is_data_debug_mode() ? 'true' : 'false' ?>;
|
69 |
+
|
70 |
+
$modal.appendTo( $( 'body' ) );
|
71 |
+
|
72 |
+
function registerEventHandlers() {
|
73 |
+
$debugLicenseLink.click(function (evt) {
|
74 |
+
evt.preventDefault();
|
75 |
+
|
76 |
+
if ( isDebugMode ) {
|
77 |
+
setDeveloperLicenseDebugMode();
|
78 |
+
return true;
|
79 |
+
}
|
80 |
+
|
81 |
+
showModal( evt );
|
82 |
+
});
|
83 |
+
|
84 |
+
$modal.on( 'input propertychange', 'input.fs-license-or-user-key', function () {
|
85 |
+
var licenseOrUserKey = $( this ).val().trim();
|
86 |
+
|
87 |
+
/**
|
88 |
+
* If license or user key is not empty, enable the submission button.
|
89 |
+
*/
|
90 |
+
if ( licenseOrUserKey.length > 0 ) {
|
91 |
+
enableSubmitButton();
|
92 |
+
}
|
93 |
+
});
|
94 |
+
|
95 |
+
$modal.on( 'blur', 'input.fs-license-or-user-key', function () {
|
96 |
+
var licenseOrUserKey = $( this ).val().trim();
|
97 |
+
|
98 |
+
/**
|
99 |
+
* If license or user key is empty, disable the submission button.
|
100 |
+
*/
|
101 |
+
if ( 0 === licenseOrUserKey.length ) {
|
102 |
+
disableSubmitButton();
|
103 |
+
}
|
104 |
+
});
|
105 |
+
|
106 |
+
$modal.on( 'click', '.button-submit-license-or-user-key', function ( evt ) {
|
107 |
+
evt.preventDefault();
|
108 |
+
|
109 |
+
if ( $( this ).hasClass( 'disabled' ) ) {
|
110 |
+
return;
|
111 |
+
}
|
112 |
+
|
113 |
+
var licenseOrUserKey = $licenseOrUserKeyInput.val().trim();
|
114 |
+
|
115 |
+
disableSubmitButton();
|
116 |
+
|
117 |
+
if ( 0 === licenseOrUserKey.length ) {
|
118 |
+
return;
|
119 |
+
}
|
120 |
+
|
121 |
+
setDeveloperLicenseDebugMode( licenseOrUserKey );
|
122 |
+
});
|
123 |
+
|
124 |
+
// If the user has clicked outside the window, close the modal.
|
125 |
+
$modal.on( 'click', '.fs-close, .button-secondary', function () {
|
126 |
+
closeModal();
|
127 |
+
return false;
|
128 |
+
} );
|
129 |
+
}
|
130 |
+
|
131 |
+
registerEventHandlers();
|
132 |
+
|
133 |
+
function setDeveloperLicenseDebugMode( licenseOrUserKey ) {
|
134 |
+
var data = {
|
135 |
+
action : '<?php echo $fs->get_ajax_action( 'set_data_debug_mode' ) ?>',
|
136 |
+
security : '<?php echo $fs->get_ajax_security( 'set_data_debug_mode' ) ?>',
|
137 |
+
license_or_user_key: licenseOrUserKey,
|
138 |
+
is_debug_mode : isDebugMode,
|
139 |
+
module_id : '<?php echo $fs->get_id() ?>'
|
140 |
+
};
|
141 |
+
|
142 |
+
$.ajax( {
|
143 |
+
url : ajaxurl,
|
144 |
+
method : 'POST',
|
145 |
+
data : data,
|
146 |
+
beforeSend: function () {
|
147 |
+
$debugLicenseLink.find('span').text( '<?php echo $processing_text ?>' );
|
148 |
+
$submitKeyButton.text( '<?php echo $processing_text ?>' );
|
149 |
+
},
|
150 |
+
success : function ( result ) {
|
151 |
+
if ( result.success ) {
|
152 |
+
closeModal();
|
153 |
+
|
154 |
+
// Reload the "Account" page so that the pricing/upgrade link will be properly hidden/shown.
|
155 |
+
window.location.reload();
|
156 |
+
} else {
|
157 |
+
showError( result.error.message ? result.error.message : result.error );
|
158 |
+
resetButtons();
|
159 |
+
}
|
160 |
+
},
|
161 |
+
error : function () {
|
162 |
+
showError( <?php echo json_encode( fs_text_inline( 'An unknown error has occurred.', 'unknown-error', $slug ) ) ?> );
|
163 |
+
resetButtons();
|
164 |
+
}
|
165 |
+
});
|
166 |
+
}
|
167 |
+
|
168 |
+
function showModal( evt ) {
|
169 |
+
resetModal();
|
170 |
+
|
171 |
+
// Display the dialog box.
|
172 |
+
$modal.addClass( 'active' );
|
173 |
+
$( 'body' ).addClass( 'has-fs-modal' );
|
174 |
+
|
175 |
+
$licenseOrUserKeyInput.val( '' );
|
176 |
+
$licenseOrUserKeyInput.focus();
|
177 |
+
}
|
178 |
+
|
179 |
+
function closeModal() {
|
180 |
+
$modal.removeClass( 'active' );
|
181 |
+
$( 'body' ).removeClass( 'has-fs-modal' );
|
182 |
+
}
|
183 |
+
|
184 |
+
function resetButtons() {
|
185 |
+
enableSubmitButton();
|
186 |
+
$submitKeyButton.text( <?php echo json_encode( $submit_button_text ) ?> );
|
187 |
+
$debugLicenseLink.find('span').text( <?php echo json_encode( $debug_license_link_text ) ?> );
|
188 |
+
}
|
189 |
+
|
190 |
+
function resetModal() {
|
191 |
+
hideError();
|
192 |
+
resetButtons();
|
193 |
+
}
|
194 |
+
|
195 |
+
function enableSubmitButton() {
|
196 |
+
$submitKeyButton.removeClass( 'disabled' );
|
197 |
+
}
|
198 |
+
|
199 |
+
function disableSubmitButton() {
|
200 |
+
$submitKeyButton.addClass( 'disabled' );
|
201 |
+
}
|
202 |
+
|
203 |
+
function hideError() {
|
204 |
+
$licenseOrUserKeySubmissionMessage.hide();
|
205 |
+
}
|
206 |
+
|
207 |
+
function showError( msg ) {
|
208 |
+
$licenseOrUserKeySubmissionMessage.find( ' > p' ).html( msg );
|
209 |
+
$licenseOrUserKeySubmissionMessage.show();
|
210 |
+
}
|
211 |
+
} );
|
212 |
+
} )( jQuery );
|
213 |
</script>
|
freemius/templates/forms/optout.php
CHANGED
@@ -1,336 +1,336 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* @package Freemius
|
4 |
-
* @copyright Copyright (c) 2015, Freemius, Inc.
|
5 |
-
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
|
6 |
-
* @since 1.2.1.5
|
7 |
-
*/
|
8 |
-
|
9 |
-
if ( ! defined( 'ABSPATH' ) ) {
|
10 |
-
exit;
|
11 |
-
}
|
12 |
-
|
13 |
-
/**
|
14 |
-
* @var array $VARS
|
15 |
-
* @var Freemius $fs
|
16 |
-
*/
|
17 |
-
$fs = freemius( $VARS['id'] );
|
18 |
-
$slug = $fs->get_slug();
|
19 |
-
|
20 |
-
$action = $fs->is_tracking_allowed() ?
|
21 |
-
'stop_tracking' :
|
22 |
-
'allow_tracking';
|
23 |
-
|
24 |
-
$reconnect_url = $fs->get_activation_url( array(
|
25 |
-
'nonce' => wp_create_nonce( $fs->get_unique_affix() . '_reconnect' ),
|
26 |
-
'fs_action' => ( $fs->get_unique_affix() . '_reconnect' ),
|
27 |
-
) );
|
28 |
-
|
29 |
-
$plugin_title = "<strong>{$fs->get_plugin()->title}</strong>";
|
30 |
-
$opt_out_text = fs_text_x_inline( 'Opt Out', 'verb', 'opt-out', $slug );
|
31 |
-
$opt_in_text = fs_text_x_inline( 'Opt In', 'verb', 'opt-in', $slug );
|
32 |
-
|
33 |
-
if ( $fs->is_premium() ) {
|
34 |
-
$opt_in_message_appreciation = fs_text_inline( 'Connectivity to the licensing engine was successfully re-established. Automatic security & feature updates are now available through the WP Admin Dashboard.', 'premium-opt-in-message-appreciation', $slug );
|
35 |
-
|
36 |
-
$opt_out_message_subtitle = sprintf( fs_text_inline( 'Warning: Opting out will block automatic updates', 'premium-opt-out-message-appreciation', $slug ), $fs->get_module_type() );
|
37 |
-
$opt_out_message_usage_tracking = sprintf( fs_text_inline( 'Ongoing connectivity with the licensing engine is essential for receiving automatic security & feature updates of the paid product. To receive these updates, data like your license key, %1$s version, and WordPress version, is periodically sent to the server to check for updates. By opting out, you understand that your site won\'t receive automatic updates for %2$s from within the WP Admin Dashboard. This can put your site at risk, and we highly recommend to keep this connection active. If you do choose to opt-out, you\'ll need to check for %1$s updates and install them manually.', 'premium-opt-out-message-usage-tracking', $slug ), $fs->get_module_type(), $plugin_title );
|
38 |
-
|
39 |
-
$primary_cta_label = fs_text_inline( 'I\'d like to keep automatic updates', 'premium-opt-out-cancel', $slug );
|
40 |
-
} else {
|
41 |
-
$opt_in_message_appreciation = sprintf( fs_text_inline( 'We appreciate your help in making the %s better by letting us track some usage data.', 'opt-in-message-appreciation', $slug ), $fs->get_module_type() );
|
42 |
-
|
43 |
-
$opt_out_message_subtitle = $opt_in_message_appreciation;
|
44 |
-
$opt_out_message_usage_tracking = sprintf( fs_text_inline( "Usage tracking is done in the name of making %s better. Making a better user experience, prioritizing new features, and more good things. We'd really appreciate if you'll reconsider letting us continue with the tracking.", 'opt-out-message-usage-tracking', $slug ), $plugin_title );
|
45 |
-
$primary_cta_label = fs_text_inline( 'On second thought - I want to continue helping', 'opt-out-cancel', $slug );
|
46 |
-
}
|
47 |
-
|
48 |
-
$opt_out_message_clicking_opt_out = sprintf(
|
49 |
-
fs_text_inline( 'By clicking "Opt Out", we will no longer be sending any data from %s to %s.', 'opt-out-message-clicking-opt-out', $slug ),
|
50 |
-
$plugin_title,
|
51 |
-
sprintf(
|
52 |
-
'<a href="%s" target="_blank" rel="noopener">%s</a>',
|
53 |
-
'https://freemius.com',
|
54 |
-
'freemius.com'
|
55 |
-
)
|
56 |
-
);
|
57 |
-
|
58 |
-
$admin_notice_params = array(
|
59 |
-
'id' => '',
|
60 |
-
'slug' => $fs->get_id(),
|
61 |
-
'type' => 'success',
|
62 |
-
'sticky' => false,
|
63 |
-
'plugin' => $fs->get_plugin()->title,
|
64 |
-
'message' => $opt_in_message_appreciation
|
65 |
-
);
|
66 |
-
|
67 |
-
$admin_notice_html = fs_get_template( 'admin-notice.php', $admin_notice_params );
|
68 |
-
|
69 |
-
$modal_content_html = "
|
70 |
-
<h2" . ( $fs->is_premium() ? ' style="color: red"' : '' ) . ">{$opt_out_message_subtitle}</h2>
|
71 |
-
<div class=\"notice notice-error inline opt-out-error-message\"><p></p></div>
|
72 |
-
<p>{$opt_out_message_usage_tracking}</p>
|
73 |
-
<p>{$opt_out_message_clicking_opt_out}</p>
|
74 |
-
<label class=\"fs-permission-extensions\"><div class=\"fs-switch fs-small fs-round fs-" . ( $fs->is_extensions_tracking_allowed() ? 'on' : 'off' ) . "\"><div class=\"fs-toggle\"></div></div> " . fs_text_inline( 'Plugins & themes tracking' ) . " <span class=\"fs-switch-feedback success\"></span></label>";
|
75 |
-
|
76 |
-
fs_enqueue_local_style( 'fs_dialog_boxes', '/admin/dialog-boxes.css' );
|
77 |
-
fs_enqueue_local_style( 'fs_common', '/admin/common.css' );
|
78 |
-
?>
|
79 |
-
<script type="text/javascript">
|
80 |
-
(function( $ ) {
|
81 |
-
$( document ).ready(function() {
|
82 |
-
var modalContentHtml = <?php echo json_encode( $modal_content_html ) ?>,
|
83 |
-
modalHtml =
|
84 |
-
'<div class="fs-modal fs-modal-opt-out">'
|
85 |
-
+ ' <div class="fs-modal-dialog">'
|
86 |
-
+ ' <div class="fs-modal-header">'
|
87 |
-
+ ' <h4><?php echo esc_js( $opt_out_text ) ?></h4>'
|
88 |
-
+ ' </div>'
|
89 |
-
+ ' <div class="fs-modal-body">'
|
90 |
-
+ ' <div class="fs-modal-panel active">' + modalContentHtml + '</div>'
|
91 |
-
+ ' </div>'
|
92 |
-
+ ' <div class="fs-modal-footer">'
|
93 |
-
+ ' <button class="button <?php echo $fs->is_premium() ? 'button-primary warn' : 'button-secondary' ?> button-opt-out" tabindex="1"><?php echo esc_js( $opt_out_text ) ?></button>'
|
94 |
-
+ ' <button class="button <?php echo $fs->is_premium() ? 'button-secondary' : 'button-primary' ?> button-close" tabindex="2"><?php echo esc_js( $primary_cta_label ) ?></button>'
|
95 |
-
+ ' </div>'
|
96 |
-
+ ' </div>'
|
97 |
-
+ '</div>',
|
98 |
-
$modal = $(modalHtml),
|
99 |
-
$adminNotice = $( <?php echo json_encode( $admin_notice_html ) ?> ),
|
100 |
-
action = '<?php echo $action ?>',
|
101 |
-
actionLinkSelector = 'span.opt-in-or-opt-out.<?php echo $slug ?> a',
|
102 |
-
$optOutButton = $modal.find( '.button-opt-out' ),
|
103 |
-
$optOutErrorMessage = $modal.find( '.opt-out-error-message' ),
|
104 |
-
$extensionsTracking = $modal.find( '.fs-permission-extensions' ),
|
105 |
-
$body = $( 'body' ),
|
106 |
-
moduleID = '<?php echo $fs->get_id() ?>';
|
107 |
-
|
108 |
-
$modal.data( 'action', action );
|
109 |
-
$modal.appendTo( $body );
|
110 |
-
|
111 |
-
function registerActionLinkClick() {
|
112 |
-
$body.on( 'click', actionLinkSelector, function( evt ) {
|
113 |
-
evt.preventDefault();
|
114 |
-
|
115 |
-
if ( 'stop_tracking' == $modal.data( 'action' ) ) {
|
116 |
-
showModal();
|
117 |
-
} else {
|
118 |
-
optIn();
|
119 |
-
}
|
120 |
-
|
121 |
-
return false;
|
122 |
-
});
|
123 |
-
}
|
124 |
-
|
125 |
-
function registerEventHandlers() {
|
126 |
-
registerActionLinkClick();
|
127 |
-
|
128 |
-
$modal.on( 'click', '.button-opt-out', function( evt ) {
|
129 |
-
evt.preventDefault();
|
130 |
-
|
131 |
-
if ( $( this ).hasClass( 'disabled' ) ) {
|
132 |
-
return;
|
133 |
-
}
|
134 |
-
|
135 |
-
disableOptOutButton();
|
136 |
-
optOut();
|
137 |
-
});
|
138 |
-
|
139 |
-
// If the user has clicked outside the window, close the modal.
|
140 |
-
$modal.on( 'click', '.fs-close, .button-close', function() {
|
141 |
-
closeModal();
|
142 |
-
return false;
|
143 |
-
});
|
144 |
-
}
|
145 |
-
|
146 |
-
<?php if ( $fs->is_registered() ) : ?>
|
147 |
-
registerEventHandlers();
|
148 |
-
<?php endif ?>
|
149 |
-
|
150 |
-
function showModal() {
|
151 |
-
resetModal();
|
152 |
-
|
153 |
-
// Display the dialog box.
|
154 |
-
$modal.addClass( 'active' );
|
155 |
-
$body.addClass( 'has-fs-modal' );
|
156 |
-
}
|
157 |
-
|
158 |
-
function closeModal() {
|
159 |
-
$modal.removeClass( 'active' );
|
160 |
-
$body.removeClass( 'has-fs-modal' );
|
161 |
-
}
|
162 |
-
|
163 |
-
function resetOptOutButton() {
|
164 |
-
enableOptOutButton();
|
165 |
-
$optOutButton.text( <?php echo json_encode( $opt_out_text ) ?> );
|
166 |
-
}
|
167 |
-
|
168 |
-
function resetModal() {
|
169 |
-
hideError();
|
170 |
-
resetOptOutButton();
|
171 |
-
}
|
172 |
-
|
173 |
-
function optIn() {
|
174 |
-
sendRequest();
|
175 |
-
}
|
176 |
-
|
177 |
-
function optOut() {
|
178 |
-
sendRequest();
|
179 |
-
}
|
180 |
-
|
181 |
-
function sendRequest() {
|
182 |
-
var $actionLink = $( actionLinkSelector );
|
183 |
-
|
184 |
-
$.ajax({
|
185 |
-
url: ajaxurl,
|
186 |
-
method: 'POST',
|
187 |
-
data: {
|
188 |
-
action : ( 'stop_tracking' == action ?
|
189 |
-
'<?php echo $fs->get_ajax_action( 'stop_tracking' ) ?>' :
|
190 |
-
'<?php echo $fs->get_ajax_action( 'allow_tracking' ) ?>'
|
191 |
-
),
|
192 |
-
security : ( 'stop_tracking' == action ?
|
193 |
-
'<?php echo $fs->get_ajax_security( 'stop_tracking' ) ?>' :
|
194 |
-
'<?php echo $fs->get_ajax_security( 'allow_tracking' ) ?>'
|
195 |
-
),
|
196 |
-
module_id: moduleID,
|
197 |
-
_wp_http_referer: '<?php echo $fs->current_page_url() ?>'
|
198 |
-
},
|
199 |
-
beforeSend: function() {
|
200 |
-
if ( 'allow_tracking' == action ) {
|
201 |
-
$actionLink.text( '<?php fs_esc_js_echo_inline( 'Opting in', 'opting-in', $slug ) ?>...' );
|
202 |
-
} else {
|
203 |
-
$optOutButton.text( '<?php fs_esc_js_echo_inline( 'Opting out', 'opting-out', $slug ) ?>...' );
|
204 |
-
}
|
205 |
-
},
|
206 |
-
success: function( resultObj ) {
|
207 |
-
if ( resultObj.success ) {
|
208 |
-
if ( 'allow_tracking' == action ) {
|
209 |
-
action = 'stop_tracking';
|
210 |
-
$actionLink.text( '<?php echo esc_js( $opt_out_text ) ?>' );
|
211 |
-
showOptInAppreciationMessageAndScrollToTop();
|
212 |
-
} else {
|
213 |
-
action = 'allow_tracking';
|
214 |
-
$actionLink.text( '<?php echo esc_js( $opt_in_text ) ?>' );
|
215 |
-
closeModal();
|
216 |
-
|
217 |
-
if ( $adminNotice.length > 0 ) {
|
218 |
-
$adminNotice.remove();
|
219 |
-
}
|
220 |
-
}
|
221 |
-
|
222 |
-
$modal.data( 'action', action );
|
223 |
-
} else {
|
224 |
-
showError( resultObj.error );
|
225 |
-
resetOptOutButton();
|
226 |
-
}
|
227 |
-
}
|
228 |
-
});
|
229 |
-
}
|
230 |
-
|
231 |
-
var isUpdatingPermission = false;
|
232 |
-
$extensionsTracking.on('click', function() {
|
233 |
-
if (isUpdatingPermission) {
|
234 |
-
return false;
|
235 |
-
}
|
236 |
-
|
237 |
-
isUpdatingPermission = true;
|
238 |
-
|
239 |
-
var $switch = $extensionsTracking.find( '.fs-switch' ),
|
240 |
-
$switchFeedback = $extensionsTracking.find( '.fs-switch-feedback' );
|
241 |
-
|
242 |
-
$switch
|
243 |
-
.toggleClass( 'fs-on' )
|
244 |
-
.toggleClass( 'fs-off' );
|
245 |
-
|
246 |
-
$switchFeedback.html( '<i class="fs-ajax-spinner"></i>' );
|
247 |
-
|
248 |
-
$.ajax({
|
249 |
-
url: ajaxurl,
|
250 |
-
method: 'POST',
|
251 |
-
data: {
|
252 |
-
action : '<?php echo $fs->get_ajax_action( 'update_tracking_permission' ) ?>',
|
253 |
-
security : '<?php echo $fs->get_ajax_security( 'update_tracking_permission' ) ?>',
|
254 |
-
module_id : moduleID,
|
255 |
-
_wp_http_referer: '<?php echo $fs->current_page_url() ?>',
|
256 |
-
permission: 'extensions',
|
257 |
-
is_enabled: $switch.hasClass('fs-on')
|
258 |
-
},
|
259 |
-
success: function( resultObj ) {
|
260 |
-
if ( resultObj.success ) {
|
261 |
-
$switchFeedback.html( '<i class="dashicons dashicons-yes"></i> <?php echo esc_js( fs_text_inline( 'Saved', 'saved', $slug ) ) ?>' )
|
262 |
-
} else {
|
263 |
-
$switch
|
264 |
-
.toggleClass( 'fs-on' )
|
265 |
-
.toggleClass( 'fs-off' );
|
266 |
-
}
|
267 |
-
|
268 |
-
isUpdatingPermission = false;
|
269 |
-
}
|
270 |
-
});
|
271 |
-
});
|
272 |
-
|
273 |
-
function enableOptOutButton() {
|
274 |
-
$optOutButton.removeClass( 'disabled' );
|
275 |
-
}
|
276 |
-
|
277 |
-
function disableOptOutButton() {
|
278 |
-
$optOutButton.addClass( 'disabled' );
|
279 |
-
}
|
280 |
-
|
281 |
-
function hideError() {
|
282 |
-
$optOutErrorMessage.hide();
|
283 |
-
}
|
284 |
-
|
285 |
-
function showOptInAppreciationMessageAndScrollToTop() {
|
286 |
-
$adminNotice.insertAfter( $( '#wpbody-content' ).find( ' > .wrap > h1' ) );
|
287 |
-
window.scrollTo(0, 0);
|
288 |
-
}
|
289 |
-
|
290 |
-
function showError( msg ) {
|
291 |
-
$optOutErrorMessage.find( ' > p' ).html( msg );
|
292 |
-
$optOutErrorMessage.show();
|
293 |
-
}
|
294 |
-
|
295 |
-
<?php if ( $fs->is_theme() ) : ?>
|
296 |
-
/**
|
297 |
-
* Add opt-in/out button to the active theme's buttons collection
|
298 |
-
* in the theme's extended details overlay.
|
299 |
-
*
|
300 |
-
* @author Vova Feldman (@svovaf)
|
301 |
-
* @since 1.2.2.7
|
302 |
-
*/
|
303 |
-
$('.theme-overlay').contentChange(function () {
|
304 |
-
if (0 === $('.theme-overlay.active').length) {
|
305 |
-
// Add opt-in/out button only to the currently active theme.
|
306 |
-
return;
|
307 |
-
}
|
308 |
-
|
309 |
-
if ($('#fs_theme_opt_in_out').length > 0){
|
310 |
-
// Button already there.
|
311 |
-
return;
|
312 |
-
}
|
313 |
-
|
314 |
-
var label = (('stop_tracking' == action) ?
|
315 |
-
'<?php echo esc_js( $opt_out_text ) ?>' :
|
316 |
-
'<?php echo esc_js( $opt_in_text ) ?>'),
|
317 |
-
href = (('stop_tracking' != action) ?
|
318 |
-
'<?php echo ( $fs->is_registered() ? '' : esc_js( $reconnect_url ) ) ?>' :
|
319 |
-
'');
|
320 |
-
|
321 |
-
var $actionLink = $('<a id="fs_theme_opt_in_out" href="' + encodeURI(href) + '" class="button">' + label + '</a>');
|
322 |
-
|
323 |
-
actionLinkSelector = '#fs_theme_opt_in_out';
|
324 |
-
|
325 |
-
$modal.data( 'action', action );
|
326 |
-
|
327 |
-
$('.theme-wrap .theme-actions .active-theme').append($actionLink);
|
328 |
-
|
329 |
-
if ('' === href) {
|
330 |
-
registerActionLinkClick();
|
331 |
-
}
|
332 |
-
});
|
333 |
-
<?php endif ?>
|
334 |
-
});
|
335 |
-
})( jQuery );
|
336 |
-
</script>
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package Freemius
|
4 |
+
* @copyright Copyright (c) 2015, Freemius, Inc.
|
5 |
+
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
|
6 |
+
* @since 1.2.1.5
|
7 |
+
*/
|
8 |
+
|
9 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
10 |
+
exit;
|
11 |
+
}
|
12 |
+
|
13 |
+
/**
|
14 |
+
* @var array $VARS
|
15 |
+
* @var Freemius $fs
|
16 |
+
*/
|
17 |
+
$fs = freemius( $VARS['id'] );
|
18 |
+
$slug = $fs->get_slug();
|
19 |
+
|
20 |
+
$action = $fs->is_tracking_allowed() ?
|
21 |
+
'stop_tracking' :
|
22 |
+
'allow_tracking';
|
23 |
+
|
24 |
+
$reconnect_url = $fs->get_activation_url( array(
|
25 |
+
'nonce' => wp_create_nonce( $fs->get_unique_affix() . '_reconnect' ),
|
26 |
+
'fs_action' => ( $fs->get_unique_affix() . '_reconnect' ),
|
27 |
+
) );
|
28 |
+
|
29 |
+
$plugin_title = "<strong>{$fs->get_plugin()->title}</strong>";
|
30 |
+
$opt_out_text = fs_text_x_inline( 'Opt Out', 'verb', 'opt-out', $slug );
|
31 |
+
$opt_in_text = fs_text_x_inline( 'Opt In', 'verb', 'opt-in', $slug );
|
32 |
+
|
33 |
+
if ( $fs->is_premium() ) {
|
34 |
+
$opt_in_message_appreciation = fs_text_inline( 'Connectivity to the licensing engine was successfully re-established. Automatic security & feature updates are now available through the WP Admin Dashboard.', 'premium-opt-in-message-appreciation', $slug );
|
35 |
+
|
36 |
+
$opt_out_message_subtitle = sprintf( fs_text_inline( 'Warning: Opting out will block automatic updates', 'premium-opt-out-message-appreciation', $slug ), $fs->get_module_type() );
|
37 |
+
$opt_out_message_usage_tracking = sprintf( fs_text_inline( 'Ongoing connectivity with the licensing engine is essential for receiving automatic security & feature updates of the paid product. To receive these updates, data like your license key, %1$s version, and WordPress version, is periodically sent to the server to check for updates. By opting out, you understand that your site won\'t receive automatic updates for %2$s from within the WP Admin Dashboard. This can put your site at risk, and we highly recommend to keep this connection active. If you do choose to opt-out, you\'ll need to check for %1$s updates and install them manually.', 'premium-opt-out-message-usage-tracking', $slug ), $fs->get_module_type(), $plugin_title );
|
38 |
+
|
39 |
+
$primary_cta_label = fs_text_inline( 'I\'d like to keep automatic updates', 'premium-opt-out-cancel', $slug );
|
40 |
+
} else {
|
41 |
+
$opt_in_message_appreciation = sprintf( fs_text_inline( 'We appreciate your help in making the %s better by letting us track some usage data.', 'opt-in-message-appreciation', $slug ), $fs->get_module_type() );
|
42 |
+
|
43 |
+
$opt_out_message_subtitle = $opt_in_message_appreciation;
|
44 |
+
$opt_out_message_usage_tracking = sprintf( fs_text_inline( "Usage tracking is done in the name of making %s better. Making a better user experience, prioritizing new features, and more good things. We'd really appreciate if you'll reconsider letting us continue with the tracking.", 'opt-out-message-usage-tracking', $slug ), $plugin_title );
|
45 |
+
$primary_cta_label = fs_text_inline( 'On second thought - I want to continue helping', 'opt-out-cancel', $slug );
|
46 |
+
}
|
47 |
+
|
48 |
+
$opt_out_message_clicking_opt_out = sprintf(
|
49 |
+
fs_text_inline( 'By clicking "Opt Out", we will no longer be sending any data from %s to %s.', 'opt-out-message-clicking-opt-out', $slug ),
|
50 |
+
$plugin_title,
|
51 |
+
sprintf(
|
52 |
+
'<a href="%s" target="_blank" rel="noopener">%s</a>',
|
53 |
+
'https://freemius.com',
|
54 |
+
'freemius.com'
|
55 |
+
)
|
56 |
+
);
|
57 |
+
|
58 |
+
$admin_notice_params = array(
|
59 |
+
'id' => '',
|
60 |
+
'slug' => $fs->get_id(),
|
61 |
+
'type' => 'success',
|
62 |
+
'sticky' => false,
|
63 |
+
'plugin' => $fs->get_plugin()->title,
|
64 |
+
'message' => $opt_in_message_appreciation
|
65 |
+
);
|
66 |
+
|
67 |
+
$admin_notice_html = fs_get_template( 'admin-notice.php', $admin_notice_params );
|
68 |
+
|
69 |
+
$modal_content_html = "
|
70 |
+
<h2" . ( $fs->is_premium() ? ' style="color: red"' : '' ) . ">{$opt_out_message_subtitle}</h2>
|
71 |
+
<div class=\"notice notice-error inline opt-out-error-message\"><p></p></div>
|
72 |
+
<p>{$opt_out_message_usage_tracking}</p>
|
73 |
+
<p>{$opt_out_message_clicking_opt_out}</p>
|
74 |
+
<label class=\"fs-permission-extensions\"><div class=\"fs-switch fs-small fs-round fs-" . ( $fs->is_extensions_tracking_allowed() ? 'on' : 'off' ) . "\"><div class=\"fs-toggle\"></div></div> " . fs_text_inline( 'Plugins & themes tracking' ) . " <span class=\"fs-switch-feedback success\"></span></label>";
|
75 |
+
|
76 |
+
fs_enqueue_local_style( 'fs_dialog_boxes', '/admin/dialog-boxes.css' );
|
77 |
+
fs_enqueue_local_style( 'fs_common', '/admin/common.css' );
|
78 |
+
?>
|
79 |
+
<script type="text/javascript">
|
80 |
+
(function( $ ) {
|
81 |
+
$( document ).ready(function() {
|
82 |
+
var modalContentHtml = <?php echo json_encode( $modal_content_html ) ?>,
|
83 |
+
modalHtml =
|
84 |
+
'<div class="fs-modal fs-modal-opt-out">'
|
85 |
+
+ ' <div class="fs-modal-dialog">'
|
86 |
+
+ ' <div class="fs-modal-header">'
|
87 |
+
+ ' <h4><?php echo esc_js( $opt_out_text ) ?></h4>'
|
88 |
+
+ ' </div>'
|
89 |
+
+ ' <div class="fs-modal-body">'
|
90 |
+
+ ' <div class="fs-modal-panel active">' + modalContentHtml + '</div>'
|
91 |
+
+ ' </div>'
|
92 |
+
+ ' <div class="fs-modal-footer">'
|
93 |
+
+ ' <button class="button <?php echo $fs->is_premium() ? 'button-primary warn' : 'button-secondary' ?> button-opt-out" tabindex="1"><?php echo esc_js( $opt_out_text ) ?></button>'
|
94 |
+
+ ' <button class="button <?php echo $fs->is_premium() ? 'button-secondary' : 'button-primary' ?> button-close" tabindex="2"><?php echo esc_js( $primary_cta_label ) ?></button>'
|
95 |
+
+ ' </div>'
|
96 |
+
+ ' </div>'
|
97 |
+
+ '</div>',
|
98 |
+
$modal = $(modalHtml),
|
99 |
+
$adminNotice = $( <?php echo json_encode( $admin_notice_html ) ?> ),
|
100 |
+
action = '<?php echo $action ?>',
|
101 |
+
actionLinkSelector = 'span.opt-in-or-opt-out.<?php echo $slug ?> a',
|
102 |
+
$optOutButton = $modal.find( '.button-opt-out' ),
|
103 |
+
$optOutErrorMessage = $modal.find( '.opt-out-error-message' ),
|
104 |
+
$extensionsTracking = $modal.find( '.fs-permission-extensions' ),
|
105 |
+
$body = $( 'body' ),
|
106 |
+
moduleID = '<?php echo $fs->get_id() ?>';
|
107 |
+
|
108 |
+
$modal.data( 'action', action );
|
109 |
+
$modal.appendTo( $body );
|
110 |
+
|
111 |
+
function registerActionLinkClick() {
|
112 |
+
$body.on( 'click', actionLinkSelector, function( evt ) {
|
113 |
+
evt.preventDefault();
|
114 |
+
|
115 |
+
if ( 'stop_tracking' == $modal.data( 'action' ) ) {
|
116 |
+
showModal();
|
117 |
+
} else {
|
118 |
+
optIn();
|
119 |
+
}
|
120 |
+
|
121 |
+
return false;
|
122 |
+
});
|
123 |
+
}
|
124 |
+
|
125 |
+
function registerEventHandlers() {
|
126 |
+
registerActionLinkClick();
|
127 |
+
|
128 |
+
$modal.on( 'click', '.button-opt-out', function( evt ) {
|
129 |
+
evt.preventDefault();
|
130 |
+
|
131 |
+
if ( $( this ).hasClass( 'disabled' ) ) {
|
132 |
+
return;
|
133 |
+
}
|
134 |
+
|
135 |
+
disableOptOutButton();
|
136 |
+
optOut();
|
137 |
+
});
|
138 |
+
|
139 |
+
// If the user has clicked outside the window, close the modal.
|
140 |
+
$modal.on( 'click', '.fs-close, .button-close', function() {
|
141 |
+
closeModal();
|
142 |
+
return false;
|
143 |
+
});
|
144 |
+
}
|
145 |
+
|
146 |
+
<?php if ( $fs->is_registered() ) : ?>
|
147 |
+
registerEventHandlers();
|
148 |
+
<?php endif ?>
|
149 |
+
|
150 |
+
function showModal() {
|
151 |
+
resetModal();
|
152 |
+
|
153 |
+
// Display the dialog box.
|
154 |
+
$modal.addClass( 'active' );
|
155 |
+
$body.addClass( 'has-fs-modal' );
|
156 |
+
}
|
157 |
+
|
158 |
+
function closeModal() {
|
159 |
+
$modal.removeClass( 'active' );
|
160 |
+
$body.removeClass( 'has-fs-modal' );
|
161 |
+
}
|
162 |
+
|
163 |
+
function resetOptOutButton() {
|
164 |
+
enableOptOutButton();
|
165 |
+
$optOutButton.text( <?php echo json_encode( $opt_out_text ) ?> );
|
166 |
+
}
|
167 |
+
|
168 |
+
function resetModal() {
|
169 |
+
hideError();
|
170 |
+
resetOptOutButton();
|
171 |
+
}
|
172 |
+
|
173 |
+
function optIn() {
|
174 |
+
sendRequest();
|
175 |
+
}
|
176 |
+
|
177 |
+
function optOut() {
|
178 |
+
sendRequest();
|
179 |
+
}
|
180 |
+
|
181 |
+
function sendRequest() {
|
182 |
+
var $actionLink = $( actionLinkSelector );
|
183 |
+
|
184 |
+
$.ajax({
|
185 |
+
url: ajaxurl,
|
186 |
+
method: 'POST',
|
187 |
+
data: {
|
188 |
+
action : ( 'stop_tracking' == action ?
|
189 |
+
'<?php echo $fs->get_ajax_action( 'stop_tracking' ) ?>' :
|
190 |
+
'<?php echo $fs->get_ajax_action( 'allow_tracking' ) ?>'
|
191 |
+
),
|
192 |
+
security : ( 'stop_tracking' == action ?
|
193 |
+
'<?php echo $fs->get_ajax_security( 'stop_tracking' ) ?>' :
|
194 |
+
'<?php echo $fs->get_ajax_security( 'allow_tracking' ) ?>'
|
195 |
+
),
|
196 |
+
module_id: moduleID,
|
197 |
+
_wp_http_referer: '<?php echo $fs->current_page_url() ?>'
|
198 |
+
},
|
199 |
+
beforeSend: function() {
|
200 |
+
if ( 'allow_tracking' == action ) {
|
201 |
+
$actionLink.text( '<?php fs_esc_js_echo_inline( 'Opting in', 'opting-in', $slug ) ?>...' );
|
202 |
+
} else {
|
203 |
+
$optOutButton.text( '<?php fs_esc_js_echo_inline( 'Opting out', 'opting-out', $slug ) ?>...' );
|
204 |
+
}
|
205 |
+
},
|
206 |
+
success: function( resultObj ) {
|
207 |
+
if ( resultObj.success ) {
|
208 |
+
if ( 'allow_tracking' == action ) {
|
209 |
+
action = 'stop_tracking';
|
210 |
+
$actionLink.text( '<?php echo esc_js( $opt_out_text ) ?>' );
|
211 |
+
showOptInAppreciationMessageAndScrollToTop();
|
212 |
+
} else {
|
213 |
+
action = 'allow_tracking';
|
214 |
+
$actionLink.text( '<?php echo esc_js( $opt_in_text ) ?>' );
|
215 |
+
closeModal();
|
216 |
+
|
217 |
+
if ( $adminNotice.length > 0 ) {
|
218 |
+
$adminNotice.remove();
|
219 |
+
}
|
220 |
+
}
|
221 |
+
|
222 |
+
$modal.data( 'action', action );
|
223 |
+
} else {
|
224 |
+
showError( resultObj.error );
|
225 |
+
resetOptOutButton();
|
226 |
+
}
|
227 |
+
}
|
228 |
+
});
|
229 |
+
}
|
230 |
+
|
231 |
+
var isUpdatingPermission = false;
|
232 |
+
$extensionsTracking.on('click', function() {
|
233 |
+
if (isUpdatingPermission) {
|
234 |
+
return false;
|
235 |
+
}
|
236 |
+
|
237 |
+
isUpdatingPermission = true;
|
238 |
+
|
239 |
+
var $switch = $extensionsTracking.find( '.fs-switch' ),
|
240 |
+
$switchFeedback = $extensionsTracking.find( '.fs-switch-feedback' );
|
241 |
+
|
242 |
+
$switch
|
243 |
+
.toggleClass( 'fs-on' )
|
244 |
+
.toggleClass( 'fs-off' );
|
245 |
+
|
246 |
+
$switchFeedback.html( '<i class="fs-ajax-spinner"></i>' );
|
247 |
+
|
248 |
+
$.ajax({
|
249 |
+
url: ajaxurl,
|
250 |
+
method: 'POST',
|
251 |
+
data: {
|
252 |
+
action : '<?php echo $fs->get_ajax_action( 'update_tracking_permission' ) ?>',
|
253 |
+
security : '<?php echo $fs->get_ajax_security( 'update_tracking_permission' ) ?>',
|
254 |
+
module_id : moduleID,
|
255 |
+
_wp_http_referer: '<?php echo $fs->current_page_url() ?>',
|
256 |
+
permission: 'extensions',
|
257 |
+
is_enabled: $switch.hasClass('fs-on')
|
258 |
+
},
|
259 |
+
success: function( resultObj ) {
|
260 |
+
if ( resultObj.success ) {
|
261 |
+
$switchFeedback.html( '<i class="dashicons dashicons-yes"></i> <?php echo esc_js( fs_text_inline( 'Saved', 'saved', $slug ) ) ?>' )
|
262 |
+
} else {
|
263 |
+
$switch
|
264 |
+
.toggleClass( 'fs-on' )
|
265 |
+
.toggleClass( 'fs-off' );
|
266 |
+
}
|
267 |
+
|
268 |
+
isUpdatingPermission = false;
|
269 |
+
}
|
270 |
+
});
|
271 |
+
});
|
272 |
+
|
273 |
+
function enableOptOutButton() {
|
274 |
+
$optOutButton.removeClass( 'disabled' );
|
275 |
+
}
|
276 |
+
|
277 |
+
function disableOptOutButton() {
|
278 |
+
$optOutButton.addClass( 'disabled' );
|
279 |
+
}
|
280 |
+
|
281 |
+
function hideError() {
|
282 |
+
$optOutErrorMessage.hide();
|
283 |
+
}
|
284 |
+
|
285 |
+
function showOptInAppreciationMessageAndScrollToTop() {
|
286 |
+
$adminNotice.insertAfter( $( '#wpbody-content' ).find( ' > .wrap > h1' ) );
|
287 |
+
window.scrollTo(0, 0);
|
288 |
+
}
|
289 |
+
|
290 |
+
function showError( msg ) {
|
291 |
+
$optOutErrorMessage.find( ' > p' ).html( msg );
|
292 |
+
$optOutErrorMessage.show();
|
293 |
+
}
|
294 |
+
|
295 |
+
<?php if ( $fs->is_theme() ) : ?>
|
296 |
+
/**
|
297 |
+
* Add opt-in/out button to the active theme's buttons collection
|
298 |
+
* in the theme's extended details overlay.
|
299 |
+
*
|
300 |
+
* @author Vova Feldman (@svovaf)
|
301 |
+
* @since 1.2.2.7
|
302 |
+
*/
|
303 |
+
$('.theme-overlay').contentChange(function () {
|
304 |
+
if (0 === $('.theme-overlay.active').length) {
|
305 |
+
// Add opt-in/out button only to the currently active theme.
|
306 |
+
return;
|
307 |
+
}
|
308 |
+
|
309 |
+
if ($('#fs_theme_opt_in_out').length > 0){
|
310 |
+
// Button already there.
|
311 |
+
return;
|
312 |
+
}
|
313 |
+
|
314 |
+
var label = (('stop_tracking' == action) ?
|
315 |
+
'<?php echo esc_js( $opt_out_text ) ?>' :
|
316 |
+
'<?php echo esc_js( $opt_in_text ) ?>'),
|
317 |
+
href = (('stop_tracking' != action) ?
|
318 |
+
'<?php echo ( $fs->is_registered() ? '' : esc_js( $reconnect_url ) ) ?>' :
|
319 |
+
'');
|
320 |
+
|
321 |
+
var $actionLink = $('<a id="fs_theme_opt_in_out" href="' + encodeURI(href) + '" class="button">' + label + '</a>');
|
322 |
+
|
323 |
+
actionLinkSelector = '#fs_theme_opt_in_out';
|
324 |
+
|
325 |
+
$modal.data( 'action', action );
|
326 |
+
|
327 |
+
$('.theme-wrap .theme-actions .active-theme').append($actionLink);
|
328 |
+
|
329 |
+
if ('' === href) {
|
330 |
+
registerActionLinkClick();
|
331 |
+
}
|
332 |
+
});
|
333 |
+
<?php endif ?>
|
334 |
+
});
|
335 |
+
})( jQuery );
|
336 |
+
</script>
|
freemius/templates/forms/user-change.php
CHANGED
@@ -1,296 +1,296 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* @package Freemius
|
4 |
-
* @copyright Copyright (c) 2015, Freemius, Inc.
|
5 |
-
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
|
6 |
-
* @since 2.3.2
|
7 |
-
*/
|
8 |
-
|
9 |
-
if ( ! defined( 'ABSPATH' ) ) {
|
10 |
-
exit;
|
11 |
-
}
|
12 |
-
|
13 |
-
/**
|
14 |
-
* @var array $VARS
|
15 |
-
*
|
16 |
-
* @var Freemius $fs
|
17 |
-
*/
|
18 |
-
$fs = freemius( $VARS['id'] );
|
19 |
-
$slug = $fs->get_slug();
|
20 |
-
|
21 |
-
/**
|
22 |
-
* @var object[] $license_owners
|
23 |
-
*/
|
24 |
-
$license_owners = $VARS['license_owners'];
|
25 |
-
|
26 |
-
$change_user_message = fs_text_inline( 'By changing the user, you agree to transfer the account ownership to:', 'change-user--message', $slug );
|
27 |
-
$header_title = fs_text_inline( 'Change User', 'change-user', $slug );
|
28 |
-
$user_change_button_text = fs_text_inline( 'I Agree - Change User', 'agree-change-user', $slug );
|
29 |
-
$other_text = fs_text_inline( 'Other', 'other', $slug );
|
30 |
-
$enter_email_address_placeholder_text = fs_text_inline( 'Enter email address', 'enter-email-address', $slug );
|
31 |
-
|
32 |
-
$user_change_options_html = <<< HTML
|
33 |
-
<div class="fs-user-change-options-container">
|
34 |
-
<table>
|
35 |
-
<tbody>
|
36 |
-
HTML;
|
37 |
-
|
38 |
-
foreach ( $license_owners as $license_owner ) {
|
39 |
-
$user_change_options_html .= <<< HTML
|
40 |
-
<tr class="fs-email-address-container">
|
41 |
-
<td><input id="fs_email_address_{$license_owner->id}" type="radio" name="fs_email_address" value="{$license_owner->id}"></td>
|
42 |
-
<td><label for="fs_email_address_{$license_owner->id}">{$license_owner->email}</label></td>
|
43 |
-
</tr>
|
44 |
-
HTML;
|
45 |
-
}
|
46 |
-
|
47 |
-
$user_change_options_html .= <<< HTML
|
48 |
-
<tr>
|
49 |
-
<td><input id="fs_other_email_address_radio" type="radio" name="fs_email_address" value="other"></td>
|
50 |
-
<td class="fs-other-email-address-container">
|
51 |
-
<div>
|
52 |
-
<label for="fs_email_address">{$other_text}: </label>
|
53 |
-
<div>
|
54 |
-
<input id="fs_other_email_address_text_field" class="fs-email-address" type="text" placeholder="{$enter_email_address_placeholder_text}" tabindex="1">
|
55 |
-
</div>
|
56 |
-
</div>
|
57 |
-
</td>
|
58 |
-
</tr>
|
59 |
-
</tbody>
|
60 |
-
</table>
|
61 |
-
</div>
|
62 |
-
HTML;
|
63 |
-
|
64 |
-
$modal_content_html = <<< HTML
|
65 |
-
<div class="notice notice-error inline fs-change-user-result-message"><p></p></div>
|
66 |
-
<p>{$change_user_message}</p>
|
67 |
-
{$user_change_options_html}
|
68 |
-
HTML;
|
69 |
-
|
70 |
-
fs_enqueue_local_style( 'fs_dialog_boxes', '/admin/dialog-boxes.css' );
|
71 |
-
?>
|
72 |
-
<script type="text/javascript">
|
73 |
-
(function( $ ) {
|
74 |
-
$( document ).ready(function() {
|
75 |
-
var modalContentHtml = <?php echo json_encode( $modal_content_html ) ?>,
|
76 |
-
modalHtml =
|
77 |
-
'<div class="fs-modal fs-modal-change-user fs-modal-change-user-<?php echo $fs->get_unique_affix() ?>">'
|
78 |
-
+ ' <div class="fs-modal-dialog">'
|
79 |
-
+ ' <div class="fs-modal-header">'
|
80 |
-
+ ' <h4><?php echo esc_js( $header_title ) ?></h4>'
|
81 |
-
+ ' <a href="!#" class="fs-close"><i class="dashicons dashicons-no" title="<?php echo esc_js( fs_text_x_inline( 'Dismiss', 'close window', 'dismiss', $slug ) ) ?>"></i></a>'
|
82 |
-
+ ' </div>'
|
83 |
-
+ ' <div class="fs-modal-body">'
|
84 |
-
+ ' <div class="fs-modal-panel active">' + modalContentHtml + '</div>'
|
85 |
-
+ ' </div>'
|
86 |
-
+ ' <div class="fs-modal-footer">'
|
87 |
-
+ ' <button class="button button-secondary button-close" tabindex="4"><?php fs_esc_js_echo_inline( 'Cancel', 'cancel', $slug ) ?></button>'
|
88 |
-
+ ' <button class="button button-primary fs-user-change-button" tabindex="3"><?php echo esc_js( $user_change_button_text ) ?></button>'
|
89 |
-
+ ' </div>'
|
90 |
-
+ ' </div>'
|
91 |
-
+ '</div>',
|
92 |
-
$modal = $( modalHtml ),
|
93 |
-
$userChangeButton = $modal.find( '.fs-user-change-button' ),
|
94 |
-
$otherEmailAddressRadio = $modal.find( '#fs_other_email_address_radio' ),
|
95 |
-
$changeUserResultMessage = $modal.find( '.fs-change-user-result-message' ),
|
96 |
-
$otherEmailAddressContainer = $modal.find( '.fs-other-email-address-container' ),
|
97 |
-
$otherEmailAddressTextField = $modal.find( '#fs_other_email_address_text_field' ),
|
98 |
-
$licenseOwners = $modal.find( 'input[type="radio"][name="fs_email_address"]' );
|
99 |
-
|
100 |
-
$modal.appendTo( $( 'body' ) );
|
101 |
-
|
102 |
-
var previousEmailAddress = null;
|
103 |
-
|
104 |
-
function registerEventHandlers() {
|
105 |
-
$licenseOwners.change( function() {
|
106 |
-
var otherEmailAddress = $otherEmailAddressTextField.val().trim(),
|
107 |
-
otherEmailAddressIsSelected = isOtherEmailAddressSelected();
|
108 |
-
|
109 |
-
if ( otherEmailAddressIsSelected ) {
|
110 |
-
$otherEmailAddressTextField.focus();
|
111 |
-
}
|
112 |
-
|
113 |
-
if ( otherEmailAddress.length > 0 || ! otherEmailAddressIsSelected ) {
|
114 |
-
enableUserChangeButton();
|
115 |
-
} else {
|
116 |
-
disableUserChangeButton();
|
117 |
-
}
|
118 |
-
} );
|
119 |
-
|
120 |
-
$otherEmailAddressContainer.click( function () {
|
121 |
-
$otherEmailAddressRadio.click();
|
122 |
-
} );
|
123 |
-
|
124 |
-
// Handle for the "Change User" button on the "Account" page.
|
125 |
-
$( '#fs_change_user' ).click( function ( evt ) {
|
126 |
-
evt.preventDefault();
|
127 |
-
|
128 |
-
showModal( evt );
|
129 |
-
} );
|
130 |
-
|
131 |
-
/**
|
132 |
-
* Disables the "Change User" button when the email address is empty.
|
133 |
-
*/
|
134 |
-
$modal.on( 'keyup paste delete cut', 'input#fs_other_email_address_text_field', function () {
|
135 |
-
setTimeout( function () {
|
136 |
-
var emailAddress = $otherEmailAddressRadio.val().trim();
|
137 |
-
|
138 |
-
if ( emailAddress === previousEmailAddress ) {
|
139 |
-
return;
|
140 |
-
}
|
141 |
-
|
142 |
-
if ( '' === emailAddress ) {
|
143 |
-
disableUserChangeButton();
|
144 |
-
} else {
|
145 |
-
enableUserChangeButton();
|
146 |
-
}
|
147 |
-
|
148 |
-
previousEmailAddress = emailAddress;
|
149 |
-
}, 100 );
|
150 |
-
} ).focus();
|
151 |
-
|
152 |
-
$modal.on( 'input propertychange', 'input#fs_other_email_address_text_field', function () {
|
153 |
-
var emailAddress = $( this ).val().trim();
|
154 |
-
|
155 |
-
/**
|
156 |
-
* If email address is not empty, enable the "Change User" button.
|
157 |
-
*/
|
158 |
-
if ( emailAddress.length > 0 ) {
|
159 |
-
enableUserChangeButton();
|
160 |
-
}
|
161 |
-
} );
|
162 |
-
|
163 |
-
$modal.on( 'blur', 'input#fs_other_email_address_text_field', function( evt ) {
|
164 |
-
var emailAddress = $( this ).val().trim();
|
165 |
-
|
166 |
-
/**
|
167 |
-
* If email address is empty, disable the "Change User" button.
|
168 |
-
*/
|
169 |
-
if ( 0 === emailAddress.length ) {
|
170 |
-
disableUserChangeButton();
|
171 |
-
}
|
172 |
-
} );
|
173 |
-
|
174 |
-
$modal.on( 'click', '.fs-user-change-button', function ( evt ) {
|
175 |
-
evt.preventDefault();
|
176 |
-
|
177 |
-
if ( $( this ).hasClass( 'disabled' ) ) {
|
178 |
-
return;
|
179 |
-
}
|
180 |
-
|
181 |
-
var emailAddress = '',
|
182 |
-
licenseOwnerID = null;
|
183 |
-
|
184 |
-
if ( ! isOtherEmailAddressSelected() ) {
|
185 |
-
licenseOwnerID = $licenseOwners.filter( ':checked' ).val();
|
186 |
-
} else {
|
187 |
-
emailAddress = $otherEmailAddressTextField.val().trim();
|
188 |
-
|
189 |
-
if ( 0 === emailAddress.length ) {
|
190 |
-
return;
|
191 |
-
}
|
192 |
-
}
|
193 |
-
|
194 |
-
disableUserChangeButton();
|
195 |
-
|
196 |
-
$.ajax( {
|
197 |
-
url : ajaxurl,
|
198 |
-
method : 'POST',
|
199 |
-
data : {
|
200 |
-
action : '<?php echo $fs->get_ajax_action( 'change_user' ) ?>',
|
201 |
-
security : '<?php echo $fs->get_ajax_security( 'change_user' ) ?>',
|
202 |
-
email_address: emailAddress,
|
203 |
-
user_id : licenseOwnerID,
|
204 |
-
module_id : '<?php echo $fs->get_id() ?>'
|
205 |
-
},
|
206 |
-
beforeSend: function () {
|
207 |
-
$userChangeButton
|
208 |
-
.text( '<?php fs_esc_js_echo_inline( 'Changing user, please wait', 'changing-user-please-wait', $slug ) ?>...' )
|
209 |
-
.prepend('<i class="fs-ajax-spinner"></i>');
|
210 |
-
|
211 |
-
$(document.body).css({'cursor': 'wait'});
|
212 |
-
},
|
213 |
-
success : function( result ) {
|
214 |
-
if ( result.success ) {
|
215 |
-
// Redirect to the "Account" page.
|
216 |
-
window.location.reload();
|
217 |
-
} else {
|
218 |
-
$(document.body).css({'cursor': 'auto'});
|
219 |
-
|
220 |
-
showError( result.error.message ? result.error.message : result.error );
|
221 |
-
resetUserChangeButton();
|
222 |
-
}
|
223 |
-
},
|
224 |
-
error : function () {
|
225 |
-
$(document.body).css({'cursor': 'auto'});
|
226 |
-
|
227 |
-
showError( '<?php fs_esc_js_echo_inline( 'Unexpected error, try again in 5 minutes. If the error persists, please contact support.', 'unexpected-error', $slug ) ?>' );
|
228 |
-
|
229 |
-
resetUserChangeButton();
|
230 |
-
}
|
231 |
-
} );
|
232 |
-
} );
|
233 |
-
|
234 |
-
// If the user has clicked outside the window, close the modal.
|
235 |
-
$modal.on( 'click', '.fs-close, .button-secondary', function () {
|
236 |
-
closeModal();
|
237 |
-
return false;
|
238 |
-
} );
|
239 |
-
}
|
240 |
-
|
241 |
-
registerEventHandlers();
|
242 |
-
|
243 |
-
/**
|
244 |
-
* @returns {Boolean}
|
245 |
-
*/
|
246 |
-
function isOtherEmailAddressSelected() {
|
247 |
-
return ( 'other' === $licenseOwners.filter( ':checked' ).val() );
|
248 |
-
}
|
249 |
-
|
250 |
-
function showModal() {
|
251 |
-
resetModal();
|
252 |
-
|
253 |
-
// Display the dialog box.
|
254 |
-
$modal.addClass( 'active' );
|
255 |
-
$( 'body' ).addClass( 'has-fs-modal' );
|
256 |
-
|
257 |
-
// Select the first radio button.
|
258 |
-
$licenseOwners.get( 0 ).click();
|
259 |
-
|
260 |
-
$otherEmailAddressTextField.val( '' );
|
261 |
-
}
|
262 |
-
|
263 |
-
function closeModal() {
|
264 |
-
$modal.removeClass( 'active' );
|
265 |
-
$( 'body' ).removeClass( 'has-fs-modal' );
|
266 |
-
}
|
267 |
-
|
268 |
-
function resetUserChangeButton() {
|
269 |
-
enableUserChangeButton();
|
270 |
-
$userChangeButton.text( <?php echo json_encode( $user_change_button_text ) ?> );
|
271 |
-
}
|
272 |
-
|
273 |
-
function resetModal() {
|
274 |
-
hideError();
|
275 |
-
resetUserChangeButton();
|
276 |
-
}
|
277 |
-
|
278 |
-
function enableUserChangeButton() {
|
279 |
-
$userChangeButton.removeClass( 'disabled' );
|
280 |
-
}
|
281 |
-
|
282 |
-
function disableUserChangeButton() {
|
283 |
-
$userChangeButton.addClass( 'disabled' );
|
284 |
-
}
|
285 |
-
|
286 |
-
function hideError() {
|
287 |
-
$changeUserResultMessage.hide();
|
288 |
-
}
|
289 |
-
|
290 |
-
function showError( msg ) {
|
291 |
-
$changeUserResultMessage.find( ' > p' ).html( msg );
|
292 |
-
$changeUserResultMessage.show();
|
293 |
-
}
|
294 |
-
});
|
295 |
-
})( jQuery );
|
296 |
-
</script>
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package Freemius
|
4 |
+
* @copyright Copyright (c) 2015, Freemius, Inc.
|
5 |
+
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
|
6 |
+
* @since 2.3.2
|
7 |
+
*/
|
8 |
+
|
9 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
10 |
+
exit;
|
11 |
+
}
|
12 |
+
|
13 |
+
/**
|
14 |
+
* @var array $VARS
|
15 |
+
*
|
16 |
+
* @var Freemius $fs
|
17 |
+
*/
|
18 |
+
$fs = freemius( $VARS['id'] );
|
19 |
+
$slug = $fs->get_slug();
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @var object[] $license_owners
|
23 |
+
*/
|
24 |
+
$license_owners = $VARS['license_owners'];
|
25 |
+
|
26 |
+
$change_user_message = fs_text_inline( 'By changing the user, you agree to transfer the account ownership to:', 'change-user--message', $slug );
|
27 |
+
$header_title = fs_text_inline( 'Change User', 'change-user', $slug );
|
28 |
+
$user_change_button_text = fs_text_inline( 'I Agree - Change User', 'agree-change-user', $slug );
|
29 |
+
$other_text = fs_text_inline( 'Other', 'other', $slug );
|
30 |
+
$enter_email_address_placeholder_text = fs_text_inline( 'Enter email address', 'enter-email-address', $slug );
|
31 |
+
|
32 |
+
$user_change_options_html = <<< HTML
|
33 |
+
<div class="fs-user-change-options-container">
|
34 |
+
<table>
|
35 |
+
<tbody>
|
36 |
+
HTML;
|
37 |
+
|
38 |
+
foreach ( $license_owners as $license_owner ) {
|
39 |
+
$user_change_options_html .= <<< HTML
|
40 |
+
<tr class="fs-email-address-container">
|
41 |
+
<td><input id="fs_email_address_{$license_owner->id}" type="radio" name="fs_email_address" value="{$license_owner->id}"></td>
|
42 |
+
<td><label for="fs_email_address_{$license_owner->id}">{$license_owner->email}</label></td>
|
43 |
+
</tr>
|
44 |
+
HTML;
|
45 |
+
}
|
46 |
+
|
47 |
+
$user_change_options_html .= <<< HTML
|
48 |
+
<tr>
|
49 |
+
<td><input id="fs_other_email_address_radio" type="radio" name="fs_email_address" value="other"></td>
|
50 |
+
<td class="fs-other-email-address-container">
|
51 |
+
<div>
|
52 |
+
<label for="fs_email_address">{$other_text}: </label>
|
53 |
+
<div>
|
54 |
+
<input id="fs_other_email_address_text_field" class="fs-email-address" type="text" placeholder="{$enter_email_address_placeholder_text}" tabindex="1">
|
55 |
+
</div>
|
56 |
+
</div>
|
57 |
+
</td>
|
58 |
+
</tr>
|
59 |
+
</tbody>
|
60 |
+
</table>
|
61 |
+
</div>
|
62 |
+
HTML;
|
63 |
+
|
64 |
+
$modal_content_html = <<< HTML
|
65 |
+
<div class="notice notice-error inline fs-change-user-result-message"><p></p></div>
|
66 |
+
<p>{$change_user_message}</p>
|
67 |
+
{$user_change_options_html}
|
68 |
+
HTML;
|
69 |
+
|
70 |
+
fs_enqueue_local_style( 'fs_dialog_boxes', '/admin/dialog-boxes.css' );
|
71 |
+
?>
|
72 |
+
<script type="text/javascript">
|
73 |
+
(function( $ ) {
|
74 |
+
$( document ).ready(function() {
|
75 |
+
var modalContentHtml = <?php echo json_encode( $modal_content_html ) ?>,
|
76 |
+
modalHtml =
|
77 |
+
'<div class="fs-modal fs-modal-change-user fs-modal-change-user-<?php echo $fs->get_unique_affix() ?>">'
|
78 |
+
+ ' <div class="fs-modal-dialog">'
|
79 |
+
+ ' <div class="fs-modal-header">'
|
80 |
+
+ ' <h4><?php echo esc_js( $header_title ) ?></h4>'
|
81 |
+
+ ' <a href="!#" class="fs-close"><i class="dashicons dashicons-no" title="<?php echo esc_js( fs_text_x_inline( 'Dismiss', 'close window', 'dismiss', $slug ) ) ?>"></i></a>'
|
82 |
+
+ ' </div>'
|
83 |
+
+ ' <div class="fs-modal-body">'
|
84 |
+
+ ' <div class="fs-modal-panel active">' + modalContentHtml + '</div>'
|
85 |
+
+ ' </div>'
|
86 |
+
+ ' <div class="fs-modal-footer">'
|
87 |
+
+ ' <button class="button button-secondary button-close" tabindex="4"><?php fs_esc_js_echo_inline( 'Cancel', 'cancel', $slug ) ?></button>'
|
88 |
+
+ ' <button class="button button-primary fs-user-change-button" tabindex="3"><?php echo esc_js( $user_change_button_text ) ?></button>'
|
89 |
+
+ ' </div>'
|
90 |
+
+ ' </div>'
|
91 |
+
+ '</div>',
|
92 |
+
$modal = $( modalHtml ),
|
93 |
+
$userChangeButton = $modal.find( '.fs-user-change-button' ),
|
94 |
+
$otherEmailAddressRadio = $modal.find( '#fs_other_email_address_radio' ),
|
95 |
+
$changeUserResultMessage = $modal.find( '.fs-change-user-result-message' ),
|
96 |
+
$otherEmailAddressContainer = $modal.find( '.fs-other-email-address-container' ),
|
97 |
+
$otherEmailAddressTextField = $modal.find( '#fs_other_email_address_text_field' ),
|
98 |
+
$licenseOwners = $modal.find( 'input[type="radio"][name="fs_email_address"]' );
|
99 |
+
|
100 |
+
$modal.appendTo( $( 'body' ) );
|
101 |
+
|
102 |
+
var previousEmailAddress = null;
|
103 |
+
|
104 |
+
function registerEventHandlers() {
|
105 |
+
$licenseOwners.change( function() {
|
106 |
+
var otherEmailAddress = $otherEmailAddressTextField.val().trim(),
|
107 |
+
otherEmailAddressIsSelected = isOtherEmailAddressSelected();
|
108 |
+
|
109 |
+
if ( otherEmailAddressIsSelected ) {
|
110 |
+
$otherEmailAddressTextField.focus();
|
111 |
+
}
|
112 |
+
|
113 |
+
if ( otherEmailAddress.length > 0 || ! otherEmailAddressIsSelected ) {
|
114 |
+
enableUserChangeButton();
|
115 |
+
} else {
|
116 |
+
disableUserChangeButton();
|
117 |
+
}
|
118 |
+
} );
|
119 |
+
|
120 |
+
$otherEmailAddressContainer.click( function () {
|
121 |
+
$otherEmailAddressRadio.click();
|
122 |
+
} );
|
123 |
+
|
124 |
+
// Handle for the "Change User" button on the "Account" page.
|
125 |
+
$( '#fs_change_user' ).click( function ( evt ) {
|
126 |
+
evt.preventDefault();
|
127 |
+
|
128 |
+
showModal( evt );
|
129 |
+
} );
|
130 |
+
|
131 |
+
/**
|
132 |
+
* Disables the "Change User" button when the email address is empty.
|
133 |
+
*/
|
134 |
+
$modal.on( 'keyup paste delete cut', 'input#fs_other_email_address_text_field', function () {
|
135 |
+
setTimeout( function () {
|
136 |
+
var emailAddress = $otherEmailAddressRadio.val().trim();
|
137 |
+
|
138 |
+
if ( emailAddress === previousEmailAddress ) {
|
139 |
+
return;
|
140 |
+
}
|
141 |
+
|
142 |
+
if ( '' === emailAddress ) {
|
143 |
+
disableUserChangeButton();
|
144 |
+
} else {
|
145 |
+
enableUserChangeButton();
|
146 |
+
}
|
147 |
+
|
148 |
+
previousEmailAddress = emailAddress;
|
149 |
+
}, 100 );
|
150 |
+
} ).focus();
|
151 |
+
|
152 |
+
$modal.on( 'input propertychange', 'input#fs_other_email_address_text_field', function () {
|
153 |
+
var emailAddress = $( this ).val().trim();
|
154 |
+
|
155 |
+
/**
|
156 |
+
* If email address is not empty, enable the "Change User" button.
|
157 |
+
*/
|
158 |
+
if ( emailAddress.length > 0 ) {
|
159 |
+
enableUserChangeButton();
|
160 |
+
}
|
161 |
+
} );
|
162 |
+
|
163 |
+
$modal.on( 'blur', 'input#fs_other_email_address_text_field', function( evt ) {
|
164 |
+
var emailAddress = $( this ).val().trim();
|
165 |
+
|
166 |
+
/**
|
167 |
+
* If email address is empty, disable the "Change User" button.
|
168 |
+
*/
|
169 |
+
if ( 0 === emailAddress.length ) {
|
170 |
+
disableUserChangeButton();
|
171 |
+
}
|
172 |
+
} );
|
173 |
+
|
174 |
+
$modal.on( 'click', '.fs-user-change-button', function ( evt ) {
|
175 |
+
evt.preventDefault();
|
176 |
+
|
177 |
+
if ( $( this ).hasClass( 'disabled' ) ) {
|
178 |
+
return;
|
179 |
+
}
|
180 |
+
|
181 |
+
var emailAddress = '',
|
182 |
+
licenseOwnerID = null;
|
183 |
+
|
184 |
+
if ( ! isOtherEmailAddressSelected() ) {
|
185 |
+
licenseOwnerID = $licenseOwners.filter( ':checked' ).val();
|
186 |
+
} else {
|
187 |
+
emailAddress = $otherEmailAddressTextField.val().trim();
|
188 |
+
|
189 |
+
if ( 0 === emailAddress.length ) {
|
190 |
+
return;
|
191 |
+
}
|
192 |
+
}
|
193 |
+
|
194 |
+
disableUserChangeButton();
|
195 |
+
|
196 |
+
$.ajax( {
|
197 |
+
url : ajaxurl,
|
198 |
+
method : 'POST',
|
199 |
+
data : {
|
200 |
+
action : '<?php echo $fs->get_ajax_action( 'change_user' ) ?>',
|
201 |
+
security : '<?php echo $fs->get_ajax_security( 'change_user' ) ?>',
|
202 |
+
email_address: emailAddress,
|
203 |
+
user_id : licenseOwnerID,
|
204 |
+
module_id : '<?php echo $fs->get_id() ?>'
|
205 |
+
},
|
206 |
+
beforeSend: function () {
|
207 |
+
$userChangeButton
|
208 |
+
.text( '<?php fs_esc_js_echo_inline( 'Changing user, please wait', 'changing-user-please-wait', $slug ) ?>...' )
|
209 |
+
.prepend('<i class="fs-ajax-spinner"></i>');
|
210 |
+
|
211 |
+
$(document.body).css({'cursor': 'wait'});
|
212 |
+
},
|
213 |
+
success : function( result ) {
|
214 |
+
if ( result.success ) {
|
215 |
+
// Redirect to the "Account" page.
|
216 |
+
window.location.reload();
|
217 |
+
} else {
|
218 |
+
$(document.body).css({'cursor': 'auto'});
|
219 |
+
|
220 |
+
showError( result.error.message ? result.error.message : result.error );
|
221 |
+
resetUserChangeButton();
|
222 |
+
}
|
223 |
+
},
|
224 |
+
error : function () {
|
225 |
+
$(document.body).css({'cursor': 'auto'});
|
226 |
+
|
227 |
+
showError( '<?php fs_esc_js_echo_inline( 'Unexpected error, try again in 5 minutes. If the error persists, please contact support.', 'unexpected-error', $slug ) ?>' );
|
228 |
+
|
229 |
+
resetUserChangeButton();
|
230 |
+
}
|
231 |
+
} );
|
232 |
+
} );
|
233 |
+
|
234 |
+
// If the user has clicked outside the window, close the modal.
|
235 |
+
$modal.on( 'click', '.fs-close, .button-secondary', function () {
|
236 |
+
closeModal();
|
237 |
+
return false;
|
238 |
+
} );
|
239 |
+
}
|
240 |
+
|
241 |
+
registerEventHandlers();
|
242 |
+
|
243 |
+
/**
|
244 |
+
* @returns {Boolean}
|
245 |
+
*/
|
246 |
+
function isOtherEmailAddressSelected() {
|
247 |
+
return ( 'other' === $licenseOwners.filter( ':checked' ).val() );
|
248 |
+
}
|
249 |
+
|
250 |
+
function showModal() {
|
251 |
+
resetModal();
|
252 |
+
|
253 |
+
// Display the dialog box.
|
254 |
+
$modal.addClass( 'active' );
|
255 |
+
$( 'body' ).addClass( 'has-fs-modal' );
|
256 |
+
|
257 |
+
// Select the first radio button.
|
258 |
+
$licenseOwners.get( 0 ).click();
|
259 |
+
|
260 |
+
$otherEmailAddressTextField.val( '' );
|
261 |
+
}
|
262 |
+
|
263 |
+
function closeModal() {
|
264 |
+
$modal.removeClass( 'active' );
|
265 |
+
$( 'body' ).removeClass( 'has-fs-modal' );
|
266 |
+
}
|
267 |
+
|
268 |
+
function resetUserChangeButton() {
|
269 |
+
enableUserChangeButton();
|
270 |
+
$userChangeButton.text( <?php echo json_encode( $user_change_button_text ) ?> );
|
271 |
+
}
|
272 |
+
|
273 |
+
function resetModal() {
|
274 |
+
hideError();
|
275 |
+
resetUserChangeButton();
|
276 |
+
}
|
277 |
+
|
278 |
+
function enableUserChangeButton() {
|
279 |
+
$userChangeButton.removeClass( 'disabled' );
|
280 |
+
}
|
281 |
+
|
282 |
+
function disableUserChangeButton() {
|
283 |
+
$userChangeButton.addClass( 'disabled' );
|
284 |
+
}
|
285 |
+
|
286 |
+
function hideError() {
|
287 |
+
$changeUserResultMessage.hide();
|
288 |
+
}
|
289 |
+
|
290 |
+
function showError( msg ) {
|
291 |
+
$changeUserResultMessage.find( ' > p' ).html( msg );
|
292 |
+
$changeUserResultMessage.show();
|
293 |
+
}
|
294 |
+
});
|
295 |
+
})( jQuery );
|
296 |
+
</script>
|
freemius/templates/partials/network-activation.php
CHANGED
@@ -1,4 +1,9 @@
|
|
1 |
<?php
|
|
|
|
|
|
|
|
|
|
|
2 |
/**
|
3 |
* @var array $VARS
|
4 |
* @var Freemius $fs
|
@@ -86,4 +91,4 @@
|
|
86 |
</tbody>
|
87 |
</table>
|
88 |
</div>
|
89 |
-
</div>
|
1 |
<?php
|
2 |
+
|
3 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
4 |
+
exit;
|
5 |
+
}
|
6 |
+
|
7 |
/**
|
8 |
* @var array $VARS
|
9 |
* @var Freemius $fs
|
91 |
</tbody>
|
92 |
</table>
|
93 |
</div>
|
94 |
+
</div>
|
freemius/templates/sticky-admin-notice-js.php
CHANGED
@@ -23,7 +23,9 @@
|
|
23 |
|
24 |
notice.fadeOut( 'fast', function() {
|
25 |
var data = {
|
26 |
-
action
|
|
|
|
|
27 |
message_id: id
|
28 |
};
|
29 |
|
@@ -36,4 +38,4 @@
|
|
36 |
});
|
37 |
});
|
38 |
});
|
39 |
-
</script>
|
23 |
|
24 |
notice.fadeOut( 'fast', function() {
|
25 |
var data = {
|
26 |
+
action : 'fs_dismiss_notice_action_' + ajaxActionSuffix,
|
27 |
+
// As such we don't need to use `wp_json_encode` method but using it to follow wp.org guideline.
|
28 |
+
_wpnonce : <?php echo wp_json_encode( wp_create_nonce( 'fs_dismiss_notice_action' ) ); ?>,
|
29 |
message_id: id
|
30 |
};
|
31 |
|
38 |
});
|
39 |
});
|
40 |
});
|
41 |
+
</script>
|
freemius/templates/tabs.php
CHANGED
@@ -1,190 +1,190 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* @package Freemius
|
4 |
-
* @copyright Copyright (c) 2015, Freemius, Inc.
|
5 |
-
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
|
6 |
-
* @since 1.2.2.7
|
7 |
-
*/
|
8 |
-
|
9 |
-
if ( ! defined( 'ABSPATH' ) ) {
|
10 |
-
exit;
|
11 |
-
}
|
12 |
-
|
13 |
-
/**
|
14 |
-
* @var array $VARS
|
15 |
-
* @var Freemius $fs
|
16 |
-
*/
|
17 |
-
$fs = freemius( $VARS['id'] );
|
18 |
-
|
19 |
-
$slug = $fs->get_slug();
|
20 |
-
|
21 |
-
$menu_items = $fs->get_menu_items();
|
22 |
-
|
23 |
-
$show_settings_with_tabs = $fs->show_settings_with_tabs();
|
24 |
-
|
25 |
-
$tabs = array();
|
26 |
-
foreach ( $menu_items as $priority => $items ) {
|
27 |
-
foreach ( $items as $item ) {
|
28 |
-
if ( ! $item['show_submenu'] ) {
|
29 |
-
$submenu_name = ('wp-support-forum' === $item['menu_slug']) ?
|
30 |
-
'support' :
|
31 |
-
$item['menu_slug'];
|
32 |
-
|
33 |
-
if ( 'pricing' === $submenu_name && ! $fs->is_pricing_page_visible() ) {
|
34 |
-
continue;
|
35 |
-
}
|
36 |
-
|
37 |
-
if ( ! $show_settings_with_tabs || ! $fs->is_submenu_item_visible( $submenu_name, true ) ) {
|
38 |
-
continue;
|
39 |
-
}
|
40 |
-
}
|
41 |
-
|
42 |
-
$url = $fs->_get_admin_page_url( $item['menu_slug'] );
|
43 |
-
$title = $item['menu_title'];
|
44 |
-
|
45 |
-
$tab = array(
|
46 |
-
'label' => $title,
|
47 |
-
'href' => $url,
|
48 |
-
'slug' => $item['menu_slug'],
|
49 |
-
);
|
50 |
-
|
51 |
-
if ( 'pricing' === $item['menu_slug'] && $fs->is_in_trial_promotion() ) {
|
52 |
-
$tab['href'] .= '&trial=true';
|
53 |
-
}
|
54 |
-
|
55 |
-
$tabs[] = $tab;
|
56 |
-
}
|
57 |
-
}
|
58 |
-
?>
|
59 |
-
<script type="text/javascript">
|
60 |
-
(function ($) {
|
61 |
-
$(document).ready(function () {
|
62 |
-
var $wrap = $('.wrap');
|
63 |
-
if (0 === $wrap.length) {
|
64 |
-
$wrap = $('<div class="wrap">');
|
65 |
-
$wrap.insertBefore($('#wpbody-content .clear'));
|
66 |
-
}
|
67 |
-
|
68 |
-
var
|
69 |
-
$tabsWrapper = $('.nav-tab-wrapper'),
|
70 |
-
$tabs = $tabsWrapper.find('.nav-tab'),
|
71 |
-
$tab = null;
|
72 |
-
|
73 |
-
if (0 < $tabs.length) {
|
74 |
-
// Tries to set $tab to the first inactive tab.
|
75 |
-
for (var i = 0; i < $tabs.length; i++) {
|
76 |
-
$tab = $($tabs[i]);
|
77 |
-
|
78 |
-
if (!$tab.hasClass('nav-tab-active')) {
|
79 |
-
break;
|
80 |
-
}
|
81 |
-
}
|
82 |
-
}
|
83 |
-
|
84 |
-
<?php if (0 < count( $tabs )) : ?>
|
85 |
-
if (null == $tab) {
|
86 |
-
// No tabs found, therefore, create new tabs section if required.
|
87 |
-
var $h1 = $wrap.find('h1');
|
88 |
-
|
89 |
-
$tabsWrapper = $('<h2 class="nav-tab-wrapper"></h2>');
|
90 |
-
|
91 |
-
if (0 < $h1.length) {
|
92 |
-
$tabsWrapper.insertAfter($h1);
|
93 |
-
} else if (0 < $wrap.length) {
|
94 |
-
$wrap.prepend($tabsWrapper);
|
95 |
-
}
|
96 |
-
|
97 |
-
$tab = $('<a href="#" class="nav-tab"></a>');
|
98 |
-
}
|
99 |
-
|
100 |
-
// Create a clone.
|
101 |
-
$tab = $tab.clone();
|
102 |
-
// Open in current page.
|
103 |
-
$tab.removeAttr('target');
|
104 |
-
$tab.removeClass('nav-tab-active');
|
105 |
-
$tab.addClass('fs-tab');
|
106 |
-
$tab.addClass('<?php echo $fs->get_unique_affix() ?>');
|
107 |
-
|
108 |
-
var $tabClone = null;
|
109 |
-
|
110 |
-
<?php $freemius_context_page = null ?>
|
111 |
-
|
112 |
-
<?php foreach ($tabs as $tab) : ?>
|
113 |
-
<?php $is_support_tab = ( 'wp-support-forum' == $tab['slug'] ) ?>
|
114 |
-
// Add the Freemius tabs.
|
115 |
-
$tabClone = $tab.clone();
|
116 |
-
$tabClone.html(<?php echo json_encode( $tab['label'] ) ?>)
|
117 |
-
.attr('href', '<?php echo $is_support_tab ? $fs->get_support_forum_url() : $tab['href'] ?>')
|
118 |
-
.appendTo($tabsWrapper)
|
119 |
-
// Remove any custom click events.
|
120 |
-
.off('click', '**')
|
121 |
-
.addClass('<?php echo $tab['slug'] ?>')
|
122 |
-
// Avoid tab click triggering parent events.
|
123 |
-
.click(function (e) {
|
124 |
-
e.stopPropagation();
|
125 |
-
});
|
126 |
-
|
127 |
-
<?php if ($is_support_tab) : ?>
|
128 |
-
// Open support in a new tab/page.
|
129 |
-
$tabClone.attr('target', '_blank');
|
130 |
-
<?php endif ?>
|
131 |
-
|
132 |
-
<?php if ($fs->is_admin_page( $tab['slug'] )) : ?>
|
133 |
-
<?php $freemius_context_page = $tab['slug'] ?>
|
134 |
-
// Select the relevant Freemius tab.
|
135 |
-
$tabs.removeClass('nav-tab-active');
|
136 |
-
$tabClone.addClass('nav-tab-active');
|
137 |
-
|
138 |
-
<?php if (in_array( $freemius_context_page, array( 'pricing', 'contact', 'checkout' ) )) : ?>
|
139 |
-
// Add AJAX loader.
|
140 |
-
$tabClone.prepend('<i class="fs-ajax-spinner"></i>');
|
141 |
-
// Hide loader after content fully loaded.
|
142 |
-
$('.wrap i' + 'frame').load(function () {
|
143 |
-
$(".fs-ajax-spinner").hide();
|
144 |
-
});
|
145 |
-
<?php endif ?>
|
146 |
-
|
147 |
-
// Fix URLs that are starting with a hashtag.
|
148 |
-
$tabs.each(function (j, tab) {
|
149 |
-
if (0 === $(tab).attr('href').indexOf('#')) {
|
150 |
-
$(tab).attr('href', '<?php echo esc_js( $fs->main_menu_url() ) ?>' + $(tab).attr('href'));
|
151 |
-
}
|
152 |
-
});
|
153 |
-
<?php endif ?>
|
154 |
-
<?php endforeach ?>
|
155 |
-
|
156 |
-
var selectTab = function ($tab) {
|
157 |
-
$(window).load(function () {
|
158 |
-
$tab.click();
|
159 |
-
|
160 |
-
// Scroll to the top since the browser will auto scroll to the anchor.
|
161 |
-
document.body.scrollTop = 0;
|
162 |
-
document.body.scrollLeft = 0;
|
163 |
-
// window.scrollTo(0,0);
|
164 |
-
});
|
165 |
-
};
|
166 |
-
|
167 |
-
// If the URL is loaded with a hashtag, find the context tab and select it.
|
168 |
-
if (window.location.hash) {
|
169 |
-
for (var j = 0; j < $tabs.length; j++) {
|
170 |
-
if (window.location.hash === $($tabs[j]).attr('href')) {
|
171 |
-
selectTab($($tabs[j]));
|
172 |
-
break;
|
173 |
-
}
|
174 |
-
}
|
175 |
-
}
|
176 |
-
|
177 |
-
<?php if (is_string( $freemius_context_page ) && in_array( $freemius_context_page, array(
|
178 |
-
'pricing',
|
179 |
-
'contact',
|
180 |
-
'checkout'
|
181 |
-
) )) : ?>
|
182 |
-
// Add margin to the upper section of the tabs to give extra space for the HTTPS header.
|
183 |
-
// @todo This code assumes that the wrapper style is fully loaded, if there's a stylesheet that is not loaded via the HTML head, it may cause unpredicted margin-top.
|
184 |
-
var $tabsWrap = $tabsWrapper.parents('.wrap');
|
185 |
-
$tabsWrap.css('marginTop', (parseInt($tabsWrap.css('marginTop'), 10) + 30) + 'px');
|
186 |
-
<?php endif ?>
|
187 |
-
<?php endif ?>
|
188 |
-
});
|
189 |
-
})(jQuery);
|
190 |
</script>
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package Freemius
|
4 |
+
* @copyright Copyright (c) 2015, Freemius, Inc.
|
5 |
+
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
|
6 |
+
* @since 1.2.2.7
|
7 |
+
*/
|
8 |
+
|
9 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
10 |
+
exit;
|
11 |
+
}
|
12 |
+
|
13 |
+
/**
|
14 |
+
* @var array $VARS
|
15 |
+
* @var Freemius $fs
|
16 |
+
*/
|
17 |
+
$fs = freemius( $VARS['id'] );
|
18 |
+
|
19 |
+
$slug = $fs->get_slug();
|
20 |
+
|
21 |
+
$menu_items = $fs->get_menu_items();
|
22 |
+
|
23 |
+
$show_settings_with_tabs = $fs->show_settings_with_tabs();
|
24 |
+
|
25 |
+
$tabs = array();
|
26 |
+
foreach ( $menu_items as $priority => $items ) {
|
27 |
+
foreach ( $items as $item ) {
|
28 |
+
if ( ! $item['show_submenu'] ) {
|
29 |
+
$submenu_name = ('wp-support-forum' === $item['menu_slug']) ?
|
30 |
+
'support' :
|
31 |
+
$item['menu_slug'];
|
32 |
+
|
33 |
+
if ( 'pricing' === $submenu_name && ! $fs->is_pricing_page_visible() ) {
|
34 |
+
continue;
|
35 |
+
}
|
36 |
+
|
37 |
+
if ( ! $show_settings_with_tabs || ! $fs->is_submenu_item_visible( $submenu_name, true ) ) {
|
38 |
+
continue;
|
39 |
+
}
|
40 |
+
}
|
41 |
+
|
42 |
+
$url = $fs->_get_admin_page_url( $item['menu_slug'] );
|
43 |
+
$title = $item['menu_title'];
|
44 |
+
|
45 |
+
$tab = array(
|
46 |
+
'label' => $title,
|
47 |
+
'href' => $url,
|
48 |
+
'slug' => $item['menu_slug'],
|
49 |
+
);
|
50 |
+
|
51 |
+
if ( 'pricing' === $item['menu_slug'] && $fs->is_in_trial_promotion() ) {
|
52 |
+
$tab['href'] .= '&trial=true';
|
53 |
+
}
|
54 |
+
|
55 |
+
$tabs[] = $tab;
|
56 |
+
}
|
57 |
+
}
|
58 |
+
?>
|
59 |
+
<script type="text/javascript">
|
60 |
+
(function ($) {
|
61 |
+
$(document).ready(function () {
|
62 |
+
var $wrap = $('.wrap');
|
63 |
+
if (0 === $wrap.length) {
|
64 |
+
$wrap = $('<div class="wrap">');
|
65 |
+
$wrap.insertBefore($('#wpbody-content .clear'));
|
66 |
+
}
|
67 |
+
|
68 |
+
var
|
69 |
+
$tabsWrapper = $('.nav-tab-wrapper'),
|
70 |
+
$tabs = $tabsWrapper.find('.nav-tab'),
|
71 |
+
$tab = null;
|
72 |
+
|
73 |
+
if (0 < $tabs.length) {
|
74 |
+
// Tries to set $tab to the first inactive tab.
|
75 |
+
for (var i = 0; i < $tabs.length; i++) {
|
76 |
+
$tab = $($tabs[i]);
|
77 |
+
|
78 |
+
if (!$tab.hasClass('nav-tab-active')) {
|
79 |
+
break;
|
80 |
+
}
|
81 |
+
}
|
82 |
+
}
|
83 |
+
|
84 |
+
<?php if (0 < count( $tabs )) : ?>
|
85 |
+
if (null == $tab) {
|
86 |
+
// No tabs found, therefore, create new tabs section if required.
|
87 |
+
var $h1 = $wrap.find('h1');
|
88 |
+
|
89 |
+
$tabsWrapper = $('<h2 class="nav-tab-wrapper"></h2>');
|
90 |
+
|
91 |
+
if (0 < $h1.length) {
|
92 |
+
$tabsWrapper.insertAfter($h1);
|
93 |
+
} else if (0 < $wrap.length) {
|
94 |
+
$wrap.prepend($tabsWrapper);
|
95 |
+
}
|
96 |
+
|
97 |
+
$tab = $('<a href="#" class="nav-tab"></a>');
|
98 |
+
}
|
99 |
+
|
100 |
+
// Create a clone.
|
101 |
+
$tab = $tab.clone();
|
102 |
+
// Open in current page.
|
103 |
+
$tab.removeAttr('target');
|
104 |
+
$tab.removeClass('nav-tab-active');
|
105 |
+
$tab.addClass('fs-tab');
|
106 |
+
$tab.addClass('<?php echo $fs->get_unique_affix() ?>');
|
107 |
+
|
108 |
+
var $tabClone = null;
|
109 |
+
|
110 |
+
<?php $freemius_context_page = null ?>
|
111 |
+
|
112 |
+
<?php foreach ($tabs as $tab) : ?>
|
113 |
+
<?php $is_support_tab = ( 'wp-support-forum' == $tab['slug'] ) ?>
|
114 |
+
// Add the Freemius tabs.
|
115 |
+
$tabClone = $tab.clone();
|
116 |
+
$tabClone.html(<?php echo json_encode( $tab['label'] ) ?>)
|
117 |
+
.attr('href', '<?php echo $is_support_tab ? $fs->get_support_forum_url() : $tab['href'] ?>')
|
118 |
+
.appendTo($tabsWrapper)
|
119 |
+
// Remove any custom click events.
|
120 |
+
.off('click', '**')
|
121 |
+
.addClass('<?php echo $tab['slug'] ?>')
|
122 |
+
// Avoid tab click triggering parent events.
|
123 |
+
.click(function (e) {
|
124 |
+
e.stopPropagation();
|
125 |
+
});
|
126 |
+
|
127 |
+
<?php if ($is_support_tab) : ?>
|
128 |
+
// Open support in a new tab/page.
|
129 |
+
$tabClone.attr('target', '_blank');
|
130 |
+
<?php endif ?>
|
131 |
+
|
132 |
+
<?php if ($fs->is_admin_page( $tab['slug'] )) : ?>
|
133 |
+
<?php $freemius_context_page = $tab['slug'] ?>
|
134 |
+
// Select the relevant Freemius tab.
|
135 |
+
$tabs.removeClass('nav-tab-active');
|
136 |
+
$tabClone.addClass('nav-tab-active');
|
137 |
+
|
138 |
+
<?php if (in_array( $freemius_context_page, array( 'pricing', 'contact', 'checkout' ) )) : ?>
|
139 |
+
// Add AJAX loader.
|
140 |
+
$tabClone.prepend('<i class="fs-ajax-spinner"></i>');
|
141 |
+
// Hide loader after content fully loaded.
|
142 |
+
$('.wrap i' + 'frame').load(function () {
|
143 |
+
$(".fs-ajax-spinner").hide();
|
144 |
+
});
|
145 |
+
<?php endif ?>
|
146 |
+
|
147 |
+
// Fix URLs that are starting with a hashtag.
|
148 |
+
$tabs.each(function (j, tab) {
|
149 |
+
if (0 === $(tab).attr('href').indexOf('#')) {
|
150 |
+
$(tab).attr('href', '<?php echo esc_js( $fs->main_menu_url() ) ?>' + $(tab).attr('href'));
|
151 |
+
}
|
152 |
+
});
|
153 |
+
<?php endif ?>
|
154 |
+
<?php endforeach ?>
|
155 |
+
|
156 |
+
var selectTab = function ($tab) {
|
157 |
+
$(window).load(function () {
|
158 |
+
$tab.click();
|
159 |
+
|
160 |
+
// Scroll to the top since the browser will auto scroll to the anchor.
|
161 |
+
document.body.scrollTop = 0;
|
162 |
+
document.body.scrollLeft = 0;
|
163 |
+
// window.scrollTo(0,0);
|
164 |
+
});
|
165 |
+
};
|
166 |
+
|
167 |
+
// If the URL is loaded with a hashtag, find the context tab and select it.
|
168 |
+
if (window.location.hash) {
|
169 |
+
for (var j = 0; j < $tabs.length; j++) {
|
170 |
+
if (window.location.hash === $($tabs[j]).attr('href')) {
|
171 |
+
selectTab($($tabs[j]));
|
172 |
+
break;
|
173 |
+
}
|
174 |
+
}
|
175 |
+
}
|
176 |
+
|
177 |
+
<?php if (is_string( $freemius_context_page ) && in_array( $freemius_context_page, array(
|
178 |
+
'pricing',
|
179 |
+
'contact',
|
180 |
+
'checkout'
|
181 |
+
) )) : ?>
|
182 |
+
// Add margin to the upper section of the tabs to give extra space for the HTTPS header.
|
183 |
+
// @todo This code assumes that the wrapper style is fully loaded, if there's a stylesheet that is not loaded via the HTML head, it may cause unpredicted margin-top.
|
184 |
+
var $tabsWrap = $tabsWrapper.parents('.wrap');
|
185 |
+
$tabsWrap.css('marginTop', (parseInt($tabsWrap.css('marginTop'), 10) + 30) + 'px');
|
186 |
+
<?php endif ?>
|
187 |
+
<?php endif ?>
|
188 |
+
});
|
189 |
+
})(jQuery);
|
190 |
</script>
|
lib/src/blocks/extend-blocks.js
CHANGED
@@ -169,7 +169,7 @@ addFilter(
|
|
169 |
</PanelRow>
|
170 |
<PanelRow className="simple-sitemap">
|
171 |
<TextControl
|
172 |
-
label="Exclude
|
173 |
help="Enter comma separated list of post ID's"
|
174 |
value={ exclude }
|
175 |
onChange={ ( value ) => {
|
@@ -179,7 +179,7 @@ addFilter(
|
|
179 |
</PanelRow>
|
180 |
<PanelRow className="simple-sitemap">
|
181 |
<TextControl
|
182 |
-
label="Include
|
183 |
help="Enter comma separated list of post ID's"
|
184 |
value={ include }
|
185 |
onChange={ ( value ) => {
|
169 |
</PanelRow>
|
170 |
<PanelRow className="simple-sitemap">
|
171 |
<TextControl
|
172 |
+
label="Exclude post IDs from sitemap"
|
173 |
help="Enter comma separated list of post ID's"
|
174 |
value={ exclude }
|
175 |
onChange={ ( value ) => {
|
179 |
</PanelRow>
|
180 |
<PanelRow className="simple-sitemap">
|
181 |
<TextControl
|
182 |
+
label="Include post IDs in sitemap"
|
183 |
help="Enter comma separated list of post ID's"
|
184 |
value={ include }
|
185 |
onChange={ ( value ) => {
|
readme.txt
CHANGED
@@ -4,7 +4,7 @@ Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_i
|
|
4 |
Tags: seo sitemap, html, sitemap, html sitemap, responsive sitemap
|
5 |
Requires at least: 5.0
|
6 |
Tested up to: 5.9
|
7 |
-
Stable tag: 3.5.
|
8 |
License: GPLv2 or later
|
9 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
10 |
|
@@ -159,6 +159,10 @@ No. This plugin is for generating an HTML sitemap only.
|
|
159 |
|
160 |
== Changelog ==
|
161 |
|
|
|
|
|
|
|
|
|
162 |
= 3.5.4 - November 23, 2021 =
|
163 |
|
164 |
* Tidied up configuration files. Added .eslintignore to the list of files to auto-remove from deployed free/pro plugin versions.
|
4 |
Tags: seo sitemap, html, sitemap, html sitemap, responsive sitemap
|
5 |
Requires at least: 5.0
|
6 |
Tested up to: 5.9
|
7 |
+
Stable tag: 3.5.5
|
8 |
License: GPLv2 or later
|
9 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
10 |
|
159 |
|
160 |
== Changelog ==
|
161 |
|
162 |
+
= 3.5.5 - March 4, 2022 =
|
163 |
+
|
164 |
+
* Security update. Fixes minor issues in the Freemius SDK (that handle licensing and plugin updates).
|
165 |
+
|
166 |
= 3.5.4 - November 23, 2021 =
|
167 |
|
168 |
* Tidied up configuration files. Added .eslintignore to the list of files to auto-remove from deployed free/pro plugin versions.
|
simple-sitemap.php
CHANGED
@@ -4,7 +4,7 @@
|
|
4 |
Plugin Name: Simple Sitemap
|
5 |
Plugin URI: http://wordpress.org/plugins/simple-sitemap/
|
6 |
Description: HTML sitemap to display content as a single linked list of posts, pages, or custom post types. You can even display posts in groups sorted by taxonomy!
|
7 |
-
Version: 3.5.
|
8 |
Author: David Gwyer
|
9 |
Author URI: http://www.wpgoplugins.com
|
10 |
Text Domain: simple-sitemap
|
4 |
Plugin Name: Simple Sitemap
|
5 |
Plugin URI: http://wordpress.org/plugins/simple-sitemap/
|
6 |
Description: HTML sitemap to display content as a single linked list of posts, pages, or custom post types. You can even display posts in groups sorted by taxonomy!
|
7 |
+
Version: 3.5.5
|
8 |
Author: David Gwyer
|
9 |
Author URI: http://www.wpgoplugins.com
|
10 |
Text Domain: simple-sitemap
|