availabilityController = new AvailabilityController( $availability, $offerings ); $this->lessonController = new LessonController( $bookings, $payments ); $this->offeringController = new OfferingController( $offerings ); $this->questionController = new QuestionController( $questions, $offerings ); $this->policyController = new PolicyController( $policies, $policyVersions, $policyService ); $this->registrationController = new RegistrationController( $invites ); $this->groupClassController = new GroupClassController( $enrollments, $offerings ); $this->studentController = new StudentController( $bookings, $availability, $offerings, $enrollments, $resolver ); $this->settings = $settings; $this->paymentController = new PaymentController( $payments, $paymentService ); } public function register(): void { add_action( 'admin_menu', [ $this, 'addPages' ] ); } public function addPages(): void { $this->addStudioSeparators(); // Studio-wide dashboard: all upcoming lessons across instructors. add_menu_page( __( 'Scheduler', 'unsupervised-schedular' ), __( 'Scheduler', 'unsupervised-schedular' ), RoleManager::CAP_VIEW_ALL_LESSONS, 'us-scheduler', [ $this->lessonController, 'renderAdminDashboard' ], 'dashicons-calendar-alt', 39 ); // Instructor: manage their own availability. add_menu_page( __( 'My Availability', 'unsupervised-schedular' ), __( 'My Availability', 'unsupervised-schedular' ), RoleManager::CAP_MANAGE_AVAILABILITY, 'us-availability', [ $this->availabilityController, 'renderPage' ], 'dashicons-clock', 40 ); // Studio admin / instructor: manage offerings. add_menu_page( __( 'Offerings', 'unsupervised-schedular' ), __( 'Offerings', 'unsupervised-schedular' ), RoleManager::CAP_MANAGE_OFFERINGS, 'us-offerings', [ $this->offeringController, 'renderPage' ], 'dashicons-tag', 33 ); // Studio admin / instructor: manage per-offering intake questions. add_submenu_page( 'us-offerings', __( 'Questions', 'unsupervised-schedular' ), __( 'Questions', 'unsupervised-schedular' ), RoleManager::CAP_MANAGE_QUESTIONS, 'us-questions', [ $this->questionController, 'renderPage' ] ); // Studio admin: draft, version, and publish policies. add_menu_page( __( 'Policies', 'unsupervised-schedular' ), __( 'Policies', 'unsupervised-schedular' ), RoleManager::CAP_MANAGE_POLICIES, 'us-policies', [ $this->policyController, 'renderPage' ], 'dashicons-text-page', 31 ); // Studio admin: invite students to register. add_menu_page( __( 'Invites', 'unsupervised-schedular' ), __( 'Invites', 'unsupervised-schedular' ), RoleManager::CAP_MANAGE_STUDENTS, 'us-invites', [ $this->registrationController, 'renderPage' ], 'dashicons-email', 32 ); // Studio admin: all group-class enrolments. add_menu_page( __( 'Group Classes', 'unsupervised-schedular' ), __( 'Group Classes', 'unsupervised-schedular' ), RoleManager::CAP_VIEW_ALL_LESSONS, 'us-group-classes', [ $this->groupClassController, 'renderPage' ], 'dashicons-groups', 36 ); // Studio admin: browse students and their activity. add_menu_page( __( 'Students', 'unsupervised-schedular' ), __( 'Students', 'unsupervised-schedular' ), RoleManager::CAP_MANAGE_STUDENTS, 'us-students', [ $this->studentController, 'renderPage' ], 'dashicons-id', 35 ); // Studio admin: confirm pending (e-transfer) payments. add_menu_page( __( 'Payments', 'unsupervised-schedular' ), __( 'Payments', 'unsupervised-schedular' ), RoleManager::CAP_MANAGE_BILLING, 'us-payments', [ $this->paymentController, 'renderPage' ], 'dashicons-money-alt', 37 ); // Studio admin: Stripe credentials and billing settings. add_menu_page( __( 'Studio Settings', 'unsupervised-schedular' ), __( 'Studio Settings', 'unsupervised-schedular' ), RoleManager::CAP_MANAGE_BILLING, 'us-settings', [ $this->settings, 'renderPage' ], 'dashicons-admin-settings', 30 ); // Instructor: view their upcoming lessons. add_menu_page( __( 'My Lessons', 'unsupervised-schedular' ), __( 'My Lessons', 'unsupervised-schedular' ), RoleManager::CAP_VIEW_LESSONS, 'us-my-lessons', [ $this->lessonController, 'renderInstructorLessons' ], 'dashicons-welcome-learn-more', 41 ); } /** * Insert sidebar separators around the studio menus so they sit visually * apart from the core WordPress items and split into three sections — * mirroring the dividers core uses. Each separator is only added when the user * can see a menu in the following section, to avoid orphaned dividers. * * Layout: [29] · setup (Settings/Policies/Invites/Offerings) · [34] · * people & money (Students/Group Classes/Payments) · [38] · operations * (Scheduler) and instructor menus. */ private function addStudioSeparators(): void { $this->addSeparatorAt( 29, $this->userSeesStudioMenu() ); $this->addSeparatorAt( 34, $this->userSeesPeopleSection() ); $this->addSeparatorAt( 38, current_user_can( RoleManager::CAP_VIEW_ALL_LESSONS ) ); } private function addSeparatorAt( int $position, bool $visible ): void { if ( ! $visible ) { return; } global $menu; if ( ! is_array( $menu ) || isset( $menu[ $position ] ) ) { return; } // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- writing to $menu is the supported way to add an admin sidebar separator. $menu[ $position ] = [ '', 'read', 'us-studio-separator-' . $position, '', 'wp-menu-separator' ]; } private function userSeesPeopleSection(): bool { return current_user_can( RoleManager::CAP_MANAGE_STUDENTS ) || current_user_can( RoleManager::CAP_VIEW_ALL_LESSONS ) || current_user_can( RoleManager::CAP_MANAGE_BILLING ); } private function userSeesStudioMenu(): bool { $caps = [ RoleManager::CAP_VIEW_ALL_LESSONS, RoleManager::CAP_VIEW_LESSONS, RoleManager::CAP_MANAGE_AVAILABILITY, RoleManager::CAP_MANAGE_OFFERINGS, RoleManager::CAP_MANAGE_QUESTIONS, RoleManager::CAP_MANAGE_POLICIES, RoleManager::CAP_MANAGE_STUDENTS, RoleManager::CAP_MANAGE_BILLING, ]; foreach ( $caps as $cap ) { if ( current_user_can( $cap ) ) { return true; } } return false; } }