[ 'name' => __('Lightify', 'crownify'), 'tagline' => __('Caching, asset & media optimization and Core Web Vitals monitoring.', 'crownify'), 'constant' => 'LIGHTIFY_VERSION', ], 'generatify' => [ 'name' => __('Generatify', 'crownify'), 'tagline' => __('AI suite with chatbot, content generation, agents and automations.', 'crownify'), 'constant' => 'GENERATIFY_VERSION', ], 'progressify' => [ 'name' => __('Progressify', 'crownify'), 'tagline' => __('PWA features: install prompts, offline access and push notifications.', 'crownify'), 'constant' => 'PROGRESSIFY_VERSION', ], 'snapshotify' => [ 'name' => __('Snapshotify', 'crownify'), 'tagline' => __('Backup, restore and migration with scheduled snapshots and cloud storage.', 'crownify'), 'constant' => 'SNAPSHOTIFY_VERSION', ], ]; } public function recordInstallTime() { if (!get_option(self::INSTALL_TIME_OPTION)) { update_option(self::INSTALL_TIME_OPTION, time()); } } public function renderNotices() { if (!current_user_can('manage_options')) { return; } if (Utils::hasActivePro()) { return; } $showReview = $this->shouldShow(self::REVIEW_DISMISS_OPTION, self::REVIEW_DELAY, self::REVIEW_DISMISS_PERIOD); $showUpgrade = $this->shouldShow(self::UPGRADE_DISMISS_OPTION, self::UPGRADE_DELAY, self::UPGRADE_DISMISS_PERIOD); $stackPlugins = []; $showStack = false; if ($this->canInstallPlugins() && $this->shouldShow(self::STACK_DISMISS_OPTION, self::STACK_DELAY, self::STACK_DISMISS_PERIOD)) { $stackPlugins = $this->getStackPluginsWithStatus(); foreach ($stackPlugins as $plugin) { if ($plugin['state'] !== 'active') { $showStack = true; break; } } } if (!$showReview && !$showUpgrade && !$showStack) { return; } $this->renderStyles(); $this->renderDismissScript(); if ($showReview) { $this->renderReviewNotice(); } if ($showUpgrade) { $this->renderUpgradeNotice(); } if ($showStack) { $this->renderStackNotice($stackPlugins); } } private function canInstallPlugins() { if (Utils::hasActivePro()) { return false; } return current_user_can('install_plugins') && current_user_can('activate_plugins'); } private function getStackPluginsWithStatus() { $plugins = $this->getStackPlugins(); foreach ($plugins as $slug => $plugin) { $status = $this->getPluginStatus($slug, $plugin['constant']); $plugins[$slug]['state'] = $status['state']; $plugins[$slug]['file'] = $status['file']; } return $plugins; } /** * Detect whether a stack plugin is active, installed but inactive, or missing. * Matches both the free folder and the Freemius premium folder (slug-pro / slug-premium). */ private function getPluginStatus($slug, $constant) { if (defined($constant)) { return ['state' => 'active', 'file' => '']; } if (!function_exists('get_plugins')) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } $folders = [$slug, "{$slug}-pro", "{$slug}-premium"]; foreach (get_plugins() as $file => $data) { if (!in_array(strtolower(dirname($file)), $folders, true)) { continue; } return [ 'state' => is_plugin_active($file) ? 'active' : 'inactive', 'file' => $file, ]; } return ['state' => 'missing', 'file' => '']; } private function shouldShow($dismissOption, $delay, $dismissPeriod) { $installedAt = (int) get_option(self::INSTALL_TIME_OPTION, 0); if (!$installedAt || time() - $installedAt < $delay) { return false; } $dismissedAt = (int) get_option($dismissOption, 0); if ($dismissedAt && time() - $dismissedAt < $dismissPeriod) { return false; } return true; } private function renderReviewNotice() { $nonce = wp_create_nonce('rankify_dismiss_notice'); $logoUrl = plugins_url('assets/media/icons/icon-256x256.png', RANKIFY_FILE); ?>

__('Not installed', 'crownify'), 'inactive' => __('Installed, inactive', 'crownify'), 'active' => __('Active', 'crownify'), ]; ?>

    $plugin): ?>

renderStackScript(); } private function renderStackScript() { $strings = [ 'working' => __('Installing…', 'crownify'), /* translators: 1: plugin name, 2: current item number, 3: total number of items. */ 'progress' => __('Installing %1$s (%2$d of %3$d)…', 'crownify'), 'done' => __('Active', 'crownify'), 'failed' => __('Failed', 'crownify'), 'allDone' => __('All done! Your DaftPlug stack is ready. Reloading…', 'crownify'), 'someFailed' => __('Some plugins could not be installed. You can install them manually from Plugins → Add New.', 'crownify'), 'genericError' => __('Something went wrong. Please try again.', 'crownify'), 'buttonWorking' => __('Installing…', 'crownify'), 'buttonRetry' => __('Retry', 'crownify'), ]; ?> self::REVIEW_DISMISS_OPTION, 'upgrade' => self::UPGRADE_DISMISS_OPTION, 'stack' => self::STACK_DISMISS_OPTION, ]; if (!isset($map[$notice])) { wp_send_json_error(); } update_option($map[$notice], time()); wp_send_json_success(); } /** * Installs (if needed) and activates a single plugin of the DaftPlug stack. * The JavaScript calls this once per missing plugin so each request stays short. */ public function handleStackInstall() { if (Utils::hasActivePro()) { wp_send_json_error(['message' => __('Cross-promotion is only available in the free version.', 'crownify')]); } check_ajax_referer('rankify_install_stack'); if (!$this->canInstallPlugins()) { wp_send_json_error(['message' => __('You do not have permission to install plugins on this site.', 'crownify')]); } $slug = isset($_POST['slug']) ? sanitize_key(wp_unslash($_POST['slug'])) : ''; $plugins = $this->getStackPlugins(); if (!isset($plugins[$slug])) { wp_send_json_error(['message' => __('Unknown plugin.', 'crownify')]); } require_once ABSPATH . 'wp-admin/includes/plugin.php'; require_once ABSPATH . 'wp-admin/includes/file.php'; require_once ABSPATH . 'wp-admin/includes/misc.php'; require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; $status = $this->getPluginStatus($slug, $plugins[$slug]['constant']); if ($status['state'] === 'active') { wp_send_json_success(['state' => 'active']); } $pluginFile = $status['file']; if ($status['state'] === 'missing') { $api = plugins_api('plugin_information', [ 'slug' => $slug, 'fields' => ['sections' => false], ]); if (is_wp_error($api)) { wp_send_json_error(['message' => $api->get_error_message()]); } $skin = new \WP_Ajax_Upgrader_Skin(); $upgrader = new \Plugin_Upgrader($skin); $result = $upgrader->install($api->download_link); if (is_wp_error($result)) { wp_send_json_error(['message' => $result->get_error_message()]); } if (is_wp_error($skin->result)) { wp_send_json_error(['message' => $skin->result->get_error_message()]); } if ($skin->get_errors()->has_errors()) { wp_send_json_error(['message' => $skin->get_error_messages()]); } if ($result === null) { // Usually means WordPress could not write to the filesystem without FTP credentials. wp_send_json_error(['message' => __('WordPress could not access the filesystem to install the plugin. Please install it manually from Plugins → Add New.', 'crownify')]); } $pluginFile = $upgrader->plugin_info(); } if (empty($pluginFile)) { wp_send_json_error(['message' => __('The plugin was installed but its main file could not be found.', 'crownify')]); } $activated = activate_plugin($pluginFile); if (is_wp_error($activated)) { wp_send_json_error(['message' => $activated->get_error_message()]); } wp_send_json_success(['state' => 'active']); } }