PluginProbe
Easy Invoice – Invoice Generator, PDF Quotes & Payments / 2.4.1
Easy Invoice – Invoice Generator, PDF Quotes & Payments v2.4.1
2.4.0 2.4.1 2.3.8 2.3.7 2.3.6 2.3.5 2.3.4 2.3.3 2.3.2 2.3.1 2.2.0 2.1.21 2.1.20 2.1.19 2.1.18 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.16 2.1.2 All 57 releases
← All changes | includes/Admin/EasyInvoiceAdmin.php +101 -24 2.3.42.4.1 View file →
@@ -109,8 +109,9 @@
109 109 */
110 110 public function init() {
111 111 // Add menu items
112 112 add_action('admin_menu', array($this, 'registerMenuPages'));
113 + add_action('admin_menu', array($this, 'relaxMenuCapabilities'), 9999);
113 114
114 115 // Register admin assets
115 116 $admin_assets = new AdminAssets();
116 117 $admin_assets->register();
@@ -140,8 +141,56 @@
140 141 add_action('easy_invoice_admin_main_content', array($this, 'mainPageContent'));
141 142 }
142 143
143 144 /**
145 + * Let an addon lower the capability a menu page demands.
146 + *
147 + * Every page is registered with `manage_options`, which is right for a
148 + * site with one administrator and wrong the moment the Team Roles addon
149 + * hands a colleague an "EI Viewer" role: WordPress refused them every
150 + * screen before the plugin's own capability checks were even consulted.
151 + * add_submenu_page() drops a page outright when the current user lacks
152 + * the capability, so the plugin's own pages pass through menuCapability()
153 + * at registration; this late pass covers pages registered elsewhere.
154 + */
155 + public function menuCapability($slug) {
156 + /** This filter is documented in relaxMenuCapabilities(). */
157 + return (string) apply_filters('easy_invoice_menu_capability', 'manage_options', (string) $slug);
158 + }
159 +
160 + public function relaxMenuCapabilities() {
161 + global $menu, $submenu;
162 + $relax = static function ($cap, $slug) {
163 + if (!is_string($slug) || 0 !== strpos($slug, 'easy-') ) {
164 + return $cap;
165 + }
166 + /**
167 + * Filter the capability required to open an Easy Invoice admin page.
168 + *
169 + * @param string $cap Capability registered for the page.
170 + * @param string $slug Page slug.
171 + */
172 + return (string) apply_filters('easy_invoice_menu_capability', $cap, $slug);
173 + };
174 + if (is_array($menu)) {
175 + foreach ($menu as $i => $item) {
176 + if (isset($item[1], $item[2])) {
177 + $menu[$i][1] = $relax($item[1], $item[2]);
178 + }
179 + }
180 + }
181 + if (is_array($submenu)) {
182 + foreach ($submenu as $parent => $items) {
183 + foreach ((array) $items as $i => $item) {
184 + if (isset($item[1], $item[2])) {
185 + $submenu[$parent][$i][1] = $relax($item[1], $item[2]);
186 + }
187 + }
188 + }
189 + }
190 + }
191 +
192 + /**
144 193 * Register admin menu pages
145 194 */
146 195 public function registerMenuPages() {
147 196 // Main menu item
@@ -147,9 +196,9 @@
147 196 // Main menu item
148 197 add_menu_page(
149 198 __('Easy Invoice', 'easy-invoice'),
150 199 __('Easy Invoice', 'easy-invoice'),
151 - 'manage_options',
200 + $this->menuCapability(PagesSlugs::DASHBOARD),
152 201 PagesSlugs::DASHBOARD,
153 202 array($this, 'displayMainPage'),
154 203 'dashicons-media-text',
155 204 25
@@ -159,9 +208,9 @@
159 208 add_submenu_page(
160 209 'easy-invoice',
161 210 __('Dashboard', 'easy-invoice'),
162 211 __('Dashboard', 'easy-invoice'),
163 - 'manage_options',
212 + $this->menuCapability(PagesSlugs::DASHBOARD),
164 213 PagesSlugs::DASHBOARD,
165 214 array($this, 'displayMainPage')
166 215 );
167 216
@@ -169,9 +218,9 @@
169 218 add_submenu_page(
170 219 'easy-invoice',
171 220 __('All Invoices', 'easy-invoice'),
172 221 __('All Invoices', 'easy-invoice'),
173 - 'manage_options',
222 + $this->menuCapability(PagesSlugs::ALL_INVOICES),
174 223 PagesSlugs::ALL_INVOICES,
175 224 array($this, 'displayMainPage')
176 225 );
177 226
@@ -179,9 +228,9 @@
179 228 add_submenu_page(
180 229 'easy-invoice-hidden',
181 230 __('Add New Invoice', 'easy-invoice'),
182 231 __('Add New', 'easy-invoice'),
183 - 'manage_options',
232 + $this->menuCapability(PagesSlugs::INVOICE_NEW),
184 233 PagesSlugs::INVOICE_NEW,
185 234 array($this, 'displayMainPage')
186 235 );
187 236
@@ -189,9 +238,9 @@
189 238 add_submenu_page(
190 239 'easy-invoice',
191 240 __('All Quotes', 'easy-invoice'),
192 241 __('All Quotes', 'easy-invoice'),
193 - 'manage_options',
242 + $this->menuCapability(PagesSlugs::ALL_QUOTES),
194 243 PagesSlugs::ALL_QUOTES,
195 244 array($this, 'displayMainPage')
196 245 );
197 246
@@ -199,9 +248,9 @@
199 248 add_submenu_page(
200 249 'easy-invoice-hidden',
201 250 __('Add New Quote', 'easy-invoice'),
202 251 __('Add New Quote', 'easy-invoice'),
203 - 'manage_options',
252 + $this->menuCapability(PagesSlugs::QUOTE_NEW),
204 253 PagesSlugs::QUOTE_NEW,
205 254 array($this, 'displayMainPage')
206 255 );
207 256
@@ -209,9 +258,9 @@
209 258 add_submenu_page(
210 259 'easy-invoice-hidden',
211 260 __('Payments', 'easy-invoice'),
212 261 __('Payments', 'easy-invoice'),
213 - 'manage_options',
262 + $this->menuCapability(PagesSlugs::PAYMENTS),
214 263 PagesSlugs::PAYMENTS,
215 264 array($this, 'displayMainPage')
216 265 );
217 266
@@ -219,9 +268,9 @@
219 268 add_submenu_page(
220 269 'easy-invoice-hidden',
221 270 __('Add New Payment', 'easy-invoice'),
222 271 __('Add New Payment', 'easy-invoice'),
223 - 'manage_options',
272 + $this->menuCapability(PagesSlugs::PAYMENT_NEW),
224 273 PagesSlugs::PAYMENT_NEW,
225 274 array($this, 'displayMainPage')
226 275 );
227 276
@@ -229,9 +278,9 @@
229 278 add_submenu_page(
230 279 'easy-invoice',
231 280 __('All Clients', 'easy-invoice'),
232 281 __('All Clients', 'easy-invoice'),
233 - 'edit_posts',
282 + $this->menuCapability(PagesSlugs::CLIENTS),
234 283 PagesSlugs::CLIENTS,
235 284 array($this, 'displayMainPage')
236 285 );
237 286
@@ -252,9 +301,9 @@
252 301 add_submenu_page(
253 302 'easy-invoice-hidden',
254 303 __('Item Library', 'easy-invoice'),
255 304 __('Item Library', 'easy-invoice'),
256 - 'manage_options',
305 + $this->menuCapability('easy-invoice-pro-item-library'),
257 306 'easy-invoice-pro-item-library',
258 307 array($this, 'displayMainPage')
259 308 );
260 309 }
@@ -264,9 +313,9 @@
264 313 add_submenu_page(
265 314 'easy-invoice-hidden',
266 315 __('Template Builder', 'easy-invoice'),
267 316 __('Template Builder', 'easy-invoice'),
268 - 'manage_options',
317 + $this->menuCapability('easy-invoice-templates'),
269 318 'easy-invoice-templates',
270 319 array($this, 'displayMainPage')
271 320 );
272 321
@@ -275,9 +324,9 @@
275 324 add_submenu_page(
276 325 'easy-invoice-hidden',
277 326 __('Create New Template', 'easy-invoice'),
278 327 __('Create New', 'easy-invoice'),
279 - 'manage_options',
328 + $this->menuCapability('easy-invoice-templates-new'),
280 329 'easy-invoice-templates-new',
281 330 array($this, 'displayMainPage')
282 331 );
283 332
@@ -286,9 +335,9 @@
286 335 add_submenu_page(
287 336 'easy-invoice-hidden',
288 337 __('Template Builder Page', 'easy-invoice'),
289 338 __('Template Builder Page', 'easy-invoice'),
290 - 'manage_options',
339 + $this->menuCapability('easy-invoice-template-builder'),
291 340 'easy-invoice-template-builder',
292 341 array($this, 'displayMainPage')
293 342 );
294 343 }
@@ -297,9 +346,9 @@
297 346 add_submenu_page(
298 347 'easy-invoice-hidden', // Use main menu as parent to avoid title issues
299 348 __('Edit Client', 'easy-invoice'),
300 349 __('Edit Client', 'easy-invoice'),
301 - 'manage_options',
350 + $this->menuCapability(PagesSlugs::CLIENT_EDIT),
302 351 PagesSlugs::CLIENT_EDIT,
303 352 array($this, 'displayMainPage')
304 353 );
305 354
@@ -306,9 +355,9 @@
306 355 add_submenu_page(
307 356 'easy-invoice-hidden', // Use main menu as parent to avoid title issues
308 357 __('View Client', 'easy-invoice'),
309 358 __('View Client', 'easy-invoice'),
310 - 'manage_options',
359 + $this->menuCapability(PagesSlugs::CLIENT_VIEW),
311 360 PagesSlugs::CLIENT_VIEW,
312 361 array($this, 'displayMainPage')
313 362 );
314 363
@@ -315,9 +364,9 @@
315 364 add_submenu_page(
316 365 'easy-invoice-hidden', // Use main menu as parent to avoid title issues
317 366 __('Preview Invoice', 'easy-invoice'),
318 367 __('Preview Invoice', 'easy-invoice'),
319 - 'manage_options',
368 + $this->menuCapability(PagesSlugs::INVOICE_PREVIEW),
320 369 PagesSlugs::INVOICE_PREVIEW,
321 370 array($this, 'displayPreviewPage')
322 371 );
323 372
@@ -324,9 +373,9 @@
324 373 add_submenu_page(
325 374 'easy-invoice-hidden', // Use main menu as parent to avoid title issues
326 375 __('Preview Quote', 'easy-invoice'),
327 376 __('Preview Quote', 'easy-invoice'),
328 - 'manage_options',
377 + $this->menuCapability(PagesSlugs::QUOTE_PREVIEW),
329 378 PagesSlugs::QUOTE_PREVIEW,
330 379 array($this, 'displayMainPage')
331 380 );
332 381
@@ -339,9 +388,9 @@
339 388 add_submenu_page(
340 389 'easy-invoice-hidden',
341 390 __('Reports', 'easy-invoice'),
342 391 __('Reports', 'easy-invoice'),
343 - 'manage_options',
392 + $this->menuCapability(PagesSlugs::REPORTS),
344 393 PagesSlugs::REPORTS,
345 394 array($this, 'displayMainPage')
346 395 );
347 396 }
@@ -350,9 +399,9 @@
350 399 add_submenu_page(
351 400 'easy-invoice',
352 401 __('Settings', 'easy-invoice'),
353 402 __('Settings', 'easy-invoice'),
354 - 'manage_options',
403 + $this->menuCapability(PagesSlugs::SETTINGS),
355 404 PagesSlugs::SETTINGS,
356 405 array($this, 'displayMainPage')
357 406 );
358 407
@@ -360,9 +409,9 @@
360 409 add_submenu_page(
361 410 'easy-invoice',
362 411 __('Addons', 'easy-invoice'),
363 412 __('Addons', 'easy-invoice'),
364 - 'manage_options',
413 + $this->menuCapability(PagesSlugs::ADDONS),
365 414 PagesSlugs::ADDONS,
366 415 array($this, 'displayMainPage')
367 416 );
368 417
@@ -370,9 +419,9 @@
370 419 add_submenu_page(
371 420 'easy-invoice',
372 421 __('License', 'easy-invoice'),
373 422 __('License', 'easy-invoice'),
374 - 'manage_options',
423 + $this->menuCapability(PagesSlugs::LICENSE),
375 424 PagesSlugs::LICENSE,
376 425 array($this, 'displayMainPage')
377 426 );
378 427
@@ -381,9 +430,9 @@
381 430 add_submenu_page(
382 431 'easy-invoice',
383 432 __('Free vs Pro', 'easy-invoice'),
384 433 __('Free vs Pro', 'easy-invoice'),
385 - 'manage_options',
434 + $this->menuCapability('easy-invoice-free-vs-pro'),
386 435 'easy-invoice-free-vs-pro',
387 436 array($this, 'displayMainPage')
388 437 );
389 438 }
@@ -402,9 +451,9 @@
402 451 /**
403 452 * Redirect to community page
404 453 */
405 454 public function redirectToCommunity() {
406 - wp_redirect(esc_url_raw('https://www.facebook.com/groups/mantrabraincommunity'));
455 + wp_safe_redirect(esc_url_raw('https://www.facebook.com/groups/mantrabraincommunity'));
407 456 exit;
408 457 }
409 458
410 459 /**
@@ -485,9 +534,9 @@
485 534 break;
486 535
487 536 case PagesSlugs::PAYMENTS:
488 537 if (isset($_GET['action'])) {
489 - $this->payment_controller->display(['page' => $_GET['action']]);
538 + $this->payment_controller->display(['page' => sanitize_key(wp_unslash($_GET['action']))]); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
490 539 } else {
491 540 $this->payment_controller->display(['page' => PagesSlugs::PAYMENTS]);
492 541 }
493 542 break;
@@ -596,8 +645,36 @@
596 645 // `easy-invoice-addon-` or (b) registered in AddonRegistry as
597 646 // an addon page — and just `break` to let the addon's own hook
598 647 // handle the rendering.
599 648 if (strpos($page, 'easy-invoice-addon-') === 0) {
649 + break;
650 + }
651 +
652 + /**
653 + * Slugs whose page draws itself on the same hook at a later
654 + * priority, and which must therefore not get the Dashboard
655 + * rendered underneath them.
656 + *
657 + * Addon pages were special-cased by prefix above, which left
658 + * anything in the free plugin that renders the same way — the
659 + * credit note screen, for one — falling through to the
660 + * Dashboard and stacking two pages on top of each other. A
661 + * filter means the next such page registers itself instead of
662 + * editing this switch and rediscovering the same bug.
663 + *
664 + * @param string[] $slugs Page slugs that render themselves.
665 + */
666 + // "Create New Template" is a deep-link slug with no screen of
667 + // its own; send it to the builder rather than draw the Dashboard.
668 + if ('easy-invoice-templates-new' === $page) {
669 + echo '<script>window.location.replace(' . wp_json_encode(admin_url('admin.php?page=easy-invoice-template-builder&action=new')) . ');</script>';
670 + break;
671 + }
672 + $self_rendering = (array) apply_filters('easy_invoice_self_rendering_pages', [
673 + \EasyInvoice\Controllers\CreditNoteController::PAGE_SLUG,
674 + ]);
675 +
676 + if (in_array($page, $self_rendering, true)) {
600 677 break;
601 678 }
602 679 if (class_exists('\\EasyInvoice\\Addons\\AddonRegistry')) {
603 680 foreach (\EasyInvoice\Addons\AddonRegistry::all() as $_addon) {