function vp_register_hooks()
{
global $versionPressContainer;
/** @var Committer $committer */
$committer = $versionPressContainer->resolve(VersionPressServices::COMMITTER);
/** @var Mirror $mirror */
$mirror = $versionPressContainer->resolve(VersionPressServices::MIRROR);
/** @var DbSchemaInfo $dbSchemaInfo */
$dbSchemaInfo = $versionPressContainer->resolve(VersionPressServices::DB_SCHEMA);
/** @var VpidRepository $vpidRepository */
$vpidRepository = $versionPressContainer->resolve(VersionPressServices::VPID_REPOSITORY);
/** @var WpdbMirrorBridge $wpdbMirrorBridge */
$wpdbMirrorBridge = $versionPressContainer->resolve(VersionPressServices::WPDB_MIRROR_BRIDGE);
/** @var \VersionPress\Database\Database $database */
$database = $versionPressContainer->resolve(VersionPressServices::DATABASE);
/** @var ActionsInfoProvider $actionsInfoProvider */
$actionsInfoProvider = $versionPressContainer->resolve(VersionPressServices::ACTIONSINFO_PROVIDER_ACTIVE_PLUGINS);
if (!function_exists('get_plugins')) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
$plugins = wp_get_active_and_valid_plugins();
foreach ($plugins as $pluginFile) {
$pluginDir = dirname($pluginFile);
$hooksFile = $pluginDir . '/.versionpress/hooks.php';
if (file_exists($hooksFile)) {
require_once $hooksFile;
}
}
add_filter('update_feedback', function () {
touch(ABSPATH . 'versionpress.maintenance');
});
WordPressMissingFunctions::pipeAction('_core_updated_successfully', 'vp_wordpress_updated');
add_action('activated_plugin', function ($pluginFile) {
$plugins = get_plugins();
$pluginName = $plugins[$pluginFile]['Name'];
do_action('vp_plugin_changed', 'activate', $pluginFile, $pluginName);
});
add_action('deactivated_plugin', function ($pluginFile) {
$plugins = get_plugins();
$pluginName = $plugins[$pluginFile]['Name'];
do_action('vp_plugin_changed', 'deactivate', $pluginFile, $pluginName);
});
add_action('upgrader_process_complete', function ($upgrader, $hook_extra) {
if ($hook_extra['type'] === 'theme') {
$themes = isset($hook_extra['bulk']) && $hook_extra['bulk'] === true ? $hook_extra['themes'] : [$upgrader->result['destination_name']];
foreach ($themes as $stylesheet) {
$themeName = wp_get_theme($stylesheet)->get('Name');
if ($themeName === $stylesheet && isset($upgrader->skin->api, $upgrader->skin->api->name)) {
$themeName = $upgrader->skin->api->name;
}
// action can be "install" or "update", see WP_Upgrader and search for `'hook_extra' =>`
$action = $hook_extra['action'];
do_action('vp_theme_changed', $action, $stylesheet, $themeName);
}
}
if (!($hook_extra['type'] === 'plugin' && $hook_extra['action'] === 'update')) {
return;
// handled by different hook
}
if (isset($hook_extra['bulk']) && $hook_extra['bulk'] === true) {
$pluginFiles = $hook_extra['plugins'];
} else {
$pluginFiles = [$hook_extra['plugin']];
}
$plugins = get_plugins();
foreach ($pluginFiles as $pluginFile) {
$pluginName = $plugins[$pluginFile]['Name'];
do_action('vp_plugin_changed', 'update', $pluginFile, $pluginName);
}
}, 10, 2);
add_filter('upgrader_pre_install', function ($_, $hook_extra) {
if (!(isset($hook_extra['type']) && $hook_extra['type'] === 'plugin' && $hook_extra['action'] === 'install')) {
return;
}
$pluginsBeforeInstallation = get_plugins();
$postInstallHook = function ($_, $hook_extra) use($pluginsBeforeInstallation, &$postInstallHook) {
if (!($hook_extra['type'] === 'plugin' && $hook_extra['action'] === 'install')) {
return;
}
wp_cache_delete('plugins', 'plugins');
$pluginsAfterInstallation = get_plugins();
$installedPlugins = array_diff_key($pluginsAfterInstallation, $pluginsBeforeInstallation);
foreach ($installedPlugins as $pluginFile => $plugin) {
do_action('vp_plugin_changed', 'install', $pluginFile, $plugin['Name']);
}
remove_filter('upgrader_post_install', $postInstallHook);
};
add_filter('upgrader_post_install', $postInstallHook, 10, 2);
}, 10, 2);
add_filter('upgrader_pre_download', function ($reply, $_, $upgrader) use($committer) {
if (!isset($upgrader->skin->language_update)) {
return $reply;
}
$languages = get_available_languages();
$postInstallHook = function ($_, $hook_extra) use($committer, $languages, &$postInstallHook) {
if (!isset($hook_extra['language_update_type'])) {
return;
}
$type = $hook_extra['language_update_type'];
$languageCode = $hook_extra['language_update']->language;
$name = $type === "core" ? null : $hook_extra['language_update']->slug;
$action = in_array($languageCode, $languages) ? "update" : "install";
do_action('vp_translation_changed', $action, $languageCode, $type, $name);
remove_filter('upgrader_post_install', $postInstallHook);
};
add_filter('upgrader_post_install', $postInstallHook, 10, 2);
return false;
}, 10, 3);
add_action('switch_theme', function () use($committer) {
if (defined('WP_CLI') && WP_CLI) {
wp_remote_get(admin_url());
//
} else {
$committer->disableCommit();
// the change will be committed on next load
}
});
add_action('after_switch_theme', function () use($committer) {
$theme = wp_get_theme();
$stylesheet = $theme->get_stylesheet();
$themeName = $theme->get('Name');
do_action('vp_theme_changed', 'switch', $stylesheet, $themeName);
});
function _vp_get_language_name_by_code($code)
{
require_once ABSPATH . 'wp-admin/includes/translation-install.php';
$translations = wp_get_available_translations();
return isset($translations[$code]) ? $translations[$code]['native_name'] : 'English (United States)';
}
add_action('add_option_WPLANG', function ($option, $value) use($committer) {
$defaultLanguage = defined('WPLANG') ? WPLANG : '';
if ($value === $defaultLanguage) {
return;
// It's just submitted settings form without changing language
}
do_action('vp_translation_changed', 'activate', $value);
}, 10, 2);
add_action('update_option_WPLANG', function ($oldValue, $newValue) use($committer) {
do_action('vp_translation_changed', 'activate', $newValue);
}, 10, 2);
add_action('wp_update_nav_menu_item', function ($menu_id, $menu_item_db_id) use($committer) {
$key = 'menu-item-' . $menu_item_db_id;
if (defined('DOING_AJAX') && DOING_AJAX && isset($_POST['action']) && $_POST['action'] === 'add-menu-item') {
$committer->postponeCommit($key);
$committer->commit();
} elseif (isset($_POST['action']) && $_POST['action'] === 'update') {
$committer->usePostponedChangeInfos($key);
}
}, 10, 2);
add_action('pre_delete_term', function ($termId, $taxonomy) use($committer, $vpidRepository, $dbSchemaInfo, $actionsInfoProvider) {
$termVpid = $vpidRepository->getVpidForEntity('term', $termId);
$term = get_term($termId, $taxonomy);
$termEntityInfo = $dbSchemaInfo->getEntityInfo('term');
$actionsInfo = $actionsInfoProvider->getActionsInfo('term');
$changeInfo = new EntityChangeInfo($termEntityInfo, $actionsInfo, 'delete', $termVpid, ['VP-Term-Name' => $term->name, 'VP-Term-Taxonomy' => $taxonomy]);
$committer->forceChangeInfo($changeInfo);
}, 10, 2);
add_filter('wp_save_image_editor_file', function ($saved, $filename, $image, $mime_type, $post_id) use($vpidRepository, $committer, $dbSchemaInfo, $actionsInfoProvider) {
$vpid = $vpidRepository->getVpidForEntity('post', $post_id);
$post = get_post($post_id);
$actionsInfo = $actionsInfoProvider->getActionsInfo('post');
$changeInfo = new EntityChangeInfo($dbSchemaInfo->getEntityInfo('post'), $actionsInfo, 'edit', $vpid, ['VP-Post-Type' => $post->post_type, 'VP-Post-Title' => $post->post_title]);
$committer->forceChangeInfo($changeInfo);
}, 10, 5);
add_filter('plugin_install_action_links', function ($links, $plugin) {
$compatibility = CompatibilityChecker::testCompatibilityBySlug($plugin['slug']);
if ($compatibility === CompatibilityResult::COMPATIBLE) {
$cssClass = 'vp-compatible';
$compatibilityAdjective = 'Compatible';
} elseif ($compatibility === CompatibilityResult::INCOMPATIBLE) {
$cssClass = 'vp-incompatible';
// @codingStandardsIgnoreLine
$compatibilityAdjective = '<a href="http://docs.versionpress.net/en/integrations/plugins" target="_blank" title="This plugin is not compatible with VersionPress. These plugins will not work correctly when used together.">Incompatible</a>';
} else {
$cssClass = 'vp-untested';
// @codingStandardsIgnoreLine
$compatibilityAdjective = '<a href="http://docs.versionpress.net/en/integrations/plugins" target="_blank" title="This plugin was not yet tested with VersionPress. Some functionality may not work as intended.">Untested</a>';
}
// @codingStandardsIgnoreLine
$compatibilityNotice = '<span class="vp-compatibility %s" data-plugin-name="%s"><strong>%s</strong> with VersionPress</span>';
$links[] = sprintf($compatibilityNotice, $cssClass, $plugin['name'], $compatibilityAdjective);
return $links;
}, 10, 2);
add_filter('plugin_row_meta', function ($plugin_meta, $plugin_file, $plugin_data, $status) {
if ($status === "dropins") {
return $plugin_meta;
}
$compatibility = CompatibilityChecker::testCompatibilityByPluginFile($plugin_file);
if ($compatibility === CompatibilityResult::COMPATIBLE) {
$cssClass = 'vp-compatible';
$compatibilityAdjective = 'Compatible';
} elseif ($compatibility === CompatibilityResult::INCOMPATIBLE) {
$cssClass = 'vp-incompatible';
// @codingStandardsIgnoreLine
$compatibilityAdjective = '<a href="http://docs.versionpress.net/en/integrations/plugins" target="_blank" title="This plugin is not compatible with VersionPress. These plugins will not work correctly when used together.">Incompatible</a>';
} elseif ($compatibility === CompatibilityResult::UNTESTED) {
$cssClass = 'vp-untested';
// @codingStandardsIgnoreLine
$compatibilityAdjective = '<a href="http://docs.versionpress.net/en/integrations/plugins" target="_blank" title="This plugin was not yet tested with VersionPress. Some functionality may not work as intended.">Untested</a>';
} else {
return $plugin_meta;
}
// @codingStandardsIgnoreLine
$compatibilityNotice = '<span class="vp-compatibility %s" data-plugin-name="%s"><strong>%s</strong> with VersionPress</span>';
$plugin_meta[] = sprintf($compatibilityNotice, $cssClass, $plugin_data['Name'], $compatibilityAdjective);
return $plugin_meta;
}, 10, 4);
add_filter('plugin_action_links', function ($actions, $plugin_file) {
$compatibility = CompatibilityChecker::testCompatibilityByPluginFile($plugin_file);
if (isset($actions['activate'])) {
if ($compatibility === CompatibilityResult::UNTESTED) {
$actions['activate'] = "<span class=\"vp-plugin-list vp-untested\">{$actions['activate']}</span>";
} elseif ($compatibility === CompatibilityResult::INCOMPATIBLE) {
$actions['activate'] = "<span class=\"vp-plugin-list vp-incompatible\">{$actions['activate']}</span>";
}
}
return $actions;
}, 10, 2);
add_action('vp_revert', function ($modifiedFiles) {
// We have to flush the rewrite rules in the next request, because
// in the current one the changed rewrite rules are not yet effective.
set_transient('vp_flush_rewrite_rules', 1);
vp_flush_regenerable_options();
// Update composer dependencies
if (array_search('composer.lock', $modifiedFiles) || array_search('composer.json', $modifiedFiles)) {
putenv('COMPOSER_HOME=' . VP_PROJECT_ROOT . '/vendor/bin/composer');
$originalCwd = getcwd();
chdir(VP_PROJECT_ROOT);
$input = new \Symfony\Component\Console\Input\ArrayInput(['command' => 'install']);
$output = new \Symfony\Component\Console\Output\NullOutput();
$application = new \Composer\Console\Application();
$application->setAutoExit(false);
// prevent `$application->run` method from exitting the script
$application->run($input, $output);
$application->getComposer();
chdir($originalCwd);
}
});
add_action('pre_delete_term', function ($term, $taxonomy) use($database, $wpdbMirrorBridge) {
if (!is_taxonomy_hierarchical($taxonomy)) {
return;
}
$term = get_term($term, $taxonomy);
if (is_wp_error($term)) {
return;
}
$wpdbMirrorBridge->update($database->term_taxonomy, ['parent' => $term->parent], ['parent' => $term->term_id]);
}, 10, 2);
add_action('before_delete_post', function ($postId) use($database, $wpdbMirrorBridge) {
// Fixing bug in WP (#34803) and WP-CLI (#2246);
$post = get_post($postId);
if (!is_wp_error($post) && $post->post_type === 'nav_menu_item') {
$newParent = get_post_meta($post->ID, '_menu_item_menu_item_parent', true);
$wpdbMirrorBridge->update($database->postmeta, ['meta_value' => $newParent], ['meta_key' => '_menu_item_menu_item_parent', 'meta_value' => $post->ID]);
$database->update($database->postmeta, ['meta_value' => $newParent], ['meta_key' => '_menu_item_menu_item_parent', 'meta_value' => $post->ID]);
}
});
//----------------------------------------
// URL and WP-CLI "hooks"
//----------------------------------------
$requestDetector = new \VersionPress\Utils\RequestDetector();
if ($requestDetector->isThemeDeleteRequest()) {
$themeIds = $requestDetector->getThemeStylesheets();
foreach ($themeIds as $stylesheet) {
$themeName = wp_get_theme($stylesheet)->get('Name');
do_action('vp_theme_changed', 'delete', $stylesheet, $themeName);
}
}
if ($requestDetector->isPluginDeleteRequest()) {
$pluginNames = $requestDetector->getPluginNames();
$plugins = get_plugins();
foreach ($pluginNames as $plugin) {
do_action('vp_plugin_changed', 'delete', $plugin, $plugins[$plugin]['Name']);
}
}
if ($requestDetector->isCoreLanguageUninstallRequest()) {
$languageCode = $requestDetector->getLanguageCode();
do_action('vp_translation_changed', 'uninstall', $languageCode);
}
if (basename($_SERVER['PHP_SELF']) === 'theme-editor.php' && isset($_GET['updated']) && $_GET['updated'] === 'true') {
$stylesheet = $_GET['theme'];
$themeName = wp_get_theme($stylesheet)->get('Name');
do_action('vp_theme_changed', 'edit', $stylesheet, $themeName);
}
if (basename($_SERVER['PHP_SELF']) === 'plugin-editor.php' && (isset($_POST['action']) && $_POST['action'] === 'update' || isset($_GET['liveupdate']))) {
$committer->disableCommit();
}
if (basename($_SERVER['PHP_SELF']) === 'plugin-editor.php' && isset($_GET['a']) && $_GET['a'] === 'te') {
if (!function_exists('get_plugins')) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
$editedFile = $_GET['file'];
$editedFilePathParts = preg_split("~[/\\\\]~", $editedFile);
$plugins = get_plugins();
$pluginNames = array_keys($plugins);
$bestRank = 0;
$bestMatch = "";
foreach ($pluginNames as $plugin) {
$rank = 0;
$pluginPathParts = preg_split("~[/\\\\]~", $plugin);
$maxEqualParts = min(count($editedFilePathParts), count($pluginPathParts));
for ($part = 0; $part < $maxEqualParts; $part++) {
if ($editedFilePathParts[$part] !== $pluginPathParts[$part]) {
break;
}
$rank += 1;
}
if ($rank > $bestRank) {
$bestRank = $rank;
$bestMatch = $plugin;
}
}
do_action('vp_plugin_changed', 'edit', $bestMatch, $plugins[$bestMatch]['Name']);
}
add_filter('cron_schedules', function ($schedules) use($dbSchemaInfo) {
$intervals = $dbSchemaInfo->getIntervalsForFrequentlyWrittenEntities();
foreach ($intervals as $interval) {
if (isset($schedules[$interval])) {
continue;
}
$seconds = strtotime($interval, 0);
$schedules[$interval] = ['interval' => $seconds, 'display' => $interval];
}
return $schedules;
});
$r = $dbSchemaInfo->getRulesForFrequentlyWrittenEntities();
$groupedByInterval = [];
foreach ($r as $entityName => $rules) {
foreach ($rules as $rule) {
$groupedByInterval[$rule['interval']][$entityName][] = $rule;
}
}
foreach ($groupedByInterval as $interval => $allRulesInInterval) {
$actionName = "vp_commit_frequently_written_entities_{$interval}";
if (!wp_next_scheduled($actionName)) {
wp_schedule_event(time(), $interval, $actionName);
}
add_action($actionName, function () use($allRulesInInterval) {
vp_save_frequently_written_entities($allRulesInInterval);
});
}
if (!function_exists('get_plugins')) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
register_shutdown_function([$committer, 'commit']);
}