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
easy-invoice / includes / Admin / EasyInvoiceAdmin.php

EasyInvoiceAdmin.php in Easy Invoice – Invoice Generator, PDF Quotes & Payments 2.4.1, at includes/Admin/EasyInvoiceAdmin.php

789 lines 30.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * EasyInvoice Admin Class
4 *
5 * @package Easy_Invoice
6 * @subpackage Admin
7 */
8
9 namespace EasyInvoice\Admin;
10
11 use EasyInvoice\Admin\AdminAssets;
12 use EasyInvoice\Constants\PagesSlugs;
13 use EasyInvoice\Controllers\InvoiceController;
14 use EasyInvoice\Controllers\QuoteController;
15 use EasyInvoice\Controllers\ClientController;
16 use EasyInvoice\Controllers\SettingsController;
17 use EasyInvoice\Controllers\ReportController;
18 use EasyInvoice\Controllers\DashboardController;
19 use EasyInvoice\Controllers\PaymentController;
20
21 /**
22 * EasyInvoiceAdmin is the main admin class that initializes all controllers
23 */
24 class EasyInvoiceAdmin {
25 /**
26 * Invoice controller instance
27 *
28 * @var InvoiceController
29 */
30 private $invoice_controller;
31
32 /**
33 * Quote controller instance
34 *
35 * @var QuoteController
36 */
37 private $quote_controller;
38
39 /**
40 * Client controller instance
41 *
42 * @var ClientController
43 */
44 private $client_controller;
45
46 /**
47 * Settings controller instance
48 *
49 * @var SettingsController
50 */
51 private $settings_controller;
52
53 /**
54 * Report controller instance
55 *
56 * @var ReportController
57 */
58 private $report_controller;
59
60 /**
61 * Dashboard controller instance
62 *
63 * @var DashboardController
64 */
65 private $dashboard_controller;
66
67 /**
68 * Payment controller instance
69 *
70 * @var PaymentController
71 */
72 private $payment_controller;
73
74 /**
75 * Template Builder controller instance
76 *
77 * @var \EasyInvoicePro\Addons\CustomTemplates\Controllers\TemplateBuilderController
78 */
79 private $template_builder_controller;
80
81 /**
82 * Constructor
83 */
84 public function __construct() {
85 $this->invoice_controller = new InvoiceController();
86 $this->quote_controller = new QuoteController();
87 $this->client_controller = new ClientController();
88 $this->settings_controller = new SettingsController();
89 $this->report_controller = new ReportController();
90 $this->dashboard_controller = new DashboardController();
91 $this->payment_controller = new PaymentController();
92
93 // Initialize Template Builder controller if Pro is active AND
94 // the custom_templates addon is enabled. The addon owns this
95 // feature — see addons/CustomTemplates/addon.php in the Pro
96 // plugin. Constructing the controller here always-on would
97 // register its admin_menu hook regardless of addon state.
98 if (
99 easy_invoice_has_pro()
100 && \EasyInvoice\Addons\AddonManager::shouldLoad('custom_templates')
101 && class_exists('\EasyInvoicePro\Addons\CustomTemplates\Controllers\TemplateBuilderController')
102 ) {
103 $this->template_builder_controller = new \EasyInvoicePro\Addons\CustomTemplates\Controllers\TemplateBuilderController();
104 }
105 }
106
107 /**
108 * Initialize the admin class
109 */
110 public function init() {
111 // Add menu items
112 add_action('admin_menu', array($this, 'registerMenuPages'));
113 add_action('admin_menu', array($this, 'relaxMenuCapabilities'), 9999);
114
115 // Register admin assets
116 $admin_assets = new AdminAssets();
117 $admin_assets->register();
118
119 // Register invoice preview page
120 add_action('admin_init', array($this, 'registerPreviewPage'));
121
122 // Ensure proper page titles to avoid WordPress core issues
123 add_filter('admin_title', array($this, 'setAdminTitle'));
124
125 // Hide admin UI
126 add_action('admin_head', array($this, 'hideAdminUi'));
127
128 // Body class for layout fixes (admin bar offset, header strip alignment)
129 add_filter('admin_body_class', array($this, 'adminBodyClass'));
130
131 // Initialize controllers
132 $this->invoice_controller->init();
133 $this->quote_controller->init();
134 $this->client_controller->init();
135 $this->settings_controller->init();
136 $this->report_controller->init();
137 $this->dashboard_controller->init();
138 $this->payment_controller->init();
139
140 // Main content hook
141 add_action('easy_invoice_admin_main_content', array($this, 'mainPageContent'));
142 }
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 /**
193 * Register admin menu pages
194 */
195 public function registerMenuPages() {
196 // Main menu item
197 add_menu_page(
198 __('Easy Invoice', 'easy-invoice'),
199 __('Easy Invoice', 'easy-invoice'),
200 $this->menuCapability(PagesSlugs::DASHBOARD),
201 PagesSlugs::DASHBOARD,
202 array($this, 'displayMainPage'),
203 'dashicons-media-text',
204 25
205 );
206
207 // Dashboard submenu
208 add_submenu_page(
209 'easy-invoice',
210 __('Dashboard', 'easy-invoice'),
211 __('Dashboard', 'easy-invoice'),
212 $this->menuCapability(PagesSlugs::DASHBOARD),
213 PagesSlugs::DASHBOARD,
214 array($this, 'displayMainPage')
215 );
216
217 // All invoices submenu
218 add_submenu_page(
219 'easy-invoice',
220 __('All Invoices', 'easy-invoice'),
221 __('All Invoices', 'easy-invoice'),
222 $this->menuCapability(PagesSlugs::ALL_INVOICES),
223 PagesSlugs::ALL_INVOICES,
224 array($this, 'displayMainPage')
225 );
226
227 // Add new invoice submenu
228 add_submenu_page(
229 'easy-invoice-hidden',
230 __('Add New Invoice', 'easy-invoice'),
231 __('Add New', 'easy-invoice'),
232 $this->menuCapability(PagesSlugs::INVOICE_NEW),
233 PagesSlugs::INVOICE_NEW,
234 array($this, 'displayMainPage')
235 );
236
237 // All quotes submenu
238 add_submenu_page(
239 'easy-invoice',
240 __('All Quotes', 'easy-invoice'),
241 __('All Quotes', 'easy-invoice'),
242 $this->menuCapability(PagesSlugs::ALL_QUOTES),
243 PagesSlugs::ALL_QUOTES,
244 array($this, 'displayMainPage')
245 );
246
247 // Add new quote submenu
248 add_submenu_page(
249 'easy-invoice-hidden',
250 __('Add New Quote', 'easy-invoice'),
251 __('Add New Quote', 'easy-invoice'),
252 $this->menuCapability(PagesSlugs::QUOTE_NEW),
253 PagesSlugs::QUOTE_NEW,
254 array($this, 'displayMainPage')
255 );
256
257 // Payments submenu
258 add_submenu_page(
259 'easy-invoice-hidden',
260 __('Payments', 'easy-invoice'),
261 __('Payments', 'easy-invoice'),
262 $this->menuCapability(PagesSlugs::PAYMENTS),
263 PagesSlugs::PAYMENTS,
264 array($this, 'displayMainPage')
265 );
266
267 // Add new payment submenu
268 add_submenu_page(
269 'easy-invoice-hidden',
270 __('Add New Payment', 'easy-invoice'),
271 __('Add New Payment', 'easy-invoice'),
272 $this->menuCapability(PagesSlugs::PAYMENT_NEW),
273 PagesSlugs::PAYMENT_NEW,
274 array($this, 'displayMainPage')
275 );
276
277 // Clients submenu
278 add_submenu_page(
279 'easy-invoice',
280 __('All Clients', 'easy-invoice'),
281 __('All Clients', 'easy-invoice'),
282 $this->menuCapability(PagesSlugs::CLIENTS),
283 PagesSlugs::CLIENTS,
284 array($this, 'displayMainPage')
285 );
286
287 // Item Library + Template Builder pages are reached via the
288 // Easy Invoice in-app sidebar (templates/main-template.php), NOT
289 // the WP admin submenu. We still register the page slugs so the
290 // URLs resolve, but we parent them under 'easy-invoice-hidden'
291 // so they don't render duplicate entries in the WP submenu —
292 // the WP submenu showed the link AND the in-app sidebar showed
293 // the same link, giving the user two visible navigation
294 // entry-points to the same page.
295 //
296 // Registration is still gated on the addon being active — if
297 // the addon is off, the slug isn't registered, so direct-URL
298 // access returns the standard "you do not have sufficient
299 // permissions" page.
300 if (easy_invoice_has_pro() && \EasyInvoice\Addons\AddonManager::shouldLoad('item_library')) {
301 add_submenu_page(
302 'easy-invoice-hidden',
303 __('Item Library', 'easy-invoice'),
304 __('Item Library', 'easy-invoice'),
305 $this->menuCapability('easy-invoice-pro-item-library'),
306 'easy-invoice-pro-item-library',
307 array($this, 'displayMainPage')
308 );
309 }
310
311 if (easy_invoice_has_pro() && \EasyInvoice\Addons\AddonManager::shouldLoad('custom_templates')) {
312 // Template listing — reachable from the in-app sidebar.
313 add_submenu_page(
314 'easy-invoice-hidden',
315 __('Template Builder', 'easy-invoice'),
316 __('Template Builder', 'easy-invoice'),
317 $this->menuCapability('easy-invoice-templates'),
318 'easy-invoice-templates',
319 array($this, 'displayMainPage')
320 );
321
322 // Create New Template — used by deep-links from inside the
323 // builder UI.
324 add_submenu_page(
325 'easy-invoice-hidden',
326 __('Create New Template', 'easy-invoice'),
327 __('Create New', 'easy-invoice'),
328 $this->menuCapability('easy-invoice-templates-new'),
329 'easy-invoice-templates-new',
330 array($this, 'displayMainPage')
331 );
332
333 // Template Builder canvas — full-screen builder, reached
334 // from the listing page's Edit / New buttons.
335 add_submenu_page(
336 'easy-invoice-hidden',
337 __('Template Builder Page', 'easy-invoice'),
338 __('Template Builder Page', 'easy-invoice'),
339 $this->menuCapability('easy-invoice-template-builder'),
340 'easy-invoice-template-builder',
341 array($this, 'displayMainPage')
342 );
343 }
344
345 // Hidden submenu pages - not shown in the menu but accessible
346 add_submenu_page(
347 'easy-invoice-hidden', // Use main menu as parent to avoid title issues
348 __('Edit Client', 'easy-invoice'),
349 __('Edit Client', 'easy-invoice'),
350 $this->menuCapability(PagesSlugs::CLIENT_EDIT),
351 PagesSlugs::CLIENT_EDIT,
352 array($this, 'displayMainPage')
353 );
354
355 add_submenu_page(
356 'easy-invoice-hidden', // Use main menu as parent to avoid title issues
357 __('View Client', 'easy-invoice'),
358 __('View Client', 'easy-invoice'),
359 $this->menuCapability(PagesSlugs::CLIENT_VIEW),
360 PagesSlugs::CLIENT_VIEW,
361 array($this, 'displayMainPage')
362 );
363
364 add_submenu_page(
365 'easy-invoice-hidden', // Use main menu as parent to avoid title issues
366 __('Preview Invoice', 'easy-invoice'),
367 __('Preview Invoice', 'easy-invoice'),
368 $this->menuCapability(PagesSlugs::INVOICE_PREVIEW),
369 PagesSlugs::INVOICE_PREVIEW,
370 array($this, 'displayPreviewPage')
371 );
372
373 add_submenu_page(
374 'easy-invoice-hidden', // Use main menu as parent to avoid title issues
375 __('Preview Quote', 'easy-invoice'),
376 __('Preview Quote', 'easy-invoice'),
377 $this->menuCapability(PagesSlugs::QUOTE_PREVIEW),
378 PagesSlugs::QUOTE_PREVIEW,
379 array($this, 'displayMainPage')
380 );
381
382 // Reports — addon-gated (Personal tier). Slug registered as a
383 // HIDDEN submenu so the WP admin sidebar doesn't double the
384 // in-app sidebar entry. When the reports addon is disabled, the
385 // slug isn't registered at all, so direct-URL access falls
386 // through to WP's "no permission" page.
387 if (\EasyInvoice\Addons\AddonManager::shouldLoad('reports')) {
388 add_submenu_page(
389 'easy-invoice-hidden',
390 __('Reports', 'easy-invoice'),
391 __('Reports', 'easy-invoice'),
392 $this->menuCapability(PagesSlugs::REPORTS),
393 PagesSlugs::REPORTS,
394 array($this, 'displayMainPage')
395 );
396 }
397
398 // Settings submenu
399 add_submenu_page(
400 'easy-invoice',
401 __('Settings', 'easy-invoice'),
402 __('Settings', 'easy-invoice'),
403 $this->menuCapability(PagesSlugs::SETTINGS),
404 PagesSlugs::SETTINGS,
405 array($this, 'displayMainPage')
406 );
407
408 // Addons submenu
409 add_submenu_page(
410 'easy-invoice',
411 __('Addons', 'easy-invoice'),
412 __('Addons', 'easy-invoice'),
413 $this->menuCapability(PagesSlugs::ADDONS),
414 PagesSlugs::ADDONS,
415 array($this, 'displayMainPage')
416 );
417
418 // License submenu
419 add_submenu_page(
420 'easy-invoice',
421 __('License', 'easy-invoice'),
422 __('License', 'easy-invoice'),
423 $this->menuCapability(PagesSlugs::LICENSE),
424 PagesSlugs::LICENSE,
425 array($this, 'displayMainPage')
426 );
427
428 // Free vs Pro submenu - only show in free version
429 if (!easy_invoice_has_pro()) {
430 add_submenu_page(
431 'easy-invoice',
432 __('Free vs Pro', 'easy-invoice'),
433 __('Free vs Pro', 'easy-invoice'),
434 $this->menuCapability('easy-invoice-free-vs-pro'),
435 'easy-invoice-free-vs-pro',
436 array($this, 'displayMainPage')
437 );
438 }
439
440 // Join Community submenu
441 add_submenu_page(
442 'easy-invoice-hidden',
443 __('Join Community', 'easy-invoice'),
444 __('Join Community', 'easy-invoice'),
445 'read',
446 'easy-invoice-join-community',
447 array($this, 'redirectToCommunity')
448 );
449 }
450
451 /**
452 * Redirect to community page
453 */
454 public function redirectToCommunity() {
455 wp_safe_redirect(esc_url_raw('https://www.facebook.com/groups/mantrabraincommunity'));
456 exit;
457 }
458
459 /**
460 * Register invoice preview page.
461 *
462 * Placeholder kept as an extension point — the preview-page hook used to
463 * be wired here but is now routed through the per-document preview
464 * controller. Left in place so themes/plugins relying on a no-op
465 * `registerPreviewPage()` call don't fatal.
466 */
467 public function registerPreviewPage() {
468 // No-op: preview rendering is handled by the per-document controller.
469 }
470
471 /**
472 * Display main page
473 */
474 public function displayMainPage() {
475 include EASY_INVOICE_PLUGIN_DIR . 'templates/main-template.php';
476 }
477
478 /**
479 * Display preview page
480 */
481 public function displayPreviewPage() {
482 $this->invoice_controller->display(['page' => PagesSlugs::INVOICE_PREVIEW]);
483 }
484
485 /**
486 * Display license page
487 */
488 public function displayLicensePage() {
489 include EASY_INVOICE_PLUGIN_DIR . 'templates/admin/license-page.php';
490 }
491
492 /**
493 * Display addons page
494 */
495 public function displayAddonsPage() {
496 if (class_exists('\\EasyInvoice\\Controllers\\AddonsController')) {
497 (new \EasyInvoice\Controllers\AddonsController())->display();
498 }
499 }
500
501 /**
502 * Handle main page content based on the current page
503 *
504 * @param string $page Current page
505 */
506 public function mainPageContent($page) {
507 switch ($page) {
508 case PagesSlugs::DASHBOARD:
509 $this->dashboard_controller->display(['page' => PagesSlugs::DASHBOARD]);
510 break;
511
512 case PagesSlugs::ALL_INVOICES:
513 $this->invoice_controller->display(['page' => PagesSlugs::ALL_INVOICES]);
514 break;
515
516 case PagesSlugs::INVOICE_NEW:
517 $this->invoice_controller->display(['page' => PagesSlugs::INVOICE_NEW]);
518 break;
519
520 case PagesSlugs::INVOICE_PREVIEW:
521 $this->invoice_controller->display(['page' => PagesSlugs::INVOICE_PREVIEW]);
522 break;
523
524 case PagesSlugs::ALL_QUOTES:
525 $this->quote_controller->display(['page' => PagesSlugs::ALL_QUOTES]);
526 break;
527
528 case PagesSlugs::QUOTE_NEW:
529 $this->quote_controller->display(['page' => PagesSlugs::QUOTE_NEW]);
530 break;
531
532 case PagesSlugs::QUOTE_PREVIEW:
533 $this->quote_controller->display(['page' => PagesSlugs::QUOTE_PREVIEW]);
534 break;
535
536 case PagesSlugs::PAYMENTS:
537 if (isset($_GET['action'])) {
538 $this->payment_controller->display(['page' => sanitize_key(wp_unslash($_GET['action']))]); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
539 } else {
540 $this->payment_controller->display(['page' => PagesSlugs::PAYMENTS]);
541 }
542 break;
543
544 case PagesSlugs::PAYMENT_NEW:
545 $this->payment_controller->display(['page' => PagesSlugs::PAYMENT_NEW]);
546 break;
547
548 case PagesSlugs::CLIENTS:
549 $this->client_controller->display(['page' => PagesSlugs::CLIENTS]);
550 break;
551
552 case PagesSlugs::CLIENT_EDIT:
553 $this->client_controller->display(['page' => PagesSlugs::CLIENT_EDIT]);
554 break;
555
556 // Handle Pro features — gated on addon-state. The menu
557 // registrations above are also addon-gated, so these cases
558 // should only fire when the addon is enabled. Re-checking
559 // shouldLoad() here is defensive in case the user hits the
560 // page slug directly via URL.
561 case 'easy-invoice-pro-item-library':
562 if (
563 easy_invoice_has_pro()
564 && \EasyInvoice\Addons\AddonManager::shouldLoad('item_library')
565 && class_exists('\EasyInvoicePro\Addons\ItemLibrary\Controllers\ItemLibraryController')
566 ) {
567 $item_library_controller = new \EasyInvoicePro\Addons\ItemLibrary\Controllers\ItemLibraryController();
568 $item_library_controller->displayMainPage();
569 }
570 break;
571
572 case 'easy-invoice-templates':
573 if (
574 easy_invoice_has_pro()
575 && \EasyInvoice\Addons\AddonManager::shouldLoad('custom_templates')
576 && class_exists('\EasyInvoicePro\Addons\CustomTemplates\Controllers\TemplateBuilderController')
577 ) {
578 $template_builder_controller = new \EasyInvoicePro\Addons\CustomTemplates\Controllers\TemplateBuilderController();
579 $template_builder_controller->displayMainPage();
580 }
581 break;
582
583 case 'easy-invoice-template-builder':
584 if (
585 easy_invoice_has_pro()
586 && \EasyInvoice\Addons\AddonManager::shouldLoad('custom_templates')
587 && class_exists('\EasyInvoicePro\Addons\CustomTemplates\Controllers\TemplateBuilderController')
588 ) {
589 $template_builder_controller = new \EasyInvoicePro\Addons\CustomTemplates\Controllers\TemplateBuilderController();
590 $template_builder_controller->displayMainPage();
591 }
592 break;
593
594 case PagesSlugs::CLIENT_VIEW:
595 $this->client_controller->display(['page' => PagesSlugs::CLIENT_VIEW]);
596 break;
597
598 case PagesSlugs::REPORTS:
599 // Reports is addon-gated. The submenu page slug is only
600 // registered when shouldLoad('reports') is true, so this
601 // case ordinarily only fires for active-addon users —
602 // but recheck defensively in case the page is reached
603 // via direct URL right after the addon was disabled.
604 if (\EasyInvoice\Addons\AddonManager::shouldLoad('reports')) {
605 $this->report_controller->display(['page' => PagesSlugs::REPORTS]);
606 }
607 break;
608
609 case PagesSlugs::SETTINGS:
610 $this->settings_controller->display(['page' => PagesSlugs::SETTINGS]);
611 break;
612
613 case PagesSlugs::LICENSE:
614 $this->displayLicensePage();
615 break;
616
617 case PagesSlugs::ADDONS:
618 $this->displayAddonsPage();
619 break;
620
621 case PagesSlugs::EMAIL_SETTINGS:
622 case PagesSlugs::EMAIL_SETTINGS_GENERAL:
623 case PagesSlugs::EMAIL_SETTINGS_INVOICE:
624 case PagesSlugs::EMAIL_SETTINGS_QUOTE:
625 case PagesSlugs::EMAIL_SETTINGS_PAYMENT:
626 $this->settings_controller->display(['page' => PagesSlugs::SETTINGS, 'subsection' => $page]);
627 break;
628
629 case 'easy-invoice-migration':
630 include EASY_INVOICE_PLUGIN_DIR . 'includes/Migration/templates/migration-page.php';
631 break;
632
633 case 'easy-invoice-free-vs-pro':
634 include EASY_INVOICE_PLUGIN_DIR . 'templates/admin/free-vs-pro-page.php';
635 break;
636
637 default:
638 // Addon pages render themselves via the
639 // `easy_invoice_admin_main_content` action at priority 11. The
640 // dispatcher must NOT fall through to the Dashboard for those
641 // slugs, otherwise the Dashboard markup gets emitted first and
642 // the addon panel renders on top of it (two pages stacked).
643 //
644 // We treat any slug that's either (a) prefixed with
645 // `easy-invoice-addon-` or (b) registered in AddonRegistry as
646 // an addon page — and just `break` to let the addon's own hook
647 // handle the rendering.
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)) {
677 break;
678 }
679 if (class_exists('\\EasyInvoice\\Addons\\AddonRegistry')) {
680 foreach (\EasyInvoice\Addons\AddonRegistry::all() as $_addon) {
681 if (empty($_addon['settings_url'])) continue;
682 $parsed = wp_parse_url($_addon['settings_url']);
683 if (empty($parsed['query'])) continue;
684 parse_str($parsed['query'], $q);
685 if (($q['page'] ?? '') === $page) {
686 return; // Addon owns this slug — bail without rendering.
687 }
688 }
689 }
690 $this->dashboard_controller->display(['page' => PagesSlugs::DASHBOARD]);
691 break;
692 }
693 }
694
695 /**
696 * Set proper admin title to avoid WordPress core issues
697 *
698 * @param string $title The current admin title
699 * @return string The modified admin title
700 */
701 public function setAdminTitle($title) {
702 $current_page = isset($_GET['page']) ? sanitize_text_field($_GET['page']) : '';
703
704 // Only modify titles for our plugin pages
705 if (strpos($current_page, 'easy-invoice') === 0 || strpos($current_page, 'easy-quote') === 0) {
706 $page_titles = [
707 'easy-invoice' => __('Dashboard', 'easy-invoice'),
708 'easy-invoice-all' => __('All Invoices', 'easy-invoice'),
709 'easy-invoice-builder' => __('Add New Invoice', 'easy-invoice'),
710 'easy-invoice-preview' => __('Preview Invoice', 'easy-invoice'),
711 'easy-quote-all' => __('All Quotes', 'easy-invoice'),
712 'easy-invoice-quote-builder' => __('Add New Quote', 'easy-invoice'),
713 'easy-quote-preview' => __('Preview Quote', 'easy-invoice'),
714 'easy-invoice-payments' => __('Payments', 'easy-invoice'),
715 'easy-invoice-payment-new' => __('Add New Payment', 'easy-invoice'),
716 'easy-invoice-clients' => __('Clients', 'easy-invoice'),
717 'easy-invoice-client-edit' => __('Edit Client', 'easy-invoice'),
718 'easy-invoice-client-view' => __('View Client', 'easy-invoice'),
719 'easy-invoice-reports' => __('Reports', 'easy-invoice'),
720 'easy-invoice-settings' => __('Settings', 'easy-invoice'),
721 'easy-invoice-email-settings' => __('Email Settings', 'easy-invoice'),
722 'easy-invoice-migration' => __('Migration', 'easy-invoice'),
723 'easy-invoice-free-vs-pro' => __('Free vs Pro', 'easy-invoice'),
724 'easy-invoice-addons' => __('Addons', 'easy-invoice'),
725 ];
726
727 if (isset($page_titles[$current_page])) {
728 return $page_titles[$current_page] . ' - ' . get_bloginfo('name');
729 }
730 }
731
732 return $title;
733 }
734
735 /**
736 * Body class for Easy Invoice full-width layout (used by hideAdminUi CSS).
737 *
738 * @param string $classes Space-separated classes.
739 * @return string
740 */
741 public function adminBodyClass($classes) {
742 $screen = get_current_screen();
743 if (!$screen || !isset($screen->id)) {
744 return $classes;
745 }
746 if (strpos($screen->id, 'easy-invoice') !== false || strpos($screen->id, 'easy-quote') !== false) {
747 $classes .= ' easy-invoice-admin-body';
748 }
749 return $classes;
750 }
751
752 /**
753 * Hide the WordPress admin UI for our custom pages
754 */
755 public function hideAdminUi() {
756 $screen = get_current_screen();
757 if ($screen && property_exists($screen, 'id') && (strpos($screen->id, 'easy-invoice') !== false || strpos($screen->id, 'easy-quote') !== false)) {
758 ?>
759 <style>
760 #wpadminbar,
761 #adminmenumain,
762 #wpfooter {
763 display: none !important;
764 }
765 #wpcontent {
766 margin-left: 0 !important;
767 padding: 0 !important;
768 }
769 .wrap {
770 margin: 0 !important;
771 padding: 0 !important;
772 }
773 /*
774 * Admin bar is hidden but body.admin-bar often still gets top padding,
775 * shifting #wpbody down while the fixed plugin sidebar stays at y=0 — header borders misalign.
776 */
777 body.easy-invoice-admin-body.admin-bar {
778 padding-top: 0 !important;
779 }
780 body.easy-invoice-admin-body.admin-bar #wpbody,
781 body.easy-invoice-admin-body.admin-bar #wpbody-content {
782 padding-top: 0 !important;
783 }
784 </style>
785 <?php
786 }
787 }
788 }
789