initial commit

This commit is contained in:
2024-04-29 13:12:44 +05:45
commit 34887303c5
19300 changed files with 5268802 additions and 0 deletions

View File

@@ -0,0 +1,177 @@
<?php
/**
* Admin.
*/
namespace Extendify\Library;
use Extendify\Library\App;
use Extendify\Library\User;
use Extendify\Library\SiteSettings;
/**
* This class handles any file loading for the admin area.
*/
class Admin
{
/**
* The instance
*
* @var $instance
*/
public static $instance = null;
/**
* Adds various actions to set up the page
*
* @return self|void
*/
public function __construct()
{
if (self::$instance) {
return self::$instance;
}
self::$instance = $this;
$this->loadScripts();
\add_filter('plugin_action_links_' . EXTENDIFY_PLUGIN_BASENAME, [ $this, 'pluginActionLinks' ]);
}
/**
* Adds action links to the plugin list table
*
* @param array $links An array of plugin action links.
* @return array An array of plugin action links.
*/
public function pluginActionLinks($links)
{
$theme = get_option('template');
$label = esc_html__('Upgrade', 'extendify');
$links['upgrade'] = sprintf('<a href="%1$s" target="_blank"><b>%2$s</b></a>', "https://extendify.com/pricing?utm_source=extendify-plugin&utm_medium=wp-dash&utm_campaign=action-link&utm_content=$label&utm_term=$theme", $label);
return $links;
}
/**
* Adds scripts to the admin
*
* @return void
*/
public function loadScripts()
{
\add_action(
'admin_enqueue_scripts',
function ($hook) {
if (!current_user_can(App::$requiredCapability)) {
return;
}
if (!$this->checkItsGutenbergPost($hook)) {
return;
}
if (!$this->isLibraryEnabled()) {
return;
}
$this->addScopedScriptsAndStyles();
}
);
}
/**
* Makes sure we are on the correct page
*
* @param string $hook - An optional hook provided by WP to identify the page.
* @return boolean
*/
public function checkItsGutenbergPost($hook = '')
{
if (isset($GLOBALS['typenow']) && \use_block_editor_for_post_type($GLOBALS['typenow'])) {
return $hook && in_array($hook, ['post.php', 'post-new.php'], true);
}
return false;
}
/**
* Adds various JS scripts
*
* @return void
*/
public function addScopedScriptsAndStyles()
{
$version = App::$environment === 'PRODUCTION' ? App::$version : uniqid();
\wp_register_script(
App::$slug . '-scripts',
EXTENDIFY_BASE_URL . 'public/build/extendify.js',
[
'wp-i18n',
'wp-components',
'wp-element',
'wp-editor',
],
$version,
true
);
\wp_localize_script(
App::$slug . '-scripts',
'extendifyData',
[
'root' => \esc_url_raw(rest_url(APP::$slug . '/' . APP::$apiVersion)),
'nonce' => \wp_create_nonce('wp_rest'),
'user' => json_decode(User::data('extendifysdk_user_data'), true),
'sitesettings' => json_decode(SiteSettings::data()),
'sdk_partner' => \esc_attr(APP::$sdkPartner),
'asset_path' => \esc_url(EXTENDIFY_URL . 'public/assets'),
'standalone' => \esc_attr(APP::$standalone),
'devbuild' => \esc_attr(APP::$environment === 'DEVELOPMENT'),
]
);
\wp_enqueue_script(App::$slug . '-scripts');
\wp_set_script_translations(App::$slug . '-scripts', 'extendify');
// Inline the library styles to keep them out of the iframe live preview.
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
$css = file_get_contents(EXTENDIFY_PATH . 'public/build/extendify.css');
\wp_register_style(App::$slug, false, [], $version);
\wp_enqueue_style(App::$slug);
\wp_add_inline_style(App::$slug, $css);
}
/**
* Check if current user is Admin
*
* @return Boolean
*/
private function isAdmin()
{
if (\is_multisite()) {
return \is_super_admin();
}
return in_array('administrator', \wp_get_current_user()->roles, true);
}
/**
* Check if scripts should add
*
* @return Boolean
*/
public function isLibraryEnabled()
{
$settings = json_decode(SiteSettings::data());
// If it's disabled, only show it for admins.
if (isset($settings->state) && (isset($settings->state->enabled)) && !$settings->state->enabled) {
return $this->isAdmin();
}
return true;
}
}

View File

@@ -0,0 +1,137 @@
<?php
/**
* API router
*/
namespace Extendify\Library;
use Extendify\Library\App;
use Extendify\Library\Http;
/**
* Simple router for the REST Endpoints
*/
class ApiRouter extends \WP_REST_Controller
{
/**
* The class instance.
*
* @var $instance
*/
protected static $instance = null;
/**
* The capablity required for access.
*
* @var $capability
*/
protected $capability;
/**
* The constructor
*/
public function __construct()
{
$this->capability = App::$requiredCapability;
add_filter(
'rest_request_before_callbacks',
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($response, $handler, $request) {
// Add the request to our helper class.
if ($request->get_header('x_extendify')) {
Http::init($request);
}
return $response;
},
10,
3
);
}
/**
* Check the authorization of the request
*
* @return boolean
*/
public function checkPermission()
{
// Check for the nonce on the server (used by WP REST).
if (isset($_SERVER['HTTP_X_WP_NONCE']) && \wp_verify_nonce(sanitize_text_field(wp_unslash($_SERVER['HTTP_X_WP_NONCE'])), 'wp_rest')) {
return \current_user_can($this->capability);
}
return false;
}
/**
* Register dynamic routes
*
* @param string $namespace - The api name space.
* @param string $endpoint - The endpoint.
* @param function $callback - The callback to run.
*
* @return void
*/
public function getHandler($namespace, $endpoint, $callback)
{
\register_rest_route(
$namespace,
$endpoint,
[
'methods' => 'GET',
'callback' => $callback,
'permission_callback' => [
$this,
'checkPermission',
],
]
);
}
/**
* The post handler
*
* @param string $namespace - The api name space.
* @param string $endpoint - The endpoint.
* @param string $callback - The callback to run.
*
* @return void
*/
public function postHandler($namespace, $endpoint, $callback)
{
\register_rest_route(
$namespace,
$endpoint,
[
'methods' => 'POST',
'callback' => $callback,
'permission_callback' => [
$this,
'checkPermission',
],
]
);
}
/**
* The caller
*
* @param string $name - The name of the method to call.
* @param array $arguments - The arguments to pass in.
*
* @return mixed
*/
public static function __callStatic($name, array $arguments)
{
$name = "{$name}Handler";
if (is_null(self::$instance)) {
self::$instance = new static();
}
$r = self::$instance;
return $r->$name(APP::$slug . '/' . APP::$apiVersion, ...$arguments);
}
}

View File

@@ -0,0 +1,111 @@
<?php
/**
* The App details file
*/
namespace Extendify\Library;
use Extendify\Library\Plugin;
/**
* Controller for handling various app data
*/
class App
{
/**
* Plugin name
*
* @var string
*/
public static $name = '';
/**
* Plugin slug
*
* @var string
*/
public static $slug = '';
/**
* Plugin version
*
* @var string
*/
public static $version = '';
/**
* Plugin API REST version
*
* @var string
*/
public static $apiVersion = 'v1';
/**
* Whether this is the standalone plugin
*
* @var boolean
*/
public static $standalone;
/**
* Plugin environment
*
* @var string
*/
public static $environment = '';
/**
* The partner plugin/theme
*
* @var string
*/
public static $sdkPartner = 'standalone';
/**
* Host plugin
*
* @var string
*/
public static $requiredCapability = 'upload_files';
/**
* Plugin config
*
* @var array
*/
public static $config = [];
/**
* Process the readme file to get version and name
*
* @return void
*/
public function __construct()
{
if (isset($GLOBALS['extendify_sdk_partner']) && $GLOBALS['extendify_sdk_partner']) {
self::$sdkPartner = $GLOBALS['extendify_sdk_partner'];
}
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
$readme = file_get_contents(dirname(__DIR__) . '/readme.txt');
preg_match('/=== (.+) ===/', $readme, $matches);
self::$name = $matches[1];
self::$slug = strtolower(trim(preg_replace('/[^A-Za-z0-9-]+/', '-', self::$name), '-'));
preg_match('/Stable tag: ([0-9.:]+)/', $readme, $matches);
self::$version = $matches[1];
// An easy way to check if we are in dev mode is to look for a dev specific file.
$isDev = is_readable(EXTENDIFY_PATH . 'node_modules') || is_readable(EXTENDIFY_PATH . '.devbuild');
self::$environment = $isDev ? 'DEVELOPMENT' : 'PRODUCTION';
self::$standalone = self::$sdkPartner === 'standalone';
// Add the config.
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
$config = file_get_contents(dirname(__DIR__) . '/config.json');
self::$config = json_decode($config, true);
}
}

View File

@@ -0,0 +1,43 @@
<?php
/**
* Controls Auth
*/
namespace Extendify\Library\Controllers;
use Extendify\Library\Http;
if (!defined('ABSPATH')) {
die('No direct access.');
}
/**
* The controller for dealing registration and authentication
*/
class AuthController
{
/**
* Login a user to extendify - it will return the API key
*
* @param \WP_REST_Request $request - The request.
* @return WP_REST_Response|WP_Error
*/
public static function login($request)
{
$response = Http::post('/login', $request->get_params());
return new \WP_REST_Response($response);
}
/**
* Handle registration - It will return the API key.
*
* @param \WP_REST_Request $request - The request.
* @return WP_REST_Response|WP_Error
*/
public static function register($request)
{
$response = Http::post('/register', $request->get_params());
return new \WP_REST_Response($response);
}
}

View File

@@ -0,0 +1,30 @@
<?php
/**
* Controls Http requests
*/
namespace Extendify\Library\Controllers;
use Extendify\Library\Http;
if (!defined('ABSPATH')) {
die('No direct access.');
}
/**
* The controller for sending little bits of info
*/
class MetaController
{
/**
* Send data about a specific topic
*
* @param \WP_REST_Request $request - The request.
* @return WP_REST_Response|WP_Error
*/
public static function getAll($request)
{
$response = Http::get('/meta-data', $request->get_params());
return new \WP_REST_Response($response);
}
}

View File

@@ -0,0 +1,30 @@
<?php
/**
* Controls Http requests
*/
namespace Extendify\Library\Controllers;
use Extendify\Library\Http;
if (!defined('ABSPATH')) {
die('No direct access.');
}
/**
* The controller for sending little bits of info
*/
class PingController
{
/**
* Send data about a specific topic
*
* @param \WP_REST_Request $request - The request.
* @return WP_REST_Response|WP_Error
*/
public static function ping($request)
{
$response = Http::post('/ping', $request->get_params());
return new \WP_REST_Response($response);
}
}

View File

@@ -0,0 +1,67 @@
<?php
/**
* Controls Plugins
*/
namespace Extendify\Library\Controllers;
use Extendify\Library\Plugin;
if (!defined('ABSPATH')) {
die('No direct access.');
}
/**
* The controller for plugin dependency checking, etc
*/
class PluginController
{
/**
* Return all plugins
*
* @return array
*/
public static function index()
{
if (! function_exists('get_plugins')) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
return \get_plugins();
}
/**
* List active plugins
*
* @return array
*/
public static function active()
{
return \get_option('active_plugins');
}
/**
* Install plugins
*
* @param \WP_REST_Request $request - The request.
* @return bool|WP_Error
*/
public static function install($request)
{
if (!\current_user_can('activate_plugins')) {
return new \WP_Error('not_allowed', \__('You are not allowed to activate plugins on this site.', 'extendify'));
}
$requiredPlugins = json_decode($request->get_param('plugins'), true);
foreach ($requiredPlugins as $plugin) {
$status = Plugin::install_and_activate_plugin($plugin);
if (\is_wp_error($status)) {
// Return first error encountered.
return $status;
}
}
return true;
}
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* Controls User info
*/
namespace Extendify\Library\Controllers;
use Extendify\Library\SiteSettings;
if (!defined('ABSPATH')) {
die('No direct access.');
}
/**
* The controller for managing Extendify SiteSettings.
*/
class SiteSettingsController
{
/**
* Return Current SiteSettings meta data
*
* @return array
*/
public static function show()
{
return new \WP_REST_Response(SiteSettings::data());
}
/**
* Persist the data
*
* @param \WP_REST_Request $request - The request.
* @return array
*/
public static function store($request)
{
$settingsData = json_decode($request->get_param('data'), true);
\update_option(SiteSettings::key(), $settingsData, true);
return new \WP_REST_Response(SiteSettings::data());
}
}

View File

@@ -0,0 +1,29 @@
<?php
/**
* Controls Taxonomies
*/
namespace Extendify\Library\Controllers;
use Extendify\Library\Http;
if (!defined('ABSPATH')) {
die('No direct access.');
}
/**
* The controller for dealing with taxonomies
*/
class TaxonomyController
{
/**
* Return all taxonomies
*
* @return WP_REST_Response|WP_Error
*/
public static function index()
{
$response = Http::get('/taxonomies', []);
return new \WP_REST_Response($response);
}
}

View File

@@ -0,0 +1,43 @@
<?php
/**
* Controls Http requests
*/
namespace Extendify\Library\Controllers;
use Extendify\Library\Http;
if (!defined('ABSPATH')) {
die('No direct access.');
}
/**
* The controller for dealing with templates
*/
class TemplateController
{
/**
* Return info about a template
*
* @param \WP_REST_Request $request - The request.
* @return WP_REST_Response|WP_Error
*/
public static function index($request)
{
$response = Http::post('/templates', $request->get_params());
return new \WP_REST_Response($response);
}
/**
* Send data about a specific template
*
* @param \WP_REST_Request $request - The request.
* @return WP_REST_Response|WP_Error
*/
public static function ping($request)
{
$response = Http::post('/templates', $request->get_params());
return new \WP_REST_Response($response);
}
}

View File

@@ -0,0 +1,91 @@
<?php
/**
* Controls User info
*/
namespace Extendify\Library\Controllers;
use Extendify\Library\Http;
use Extendify\Library\User;
if (!defined('ABSPATH')) {
die('No direct access.');
}
/**
* The controller for managing user data like API keys, etc
*/
class UserController
{
/**
* Return the current user state
*
* @return array
*/
public static function show()
{
return new \WP_REST_Response(User::state());
}
/**
* Return meta info about the current user
*
* @param \WP_REST_Request $request - The request.
* @return array
*/
public static function meta($request)
{
$key = \sanitize_text_field(\wp_unslash($request->get_param('key')));
return new \WP_REST_Response(User::data($key));
}
/**
* Persist the data
*
* @param \WP_REST_Request $request - The request.
* @return array
*/
public static function store($request)
{
$userData = json_decode($request->get_param('data'), true);
// Keep this key for historical reasons.
\update_user_meta(\get_current_user_id(), 'extendifysdk_user_data', $userData);
return new \WP_REST_Response(User::state());
}
/**
* Delete the data
*
* @return array
*/
public static function delete()
{
\delete_user_meta(\get_current_user_id(), 'extendifysdk_user_data');
return new \WP_REST_Response(User::state());
}
/**
* Sign up the user to the mailing list.
*
* @param \WP_REST_Request $request - The request.
* @return WP_REST_Response|WP_Error
*/
public static function mailingList($request)
{
$response = Http::post('/register-mailing-list', $request->get_params());
return new \WP_REST_Response($response);
}
/**
* Get the max imports
*
* @return WP_REST_Response|WP_Error
*/
public static function maxImports()
{
$response = Http::get('/max-free-imports');
return new \WP_REST_Response($response);
}
}

View File

@@ -0,0 +1,62 @@
<?php
/**
* Manage any frontend related tasks here.
*/
namespace Extendify\Library;
use Extendify\Library\App;
/**
* This class handles any file loading for the frontend of the site.
*/
class Frontend
{
/**
* The instance
*
* @var $instance
*/
public static $instance = null;
/**
* Adds various actions to set up the page
*
* @return self|void
*/
public function __construct()
{
if (self::$instance) {
return self::$instance;
}
self::$instance = $this;
$this->loadScripts();
}
/**
* Adds scripts and styles to every page is enabled
*
* @return void
*/
public function loadScripts()
{
\add_action(
'wp_enqueue_scripts',
function () {
// TODO: Determine a way to conditionally load assets (https://github.com/extendify/company-product/issues/72).
$this->addStylesheets();
}
);
}
/**
* Adds stylesheets as needed
*
* @return void
*/
public function addStylesheets()
{
}
}

View File

@@ -0,0 +1,151 @@
<?php
/**
* Helper class for making http requests
*/
namespace Extendify\Library;
use Extendify\Library\App;
use Extendify\Library\User;
/**
* Controller for http communication
*/
class Http
{
/**
* The api endpoint
*
* @var string
*/
public $baseUrl = '';
/**
* Request data sent to the server
*
* @var array
*/
public $data = [];
/**
* Any headers required
*
* @var array
*/
public $headers = [];
/**
* The class instance.
*
* @var $instance
*/
protected static $instance = null;
/**
* Set up the base object to send with every request
*
* @param \WP_REST_Request $request - The request.
* @return void
*/
public function __construct($request)
{
// Redundant, but extra prodection!
if (!\wp_verify_nonce(sanitize_text_field(wp_unslash($request->get_header('x_wp_nonce'))), 'wp_rest')) {
return;
}
// Some special cases for development.
$this->baseUrl = $request->get_header('x_extendify_dev_mode') !== 'false' ? App::$config['api']['dev'] : App::$config['api']['live'];
$this->baseUrl = $request->get_header('x_extendify_local_mode') !== 'false' ? App::$config['api']['local'] : $this->baseUrl;
$this->data = [
'wp_language' => \get_locale(),
'wp_theme' => \get_option('template'),
'mode' => App::$environment,
'uuid' => User::data('uuid'),
'library_version' => App::$version,
'wp_active_plugins' => $request->get_method() === 'POST' ? \get_option('active_plugins') : [],
'sdk_partner' => App::$sdkPartner,
];
$this->headers = [
'Accept' => 'application/json',
'referer' => $request->get_header('referer'),
'user_agent' => $request->get_header('user_agent'),
];
}
/**
* Register dynamic routes
*
* @param string $endpoint - The endpoint.
* @param array $data - The data to include.
* @param array $headers - The headers to include.
*
* @return array
*/
public function getHandler($endpoint, $data = [], $headers = [])
{
$url = \esc_url_raw(
\add_query_arg(
\urlencode_deep(\urldecode_deep(array_merge($this->data, $data))),
$this->baseUrl . $endpoint
)
);
$response = \wp_remote_get(
$url,
[
'headers' => array_merge($this->headers, $headers),
]
);
$responseBody = \wp_remote_retrieve_body($response);
return json_decode($responseBody, true);
}
/**
* Register dynamic routes
*
* @param string $endpoint - The endpoint.
* @param array $data - The arguments to include.
* @param array $headers - The headers to include.
*
* @return array
*/
public function postHandler($endpoint, $data = [], $headers = [])
{
$response = \wp_remote_post(
$this->baseUrl . $endpoint,
[
'headers' => array_merge($this->headers, $headers),
'body' => array_merge($this->data, $data),
]
);
$responseBody = \wp_remote_retrieve_body($response);
return json_decode($responseBody, true);
}
/**
* The caller
*
* @param string $name - The name of the method to call.
* @param array $arguments - The arguments to pass in.
*
* @return mixed
*/
public static function __callStatic($name, array $arguments)
{
if ($name === 'init') {
self::$instance = new static($arguments[0]);
return;
}
$name = "{$name}Handler";
$r = self::$instance;
return $r->$name(...$arguments);
}
}

View File

@@ -0,0 +1,318 @@
<?php
// phpcs:ignoreFile
// This class was copied from JetPack (mostly)
// so will be a bit of work to refactor
/**
* Manage plugin dependencies
*/
namespace Extendify\Library;
class Plugin
{
/**
* Install and activate a plugin.
*
* @since 5.8.0
*
* @param string $slug Plugin slug.
*
* @return bool|WP_Error True if installation succeeded, error object otherwise.
*/
public static function install_and_activate_plugin($slug)
{
$plugin_id = self::get_plugin_id_by_slug($slug);
if (! $plugin_id) {
$installed = self::install_plugin($slug);
if (is_wp_error($installed)) {
return $installed;
}
$plugin_id = self::get_plugin_id_by_slug($slug);
} elseif (is_plugin_active($plugin_id)) {
return true; // Already installed and active.
}
if (! current_user_can('activate_plugins')) {
return new \WP_Error('not_allowed', __('You are not allowed to activate plugins on this site.', 'jetpack'));
}
$activated = activate_plugin($plugin_id);
if (is_wp_error($activated)) {
return $activated;
}
return true;
}
/**
* Install a plugin.
*
* @since 5.8.0
*
* @param string $slug Plugin slug.
*
* @return bool|WP_Error True if installation succeeded, error object otherwise.
*/
public static function install_plugin($slug)
{
if (is_multisite() && ! current_user_can('manage_network')) {
return new \WP_Error('not_allowed', __('You are not allowed to install plugins on this site.', 'jetpack'));
}
$skin = new PluginUpgraderSkin();
$upgrader = new \Plugin_Upgrader($skin);
$zip_url = self::generate_wordpress_org_plugin_download_link($slug);
$result = $upgrader->install($zip_url);
if (is_wp_error($result)) {
return $result;
}
$plugin = self::get_plugin_id_by_slug($slug);
$error_code = 'install_error';
if (! $plugin) {
$error = __('There was an error installing your plugin', 'jetpack');
}
if (! $result) {
$error_code = $upgrader->skin->get_main_error_code();
$message = $upgrader->skin->get_main_error_message();
$error = $message ? $message : __('An unknown error occurred during installation', 'jetpack');
}
if (! empty($error)) {
if ('download_failed' === $error_code) {
// For backwards compatibility: versions prior to 3.9 would return no_package instead of download_failed.
$error_code = 'no_package';
}
return new \WP_Error($error_code, $error, 400);
}
return (array) $upgrader->skin->get_upgrade_messages();
}
/**
* Get WordPress.org zip download link from a plugin slug
*
* @param string $plugin_slug Plugin slug.
*/
protected static function generate_wordpress_org_plugin_download_link($plugin_slug)
{
return "https://downloads.wordpress.org/plugin/$plugin_slug.latest-stable.zip";
}
/**
* Get the plugin ID (composed of the plugin slug and the name of the main plugin file) from a plugin slug.
*
* @param string $slug Plugin slug.
*/
public static function get_plugin_id_by_slug($slug)
{
// Check if get_plugins() function exists. This is required on the front end of the
// site, since it is in a file that is normally only loaded in the admin.
if (! function_exists('get_plugins')) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
/** This filter is documented in wp-admin/includes/class-wp-plugins-list-table.php */
$plugins = apply_filters('all_plugins', get_plugins());
if (! is_array($plugins)) {
return false;
}
foreach ($plugins as $plugin_file => $plugin_data) {
if (self::get_slug_from_file_path($plugin_file) === $slug) {
return $plugin_file;
}
}
return false;
}
/**
* Get the plugin slug from the plugin ID (composed of the plugin slug and the name of the main plugin file)
*
* @param string $plugin_file Plugin file (ID -- e.g. hello-dolly/hello.php).
*/
protected static function get_slug_from_file_path($plugin_file)
{
// Similar to get_plugin_slug() method.
$slug = dirname($plugin_file);
if ('.' === $slug) {
$slug = preg_replace('/(.+)\.php$/', '$1', $plugin_file);
}
return $slug;
}
/**
* Get the activation status for a plugin.
*
* @since 8.9.0
*
* @param string $plugin_file The plugin file to check.
* @return string Either 'network-active', 'active' or 'inactive'.
*/
public static function get_plugin_status($plugin_file)
{
if (is_plugin_active_for_network($plugin_file)) {
return 'network-active';
}
if (is_plugin_active($plugin_file)) {
return 'active';
}
return 'inactive';
}
/**
* Returns a list of all plugins in the site.
*
* @since 8.9.0
* @uses get_plugins()
*
* @return array
*/
public static function get_plugins()
{
if (! function_exists('get_plugins')) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
/** This filter is documented in wp-admin/includes/class-wp-plugins-list-table.php */
$plugins = apply_filters('all_plugins', get_plugins());
if (is_array($plugins) && ! empty($plugins)) {
foreach ($plugins as $plugin_slug => $plugin_data) {
$plugins[ $plugin_slug ]['active'] = in_array(
self::get_plugin_status($plugin_slug),
array( 'active', 'network-active' ),
true
);
}
return $plugins;
}
return array();
}
}
include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
include_once ABSPATH . 'wp-admin/includes/file.php';
/**
* Allows us to capture that the site doesn't have proper file system access.
* In order to update the plugin.
*/
class PluginUpgraderSkin extends \Automatic_Upgrader_Skin
{
/**
* Stores the last error key;
**/
protected $main_error_code = 'install_error';
/**
* Stores the last error message.
**/
protected $main_error_message = 'An unknown error occurred during installation';
/**
* Overwrites the set_upgrader to be able to tell if we e ven have the ability to write to the files.
*
* @param WP_Upgrader $upgrader
*
*/
public function set_upgrader(&$upgrader)
{
parent::set_upgrader($upgrader);
// Check if we even have permission to.
$result = $upgrader->fs_connect(array( WP_CONTENT_DIR, WP_PLUGIN_DIR ));
if (! $result) {
// set the string here since they are not available just yet
$upgrader->generic_strings();
$this->feedback('fs_unavailable');
}
}
/**
* Overwrites the error function
*/
public function error($error)
{
if (is_wp_error($error)) {
$this->feedback($error);
}
}
private function set_main_error_code($code)
{
// Don't set the process_failed as code since it is not that helpful unless we don't have one already set.
$this->main_error_code = ($code === 'process_failed' && $this->main_error_code ? $this->main_error_code : $code);
}
private function set_main_error_message($message, $code)
{
// Don't set the process_failed as message since it is not that helpful unless we don't have one already set.
$this->main_error_message = ($code === 'process_failed' && $this->main_error_code ? $this->main_error_code : $message);
}
public function get_main_error_code()
{
return $this->main_error_code;
}
public function get_main_error_message()
{
return $this->main_error_message;
}
/**
* Overwrites the feedback function
*
* @param string|array|WP_Error $data Data.
* @param mixed ...$args Optional text replacements.
*/
public function feedback($data, ...$args)
{
$current_error = null;
if (is_wp_error($data)) {
$this->set_main_error_code($data->get_error_code());
$string = $data->get_error_message();
} elseif (is_array($data)) {
return;
} else {
$string = $data;
}
if (! empty($this->upgrader->strings[$string])) {
$this->set_main_error_code($string);
$current_error = $string;
$string = $this->upgrader->strings[$string];
}
if (strpos($string, '%') !== false) {
if (! empty($args)) {
$string = vsprintf($string, $args);
}
}
$string = trim($string);
$string = wp_kses(
$string,
array(
'a' => array(
'href' => true
),
'br' => true,
'em' => true,
'strong' => true,
)
);
$this->set_main_error_message($string, $current_error);
$this->messages[] = $string;
}
}

View File

@@ -0,0 +1,247 @@
<?php
/**
* Manage any shared assets that load within the editor and the front-end.
*/
namespace Extendify\Library;
use Extendify\Library\App;
/**
* This class handles assets that load within the editor and the front-end.
*/
class Shared
{
/**
* Adds various actions to set up the page
*
* @return self|void
*/
public function __construct()
{
\add_action(
'wp_enqueue_scripts',
function () {
$this->themeCompatInlineStyles();
}
);
\add_action(
'admin_init',
function () {
$this->themeCompatInlineStyles();
}
);
}
/**
* Inline styles to be applied for compatible themes
*
* @return void
*/
// phpcs:ignore
public function themeCompatInlineStyles()
{
$css = '';
$theme = get_option('template');
if ($theme === 'kadence') {
$css = 'body, .editor-styles-wrapper {
--wp--preset--color--background: var(--global-palette8);
--wp--preset--color--foreground: var(--global-palette4);
--wp--preset--color--primary: var(--global-palette1);
--wp--preset--color--secondary: var(--global-palette2);
--wp--preset--color--tertiary: var(--global-palette7);
--wp--custom--spacing--large: clamp(var(--global-sm-spacing), 5vw, var(--global-xxl-spacing));
--wp--preset--font-size--large: var(--h2FontSize);
--wp--preset--font-size--huge: var(--h1FontSize);
}';
}
if ($theme === 'neve') {
$css = 'body, .editor-styles-wrapper {
--wp--preset--color--background: var(--nv-site-bg);
--wp--preset--color--foreground: var(--nv-text-color);
--wp--preset--color--primary: var(--nv-primary-accent);
--wp--preset--color--secondary: var(--nv-secondary-accent);
--wp--preset--color--tertiary: var(--nv-light-bg);
--wp--custom--spacing--large: clamp(15px, 5vw, 80px);
--wp--preset--font-size--large: var(--h2FontSize);
--wp--preset--font-size--huge: var(--h1FontSize);
}';
}
if ($theme === 'blocksy') {
$css = 'body, .editor-styles-wrapper {
--wp--preset--color--background: var(--paletteColor7);
--wp--preset--color--foreground: var(--color);
--wp--preset--color--primary: var(--paletteColor1);
--wp--preset--color--secondary: var(--paletteColor4);
}';
}
if ($theme === 'go') {
$css = 'body, .editor-styles-wrapper {
--wp--preset--color--background: var(--go--color--background);
--wp--preset--color--foreground: var(--go--color--text);
}';
}
if ($theme === 'astra') {
$css = 'body, .editor-styles-wrapper {
--wp--preset--color--background: #ffffff;
--wp--preset--color--foreground: var(--ast-global-color-2);
--wp--preset--color--primary: var(--ast-global-color-0);
--wp--preset--color--secondary: var(--ast-global-color-2);
}';
}
if ($theme === 'oceanwp') {
$background = get_theme_mod('ocean_background_color', '#ffffff');
$primary = get_theme_mod('ocean_primary_color', '#13aff0');
$secondary = get_theme_mod('ocean_hover_primary_color', '#0b7cac');
$gap = get_theme_mod('ocean_separate_content_padding', '30px');
$css = 'body, .editor-styles-wrapper {
--wp--preset--color--background: ' . $background . ';
--wp--preset--color--foreground: #1B1B1B;
--wp--preset--color--primary: ' . $primary . ';
--wp--preset--color--secondary: ' . $secondary . ';
--wp--style--block-gap: ' . $gap . ';
--wp--custom--spacing--large: clamp(2rem, 7vw, 8rem);
}';
}
if ($theme === 'generatepress') {
$settings = (array) get_option('generate_settings', []);
if (! array_key_exists('background_color', $settings)) {
$background = '#f7f8f9';
} else {
$background = $settings['background_color'];
}
if (! array_key_exists('text_color', $settings)) {
$foreground = '#222222';
} else {
$foreground = $settings['text_color'];
}
if (! array_key_exists('link_color', $settings)) {
$primary = '#1e73be';
} else {
$primary = $settings['link_color'];
}
if (! array_key_exists('link_color', $settings)) {
$primary = '#1e73be';
} else {
$primary = $settings['link_color'];
}
$css = 'body, .editor-styles-wrapper {
--wp--preset--color--background: ' . $background . ';
--wp--preset--color--foreground: ' . $foreground . ';
--wp--preset--color--primary: ' . $primary . ';
--wp--preset--color--secondary: #636363;
--wp--style--block-gap: 3rem;
--wp--custom--spacing--large: clamp(2rem, 7vw, 8rem);
--responsive--alignwide-width: 1120px;
}';
}//end if
if ($theme === 'twentytwentytwo') {
$css = 'body, .editor-styles-wrapper {
--extendify--spacing--large: clamp(2rem,8vw,8rem);
}';
}
if ($theme === 'twentytwentyone') {
$css = 'body, .editor-styles-wrapper {
--wp--preset--color--background: var(--global--color-background);
--wp--preset--color--foreground: var(--global--color-primary);
--wp--preset--color--primary: var(--global--color-gray);
--wp--preset--color--secondary: #464b56;
--wp--preset--color--tertiary: var(--global--color-light-gray);
--wp--style--block-gap: var(--global--spacing-unit);
--wp--preset--font-size--large: 2.5rem;
--wp--preset--font-size--huge: var(--global--font-size-xxl);
}
.has-foreground-background-color,
.has-primary-background-color,
.has-secondary-background-color {
--local--color-primary: var(--wp--preset--color--background);
--local--color-background: var(--wp--preset--color--primary);
}';
}
if ($theme === 'twentytwenty') {
$background = sanitize_hex_color_no_hash(get_theme_mod('background_color', 'f5efe0'));
$primary = get_theme_mod(
'accent_accessible_colors',
[
'content' => [ 'accent' => '#cd2653' ],
]
);
$primary = $primary['content']['accent'];
$css = 'body, .editor-styles-wrapper {
--wp--preset--color--background: #' . $background . ';
--wp--preset--color--foreground: #000;
--wp--preset--color--primary: ' . $primary . ';
--wp--preset--color--secondary: #69603e;
--wp--style--block-gap: 3rem;
--wp--custom--spacing--large: clamp(2rem, 7vw, 8rem);
--responsive--alignwide-width: 120rem;
}';
}//end if
if ($theme === 'twentynineteen') {
/**
* Use the color from Twenty Nineteen's customizer value.
*/
$primary = 199;
if (get_theme_mod('primary_color', 'default') !== 'default') {
$primary = absint(get_theme_mod('primary_color_hue', 199));
}
/**
* Filters Twenty Nineteen default saturation level.
*
* @since Twenty Nineteen 1.0
*
* @param int $saturation Color saturation level.
*/
// phpcs:ignore
$saturation = apply_filters('twentynineteen_custom_colors_saturation', 100);
$saturation = absint($saturation) . '%';
/**
* Filters Twenty Nineteen default lightness level.
*
* @since Twenty Nineteen 1.0
*
* @param int $lightness Color lightness level.
*/
// phpcs:ignore
$lightness = apply_filters('twentynineteen_custom_colors_lightness', 33);
$lightness = absint($lightness) . '%';
$css = 'body, .editor-styles-wrapper {
--wp--preset--color--foreground: #111;
--wp--preset--color--primary: hsl( ' . $primary . ', ' . $saturation . ', ' . $lightness . ' );
--wp--preset--color--secondary: #767676;
--wp--preset--color--tertiary: #f7f7f7;
}';
}//end if
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
$content = file_get_contents(EXTENDIFY_PATH . 'public/build/extendify-utilities.css');
$version = App::$environment === 'PRODUCTION' ? App::$version : uniqid();
\wp_register_style(App::$slug . '-utilities', false, [], $version);
\wp_enqueue_style(App::$slug . '-utilities');
\wp_add_inline_style(App::$slug . '-utilities', $content . $css);
// Adds inline to the live preview.
\wp_add_inline_style('wp-components', $content . $css);
}
}

View File

@@ -0,0 +1,72 @@
<?php
/**
* Helper class for interacting with the user
*/
namespace Extendify\Library;
/**
* Helper class for interacting with the user
*/
class SiteSettings
{
/**
* SiteSettings option_name - For historical reasons do not change.
*
* @var string
*/
protected $key = 'extendifysdk_sitesettings';
/**
* SiteSettings default value
*
* @var Json
*/
protected $default = '{"state":{"enabled":true}}';
/**
* The class instance.
*
* @var $instance
*/
protected static $instance = null;
/**
* Returns Setting
* Use it like Setting::data()
*
* @return mixed - Setting Data
*/
private function dataHandler()
{
return \get_option($this->key, $this->default);
}
/**
* Returns Setting Key
* Use it like Setting::key()
*
* @return string - Setting key
*/
private function keyHandler()
{
return $this->key;
}
/**
* Use it like Setting::method() e.g. Setting::data()
*
* @param string $name - The name of the method to call.
* @param array $arguments - The arguments to pass in.
*
* @return mixed
*/
public static function __callStatic($name, array $arguments)
{
$name = "{$name}Handler";
self::$instance = new static();
$r = self::$instance;
return $r->$name(...$arguments);
}
}

View File

@@ -0,0 +1,154 @@
<?php
/**
* Helper class for interacting with the user
*/
namespace Extendify\Library;
use Extendify\Library\App;
/**
* Helper class for interacting with the user
*/
class User
{
/**
* User unique, anonymous identifier
*
* @var string
*/
public $uuid = '';
/**
* A WP user
*
* @var \WP_User
*/
protected $user = null;
/**
* The DB key for scoping. For historical reasons do not change
*
* @var string
*/
protected $key = 'extendifysdk_';
/**
* The class instance.
*
* @var $instance
*/
protected static $instance = null;
/**
* Set up the user
*
* @param WP_User $user - A WP User object.
* @return void
*/
public function __construct($user)
{
$this->user = $user;
}
/**
* Return the user ID
*
* @return void
*/
private function setupUuid()
{
$uuid = \get_user_meta($this->user->ID, $this->key . 'uuid', true);
if (!$uuid) {
$id = \wp_hash(\wp_json_encode($this->user));
\update_user_meta($this->user->ID, $this->key . 'uuid', $id);
}
$this->uuid = $uuid;
}
/**
* Returns data about the user
* Use it like User::data('ID') to get the user id
*
* @param string $arguments - Right now a string of arguments, like ID.
* @return mixed - Data about the user.
*/
private function dataHandler($arguments)
{
// Right now assume a single argument, but could expand to multiple.
if (isset($this->user->$arguments)) {
return $this->user->$arguments;
}
return \get_user_meta($this->user->ID, $this->key . $arguments, true);
}
/**
* Returns the application state for he current user
* Use it like User::data('ID') to get the user id
*
* @return string - JSON representation of the current state
*/
private function stateHandler()
{
$state = \get_user_meta($this->user->ID, $this->key . 'user_data');
// Add some state boilerplate code for the first load.
if (!isset($state[0])) {
$state[0] = '{}';
}
$userData = json_decode($state[0], true);
if (!isset($userData['version'])) {
$userData['version'] = 0;
}
// This will reset the allowed max imports to 0 once a week which will force the library to re-check.
if (!get_transient('extendify_import_max_check_' . $this->user->ID)) {
set_transient('extendify_import_max_check_' . $this->user->ID, time(), strtotime('1 week', 0));
$userData['state']['allowedImports'] = 0;
}
// Similiar to above, this will give the user free imports once a month just for logging in.
if (!get_transient('extendify_free_extra_imports_check_' . $this->user->ID)) {
set_transient('extendify_free_extra_imports_check_' . $this->user->ID, time(), strtotime('first day of next month', 0));
$userData['state']['runningImports'] = 0;
}
if (!isset($userData['state']['sdkPartner']) || !$userData['state']['sdkPartner']) {
$userData['state']['sdkPartner'] = App::$sdkPartner;
}
$userData['state']['uuid'] = self::data('uuid');
$userData['state']['canInstallPlugins'] = \current_user_can('install_plugins');
$userData['state']['canActivatePlugins'] = \current_user_can('activate_plugins');
$userData['state']['isAdmin'] = \current_user_can('create_users');
return \wp_json_encode($userData);
}
/**
* Allows to dynamically setup the user with uuid
* Use it like User::data('ID') to get the user id
*
* @param string $name - The name of the method to call.
* @param array $arguments - The arguments to pass in.
*
* @return mixed
*/
public static function __callStatic($name, array $arguments)
{
$name = "{$name}Handler";
if (is_null(self::$instance)) {
require_once ABSPATH . 'wp-includes/pluggable.php';
self::$instance = new static(\wp_get_current_user());
$r = self::$instance;
$r->setupUuid();
}
$r = self::$instance;
return $r->$name(...$arguments);
}
}