ID : absint( $GLOBALS['post_ID'] ?? 0 ); if ( $post_id <= 0 || Services::course_repo()->id_for_post( $post_id ) <= 0 || ! current_user_can( 'edit_post', $post_id ) ) { return; } add_meta_box( self::BOX, __( 'Invitations and RSVP', 'rooster-lms' ), array( $this, 'render_box' ), PostTypes::COURSE, 'normal', 'low' ); } public function render_box( $post ): void { $course_id = $post instanceof \WP_Post ? Services::course_repo()->id_for_post( (int) $post->ID ) : 0; $html = self::html( $course_id, 0, true ); if ( '' !== $html ) { $html .= '

' . esc_html__( 'All invitations', 'rooster-lms' ) . '

'; } echo wp_kses( $html, Kses::admin() ); } public static function enqueue(): void { wp_enqueue_script( self::SCRIPT, plugins_url( 'assets/ui/invitations.js', ROOSTER_LMS_FILE ), array( Assets::ask() ), ROOSTER_LMS_VERSION, true ); Assets::enqueue_table( 'rest' ); } public static function html( int $course_id, int $unit_id, bool $admin, array $opts = array() ): string { if ( $course_id <= 0 || ! InvitationService::may_manage( $course_id, get_current_user_id() ) ) { return ''; } return self::panel( array( 'course' => $course_id, 'unit' => $unit_id, ) + self::options( $opts ), $admin, trim( ( $unit_id > 0 ? __( 'Invite people to this lesson with personal "Yes / No" buttons: people who asked to be notified, earlier learners who agreed to hear from us, an organisation, a Newsletter list or a CSV list. Everyone is invited once, in their own language, with a one-click unsubscribe.', 'rooster-lms' ) : __( 'Invite people to this course with personal "Yes / No" buttons: people who asked to be notified, earlier learners who agreed to hear from us, an organisation, a Newsletter list or a CSV list. Everyone is invited once, in their own language, with a one-click unsubscribe; answers close at the deadline you set.', 'rooster-lms' ) ) . ' ' . \RoosterLMS\Newsletter\NewsletterModule::panel_note() ), true, $admin || ! empty( $opts['open'] ) || ! empty( $opts['compose'] ) ); } private static function options( array $opts ): array { $out = array(); if ( ! empty( $opts['compose'] ) ) { $out['compose'] = true; } foreach ( array( 'occurrence', 'open', 'edit' ) as $key ) { if ( (int) ( $opts[ $key ] ?? 0 ) > 0 ) { $out[ $key ] = (int) $opts[ $key ]; } } if ( 'rooster' === ( $opts['buttons'] ?? '' ) ) { $out['buttons'] = 'rooster'; } return $out; } public static function cohort_html( int $cohort_id, bool $compose = false, array $opts = array() ): string { $cohort = $cohort_id > 0 ? Services::cohort_repo()->get( $cohort_id ) : null; if ( null === $cohort || ! InvitationService::may_manage( 0, get_current_user_id(), $cohort->id ) ) { return ''; } unset( $opts['compose'], $opts['occurrence'] ); return self::panel( array( 'course' => 0, 'unit' => 0, 'cohort' => $cohort->id, 'cohortName' => $cohort->name, 'compose' => $compose && $cohort->is_active(), ) + self::options( $opts ), true, __( 'Invite people to join this cohort with personal "Yes / No" buttons: the members of a cohort or of part of the organisation, the organisation, or a CSV list. A "yes" makes the person a learner of the organisation (when they are not one yet) and a member of this cohort; the funnel shows who was invited, who accepted and who joined.', 'rooster-lms' ), $cohort->is_active(), true, \RoosterLMS\Orgs\CohortWords::panel_texts( $cohort->org_id ) ); } private static function panel( array $target, bool $admin, string $help, bool $offer, bool $open, array $texts = array() ): string { self::enqueue(); $config = array( 'rest' => esc_url_raw( rest_url( 'rooster-lms/v1/' ) ), 'nonce' => wp_create_nonce( 'wp_rest' ), 'csv' => \RoosterLMS\Rest\DownloadRoute::url( 'invitees', '/invitations/' . \RoosterLMS\Rest\DownloadRoute::ID_PLACEHOLDER . '/invitees', array( 'format' => 'csv' ) ), 'admin' => $admin, 'locale' => str_replace( '_', '-', determine_locale() ), 'i18n' => $texts + self::strings(), ) + $target; $rooster = ! $admin || 'rooster' === ( $target['buttons'] ?? '' ); $button = $rooster ? \RoosterLMS\UI\Ui::button_class( 'secondary', array( 'size' => 'sm' ) ) : 'button'; $html = '
'; $html .= '' . esc_html__( 'Invitations and RSVP', 'rooster-lms' ) . ''; $html .= '

' . esc_html( $help ) . '

'; $html .= '
'; if ( $offer ) { $html .= '

'; } $html .= ''; $html .= ''; $html .= '

'; return $html . '
'; } private static function strings(): array { return array( 'loading' => __( 'Loading…', 'rooster-lms' ), 'none' => __( 'No invitation yet.', 'rooster-lms' ), 'failed' => __( 'That did not work. Try again.', 'rooster-lms' ), 'saved' => __( 'Saved.', 'rooster-lms' ), 'subject' => __( 'Subject', 'rooster-lms' ), 'body' => __( 'Message', 'rooster-lms' ), 'placeholders' => __( 'You can use these placeholders:', 'rooster-lms' ), 'defaultText' => __( 'Leave empty to use the default wording, in each person\'s language.', 'rooster-lms' ), 'answers' => __( 'Answers offered', 'rooster-lms' ), 'yes' => __( 'Yes', 'rooster-lms' ), 'no' => __( 'No', 'rooster-lms' ), 'maybe' => __( 'Maybe', 'rooster-lms' ), 'noAnswer' => __( 'No answer', 'rooster-lms' ), /* translators: %d: hours. */ 'until' => sprintf( __( 'Answers close (UTC; empty = %d hours after the lesson)', 'rooster-lms' ), RsvpService::close_hours() ), 'yesAction' => __( 'A "yes"', 'rooster-lms' ), 'yesNone' => __( 'is counted only', 'rooster-lms' ), 'yesRegister' => __( 'offers guests an account', 'rooster-lms' ), 'yesEnrol' => __( 'enrols the person', 'rooster-lms' ), 'places' => __( 'Places (0 = the lesson\'s)', 'rooster-lms' ), 'attachments' => __( 'Programme: media library file IDs, separated by commas', 'rooster-lms' ), 'save' => __( 'Save', 'rooster-lms' ), 'close' => __( 'Close', 'rooster-lms' ), 'edit' => __( 'Edit', 'rooster-lms' ), 'addPeople' => __( 'Add people', 'rooster-lms' ), 'source' => __( 'From', 'rooster-lms' ), 'organisation' => __( 'Organisation', 'rooster-lms' ), 'list' => __( 'List', 'rooster-lms' ), 'csvFile' => __( 'CSV file (e-mail, first name, last name, language)', 'rooster-lms' ), 'csvConsent' => __( 'The people on this list agreed to get invitations from us.', 'rooster-lms' ), 'csvMax' => (string) InvitationsController::CSV_MAX_BYTES, 'csvHint' => Upload::hint( 'CSV', (string) size_format( InvitationsController::CSV_MAX_BYTES ) ), 'csvType' => Upload::type_message( 'CSV' ), 'csvSize' => Upload::size_message( (string) size_format( InvitationsController::CSV_MAX_BYTES ) ), 'check' => __( 'Check', 'rooster-lms' ), 'add' => __( 'Add', 'rooster-lms' ), /* translators: 1: number found, 2: number new. */ 'checked' => __( '%1$s found, %2$s new.', 'rooster-lms' ), /* translators: %s: number of people. */ 'added' => __( '%s added.', 'rooster-lms' ), 'skipInvalid' => __( 'invalid address', 'rooster-lms' ), 'skipDuplicate' => __( 'duplicate', 'rooster-lms' ), 'skipAlready' => __( 'already invited to this lesson', 'rooster-lms' ), 'skipSuppressed' => __( 'does not want our e-mails', 'rooster-lms' ), /* translators: %s: line number. */ 'line' => __( 'line %s', 'rooster-lms' ), 'preview' => __( 'Preview', 'rooster-lms' ), 'send' => __( 'Send now', 'rooster-lms' ), /* translators: %s: number of people. */ 'confirm' => __( 'Send this invitation now to %s people?', 'rooster-lms' ), 'sending' => __( 'Sending: the e-mails go out in the next minutes.', 'rooster-lms' ), 'report' => __( 'Answers', 'rooster-lms' ), 'export' => __( 'Export CSV', 'rooster-lms' ), 'cancel' => __( 'Stop invitation', 'rooster-lms' ), 'confirmCancel' => __( 'Stop this invitation? Nothing more is sent and its links stop working.', 'rooster-lms' ), 'dismiss' => __( 'Cancel', 'rooster-lms' ), 'toSend' => __( 'will get it now', 'rooster-lms' ), 'sentCurrent' => __( 'already have it', 'rooster-lms' ), 'noConsent' => __( 'no consent', 'rooster-lms' ), 'unsubscribed' => __( 'unsubscribed', 'rooster-lms' ), 'weeklyCap' => __( 'had enough e-mails this week', 'rooster-lms' ), 'invited' => __( 'invited', 'rooster-lms' ), 'announcement' => __( 'E-mail to earlier learners', 'rooster-lms' ), 'stateDraft' => __( 'Draft', 'rooster-lms' ), 'stateScheduled' => __( 'Scheduled', 'rooster-lms' ), 'stateSending' => __( 'Sending', 'rooster-lms' ), 'stateSent' => __( 'Sent', 'rooster-lms' ), 'stateCancelled' => __( 'Stopped', 'rooster-lms' ), 'earlier' => __( 'Earlier learners', 'rooster-lms' ), 'interest' => __( 'People who asked to be notified', 'rooster-lms' ), 'csv' => __( 'CSV list', 'rooster-lms' ), 'newsletter' => __( 'Newsletter list', 'rooster-lms' ), 'yesJoin' => __( 'joins a cohort', 'rooster-lms' ), 'targetCohort' => __( 'Cohort a "yes" joins', 'rooster-lms' ), 'funnel' => __( 'From invitation to outcome', 'rooster-lms' ), /* translators: %s: number of people invited. */ 'funnelInvited' => __( 'Invited: %s', 'rooster-lms' ), /* translators: 1: number of people who accepted, 2: their share of the people invited, e.g. 40%. */ 'funnelAccepted' => __( 'Accepted: %1$s (%2$s)', 'rooster-lms' ), /* translators: 1: number of people who attended, 2: their share of the people invited, e.g. 40%. */ 'funnelAttended' => __( 'Attended: %1$s (%2$s)', 'rooster-lms' ), /* translators: 1: number of people who enrolled, 2: their share of the people invited, e.g. 40%. */ 'funnelEnrolled' => __( 'Enrolled: %1$s (%2$s)', 'rooster-lms' ), /* translators: 1: number of people who joined the cohort, 2: their share of the people invited, e.g. 40%. */ 'funnelJoined' => __( 'Joined: %1$s (%2$s)', 'rooster-lms' ), 'untilCohort' => __( 'Answers close (UTC)', 'rooster-lms' ), 'placesCohort' => __( 'Places (0 = no limit)', 'rooster-lms' ), /* translators: %s: cohort name. */ 'joinsCohort' => __( 'A "yes" joins the cohort %s.', 'rooster-lms' ), ); } }