| 1 |
<?php |
| 2 |
/* |
| 3 |
* SOFTWARE LICENSE INFORMATION |
| 4 |
* |
| 5 |
* Copyright (c) 2017 Buttonizer, all rights reserved. |
| 6 |
* |
| 7 |
* This file is part of Buttonizer |
| 8 |
* |
| 9 |
* For detailed information regarding to the licensing of |
| 10 |
* this software, please review the license.txt or visit: |
| 11 |
* https://buttonizer.pro/license/ |
| 12 |
*/ |
| 13 |
|
| 14 |
namespace Buttonizer\Migration\Adapters; |
| 15 |
|
| 16 |
use Buttonizer\Core\PluginConfig; |
| 17 |
use Buttonizer\Migration\ConnectionAdopter; |
| 18 |
use Buttonizer\Migration\ModuleLoader; |
| 19 |
|
| 20 |
# No script kiddies |
| 21 |
defined('ABSPATH') or die('No script kiddies please!'); |
| 22 |
|
| 23 |
/** |
| 24 |
* Fits the Chat Button module into Buttonizer without editing it. |
| 25 |
* |
| 26 |
* The module under modules/chat-button/ is a byte-for-byte copy of the plugin |
| 27 |
* it came from, so it still registers a top-level menu and still links to that |
| 28 |
* plugin's own pages. Everything that has to bend lives here instead, using the |
| 29 |
* hooks the module already provides. |
| 30 |
* |
| 31 |
* Keeping the copy untouched is what makes it re-copyable: when the acquired |
| 32 |
* plugin ships a new version, the folder is replaced wholesale and only this |
| 33 |
* file may need attention. |
| 34 |
*/ |
| 35 |
class ChatButton |
| 36 |
{ |
| 37 |
/** |
| 38 |
* Called right after the module is loaded, before any hook has fired. |
| 39 |
*/ |
| 40 |
public static function attach(): void |
| 41 |
{ |
| 42 |
// The module's own bridge to the "new version" points at the acquired |
| 43 |
// plugin's dashboard, which is deactivated by the time we serve this, |
| 44 |
// and on the way there it turns the module off — taking the user's |
| 45 |
// buttons off the site with no way back. Same button, our handler. |
| 46 |
remove_action('admin_init', 'try_new_version'); |
| 47 |
add_action('admin_init', [self::class, 'handleTryNewVersion']); |
| 48 |
|
| 49 |
add_action('admin_menu', [self::class, 'arrangeMenu'], 999); |
| 50 |
|
| 51 |
// The plugin this user came from had no admin bar entry, and the one |
| 52 |
// Buttonizer adds only leads to a dashboard they cannot see. Checked |
| 53 |
// by connection and not by hostMenuHidden(), which is admin-only |
| 54 |
// while the bar also renders on the site. |
| 55 |
add_action('admin_bar_menu', [self::class, 'removeAdminBar'], 101); |
| 56 |
|
| 57 |
if (ModuleLoader::hostMenuHidden()) { |
| 58 |
add_filter('plugin_action_links_' . PluginConfig::baseName(), [self::class, 'pluginActionLinks'], 999); |
| 59 |
} |
| 60 |
} |
| 61 |
|
| 62 |
/** |
| 63 |
* Drop Buttonizer's admin bar node; its children go with it. |
| 64 |
*/ |
| 65 |
public static function removeAdminBar(\WP_Admin_Bar $bar): void |
| 66 |
{ |
| 67 |
if (ConnectionAdopter::isTargetConnected()) { |
| 68 |
return; |
| 69 |
} |
| 70 |
|
| 71 |
$bar->remove_node(PluginConfig::name()); |
| 72 |
} |
| 73 |
|
| 74 |
/** |
| 75 |
* Replace the dashboard links on the plugins screen with the one the |
| 76 |
* acquired plugin used to show. The originals all point at Buttonizer's |
| 77 |
* own page, which redirects to the module anyway, losing the route. |
| 78 |
* |
| 79 |
* @param string[] $actions |
| 80 |
* @return string[] |
| 81 |
*/ |
| 82 |
public static function pluginActionLinks(array $actions): array |
| 83 |
{ |
| 84 |
$own = admin_url('admin.php?page=' . PluginConfig::pageSlug() . '#/'); |
| 85 |
|
| 86 |
$actions = array_filter($actions, function ($link) use ($own) { |
| 87 |
return strpos($link, $own) === false; |
| 88 |
}); |
| 89 |
|
| 90 |
$actions[] = '<a href="' . admin_url('admin.php?page=contact_vr') . '">' . esc_html__('Settings', 'buttonizer-multifunctional-button') . '</a>'; |
| 91 |
|
| 92 |
return $actions; |
| 93 |
} |
| 94 |
|
| 95 |
/** |
| 96 |
* Send "Try now" to Buttonizer instead of to a plugin that is gone. |
| 97 |
* |
| 98 |
* Nothing is switched off on the way: the old buttons keep rendering until |
| 99 |
* the user actually sets Buttonizer up. |
| 100 |
*/ |
| 101 |
public static function handleTryNewVersion(): void |
| 102 |
{ |
| 103 |
if ( |
| 104 |
!current_user_can(is_multisite() ? 'manage_options' : 'activate_plugins') || |
| 105 |
!isset($_GET['page'], $_GET['try_new_version']) || |
| 106 |
$_GET['page'] !== 'contact_vr_setting' || |
| 107 |
$_GET['try_new_version'] !== 'yes' |
| 108 |
) { |
| 109 |
return; |
| 110 |
} |
| 111 |
|
| 112 |
wp_safe_redirect(add_query_arg( |
| 113 |
ModuleLoader::OWN_DASHBOARD_PARAM, |
| 114 |
1, |
| 115 |
admin_url('admin.php?page=' . PluginConfig::pageSlug()) |
| 116 |
)); |
| 117 |
exit; |
| 118 |
} |
| 119 |
|
| 120 |
/** |
| 121 |
* Decide which of the two menus the user sees. |
| 122 |
* |
| 123 |
* Runs late, after the module registered its own top-level entry: |
| 124 |
* |
| 125 |
* - Buttonizer has nothing of its own to show → it steps out of the |
| 126 |
* sidebar and hands its name over to the module's menu, which keeps |
| 127 |
* every screen underneath exactly where it was. |
| 128 |
* - Buttonizer is in use → its menu stays and the module's screens move |
| 129 |
* under it, so there is one entry instead of two. |
| 130 |
*/ |
| 131 |
public static function arrangeMenu(): void |
| 132 |
{ |
| 133 |
if (ModuleLoader::hostMenuHidden()) { |
| 134 |
self::adoptMenu(remove_menu_page(PluginConfig::pageSlug())); |
| 135 |
|
| 136 |
return; |
| 137 |
} |
| 138 |
|
| 139 |
// Re-parent: the page stays registered, so its URL and the tabs the |
| 140 |
// screens navigate with keep working. |
| 141 |
remove_menu_page('contact_vr'); |
| 142 |
|
| 143 |
add_submenu_page( |
| 144 |
PluginConfig::pageSlug(), |
| 145 |
'Button contact VR', |
| 146 |
'Button contact', |
| 147 |
'administrator', |
| 148 |
'contact_vr', |
| 149 |
'pzf_settings_page' |
| 150 |
); |
| 151 |
} |
| 152 |
|
| 153 |
/** |
| 154 |
* Put Buttonizer's name on the menu the module registered. |
| 155 |
* |
| 156 |
* The user was asked to move to Buttonizer and said yes, so a sidebar |
| 157 |
* still headed "Button contact" reads as if nothing happened. The browser |
| 158 |
* tab goes with it, for the same reason. |
| 159 |
* |
| 160 |
* Only the top-level entry changes: WordPress copied the submenu off it |
| 161 |
* back when the module registered its first child, long before this runs, |
| 162 |
* so the links underneath stay worded exactly as the plugin worded them. |
| 163 |
* |
| 164 |
* @param array|false $own Buttonizer's own menu entry, as removed. |
| 165 |
*/ |
| 166 |
private static function adoptMenu($own): void |
| 167 |
{ |
| 168 |
// Nothing was taken away, so there is nothing to take the name from |
| 169 |
if (!is_array($own)) { |
| 170 |
return; |
| 171 |
} |
| 172 |
|
| 173 |
foreach ($GLOBALS['menu'] as $position => $item) { |
| 174 |
if (($item[2] ?? null) !== 'contact_vr') { |
| 175 |
continue; |
| 176 |
} |
| 177 |
|
| 178 |
// Sidebar label, page title, icon |
| 179 |
$GLOBALS['menu'][$position][0] = $own[0]; |
| 180 |
$GLOBALS['menu'][$position][3] = $own[0]; |
| 181 |
$GLOBALS['menu'][$position][6] = $own[6] ?? ''; |
| 182 |
|
| 183 |
break; |
| 184 |
} |
| 185 |
|
| 186 |
self::adoptPageTitle($own[0]); |
| 187 |
} |
| 188 |
|
| 189 |
/** |
| 190 |
* Rename the entry the browser tab actually reads. |
| 191 |
* |
| 192 |
* WordPress treats a top-level page that has children as one of its own |
| 193 |
* children, and takes the title from the copy it made of the parent — not |
| 194 |
* from the parent itself. Renaming the menu alone leaves the old product |
| 195 |
* name in the tab. |
| 196 |
* |
| 197 |
* The label in that same copy is what the sidebar shows underneath, and it |
| 198 |
* stays as the plugin wrote it. |
| 199 |
* |
| 200 |
* @param string $title Name to show in the browser tab. |
| 201 |
*/ |
| 202 |
private static function adoptPageTitle(string $title): void |
| 203 |
{ |
| 204 |
foreach ($GLOBALS['submenu']['contact_vr'] ?? [] as $position => $item) { |
| 205 |
if (($item[2] ?? null) !== 'contact_vr') { |
| 206 |
continue; |
| 207 |
} |
| 208 |
|
| 209 |
$GLOBALS['submenu']['contact_vr'][$position][3] = $title; |
| 210 |
|
| 211 |
return; |
| 212 |
} |
| 213 |
} |
| 214 |
} |
| 215 |
|