handleFormAction(); } // phpcs:disable WordPress.Security.NonceVerification.Recommended -- read-only policy/version selectors. $policyId = absint( Val::int( $_GET['policy_id'] ?? 0 ) ); if ( 0 === $viewVersionId ) { $viewVersionId = absint( Val::int( $_GET['version_id'] ?? 0 ) ); } // phpcs:enable WordPress.Security.NonceVerification.Recommended $policyList = $this->policies->findAll(); $selectedPolicy = $policyId > 0 ? $this->policies->findById( $policyId ) : null; $policyVersions = null !== $selectedPolicy ? $this->versions->findByPolicy( (int) $selectedPolicy->id ) : null; $viewedVersion = null !== $selectedPolicy ? $this->loadVersionForPolicy( (int) $selectedPolicy->id, $viewVersionId ) : null; include USC_PLUGIN_DIR . 'templates/admin/policies.php'; } /** * Process the posted action. * * @return array{string, int} Status notice, and the version to open in the * viewer (0 to leave the current selection alone). */ private function handleFormAction(): array { // Nonce is verified by the caller (renderPage) before this method runs. // phpcs:disable WordPress.Security.NonceVerification.Missing $action = sanitize_key( Val::string( wp_unslash( $_POST['usc_action'] ?? '' ) ) ); if ( 'create_policy' === $action ) { $title = sanitize_text_field( Val::string( wp_unslash( $_POST['title'] ?? '' ) ) ); $slugRaw = sanitize_text_field( Val::string( wp_unslash( $_POST['slug'] ?? '' ) ) ); $slug = sanitize_title( '' !== $slugRaw ? $slugRaw : $title ); $scope = sanitize_key( Val::string( wp_unslash( $_POST['acceptance_scope'] ?? Policy::SCOPE_BOOKING ) ) ); if ( ! in_array( $scope, Policy::VALID_SCOPES, true ) ) { $scope = Policy::SCOPE_BOOKING; } $withinLimits = mb_strlen( $title ) <= Policy::MAX_TITLE_LENGTH && mb_strlen( $slug ) <= Policy::MAX_SLUG_LENGTH; if ( '' !== $title && '' !== $slug && $withinLimits && null === $this->policies->findBySlug( $slug ) ) { $this->service->createPolicy( $title, $slug, $scope ); } return [ '', 0 ]; } $policyId = absint( Val::int( $_POST['policy_id'] ?? 0 ) ); if ( $policyId <= 0 || null === $this->policies->findById( $policyId ) ) { return [ '', 0 ]; } if ( 'add_version' === $action ) { $body = wp_kses_post( Val::string( wp_unslash( $_POST['body'] ?? '' ) ) ); $this->service->addDraftVersion( $policyId, $body ); } if ( 'edit_version' === $action ) { $source = $this->loadVersionForPolicy( $policyId, absint( Val::int( $_POST['version_id'] ?? 0 ) ) ); if ( null === $source ) { return [ '', 0 ]; } $body = wp_kses_post( Val::string( wp_unslash( $_POST['body'] ?? '' ) ) ); // A draft has never been shown to a student, so it is edited in place. // A published (or archived) version is what students accepted, so an // edit branches a new draft and leaves the original untouched. if ( PolicyVersion::STATUS_DRAFT === $source->status ) { $this->versions->updateBody( (int) $source->id, $body ); return [ sprintf( /* translators: %d: the edited version number. */ __( 'Draft version %d was updated.', 'unsupervised-schedular' ), $source->versionNumber ), (int) $source->id, ]; } return [ sprintf( /* translators: %d: the version number the edit was based on. */ __( 'Your changes to version %d were saved as a new draft version.', 'unsupervised-schedular' ), $source->versionNumber ), $this->service->addDraftVersion( $policyId, $body ), ]; } if ( 'publish_version' === $action ) { $versionId = absint( Val::int( $_POST['version_id'] ?? 0 ) ); if ( $versionId > 0 ) { $this->service->publishVersion( $policyId, $versionId ); } } // phpcs:enable WordPress.Security.NonceVerification.Missing return [ '', 0 ]; } /** * Load a version by id, confirming it belongs to the given policy. */ private function loadVersionForPolicy( int $policyId, int $versionId ): ?PolicyVersion { if ( $versionId <= 0 ) { return null; } $version = $this->versions->findById( $versionId ); return null !== $version && $version->policyId === $policyId ? $version : null; } }