PluginProbe
Site Reviews / trunk
Site Reviews vtrunk
8.3.1 8.3.0 8.2.2 8.2.1 8.2.0 8.1.0 8.0.13 8.0.12 8.0.11 trunk 1.2.2 2.17.1 3.5.4 4.7.0 5.25.1 6.11.8 7.0.10 7.0.11 7.0.12 7.0.13 7.0.14 7.0.15 7.0.16 7.0.17 7.0.18 All 54 releases
site-reviews / plugin / Controllers / MenuController.php

MenuController.php in Site Reviews trunk, at plugin/Controllers/MenuController.php

287 lines 10.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace GeminiLabs\SiteReviews\Controllers;
4
5 use GeminiLabs\SiteReviews\Api;
6 use GeminiLabs\SiteReviews\Database\Cache;
7 use GeminiLabs\SiteReviews\Database\Tables;
8 use GeminiLabs\SiteReviews\Defaults\FeatureDefaults;
9 use GeminiLabs\SiteReviews\Helper;
10 use GeminiLabs\SiteReviews\Helpers\Arr;
11 use GeminiLabs\SiteReviews\Helpers\Str;
12 use GeminiLabs\SiteReviews\Modules\Console;
13 use GeminiLabs\SiteReviews\Modules\Html\Builder;
14 use GeminiLabs\SiteReviews\Modules\Html\SettingForm;
15 use GeminiLabs\SiteReviews\Modules\Notice;
16 use GeminiLabs\SiteReviews\Overrides\ScheduledActionsTable;
17
18 class MenuController extends AbstractController
19 {
20 /**
21 * This is necessary because the ActionScheduler table is rendered late
22 * after request headers have already be set which breaks redirects.
23 *
24 * @action load-site-review_page_glsr-tools
25 */
26 public function processPageActions(): void
27 {
28 glsr(ScheduledActionsTable::class)->process_actions();
29 }
30
31 /**
32 * @action admin_menu
33 */
34 public function registerMenuCount(): void
35 {
36 global $menu, $typenow;
37 foreach ($menu as $key => $value) {
38 if (!isset($value[2]) || $value[2] != 'edit.php?post_type='.glsr()->post_type) {
39 continue;
40 }
41 $postCount = wp_count_posts(glsr()->post_type);
42 $pendingCount = glsr(Builder::class)->span(number_format_i18n($postCount->pending), [
43 'class' => 'unapproved-count',
44 ]);
45 $awaitingModeration = glsr(Builder::class)->span($pendingCount, [
46 'class' => "awaiting-mod count-{$postCount->pending}",
47 ]);
48 $menu[$key][0] .= " {$awaitingModeration}";
49 if (glsr()->post_type === $typenow) {
50 $menu[$key][4] .= ' current';
51 }
52 break;
53 }
54 }
55
56 /**
57 * @action admin_menu
58 */
59 public function registerSubMenus(): void
60 {
61 global $submenu;
62 $pages = $this->parseWithFilter('submenu/pages', [
63 'settings' => _x('Settings', 'admin-text', 'site-reviews'),
64 'tools' => _x('Tools', 'admin-text', 'site-reviews'),
65 'documentation' => _x('Help & Support', 'admin-text', 'site-reviews'),
66 'premium' => _x('Upgrade to Premium', 'admin-text', 'site-reviews'),
67 ]);
68 $parentSlug = 'edit.php?post_type='.glsr()->post_type;
69 $slugPrefix = Str::dashCase(glsr()->prefix);
70 foreach ($pages as $slug => $title) {
71 $method = Helper::buildMethodName('render', $slug, 'menu', 'callback');
72 if (!method_exists($this, $method)) {
73 continue;
74 }
75 $callback = glsr()->filter('addon/submenu/callback', [$this, $method], $slug);
76 if (!is_callable($callback)) {
77 continue;
78 }
79 add_submenu_page($parentSlug, $title, $title, glsr()->getPermission($slug), $slugPrefix.$slug, $callback);
80 }
81 foreach ($submenu[$parentSlug] as $index => $menu) {
82 $slug = $menu[2] ?? '';
83 if (!str_starts_with($slug, $slugPrefix)) {
84 continue;
85 }
86 $submenu[$parentSlug][$index][4] = "submenu_{$slug}"; // add submenu class
87 }
88 }
89
90 /**
91 * We don't use admin_menu because it breaks the privilege check which runs
92 * after the admin_menu hook is triggered in wp-admin/includes/menu.php.
93 *
94 * @action admin_init
95 */
96 public function removeSubMenu(): void
97 {
98 if (!function_exists('remove_submenu_page')) {
99 require_once \ABSPATH.'wp-admin/includes/plugin.php';
100 }
101 remove_submenu_page(
102 'edit.php?post_type='.glsr()->post_type,
103 'post-new.php?post_type='.glsr()->post_type
104 );
105 }
106
107 /**
108 * @see registerSubMenus
109 */
110 public function renderDocumentationMenuCallback(): void
111 {
112 $tabs = $this->parseWithFilter('documentation/tabs', [
113 'support' => _x('Support', 'admin-text', 'site-reviews'),
114 'faq' => _x('FAQ', 'admin-text', 'site-reviews'),
115 'shortcodes' => _x('Shortcodes', 'admin-text', 'site-reviews'),
116 'hooks' => _x('Hooks', 'admin-text', 'site-reviews'),
117 'functions' => _x('Functions', 'admin-text', 'site-reviews'),
118 'api' => _x('API', 'admin-text', 'site-reviews'),
119 'integrations' => _x('Integrations', 'admin-text', 'site-reviews'),
120 'addons' => _x('Addons', 'admin-text', 'site-reviews'),
121 ]);
122 $addons = glsr()->filterArray('addon/documentation', []);
123 uksort($addons, fn ($a, $b) => strnatcasecmp(glsr($a)->name, glsr($b)->name));
124 if (empty($addons)) {
125 unset($tabs['addons']);
126 }
127 $this->renderPage('documentation', [
128 'addons' => $addons,
129 'tabs' => $tabs,
130 ]);
131 }
132
133 /**
134 * @see registerSubMenus
135 */
136 public function renderPremiumMenuCallback(): void
137 {
138 $features = [];
139 $data = glsr(Api::class)->get('features')->data();
140 foreach ($data as $values) {
141 $features[] = glsr(FeatureDefaults::class)->restrict($values);
142 }
143 $feature = array_column($features, 'feature');
144 $premium = array_column($features, 'premium');
145 array_multisort(
146 $premium, \SORT_DESC,
147 $feature, \SORT_ASC | \SORT_NATURAL,
148 $features
149 );
150 $this->renderPage('premium', [
151 'features' => $features,
152 ]);
153 }
154
155 /**
156 * @see registerSubMenus
157 */
158 public function renderSettingsMenuCallback(): void
159 {
160 $tabs = $this->parseWithFilter('settings/tabs', [ // order is intentional
161 'general' => _x('General', 'admin-text', 'site-reviews'),
162 'reviews' => _x('Reviews', 'admin-text', 'site-reviews'),
163 'forms' => _x('Forms', 'admin-text', 'site-reviews'),
164 'schema' => _x('Schema', 'admin-text', 'site-reviews'),
165 'strings' => _x('Strings', 'admin-text', 'site-reviews'),
166 'integrations' => _x('Integrations', 'admin-text', 'site-reviews'),
167 'addons' => _x('Addons', 'admin-text', 'site-reviews'),
168 'licenses' => _x('Licenses', 'admin-text', 'site-reviews'),
169 ]);
170 // The installed premium plugin keeps the tab (renamed "Premium"
171 // through the addon/settings/tabs filter) even when every feature is
172 // toggled off; otherwise the tab exists only if an addon registered
173 // settings under it.
174 if (is_null(glsr()->addon('site-reviews-premium'))
175 && empty(Arr::get(glsr()->defaults(), 'settings.addons'))) {
176 unset($tabs['addons']);
177 }
178 $this->renderPage('settings', [
179 'fields' => glsr(SettingForm::class, ['groups' => $tabs])->build(),
180 'tabs' => $tabs,
181 ]);
182 }
183
184 /**
185 * @see registerSubMenus
186 */
187 public function renderToolsMenuCallback(): void
188 {
189 $tabs = $this->parseWithFilter('tools/tabs', [
190 'general' => _x('General', 'admin-text', 'site-reviews'),
191 'scheduled' => _x('Scheduled Actions', 'admin-text', 'site-reviews'),
192 'sync' => _x('Sync Reviews', 'admin-text', 'site-reviews'),
193 'console' => _x('Console', 'admin-text', 'site-reviews'),
194 'system-info' => _x('System Info', 'admin-text', 'site-reviews'),
195 ]);
196 if (!glsr()->filterBool('addon/sync/enable', false)) {
197 unset($tabs['sync']);
198 }
199 $this->renderPage('tools', [
200 'data' => [
201 'console_level' => glsr(Console::class)->getLevel(),
202 'context' => [
203 'base_url' => glsr_admin_url(),
204 'console' => glsr(Console::class)->get(),
205 'id' => glsr()->id,
206 ],
207 'myisam_tables' => Arr::get(glsr(Tables::class)->tableEngines(), 'MyISAM', []),
208 'rollback_script' => file_get_contents(glsr()->path('assets/scripts/rollback.js')),
209 'rollback_versions' => glsr(Cache::class)->getPluginVersions(),
210 'services' => glsr()->filterArray('addon/sync/services', []),
211 ],
212 'tabs' => $tabs,
213 ]);
214 }
215
216 /**
217 * Reorders the submenu to keep post types at the top
218 *
219 * @action admin_menu
220 */
221 public function reorderSubMenu(): void
222 {
223 global $submenu;
224 $prefix = 'edit.php?post_type='.glsr()->post_type;
225 if (empty($submenu[$prefix])) {
226 return;
227 }
228 $deferred = [];
229 $reindexed = [];
230 $index = 0;
231 $step = 5;
232 foreach ($submenu[$prefix] as $item) {
233 if (str_starts_with($item[0] ?? '', 'All ')) {
234 $index += $step;
235 $reindexed[$index] = $item;
236 } else {
237 $deferred[] = $item;
238 }
239 }
240 foreach ($deferred as $item) {
241 $index += $step;
242 $reindexed[$index] = $item;
243 }
244 $submenu[$prefix] = $reindexed;
245 }
246
247 /**
248 * @action admin_init
249 */
250 public function setCustomPermissions(): void
251 {
252 foreach (wp_roles()->roles as $role => $value) {
253 wp_roles()->remove_cap($role, 'create_'.glsr()->post_type);
254 }
255 }
256
257 protected function getNotices(): string
258 {
259 return glsr(Builder::class)->div(glsr(Notice::class)->get(), [
260 'id' => 'glsr-notices',
261 ]);
262 }
263
264 protected function parseWithFilter(string $hookSuffix, array $args = []): array
265 {
266 if (str_ends_with($hookSuffix, '/tabs')) {
267 $page = str_replace('/tabs', '', $hookSuffix);
268 foreach ($args as $tab => $title) {
269 if (!glsr()->hasPermission($page, $tab)) {
270 unset($args[$tab]);
271 }
272 }
273 }
274 // The "premium" submenu title is an upsell ("Upgrade to Premium")
275 // unless the premium plugin is installed — it renames the page via
276 // the filter below and renders its own feature control panel.
277 return glsr()->filterArray("addon/{$hookSuffix}", $args);
278 }
279
280 protected function renderPage(string $page, array $data = []): void
281 {
282 $data['http_referer'] = (string) wp_get_referer();
283 $data['notices'] = $this->getNotices();
284 glsr()->render("pages/{$page}/index", $data);
285 }
286 }
287