first commit

This commit is contained in:
2023-09-12 21:41:04 +02:00
commit 3361a7f053
13284 changed files with 2116755 additions and 0 deletions

View File

@@ -0,0 +1,356 @@
<?php
namespace OTGS\Installer\Recommendations;
class RecommendationsManager {
/**
* @var \OTGS_Installer_Repositories
*/
private $repositories;
/**
* @var array
*/
private $settings;
/**
* @var array
*/
private $repositoriesForRecommendation = [ 'wpml' ];
/**
* @var Storage
*/
private $noticesStorage;
/**
* RecommendationsManager constructor.
*
* @param \OTGS_Installer_Repositories $repositories
* @param array $settings
* @param Storage $settings
*/
public function __construct( \OTGS_Installer_Repositories $repositories, $settings, Storage $noticesStorage ) {
$this->repositories = $repositories;
$this->settings = $settings;
$this->noticesStorage = $noticesStorage;
}
public function addHooks() {
add_action( 'activated_plugin', [ $this, 'activatedPluginRecommendation' ] );
add_action( 'deactivated_plugin', [ $this, 'deactivatedPluginRecommendation' ] );
add_action( 'wp_ajax_installer_recommendation_success', [ $this, 'recommendationSuccess' ] );
add_filter( 'wpml_installer_get_stored_recommendation_notices', [ $this, 'getRecommendationStoredNotices' ] );
}
public function activatedPluginRecommendation( $plugin ) {
$activatedPluginSlug = dirname( $plugin );
$gluePluginData = $this->getActivatedPluginGlue( $activatedPluginSlug );
if ( $gluePluginData && ! $this->isGluePluginActive( $gluePluginData['glue_plugin_slug'] ) ) {
$this->noticesStorage->save( $activatedPluginSlug, $gluePluginData );
}
}
private function isGluePluginActive( $gluePluginSlug ) {
$pluginData = isset( $this->getInstalledPlugins()[ $gluePluginSlug ] ) ? $this->getInstalledPlugins()[ $gluePluginSlug ] : null;
return $pluginData && $pluginData['is_active'];
}
public function deactivatedPluginRecommendation( $plugin ) {
$deactivatedPluginSlug = dirname( $plugin );
$gluePluginData = $this->getActivatedPluginGlue( $deactivatedPluginSlug );
if ( $gluePluginData ) {
$this->noticesStorage->delete( $deactivatedPluginSlug, $gluePluginData['repository_id'] );
}
}
public function recommendationSuccess() {
if ( array_key_exists( 'nonce', $_POST )
&& array_key_exists( 'pluginData', $_POST )
&& wp_verify_nonce( $_POST['nonce'], 'recommendation_success_nonce' ) ) {
$data = json_decode( base64_decode( sanitize_text_field( $_POST['pluginData'] ) ), true );
$this->noticesStorage->delete( $data['slug'], $data['repository_id'] );
}
}
/**
* @param string $activatedPluginSlug
*
* @return array|null
*/
private function getActivatedPluginGlue( $activatedPluginSlug ) {
$language = $this->getCurrentLanguage();
foreach ( $this->repositoriesForRecommendation as $repositoryId ) {
$downloads = isset( $this->settings[ $repositoryId ]['data']['downloads']['plugins'] )
? $this->settings[ $repositoryId ]['data']['downloads']['plugins'] : [];
foreach ( $downloads as $pluginData ) {
$gluePluginSlug = isset( $pluginData['glue_check_slug'] ) ? $pluginData['glue_check_slug'] : false;
if ( $gluePluginSlug && $activatedPluginSlug === $pluginData['glue_check_slug'] ) {
$repository = $this->repositories->get( $repositoryId );
$subscription = $repository->get_subscription();
if ( ! $subscription ) {
return null;
}
$downloadData = [
'url' => $pluginData['url'],
'slug' => $pluginData['slug'],
'repository_id' => $repositoryId,
];
return [
'repository_id' => $repositoryId,
'glue_check_slug' => $pluginData['glue_check_slug'],
'glue_check_name' => $pluginData['glue_check_name'],
'glue_plugin_name' => $pluginData['name'],
'glue_plugin_slug' => $pluginData['slug'],
'recommendation_notification' => $this->getNotificationForLanguage( $pluginData, $language ),
'download_data' => $downloadData,
];
}
}
}
return null;
}
private function getNotificationForLanguage( $pluginData, $language ) {
$default = isset( $pluginData['recommendation_notification'][ 'en' ] ) ? $pluginData['recommendation_notification'][ 'en' ] : '';
return isset( $pluginData['recommendation_notification'][ $language ] )
? $pluginData['recommendation_notification'][ $language ]
: $default;
}
/**
* @return array
*/
public function getRepositoryPluginsRecommendations() {
$pluginsRecommendations = [];
$pluginsData = [];
$language = $this->getCurrentLanguage();
foreach ( $this->repositoriesForRecommendation as $repositoryId ) {
$repository = $this->repositories->get( $repositoryId );
if ( $this->settings[ $repositoryId ]['data']['downloads']['plugins']
&& $this->settings[ $repositoryId ]['data']['recommendation_sections'] ) {
$downloads = $this->settings[ $repositoryId ]['data']['downloads']['plugins'];
$sections = $this->settings[ $repositoryId ]['data']['recommendation_sections'];
} else {
continue;
}
$subscription = $repository->get_subscription();
if ( ! $subscription ) {
continue;
}
$available_plugins_list = $this->getAvailablePluginsForSubscription( $repository );
$installedPlugins = $this->getInstalledPlugins();
foreach ( $downloads as $pluginData ) {
if ( $pluginData['recommended']
&& in_array( $pluginData['slug'], $available_plugins_list, true )
&& $this->shouldBeDisplayed( $pluginData )
) {
$isInstalled = isset( $installedPlugins[ $pluginData['slug'] ] );
$isActive = $isInstalled && $installedPlugins[ $pluginData['slug'] ]['is_active'];
if ( ! $isInstalled || ! $isActive ) {
$recommendation = $this->preparePluginData(
$language,
$pluginData,
$subscription->get_site_key(),
$repositoryId,
$subscription->get_site_url(),
$isInstalled,
$isActive
);
$sectionPlugin = $this->prepareSectionPlugin(
$language,
$pluginData,
$isInstalled,
$isActive
);
if ($pluginData['download_recommendation_section']) {
$pluginsRecommendations[ $pluginData['download_recommendation_section'] ]['plugins'][ $pluginData['slug'] ] = $sectionPlugin;
$pluginsData[$pluginData['slug']] = $recommendation;
}
}
}
}
foreach ( $pluginsRecommendations as $section => $plugins_recommendation ) {
$pluginsRecommendations[$section]['title'] = $sections[$section][$language]['name'];
$pluginsRecommendations[$section]['order'] = $sections[$section][$language]['order'];
}
}
uasort( $pluginsRecommendations, function ( $a, $b ) {
return (int) $a['order'] - (int) $b['order'];
} );
return ['sections' => $pluginsRecommendations, 'plugins' => $pluginsData];
}
private function getCurrentLanguage() {
global $sitepress;
return $sitepress ? $sitepress->get_admin_language() : 'en';
}
/**
* @param \OTGS_Installer_Repository $repository
*
* @return array
*/
private function getAvailablePluginsForSubscription( \OTGS_Installer_Repository $repository ) {
$product = $repository->get_product_by_subscription_type();
if ( ! $product ) {
$product = $repository->get_product_by_subscription_type_equivalent();
}
return $product->get_plugins();
}
/**
* @return array
*/
private function getInstalledPlugins() {
$installed_plugins = [];
foreach ( get_plugins() as $plugin_id => $plugin_data ) {
$installed_plugins[ dirname( $plugin_id ) ] = [
'is_active' => is_plugin_active( $plugin_id ),
];
}
return $installed_plugins;
}
/**
* @param string $language
* @param array $pluginData
* @param string $siteKey
* @param string $repositoryId
* @param string $siteUrl
* @param bool $isInstalled
* @param bool $isActive
*
* @return array
*/
private function preparePluginData( $language, $pluginData, $siteKey, $repositoryId, $siteUrl, $isInstalled, $isActive ) {
$url = $this->appendSiteKeyToDownloadUrl( $pluginData['url'], $siteKey, $siteUrl );
$downloadData = [
'url' => $url,
'slug' => $pluginData['slug'],
'nonce' => wp_create_nonce( 'install_plugin_' . $url ),
'repository_id' => $repositoryId,
];
return [
'name' => $pluginData['name'],
'short_description' => isset( $pluginData['short_description'][ $language ] ) ? $pluginData['short_description'][ $language ] : '',
'is_installed' => $isInstalled,
'is_active' => $isActive,
'slug' => $pluginData['slug'],
'recommendation_icon_url' => isset( $pluginData['recommendation_icon_url'] ) ? $pluginData['recommendation_icon_url'] : '',
'download_data' => base64_encode( json_encode( $downloadData ) ),
];
}
private function prepareSectionPlugin( $language, $pluginData, $isInstalled, $isActive ) {
return [
'name' => $pluginData['name'],
'is_installed' => $isInstalled,
'is_active' => $isActive,
'slug' => $pluginData['slug'],
'short_description' => isset( $pluginData['short_description'][ $language ] ) ? $pluginData['short_description'][ $language ] : '',
'recommendation_icon_url' => isset( $pluginData['recommendation_icon_url'] ) ? $pluginData['recommendation_icon_url'] : '',
];
}
/**
* @param array $pluginData
*
* @return bool
*/
private function shouldBeDisplayed( $pluginData ) {
$glueCheckType = isset( $pluginData['glue_check_type'] ) ? $pluginData['glue_check_type'] : null;
$glueCheckValue = isset( $pluginData['glue_check_value'] ) ? $pluginData['glue_check_value'] : null;
if ( $glueCheckType && $glueCheckValue ) {
switch ( $glueCheckType ) {
case 'class':
return class_exists( $glueCheckValue );
case 'constant':
return defined( $glueCheckValue );
case 'function':
return function_exists( $glueCheckValue );
default:
return false;
}
}
if ($pluginData['slug'] === 'wpml-translation-management') {
return false;
}
return true;
}
/**
* @param string $url
* @param string $siteKey
* @param string $siteUrl
*
* @return string
*/
private function appendSiteKeyToDownloadUrl( $url, $siteKey, $siteUrl ) {
return add_query_arg(
[
'site_key' => $siteKey,
'site_url' => $siteUrl,
],
$url
);
}
public function getRecommendationStoredNotices( $existingNotices ) {
$storedRecommendations = Storage::getAll();
foreach ($storedRecommendations as $repositoryId => $recommendations) {
$repository = $this->repositories->get( $repositoryId );
$subscription = $repository->get_subscription();
if ( ! $subscription ) {
continue;
}
foreach ($recommendations as $recommendationSlug => $recommendation) {
$url = $this->appendSiteKeyToDownloadUrl( $recommendation['download_data']['url'], $subscription->get_site_key(), $subscription->get_site_url() );
$appendedDownloadData = [
'url' => $url,
'slug' => $recommendation['download_data']['slug'],
'repository_id' => $recommendation['download_data']['repository_id'],
'nonce' => wp_create_nonce( 'install_plugin_' . $url ),
];
$storedRecommendations[$repositoryId][$recommendationSlug]['download_data'] = $appendedDownloadData;
}
}
return array_merge($existingNotices, $storedRecommendations);
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace OTGS\Installer\Recommendations;
class Storage {
const ADMIN_NOTICES_OPTION = 'otgs_installer_recommendations_admin_notices_v2';
/**
* @param string $key
* @param array $data
*/
public static function save( $key, $data ) {
$current = get_option( self::ADMIN_NOTICES_OPTION, [] );
$current[ $data['repository_id'] ][ $key ] = $data;
update_option( self::ADMIN_NOTICES_OPTION, $current, 'no' );
}
public static function delete( $pluginSlug, $repositoryId ) {
$current = get_option( self::ADMIN_NOTICES_OPTION, [] );
unset( $current[ $repositoryId ][ $pluginSlug ] );
update_option( self::ADMIN_NOTICES_OPTION, $current, 'no' );
}
/**
* @return array
*/
public static function getAll() {
return get_option( self::ADMIN_NOTICES_OPTION, [] );
}
}

View File

@@ -0,0 +1,2 @@
import '../../scss/component-settings-reports/styles.scss';

View File

@@ -0,0 +1,5 @@
import DOMPurify from "dompurify";
window.OTGS = window.OTGS || {};
window.OTGS.purify = DOMPurify.sanitize;

View File

@@ -0,0 +1,86 @@
import partial from 'ramda/src/partial';
document.addEventListener('DOMContentLoaded', function () {
const mailFormat = /^\w+([\.+-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/;
const find = (element, selector) => element && element.querySelector(selector);
const main = document.querySelector('.otgs-installer-expired'),
findSection = partial(find, [main]),
questionSection = findSection('.js-question'),
yesSection = findSection('.js-yes-section'),
noSection = findSection('.js-no-section'),
accountSection = findSection('.js-find-account-section'),
checkAccountButton = find(accountSection, '.js-find-account');
const show = elem => elem.style.display = '';
const hide = elem => elem.style.display = 'none';
const showSection = (section, event) => {
event && event.preventDefault();
hide(questionSection);
show(section);
};
const onAccountResponse = response => {
hide(accountSection);
const section = response.data.found ? yesSection : noSection;
const p = find(section, 'p');
p.textContent = p.dataset.alternative;
showSection(section)
};
const onAccountError = () => {
onAccountResponse({data: {found: false}});
};
const createSpinnerAfter = element => {
const spinner = document.createElement('span');
spinner.className = 'spinner';
spinner.style.display = 'inline-block';
spinner.style.visibility = 'visible';
spinner.style.float = 'none';
element.parentNode.insertBefore(spinner, element.nextSibling);
};
const checkAccount = event => {
event.preventDefault();
const input = find(accountSection, 'input');
input.disabled = true;
checkAccountButton.onclick = event => event.preventDefault();
createSpinnerAfter(checkAccountButton);
const data = {
email: input.value,
repository_id: checkAccountButton.dataset.repository,
nonce: checkAccountButton.dataset.nonce,
action: 'find_account'
};
otgs_wp_installer.check_account(data, onAccountResponse, onAccountError);
};
const isValidEmail = email => email.match(mailFormat);
const setCheckAccountState = event => {
if( isValidEmail(event.target.value) ) {
checkAccountButton.setAttribute( 'href', '#');
checkAccountButton.onclick = checkAccount;
checkAccountButton.classList.remove('btn-disabled');
} else {
checkAccountButton.setAttribute( 'href', '');
checkAccountButton.onclick = event => event.preventDefault();
checkAccountButton.classList.add('btn-disabled');
}
};
if (questionSection) {
const findQuestionButton = partial(find, [questionSection]);
findQuestionButton('.js-yes-button').onclick = partial(showSection, [yesSection]);
findQuestionButton('.js-no-button').onclick = partial(showSection, [noSection]);
findQuestionButton('.js-dont-know').onclick = partial(showSection, [accountSection]);
find(accountSection, 'input').oninput = setCheckAccountState;
checkAccountButton.onclick = event => event.preventDefault();
}
});

View File

@@ -0,0 +1,7 @@
import '../../scss/otgs-installer-support/styles.scss';
import testConnection from './testConnection.js';
document.addEventListener('DOMContentLoaded', function(){
const testConnectionObj = new testConnection( document );
testConnectionObj.addEvents();
});

View File

@@ -0,0 +1,67 @@
/*globals ajaxurl*/
class testConnection {
constructor( element ) {
this.element = element;
this.container = element.querySelector('.otgs-installer-support-test-connection');
}
addEvents() {
this.container.querySelector('.check-again').addEventListener('click', () => this.checkConnection());
}
checkConnection() {
const cssClasses = {
successClass: 'dashicons-yes',
failClass: 'dashicons-no-alt',
iconClass: 'dashicons',
spinnerClass: 'spinner',
activeSpinnerClass: 'is-active'
};
this.container.querySelectorAll( '.endpoint' ).forEach((el) => {
const status = el.querySelector('.status'),
nonce = this.element.querySelector('#otgs_installer_test_connection').value;
this.resetStatus(status, cssClasses);
fetch(ajaxurl, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'
},
credentials: 'same-origin',
method: "POST",
body: `nonce=${nonce}&action=otgs_installer_test_connection&repository=${el.getAttribute('data-repository')}&type=${el.getAttribute('data-type')}`
})
.then(response => {
response.json()
.then(res => {
if (res.success) {
this.setSuccessStatus(status, cssClasses);
} else {
this.setFailStatus(status, cssClasses);
}
});
});
});
}
resetStatus(status, cssClasses) {
status.classList.remove(cssClasses.successClass, cssClasses.failClass, cssClasses.iconClass);
status.classList.add(cssClasses.spinnerClass, cssClasses.activeSpinnerClass);
}
setSuccessStatus(status, cssClasses) {
status.classList.add(cssClasses.successClass, cssClasses.iconClass);
status.classList.remove(cssClasses.failClass, cssClasses.spinnerClass, cssClasses.activeSpinnerClass);
}
setFailStatus(status, cssClasses) {
status.classList.remove(cssClasses.successClass, cssClasses.spinnerClass, cssClasses.activeSpinnerClass);
status.classList.add(cssClasses.failClass, cssClasses.iconClass);
}
}
export default testConnection;