process_actions(); } /** * @action admin_menu */ public function registerMenuCount(): void { global $menu, $typenow; foreach ($menu as $key => $value) { if (!isset($value[2]) || $value[2] != 'edit.php?post_type='.glsr()->post_type) { continue; } $postCount = wp_count_posts(glsr()->post_type); $pendingCount = glsr(Builder::class)->span(number_format_i18n($postCount->pending), [ 'class' => 'unapproved-count', ]); $awaitingModeration = glsr(Builder::class)->span($pendingCount, [ 'class' => "awaiting-mod count-{$postCount->pending}", ]); $menu[$key][0] .= " {$awaitingModeration}"; if (glsr()->post_type === $typenow) { $menu[$key][4] .= ' current'; } break; } } /** * @action admin_menu */ public function registerSubMenus(): void { global $submenu; $pages = $this->parseWithFilter('submenu/pages', [ 'settings' => _x('Settings', 'admin-text', 'site-reviews'), 'tools' => _x('Tools', 'admin-text', 'site-reviews'), 'documentation' => _x('Help & Support', 'admin-text', 'site-reviews'), 'premium' => _x('Upgrade to Premium', 'admin-text', 'site-reviews'), ]); $parentSlug = 'edit.php?post_type='.glsr()->post_type; $slugPrefix = Str::dashCase(glsr()->prefix); foreach ($pages as $slug => $title) { $method = Helper::buildMethodName('render', $slug, 'menu', 'callback'); if (!method_exists($this, $method)) { continue; } $callback = glsr()->filter('addon/submenu/callback', [$this, $method], $slug); if (!is_callable($callback)) { continue; } add_submenu_page($parentSlug, $title, $title, glsr()->getPermission($slug), $slugPrefix.$slug, $callback); } foreach ($submenu[$parentSlug] as $index => $menu) { $slug = $menu[2] ?? ''; if (!str_starts_with($slug, $slugPrefix)) { continue; } $submenu[$parentSlug][$index][4] = "submenu_{$slug}"; // add submenu class } } /** * We don't use admin_menu because it breaks the privilege check which runs * after the admin_menu hook is triggered in wp-admin/includes/menu.php. * * @action admin_init */ public function removeSubMenu(): void { if (!function_exists('remove_submenu_page')) { require_once \ABSPATH.'wp-admin/includes/plugin.php'; } remove_submenu_page( 'edit.php?post_type='.glsr()->post_type, 'post-new.php?post_type='.glsr()->post_type ); } /** * @see registerSubMenus */ public function renderDocumentationMenuCallback(): void { $tabs = $this->parseWithFilter('documentation/tabs', [ 'support' => _x('Support', 'admin-text', 'site-reviews'), 'faq' => _x('FAQ', 'admin-text', 'site-reviews'), 'shortcodes' => _x('Shortcodes', 'admin-text', 'site-reviews'), 'hooks' => _x('Hooks', 'admin-text', 'site-reviews'), 'functions' => _x('Functions', 'admin-text', 'site-reviews'), 'api' => _x('API', 'admin-text', 'site-reviews'), 'integrations' => _x('Integrations', 'admin-text', 'site-reviews'), 'addons' => _x('Addons', 'admin-text', 'site-reviews'), ]); $addons = glsr()->filterArray('addon/documentation', []); uksort($addons, fn ($a, $b) => strnatcasecmp(glsr($a)->name, glsr($b)->name)); if (empty($addons)) { unset($tabs['addons']); } $this->renderPage('documentation', [ 'addons' => $addons, 'tabs' => $tabs, ]); } /** * @see registerSubMenus */ public function renderPremiumMenuCallback(): void { $features = []; $data = glsr(Api::class)->get('features')->data(); foreach ($data as $values) { $features[] = glsr(FeatureDefaults::class)->restrict($values); } $feature = array_column($features, 'feature'); $premium = array_column($features, 'premium'); array_multisort( $premium, \SORT_DESC, $feature, \SORT_ASC | \SORT_NATURAL, $features ); $this->renderPage('premium', [ 'features' => $features, ]); } /** * @see registerSubMenus */ public function renderSettingsMenuCallback(): void { $tabs = $this->parseWithFilter('settings/tabs', [ // order is intentional 'general' => _x('General', 'admin-text', 'site-reviews'), 'reviews' => _x('Reviews', 'admin-text', 'site-reviews'), 'forms' => _x('Forms', 'admin-text', 'site-reviews'), 'schema' => _x('Schema', 'admin-text', 'site-reviews'), 'strings' => _x('Strings', 'admin-text', 'site-reviews'), 'integrations' => _x('Integrations', 'admin-text', 'site-reviews'), 'addons' => _x('Addons', 'admin-text', 'site-reviews'), 'licenses' => _x('Licenses', 'admin-text', 'site-reviews'), ]); // The installed premium plugin keeps the tab (renamed "Premium" // through the addon/settings/tabs filter) even when every feature is // toggled off; otherwise the tab exists only if an addon registered // settings under it. if (is_null(glsr()->addon('site-reviews-premium')) && empty(Arr::get(glsr()->defaults(), 'settings.addons'))) { unset($tabs['addons']); } $this->renderPage('settings', [ 'fields' => glsr(SettingForm::class, ['groups' => $tabs])->build(), 'tabs' => $tabs, ]); } /** * @see registerSubMenus */ public function renderToolsMenuCallback(): void { $tabs = $this->parseWithFilter('tools/tabs', [ 'general' => _x('General', 'admin-text', 'site-reviews'), 'scheduled' => _x('Scheduled Actions', 'admin-text', 'site-reviews'), 'sync' => _x('Sync Reviews', 'admin-text', 'site-reviews'), 'console' => _x('Console', 'admin-text', 'site-reviews'), 'system-info' => _x('System Info', 'admin-text', 'site-reviews'), ]); if (!glsr()->filterBool('addon/sync/enable', false)) { unset($tabs['sync']); } $this->renderPage('tools', [ 'data' => [ 'console_level' => glsr(Console::class)->getLevel(), 'context' => [ 'base_url' => glsr_admin_url(), 'console' => glsr(Console::class)->get(), 'id' => glsr()->id, ], 'myisam_tables' => Arr::get(glsr(Tables::class)->tableEngines(), 'MyISAM', []), 'rollback_script' => file_get_contents(glsr()->path('assets/scripts/rollback.js')), 'rollback_versions' => glsr(Cache::class)->getPluginVersions(), 'services' => glsr()->filterArray('addon/sync/services', []), ], 'tabs' => $tabs, ]); } /** * Reorders the submenu to keep post types at the top * * @action admin_menu */ public function reorderSubMenu(): void { global $submenu; $prefix = 'edit.php?post_type='.glsr()->post_type; if (empty($submenu[$prefix])) { return; } $deferred = []; $reindexed = []; $index = 0; $step = 5; foreach ($submenu[$prefix] as $item) { if (str_starts_with($item[0] ?? '', 'All ')) { $index += $step; $reindexed[$index] = $item; } else { $deferred[] = $item; } } foreach ($deferred as $item) { $index += $step; $reindexed[$index] = $item; } $submenu[$prefix] = $reindexed; } /** * @action admin_init */ public function setCustomPermissions(): void { foreach (wp_roles()->roles as $role => $value) { wp_roles()->remove_cap($role, 'create_'.glsr()->post_type); } } protected function getNotices(): string { return glsr(Builder::class)->div(glsr(Notice::class)->get(), [ 'id' => 'glsr-notices', ]); } protected function parseWithFilter(string $hookSuffix, array $args = []): array { if (str_ends_with($hookSuffix, '/tabs')) { $page = str_replace('/tabs', '', $hookSuffix); foreach ($args as $tab => $title) { if (!glsr()->hasPermission($page, $tab)) { unset($args[$tab]); } } } // The "premium" submenu title is an upsell ("Upgrade to Premium") // unless the premium plugin is installed — it renames the page via // the filter below and renders its own feature control panel. return glsr()->filterArray("addon/{$hookSuffix}", $args); } protected function renderPage(string $page, array $data = []): void { $data['http_referer'] = (string) wp_get_referer(); $data['notices'] = $this->getNotices(); glsr()->render("pages/{$page}/index", $data); } }