woocommerce-multilingual
/
vendor
/
otgs
/
installer
/
src
/
Recommendations
/
RecommendationsManager.php
GluePluginData.php
1 month ago
RecommendationsForInstallerPlugins.php
1 month ago
RecommendationsManager.php
1 month ago
Storage.php
1 month ago
RecommendationsManager.php
439 lines
| 1 | <?php |
| 2 | |
| 3 | namespace OTGS\Installer\Recommendations; |
| 4 | |
| 5 | use OTGS_Installer_Subscription; |
| 6 | use WP_Installer; |
| 7 | |
| 8 | class RecommendationsManager { |
| 9 | private $repositories; |
| 10 | |
| 11 | private $settings; |
| 12 | |
| 13 | private $repositoriesForRecommendation = [ 'wpml' ]; |
| 14 | |
| 15 | private $noticesStorage; |
| 16 | |
| 17 | public function __construct( \OTGS_Installer_Repositories $repositories, Storage $noticesStorage ) { |
| 18 | $this->repositories = $repositories; |
| 19 | $this->noticesStorage = $noticesStorage; |
| 20 | } |
| 21 | |
| 22 | private function settings() { |
| 23 | if ( $this->settings === null ) { |
| 24 | $this->settings = WP_Installer::instance()->get_settings()['repositories']; |
| 25 | } |
| 26 | return $this->settings; |
| 27 | } |
| 28 | |
| 29 | public function addHooks() { |
| 30 | add_action( 'deactivated_plugin', [ $this, 'deactivatedPluginRecommendation' ] ); |
| 31 | add_action( 'wp_ajax_installer_recommendation_success', [ $this, 'recommendationSuccess' ] ); |
| 32 | add_action( 'current_screen', [ $this, 'checkAllInstalledPluginsForRecommendations' ] ); |
| 33 | |
| 34 | add_filter( 'wpml_installer_get_stored_recommendation_notices', [ $this, 'getRecommendationNotices' ] ); |
| 35 | } |
| 36 | |
| 37 | |
| 38 | |
| 39 | public function checkAllInstalledPluginsForRecommendations( $screen = null ) { |
| 40 | if ( ! $screen || $screen->id !== 'plugins' ) { |
| 41 | return; |
| 42 | } |
| 43 | |
| 44 | $installedPlugins = $this->getInstalledPlugins(); |
| 45 | |
| 46 | foreach ( $installedPlugins as $pluginSlug => $pluginData ) { |
| 47 | if ( ! $pluginData['is_active'] ) { |
| 48 | continue; |
| 49 | } |
| 50 | |
| 51 | $pluginInfo = $this->getPluginData( $pluginSlug . '/plugin.php' ); |
| 52 | $gluePluginData = $pluginInfo->getGluePluginData(); |
| 53 | |
| 54 | if ( $gluePluginData |
| 55 | && ! $this->isGluePluginActive( $gluePluginData['glue_plugin_slug'] ) |
| 56 | && $this->noticesStorage->missing( $pluginSlug, $gluePluginData['repository_id'] ) |
| 57 | ) { |
| 58 | $this->noticesStorage->save($pluginSlug, $gluePluginData ); |
| 59 | } |
| 60 | } |
| 61 | } |
| 62 | |
| 63 | private function isGluePluginActive( $gluePluginSlug ) { |
| 64 | $pluginData = isset( $this->getInstalledPlugins()[ $gluePluginSlug ] ) ? $this->getInstalledPlugins()[ $gluePluginSlug ] : null; |
| 65 | |
| 66 | return $pluginData && $pluginData['is_active']; |
| 67 | } |
| 68 | |
| 69 | public function deactivatedPluginRecommendation( $plugin ) { |
| 70 | $deactivatedPlugin = $this->getPluginData( $plugin ); |
| 71 | $gluePluginData = $deactivatedPlugin->getGluePluginData(); |
| 72 | if ( $gluePluginData ) { |
| 73 | $this->noticesStorage->delete( $deactivatedPlugin->getPluginSlug(), $gluePluginData['repository_id'] ); |
| 74 | } |
| 75 | } |
| 76 | |
| 77 | public function recommendationSuccess() { |
| 78 | if ( array_key_exists( 'nonce', $_POST ) |
| 79 | && array_key_exists( 'pluginData', $_POST ) |
| 80 | && wp_verify_nonce( $_POST['nonce'], 'recommendation_success_nonce' ) ) { |
| 81 | $data = json_decode( base64_decode( sanitize_text_field( $_POST['pluginData'] ) ), true ); |
| 82 | $this->noticesStorage->delete( $data['slug'], $data['repository_id'] ); |
| 83 | } |
| 84 | |
| 85 | } |
| 86 | |
| 87 | private function getActivatedPluginGlue( $activatedPluginSlug ) { |
| 88 | $language = $this->getCurrentLanguage(); |
| 89 | |
| 90 | foreach ( $this->repositoriesForRecommendation as $repositoryId ) { |
| 91 | $downloads = isset( $this->settings()[ $repositoryId ]['data']['downloads']['plugins'] ) |
| 92 | ? $this->settings()[ $repositoryId ]['data']['downloads']['plugins'] : []; |
| 93 | foreach ( $downloads as $pluginData ) { |
| 94 | $gluePluginSlug = isset( $pluginData['glue_check_slug'] ) ? $pluginData['glue_check_slug'] : false; |
| 95 | if ( $gluePluginSlug && $activatedPluginSlug === $pluginData['glue_check_slug'] ) { |
| 96 | |
| 97 | return $this->prepareRecommendedPluginData( $repositoryId, $pluginData, $language ); |
| 98 | |
| 99 | } |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | return null; |
| 104 | } |
| 105 | |
| 106 | private function getGluePluginData( $gluePluginSlug, $mappingData ) { |
| 107 | |
| 108 | $language = $this->getCurrentLanguage(); |
| 109 | |
| 110 | foreach ( $this->repositoriesForRecommendation as $repositoryId ) { |
| 111 | $downloads = isset( $this->settings()[ $repositoryId ]['data']['downloads']['plugins'] ) |
| 112 | ? $this->settings()[ $repositoryId ]['data']['downloads']['plugins'] : []; |
| 113 | if ( isset( $downloads[ $gluePluginSlug ] ) ) { |
| 114 | $pluginData = $downloads[ $gluePluginSlug ]; |
| 115 | |
| 116 | return $this->prepareRecommendedPluginData( $repositoryId, $pluginData, $language, $mappingData ); |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | return null; |
| 121 | } |
| 122 | |
| 123 | private function getNotificationForLanguage( $pluginData, $language ) { |
| 124 | $default = isset( $pluginData['recommendation_notification']['en'] ) ? $pluginData['recommendation_notification']['en'] : ''; |
| 125 | |
| 126 | return isset( $pluginData['recommendation_notification'][ $language ] ) |
| 127 | ? $pluginData['recommendation_notification'][ $language ] |
| 128 | : $default; |
| 129 | } |
| 130 | |
| 131 | public function getRepositoryPluginsRecommendations() { |
| 132 | $pluginsRecommendations = []; |
| 133 | $pluginsData = []; |
| 134 | $language = $this->getCurrentLanguage(); |
| 135 | |
| 136 | foreach ( $this->repositoriesForRecommendation as $repositoryId ) { |
| 137 | $repository = $this->repositories->get( $repositoryId ); |
| 138 | |
| 139 | if ( $this->settings()[ $repositoryId ]['data']['downloads']['plugins'] |
| 140 | && $this->settings()[ $repositoryId ]['data']['recommendation_sections'] ) { |
| 141 | $downloads = $this->settings()[ $repositoryId ]['data']['downloads']['plugins']; |
| 142 | $sections = $this->settings()[ $repositoryId ]['data']['recommendation_sections']; |
| 143 | } else { |
| 144 | continue; |
| 145 | } |
| 146 | |
| 147 | $subscription = $repository->get_subscription(); |
| 148 | if ( ! $subscription ) { |
| 149 | continue; |
| 150 | } |
| 151 | |
| 152 | $available_plugins_list = $this->getAvailablePluginsForSubscription( $repository ); |
| 153 | $installedPlugins = $this->getInstalledPlugins(); |
| 154 | |
| 155 | foreach ( $downloads as $pluginData ) { |
| 156 | if ( $pluginData['recommended'] |
| 157 | && in_array( $pluginData['slug'], $available_plugins_list, true ) |
| 158 | && $this->shouldBeDisplayed( $pluginData ) |
| 159 | ) { |
| 160 | $isInstalled = isset( $installedPlugins[ $pluginData['slug'] ] ); |
| 161 | $isActive = $isInstalled && $installedPlugins[ $pluginData['slug'] ]['is_active']; |
| 162 | |
| 163 | if ( ! $isInstalled || ! $isActive ) { |
| 164 | $recommendation = $this->preparePluginData( |
| 165 | $language, |
| 166 | $pluginData, |
| 167 | $subscription->get_site_key(), |
| 168 | $repositoryId, |
| 169 | $subscription->get_site_url(), |
| 170 | $isInstalled, |
| 171 | $isActive |
| 172 | ); |
| 173 | |
| 174 | $sectionPlugin = $this->prepareSectionPlugin( |
| 175 | $language, |
| 176 | $pluginData, |
| 177 | $isInstalled, |
| 178 | $isActive |
| 179 | ); |
| 180 | |
| 181 | if ( |
| 182 | array_key_exists( 'download_recommendation_section', $pluginData ) && |
| 183 | is_string( $pluginData['download_recommendation_section']) && !empty( $pluginData['download_recommendation_section'] ) |
| 184 | ) { |
| 185 | $pluginsRecommendations[ $pluginData['download_recommendation_section'] ]['plugins'][ $pluginData['slug'] ] = $sectionPlugin; |
| 186 | $pluginsData[ $pluginData['slug'] ] = $recommendation; |
| 187 | } |
| 188 | } |
| 189 | } |
| 190 | } |
| 191 | |
| 192 | $recommendationsForInstallerPlugins = $this->prepareRecommendationsForInstalledPlugins( $repositoryId, $subscription, $downloads, $installedPlugins, $pluginsRecommendations, $pluginsData ); |
| 193 | $pluginsData = $recommendationsForInstallerPlugins->getPluginsData(); |
| 194 | |
| 195 | $pluginsRecommendations = $recommendationsForInstallerPlugins->getRecommendations(); |
| 196 | |
| 197 | foreach ( $recommendationsForInstallerPlugins->getRecommendations() as $section => $plugins_recommendation ) { |
| 198 | $pluginsRecommendations[ $section ]['title'] = $sections[ $section ][ $language ]['name'] ?? $sections[ $section ]['en']['name'] ?? ''; |
| 199 | $pluginsRecommendations[ $section ]['order'] = $sections[ $section ][ $language ]['order'] ?? $sections[ $section ]['en']['order'] ?? ''; |
| 200 | |
| 201 | } |
| 202 | } |
| 203 | |
| 204 | uasort( $pluginsRecommendations, function ( $a, $b ) { |
| 205 | return (int) $a['order'] - (int) $b['order']; |
| 206 | } ); |
| 207 | |
| 208 | return [ 'sections' => $pluginsRecommendations, 'plugins' => $pluginsData ]; |
| 209 | } |
| 210 | |
| 211 | private function prepareRecommendationsForInstalledPlugins( $repositoryId, OTGS_Installer_Subscription $subscription, $downloads, $installedPlugins, $pluginsRecommendations, $pluginsData ) { |
| 212 | $language = $this->getCurrentLanguage(); |
| 213 | |
| 214 | if ( isset($this->settings()[ $repositoryId ]['data']['glue_plugins_mapping']) ) { |
| 215 | $gluePluginsMapping = $this->settings()[ $repositoryId ]['data']['glue_plugins_mapping']; |
| 216 | } else { |
| 217 | return new RecommendationsForInstallerPlugins( $pluginsRecommendations, $pluginsData ); |
| 218 | } |
| 219 | |
| 220 | foreach ( $installedPlugins as $pluginSlug => $pluginData ) { |
| 221 | if(isset($pluginData['is_active']) && !$pluginData['is_active']) { |
| 222 | continue; |
| 223 | } |
| 224 | |
| 225 | if ( isset( $gluePluginsMapping[ $pluginSlug ] ) ) { |
| 226 | $gluePluginSlug = $gluePluginsMapping[ $pluginSlug ]['glue_plugin']; |
| 227 | $gluePluginData = $this->getGluePluginData( $gluePluginSlug, $gluePluginsMapping[ $pluginSlug ] ); |
| 228 | |
| 229 | if ( $gluePluginData ) { |
| 230 | $isGlueInstalled = isset( $installedPlugins[ $gluePluginSlug ] ); |
| 231 | $isGlueActive = $isGlueInstalled && $installedPlugins[ $gluePluginSlug ]['is_active']; |
| 232 | |
| 233 | if ( ! $isGlueInstalled || ! $isGlueActive ) { |
| 234 | $recommendation = $this->preparePluginData( |
| 235 | $language, |
| 236 | $downloads[ $gluePluginSlug ], |
| 237 | $subscription->get_site_key(), |
| 238 | $repositoryId, |
| 239 | $subscription->get_site_url(), |
| 240 | $isGlueInstalled, |
| 241 | $isGlueActive |
| 242 | ); |
| 243 | |
| 244 | $sectionPlugin = $this->prepareSectionPlugin( |
| 245 | $language, |
| 246 | $downloads[ $gluePluginSlug ], |
| 247 | $isGlueInstalled, |
| 248 | $isGlueActive |
| 249 | ); |
| 250 | |
| 251 | if ( $downloads[ $gluePluginSlug ] ) { |
| 252 | $pluginsRecommendations[ $downloads[ $gluePluginSlug ]['download_recommendation_section'] ]['plugins'][ $gluePluginSlug ] = $sectionPlugin; |
| 253 | $pluginsData[ $gluePluginSlug ] = $recommendation; |
| 254 | } |
| 255 | } |
| 256 | } |
| 257 | } |
| 258 | } |
| 259 | |
| 260 | return new RecommendationsForInstallerPlugins( $pluginsRecommendations, $pluginsData ); |
| 261 | } |
| 262 | |
| 263 | private function getCurrentLanguage() { |
| 264 | global $sitepress; |
| 265 | |
| 266 | return $sitepress ? $sitepress->get_admin_language() : 'en'; |
| 267 | } |
| 268 | |
| 269 | private function getAvailablePluginsForSubscription( \OTGS_Installer_Repository $repository ) { |
| 270 | $product = $repository->get_product_by_subscription_type(); |
| 271 | if ( ! $product ) { |
| 272 | $product = $repository->get_product_by_subscription_type_equivalent(); |
| 273 | } |
| 274 | |
| 275 | return $product->get_plugins(); |
| 276 | } |
| 277 | |
| 278 | private function getInstalledPlugins() { |
| 279 | $installed_plugins = []; |
| 280 | |
| 281 | foreach ( get_plugins() as $plugin_id => $plugin_data ) { |
| 282 | $installed_plugins[ dirname( $plugin_id ) ] = [ |
| 283 | 'is_active' => is_plugin_active( $plugin_id ), |
| 284 | ]; |
| 285 | } |
| 286 | |
| 287 | return $installed_plugins; |
| 288 | } |
| 289 | |
| 290 | private function preparePluginData( $language, $pluginData, $siteKey, $repositoryId, $siteUrl, $isInstalled, $isActive ) { |
| 291 | $url = $this->appendSiteKeyToDownloadUrl( $pluginData['url'], $siteKey, $siteUrl ); |
| 292 | |
| 293 | $downloadData = [ |
| 294 | 'url' => $url, |
| 295 | 'slug' => $pluginData['slug'], |
| 296 | 'nonce' => wp_create_nonce( 'install_plugin_' . $url ), |
| 297 | 'repository_id' => $repositoryId, |
| 298 | ]; |
| 299 | |
| 300 | return [ |
| 301 | 'name' => $pluginData['name'], |
| 302 | 'short_description' => isset( $pluginData['short_description'][ $language ] ) ? $pluginData['short_description'][ $language ] : '', |
| 303 | 'is_installed' => $isInstalled, |
| 304 | 'is_active' => $isActive, |
| 305 | 'slug' => $pluginData['slug'], |
| 306 | 'recommendation_icon_url' => isset( $pluginData['recommendation_icon_url'] ) ? $pluginData['recommendation_icon_url'] : '', |
| 307 | 'download_data' => base64_encode( (string) json_encode( $downloadData ) ), |
| 308 | ]; |
| 309 | } |
| 310 | |
| 311 | private function prepareSectionPlugin( $language, $pluginData, $isInstalled, $isActive ) { |
| 312 | return [ |
| 313 | 'name' => $pluginData['name'], |
| 314 | 'is_installed' => $isInstalled, |
| 315 | 'is_active' => $isActive, |
| 316 | 'slug' => $pluginData['slug'], |
| 317 | 'short_description' => isset( $pluginData['short_description'][ $language ] ) ? $pluginData['short_description'][ $language ] : '', |
| 318 | 'recommendation_icon_url' => isset( $pluginData['recommendation_icon_url'] ) ? $pluginData['recommendation_icon_url'] : '', |
| 319 | ]; |
| 320 | } |
| 321 | |
| 322 | private function shouldBeDisplayed( $pluginData ) { |
| 323 | $glueCheckType = isset( $pluginData['glue_check_type'] ) ? $pluginData['glue_check_type'] : null; |
| 324 | $glueCheckValue = isset( $pluginData['glue_check_value'] ) ? $pluginData['glue_check_value'] : null; |
| 325 | |
| 326 | if ( $glueCheckType && $glueCheckValue ) { |
| 327 | switch ( $glueCheckType ) { |
| 328 | case 'class': |
| 329 | return class_exists( $glueCheckValue ); |
| 330 | case 'constant': |
| 331 | return defined( $glueCheckValue ); |
| 332 | case 'function': |
| 333 | return function_exists( $glueCheckValue ); |
| 334 | default: |
| 335 | return false; |
| 336 | } |
| 337 | } |
| 338 | |
| 339 | if ( $pluginData['slug'] === 'wpml-translation-management' ) { |
| 340 | return false; |
| 341 | } |
| 342 | |
| 343 | return true; |
| 344 | } |
| 345 | |
| 346 | private function appendSiteKeyToDownloadUrl( $url, $siteKey, $siteUrl ) { |
| 347 | return add_query_arg( |
| 348 | [ |
| 349 | 'site_key' => $siteKey, |
| 350 | 'site_url' => $siteUrl, |
| 351 | ], |
| 352 | $url |
| 353 | ); |
| 354 | } |
| 355 | |
| 356 | public function getRecommendationNotices( $existingNotices ) { |
| 357 | $notices = []; |
| 358 | |
| 359 | foreach ( Storage::getAll() as $repositoryId => $recommendations ) { |
| 360 | $repository = $this->repositories->get( $repositoryId ); |
| 361 | |
| 362 | $subscription = $repository->get_subscription(); |
| 363 | if ( ! $subscription ) { |
| 364 | continue; |
| 365 | } |
| 366 | |
| 367 | foreach ( $recommendations as $recommendationSlug => $recommendation ) { |
| 368 | if ( isset( $recommendation['notice_dismissed'] ) && $recommendation['notice_dismissed'] === true ) { |
| 369 | continue; |
| 370 | } |
| 371 | |
| 372 | if ( ! $this->isGluePluginActive( $recommendation['glue_plugin_slug'] ) ) { |
| 373 | $url = $this->appendSiteKeyToDownloadUrl( $recommendation['download_data']['url'], $subscription->get_site_key(), $subscription->get_site_url() ); |
| 374 | |
| 375 | $appendedDownloadData = [ |
| 376 | 'url' => $url, |
| 377 | 'slug' => $recommendation['download_data']['slug'], |
| 378 | 'repository_id' => $recommendation['download_data']['repository_id'], |
| 379 | 'nonce' => wp_create_nonce( 'install_plugin_' . $url ), |
| 380 | ]; |
| 381 | |
| 382 | $notices[ $repositoryId ][ $recommendationSlug ] = $recommendation; |
| 383 | $notices[ $repositoryId ][ $recommendationSlug ]['download_data'] = $appendedDownloadData; |
| 384 | } else { |
| 385 | Storage::delete( $recommendationSlug, $repositoryId ); |
| 386 | } |
| 387 | } |
| 388 | } |
| 389 | |
| 390 | return array_merge( $existingNotices, $notices ); |
| 391 | } |
| 392 | |
| 393 | private function getPluginData( $plugin ) { |
| 394 | $pluginSlug = dirname( $plugin ); |
| 395 | $gluePluginData = $this->getActivatedPluginGlue( $pluginSlug ); |
| 396 | |
| 397 | if ( ! $gluePluginData ) { |
| 398 | foreach ( $this->repositoriesForRecommendation as $repositoryId ) { |
| 399 | if ( isset( $this->settings()[ $repositoryId ]['data']['glue_plugins_mapping'] ) ) { |
| 400 | $gluePluginsMapping = $this->settings()[ $repositoryId ]['data']['glue_plugins_mapping']; |
| 401 | |
| 402 | if ( isset( $gluePluginsMapping[ $pluginSlug ] ) ) { |
| 403 | $gluePluginSlug = $gluePluginsMapping[ $pluginSlug ]['glue_plugin']; |
| 404 | $gluePluginData = $this->getGluePluginData( $gluePluginSlug, $gluePluginsMapping[ $pluginSlug ] ); |
| 405 | } |
| 406 | } |
| 407 | } |
| 408 | |
| 409 | } |
| 410 | |
| 411 | return new GluePluginData( $pluginSlug, $gluePluginData ); |
| 412 | } |
| 413 | |
| 414 | private function prepareRecommendedPluginData( $repositoryId, $pluginData, $language, $mappingData = null ) { |
| 415 | $repository = $this->repositories->get( $repositoryId ); |
| 416 | $subscription = $repository->get_subscription(); |
| 417 | if ( ! $subscription ) { |
| 418 | return null; |
| 419 | } |
| 420 | |
| 421 | $downloadData = [ |
| 422 | 'url' => $pluginData['url'], |
| 423 | 'slug' => $pluginData['slug'], |
| 424 | 'repository_id' => $repositoryId, |
| 425 | ]; |
| 426 | |
| 427 | |
| 428 | return [ |
| 429 | 'repository_id' => $repositoryId, |
| 430 | 'glue_check_slug' => $mappingData ? $mappingData['glue_check_slug'] : $pluginData['glue_check_slug'], |
| 431 | 'glue_check_name' => $mappingData ? $mappingData['glue_check_name'] : $pluginData['glue_check_name'], |
| 432 | 'glue_plugin_name' => $pluginData['name'], |
| 433 | 'glue_plugin_slug' => $pluginData['slug'], |
| 434 | 'recommendation_notification' => $this->getNotificationForLanguage( $mappingData ?: $pluginData, $language ), |
| 435 | 'download_data' => $downloadData, |
| 436 | ]; |
| 437 | } |
| 438 | } |
| 439 |