This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
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\AddonDefaults; |
||||
9 | use GeminiLabs\SiteReviews\Defaults\FeatureDefaults; |
||||
10 | use GeminiLabs\SiteReviews\Helper; |
||||
11 | use GeminiLabs\SiteReviews\Helpers\Arr; |
||||
12 | use GeminiLabs\SiteReviews\Helpers\Str; |
||||
13 | use GeminiLabs\SiteReviews\License; |
||||
14 | use GeminiLabs\SiteReviews\Modules\Console; |
||||
15 | use GeminiLabs\SiteReviews\Modules\Html\Builder; |
||||
16 | use GeminiLabs\SiteReviews\Modules\Html\SettingForm; |
||||
17 | use GeminiLabs\SiteReviews\Modules\Notice; |
||||
18 | use GeminiLabs\SiteReviews\Overrides\ScheduledActionsTable; |
||||
19 | |||||
20 | class MenuController extends AbstractController |
||||
21 | { |
||||
22 | /** |
||||
23 | * This is necessary because the ActionScheduler table is rendered late |
||||
24 | * after request headers have already be set which breaks redirects. |
||||
25 | * |
||||
26 | * @action load-site-review_page_glsr-tools |
||||
27 | */ |
||||
28 | public function processPageActions(): void |
||||
29 | { |
||||
30 | glsr(ScheduledActionsTable::class)->process_actions(); |
||||
31 | } |
||||
32 | |||||
33 | /** |
||||
34 | * @action admin_menu |
||||
35 | */ |
||||
36 | public function registerMenuCount(): void |
||||
37 | { |
||||
38 | global $menu, $typenow; |
||||
39 | foreach ($menu as $key => $value) { |
||||
40 | if (!isset($value[2]) || $value[2] != 'edit.php?post_type='.glsr()->post_type) { |
||||
41 | continue; |
||||
42 | } |
||||
43 | $postCount = wp_count_posts(glsr()->post_type); |
||||
44 | $pendingCount = glsr(Builder::class)->span(number_format_i18n($postCount->pending), [ |
||||
45 | 'class' => 'unapproved-count', |
||||
46 | ]); |
||||
47 | $awaitingModeration = glsr(Builder::class)->span($pendingCount, [ |
||||
48 | 'class' => "awaiting-mod count-{$postCount->pending}", |
||||
49 | ]); |
||||
50 | $menu[$key][0] .= " {$awaitingModeration}"; |
||||
51 | if (glsr()->post_type === $typenow) { |
||||
52 | $menu[$key][4] .= ' current'; |
||||
53 | } |
||||
54 | break; |
||||
55 | } |
||||
56 | } |
||||
57 | |||||
58 | /** |
||||
59 | * @action admin_menu |
||||
60 | */ |
||||
61 | public function registerSubMenus(): void |
||||
62 | { |
||||
63 | global $submenu; |
||||
64 | $pages = $this->parseWithFilter('submenu/pages', [ |
||||
65 | 'settings' => _x('Settings', 'admin-text', 'site-reviews'), |
||||
66 | 'tools' => _x('Tools', 'admin-text', 'site-reviews'), |
||||
67 | 'documentation' => _x('Help & Support', 'admin-text', 'site-reviews'), |
||||
68 | 'premium' => _x('Upgrade to Premium', 'admin-text', 'site-reviews'), |
||||
69 | ]); |
||||
70 | $parentSlug = 'edit.php?post_type='.glsr()->post_type; |
||||
71 | $slugPrefix = Str::dashCase(glsr()->prefix); |
||||
72 | foreach ($pages as $slug => $title) { |
||||
73 | $method = Helper::buildMethodName('render', $slug, 'menu', 'callback'); |
||||
74 | 8 | if (!method_exists($this, $method)) { |
|||
75 | continue; |
||||
76 | 8 | } |
|||
77 | $callback = glsr()->filter('addon/submenu/callback', [$this, $method], $slug); |
||||
78 | if (!is_callable($callback)) { |
||||
79 | 8 | continue; |
|||
80 | 8 | } |
|||
81 | 8 | add_submenu_page($parentSlug, $title, $title, glsr()->getPermission($slug), $slugPrefix.$slug, $callback); |
|||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||
82 | 8 | } |
|||
83 | foreach ($submenu[$parentSlug] as $index => $menu) { |
||||
84 | $slug = $menu[2] ?? ''; |
||||
85 | if (!str_starts_with($slug, $slugPrefix)) { |
||||
86 | continue; |
||||
87 | } |
||||
88 | $submenu[$parentSlug][$index][4] = "submenu_{$slug}"; // add submenu class |
||||
89 | } |
||||
90 | } |
||||
91 | |||||
92 | /** |
||||
93 | * We don't use admin_menu because it breaks the privilege check which runs |
||||
94 | * after the admin_menu hook is triggered in wp-admin/includes/menu.php. |
||||
95 | * |
||||
96 | * @action admin_init |
||||
97 | */ |
||||
98 | public function removeSubMenu(): void |
||||
99 | { |
||||
100 | if (!function_exists('remove_submenu_page')) { |
||||
101 | require_once ABSPATH.'wp-admin/includes/plugin.php'; |
||||
102 | } |
||||
103 | remove_submenu_page( |
||||
104 | 'edit.php?post_type='.glsr()->post_type, |
||||
105 | 'post-new.php?post_type='.glsr()->post_type |
||||
106 | ); |
||||
107 | } |
||||
108 | |||||
109 | /** |
||||
110 | * @see registerSubMenus |
||||
111 | */ |
||||
112 | public function renderDocumentationMenuCallback(): void |
||||
113 | { |
||||
114 | $tabs = $this->parseWithFilter('documentation/tabs', [ |
||||
115 | 'support' => _x('Support', 'admin-text', 'site-reviews'), |
||||
116 | 'faq' => _x('FAQ', 'admin-text', 'site-reviews'), |
||||
117 | 'shortcodes' => _x('Shortcodes', 'admin-text', 'site-reviews'), |
||||
118 | 'hooks' => _x('Hooks', 'admin-text', 'site-reviews'), |
||||
119 | 'functions' => _x('Functions', 'admin-text', 'site-reviews'), |
||||
120 | 'api' => _x('API', 'admin-text', 'site-reviews'), |
||||
121 | 'integrations' => _x('Integrations', 'admin-text', 'site-reviews'), |
||||
122 | 'addons' => _x('Addons', 'admin-text', 'site-reviews'), |
||||
123 | ]); |
||||
124 | $addons = glsr()->filterArray('addon/documentation', []); |
||||
125 | uksort($addons, fn ($a, $b) => strnatcasecmp(glsr($a)->name, glsr($b)->name)); |
||||
126 | if (empty($addons)) { |
||||
127 | unset($tabs['addons']); |
||||
128 | } |
||||
129 | $this->renderPage('documentation', [ |
||||
130 | 'addons' => $addons, |
||||
131 | 'tabs' => $tabs, |
||||
132 | ]); |
||||
133 | } |
||||
134 | |||||
135 | /** |
||||
136 | * @see registerSubMenus |
||||
137 | */ |
||||
138 | public function renderPremiumMenuCallback(): void |
||||
139 | { |
||||
140 | $addons = []; |
||||
141 | $features = []; |
||||
142 | $isPremium = glsr(License::class)->isPremium(); |
||||
143 | if ($isPremium) { |
||||
144 | $data = glsr(Api::class)->get('addons')->data(); |
||||
145 | foreach ($data as $values) { |
||||
146 | $context = glsr(AddonDefaults::class)->restrict($values); |
||||
147 | $addons[] = array_merge($context, compact('context')); |
||||
148 | } |
||||
149 | } else { |
||||
150 | $data = glsr(Api::class)->get('features')->data(); |
||||
151 | foreach ($data as $values) { |
||||
152 | $features[] = glsr(FeatureDefaults::class)->restrict($values); |
||||
153 | } |
||||
154 | $feature = array_column($features, 'feature'); |
||||
155 | $premium = array_column($features, 'premium'); |
||||
156 | array_multisort( |
||||
157 | $premium, \SORT_DESC, |
||||
0 ignored issues
–
show
SORT_DESC cannot be passed to array_multisort() as the parameter $rest expects a reference.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
158 | $feature, \SORT_ASC | \SORT_NATURAL, |
||||
0 ignored issues
–
show
SORT_ASC | SORT_NATURAL cannot be passed to array_multisort() as the parameter $rest expects a reference.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
159 | $features |
||||
160 | ); |
||||
161 | } |
||||
162 | $this->renderPage('premium', [ |
||||
163 | 'addons' => $addons, |
||||
164 | 'features' => $features, |
||||
165 | 'is_premium' => $isPremium, |
||||
166 | ]); |
||||
167 | } |
||||
168 | |||||
169 | /** |
||||
170 | * @see registerSubMenus |
||||
171 | */ |
||||
172 | public function renderSettingsMenuCallback(): void |
||||
173 | { |
||||
174 | $tabs = $this->parseWithFilter('settings/tabs', [ // order is intentional |
||||
175 | 'general' => _x('General', 'admin-text', 'site-reviews'), |
||||
176 | 'reviews' => _x('Reviews', 'admin-text', 'site-reviews'), |
||||
177 | 'forms' => _x('Forms', 'admin-text', 'site-reviews'), |
||||
178 | 'schema' => _x('Schema', 'admin-text', 'site-reviews'), |
||||
179 | 'strings' => _x('Strings', 'admin-text', 'site-reviews'), |
||||
180 | 'integrations' => _x('Integrations', 'admin-text', 'site-reviews'), |
||||
181 | 'addons' => _x('Addons', 'admin-text', 'site-reviews'), |
||||
182 | 'licenses' => _x('Licenses', 'admin-text', 'site-reviews'), |
||||
183 | ]); |
||||
184 | if (empty(Arr::get(glsr()->defaults(), 'settings.addons'))) { |
||||
185 | unset($tabs['addons']); |
||||
186 | } |
||||
187 | 8 | if (empty(Arr::get(glsr()->defaults(), 'settings.licenses'))) { |
|||
188 | unset($tabs['licenses']); |
||||
189 | 8 | } |
|||
190 | 8 | $this->renderPage('settings', [ |
|||
191 | 'fields' => glsr(SettingForm::class, ['groups' => $tabs])->build(), |
||||
192 | 'tabs' => $tabs, |
||||
193 | ]); |
||||
194 | } |
||||
195 | |||||
196 | /** |
||||
197 | * @see registerSubMenus |
||||
198 | */ |
||||
199 | public function renderToolsMenuCallback(): void |
||||
200 | { |
||||
201 | $tabs = $this->parseWithFilter('tools/tabs', [ |
||||
202 | 'general' => _x('General', 'admin-text', 'site-reviews'), |
||||
203 | 'scheduled' => _x('Scheduled Actions', 'admin-text', 'site-reviews'), |
||||
204 | 'sync' => _x('Sync Reviews', 'admin-text', 'site-reviews'), |
||||
205 | 'console' => _x('Console', 'admin-text', 'site-reviews'), |
||||
206 | 'system-info' => _x('System Info', 'admin-text', 'site-reviews'), |
||||
207 | ]); |
||||
208 | if (!glsr()->filterBool('addon/sync/enable', false)) { |
||||
209 | unset($tabs['sync']); |
||||
210 | } |
||||
211 | $this->renderPage('tools', [ |
||||
212 | 'data' => [ |
||||
213 | 'console_level' => glsr(Console::class)->getLevel(), |
||||
214 | 'context' => [ |
||||
215 | 'base_url' => glsr_admin_url(), |
||||
216 | 'console' => glsr(Console::class)->get(), |
||||
217 | 'id' => glsr()->id, |
||||
218 | ], |
||||
219 | 'myisam_tables' => Arr::get(glsr(Tables::class)->tableEngines(), 'MyISAM', []), |
||||
220 | 'rollback_script' => file_get_contents(glsr()->path('assets/scripts/rollback.js')), |
||||
221 | 'rollback_versions' => glsr(Cache::class)->getPluginVersions(), |
||||
222 | 'services' => glsr()->filterArray('addon/sync/services', []), |
||||
223 | ], |
||||
224 | 'tabs' => $tabs, |
||||
225 | ]); |
||||
226 | } |
||||
227 | |||||
228 | /** |
||||
229 | * @action admin_init |
||||
230 | */ |
||||
231 | public function setCustomPermissions(): void |
||||
232 | { |
||||
233 | foreach (wp_roles()->roles as $role => $value) { |
||||
234 | wp_roles()->remove_cap($role, 'create_'.glsr()->post_type); |
||||
235 | } |
||||
236 | } |
||||
237 | |||||
238 | protected function getNotices(): string |
||||
239 | { |
||||
240 | return glsr(Builder::class)->div(glsr(Notice::class)->get(), [ |
||||
241 | 'id' => 'glsr-notices', |
||||
242 | ]); |
||||
243 | } |
||||
244 | |||||
245 | protected function parseWithFilter(string $hookSuffix, array $args = []): array |
||||
246 | { |
||||
247 | if (str_ends_with($hookSuffix, '/tabs')) { |
||||
248 | $page = str_replace('/tabs', '', $hookSuffix); |
||||
249 | foreach ($args as $tab => $title) { |
||||
250 | if (!glsr()->hasPermission($page, $tab)) { |
||||
251 | unset($args[$tab]); |
||||
252 | } |
||||
253 | } |
||||
254 | } elseif (array_key_exists('premium', $args) && glsr(License::class)->isPremium()) { |
||||
255 | $args['premium'] = _x('Premium Addons', 'admin-text', 'site-reviews'); |
||||
256 | } |
||||
257 | return glsr()->filterArray("addon/{$hookSuffix}", $args); |
||||
258 | } |
||||
259 | |||||
260 | protected function renderPage(string $page, array $data = []): void |
||||
261 | { |
||||
262 | $data['http_referer'] = (string) wp_get_referer(); |
||||
263 | $data['notices'] = $this->getNotices(); |
||||
264 | glsr()->render("pages/{$page}/index", $data); |
||||
265 | } |
||||
266 | } |
||||
267 |