value ] = array_merge( (array) ( $entries[ NotificationKey::INVITATION->value ] ?? array() ), array( 'group' => 'invitations', 'label' => NotificationKey::INVITATION->label(), 'staff_label' => '', 'sides' => array( Side::LEARNER->value ), 'staff_on' => false, 'staff_roles' => 0, 'timing' => 0, 'offset_min' => 0, 'placeholders' => array( 'recipient', 'course', 'lesson', 'session', 'venue', 'invitation', 'site', 'message' ), 'description' => __( 'An invitation to a lesson with the personal Yes / No buttons; sent when its author presses Send, to people who agreed to hear from us. Its own subject and text, when its author wrote them, replace this one.', 'rooster-lms' ), 'subject' => $text['subject'], 'body' => $text['body'], ) ); return $entries; } public static function placeholders( $groups ) { if ( is_array( $groups ) && isset( $groups['invitation']['fields'] ) && is_array( $groups['invitation']['fields'] ) ) { $groups['invitation']['fields'] += array( 'when' => __( 'Date and time of the lesson (its time zone)', 'rooster-lms' ), 'rsvp_until' => __( 'Answers close', 'rooster-lms' ), ); } return $groups; } public static function value( $value, $name, $ctx, $params = array() ) { unset( $params ); if ( 'course.title' === $name && '' === (string) $value && $ctx instanceof Context && $ctx->invitation_id > 0 && $ctx->course_id <= 0 ) { $inv = InvitationRepository::get( $ctx->invitation_id ); return null !== $inv && $inv->is_cohort_only() ? InvitationTexts::facts( $inv, $ctx->lang )['course'] : $value; } if ( '' !== (string) $value || ! is_string( $name ) || ! str_starts_with( $name, 'invitation.' ) || ! $ctx instanceof Context || $ctx->invitation_id <= 0 ) { return $value; } $inv = InvitationRepository::get( $ctx->invitation_id ); if ( null === $inv ) { return $value; } $token = (string) ( $ctx->extra['rsvp_token'] ?? '' ); switch ( $name ) { case 'invitation.title': return InvitationTexts::facts( $inv, $ctx->lang )['title']; case 'invitation.when': $when = InvitationTexts::facts( $inv, $ctx->lang )['when']; return '' !== $when ? $when : __( 'Date to be announced', 'rooster-lms' ); case 'invitation.rsvp_until': return InvitationTexts::values( $inv, '', $ctx->lang )['{rsvp_until}']; case 'invitation.rsvp_url': return '' === $token ? '' : RsvpRoute::url( $token ); case 'invitation.rsvp_buttons': return InvitationMail::buttons( $inv, $token ); default: return $value; } } public static function template( int $template_id ): ?Template { $t = $template_id > 0 ? TemplateRepository::get( $template_id ) : null; if ( null !== $t && ( NotificationKey::INVITATION->value === $t->msg_key || $t->is_custom() ) && Side::LEARNER === $t->side ) { return $t; } return TemplateRepository::default_row( NotificationKey::INVITATION->value, Side::LEARNER ); } public static function text( $text, $invitation_id, $template_id, $lang ) { unset( $invitation_id ); $t = self::template( (int) $template_id ); if ( null === $t ) { return $text; } $texts = Renderer::texts( $t, (string) $lang ); if ( '' === trim( $texts['subject'] ) || '' === trim( $texts['body'] ) ) { return $text; } return array( 'subject' => $texts['subject'], 'body' => $texts['body'], 'format' => $texts['format'], 'template_id' => $t->id, ); } public static function invite( $done, $occurrence_id, $email, $context = array() ) { if ( null !== $done ) { return $done; } $context = is_array( $context ) ? $context : array(); try { $made = false; $inv = self::invitation_for( (int) $occurrence_id, (int) ( $context['invitation_id'] ?? 0 ), $made ); if ( null === $inv ) { return false; } $row = array( 'email' => (string) $email, 'first_name' => (string) ( $context['first_name'] ?? '' ), 'last_name' => (string) ( $context['last_name'] ?? '' ), 'lang' => (string) ( $context['lang'] ?? '' ), ); $out = Audiences::rows( $inv, array( 1 => $row ), InviteeSource::MANUAL, false, 'manual' ); if ( $out['added'] <= 0 ) { return false; } $alone = $made || ( InvitationState::SENT === $inv->state && array( self::row_id( $inv->id, (string) $email ) ) === SendRules::due_ids( $inv ) ); if ( $alone ) { InvitationService::send( $inv->id, Actor::system() ); } return true; } catch ( DomainError $e ) { unset( $e ); return false; } } private static function row_id( int $invitation_id, string $email ): int { return (int) Db::value( 'SELECT id FROM %i WHERE invitation_id = %d AND email_hash = %s', Db::table( 'invitees' ), $invitation_id, Tokens::email_hash( $email ) ); } private static function invitation_for( int $occurrence_id, int $invitation_id, bool &$made = false ): ?Invitation { $o = $occurrence_id > 0 ? \RoosterLMS\Domain\Services::live_session_repo()->occurrence( $occurrence_id ) : null; $s = null === $o ? null : \RoosterLMS\Domain\Services::live_session_repo()->get( $o->session_id ); if ( null === $s ) { return null; } foreach ( InvitationRepository::for_target( $s->course_id, $s->unit_id ) as $inv ) { if ( InvitationKind::RSVP === $inv->kind && ! $inv->is_cancelled() && $inv->occurrence_id === $occurrence_id && ( 0 === $invitation_id || $inv->id === $invitation_id ) ) { return $inv; } } if ( $invitation_id > 0 ) { return null; } $made = true; return InvitationService::create( $s->course_id, $s->unit_id, array( 'occurrence_id' => $occurrence_id ), Actor::system() ); } }