isset( $attributes['lessonPost'] ) ? max( 0, (int) $attributes['lessonPost'] ) : 0 ); } public static function render( array $attributes, string $content = '', ?\WP_Block $block = null ): string { unset( $content ); $item_id = Context::item_id( self::attributes( $attributes ), $block ); $item = $item_id > 0 ? ( Services::curriculum_repo()->items( array( $item_id ) )[ $item_id ] ?? null ) : null; if ( null === $item ) { return ''; } LessonExtras::rendered( self::NAME, $item->id ); $unit = Services::unit_repo()->get_many( array( $item->unit_id ) )[ $item->unit_id ] ?? null; if ( null === $unit || UnitType::ASSIGNMENT !== $unit->type ) { return ''; } $user = get_current_user_id(); $settings = Services::assignments()->settings( $item->unit_id ); if ( $user <= 0 ) { return self::wrap( $block, 'guest', self::facts( $settings, -2 ) . Markup::login_prompt( __( 'Log in to hand in this assignment.', 'rooster-lms' ) ) ); } $open = Services::gate()->can_open_item( $user, $item->id ); if ( $open->allowed && $open->reason->is_staff() ) { return self::wrap( $block, 'staff', self::facts( $settings, -2 ) . Ui::notice( __( 'Learners hand in their answers here. You grade them in the grading queue.', 'rooster-lms' ), 'info' ) ); } $decision = Services::gate()->can_complete_item( $user, $item->id ); if ( $decision->allowed || AccessReason::CONTENT_CONTROLLED !== $decision->reason ) { return ''; } $state = Services::assignments()->state( $user, $item->id ); return self::learner( $item, $block, $settings, $state ); } private static function learner( Item $item, ?\WP_Block $block, array $settings, array $state ): string { $tries = $state['tries']; $latest = array() === $tries ? null : $tries[ count( $tries ) - 1 ]; $status = null === $latest ? null : ( self::status_of( (string) $latest['status'] ) ?? SubmissionStatus::DRAFT ); $body = self::facts( $settings, $state['left'] ); $config = array( 'item' => $item->id, 'i18n' => self::texts(), ); $mode = 'start'; if ( null === $latest ) { $body .= self::start_button( $settings, false ); } elseif ( SubmissionStatus::DRAFT === $status ) { $mode = 'draft'; $config = array_merge( $config, self::draft_config( $latest ) ); $body .= self::draft( $latest ); } elseif ( SubmissionStatus::SUBMITTED === $status ) { $mode = 'submitted'; /* translators: %s: date and time. */ $body .= Ui::notice( sprintf( __( 'Handed in on %s. Your answer is waiting for grading.', 'rooster-lms' ), Front::when( $latest['submitted_at'] ?? null ) ), 'info' ) . self::answer( $latest ); } else { $mode = 'graded'; $body .= self::result( $latest ); if ( SubmissionStatus::FAILED === $status && 0 !== $state['left'] ) { $body .= self::start_button( $settings, true ); } elseif ( SubmissionStatus::FAILED === $status ) { $body .= Ui::notice( __( 'You have no tries left.', 'rooster-lms' ), 'warning' ); } } if ( count( $tries ) > 1 ) { $body .= self::history( array_slice( $tries, 0, -1 ) ); } if ( in_array( $mode, array( 'start', 'draft', 'graded' ), true ) ) { Front::enqueue( self::NAME ); } return self::wrap( $block, $mode, $body, array( 'data-rl-assignment' => (string) wp_json_encode( Front::config( $config ) ) ) ); } private static function draft_config( array $try ): array { $remaining = -1; if ( null !== $try['deadline_at'] ) { $deadline = strtotime( $try['deadline_at'] . ' UTC' ); $remaining = false === $deadline ? 0 : max( 0, $deadline - Clock::timestamp() ); } return array( 'submission' => (int) $try['id'], 'files' => $try['files'], 'remaining' => $remaining, 'maxFiles' => (int) Settings::get( 'assignment_max_files' ), 'maxFileMb' => (int) Settings::get( 'assignment_max_file_mb' ), 'maxMediaMb' => (int) Settings::get( 'assignment_media_max_mb' ), 'audio' => (bool) Settings::get( 'assignment_audio' ), 'video' => (bool) Settings::get( 'assignment_video' ), 'exts' => AssignmentService::upload_extensions( true ), ); } private static function facts( array $settings, int $left ): string { $items = array(); if ( -2 === $left ) { $items[] = null === $settings['tries'] ? __( 'Unlimited tries', 'rooster-lms' ) /* translators: %s: number of tries. */ : sprintf( _n( '%s try', '%s tries', (int) $settings['tries'], 'rooster-lms' ), Format::number( (int) $settings['tries'] ) ); } elseif ( -1 === $left ) { $items[] = __( 'Unlimited tries', 'rooster-lms' ); } else { /* translators: %s: number of tries left. */ $items[] = sprintf( _n( '%s try left', '%s tries left', $left, 'rooster-lms' ), Format::number( $left ) ); } if ( null !== $settings['time_limit_sec'] ) { /* translators: %s: a duration such as "1 hour 30 minutes". */ $items[] = sprintf( __( 'Time limit: %s', 'rooster-lms' ), self::duration( (int) $settings['time_limit_sec'] ) ); } if ( null !== $settings['pass_percent'] ) { /* translators: %s: a percentage. */ $items[] = sprintf( __( 'Pass mark: %s', 'rooster-lms' ), Format::percent( (int) $settings['pass_percent'] ) ); } $html = '
' /* translators: %s: a duration. */ . esc_html( sprintf( __( 'When you start, you have %s to hand in your answer.', 'rooster-lms' ), self::duration( (int) $settings['time_limit_sec'] ) ) ) . '
'; return $warn . '' . Ui::button( $again ? __( 'Try again', 'rooster-lms' ) : __( 'Start the assignment', 'rooster-lms' ), 'primary', array( 'data-rl-asg' => 'start' ) ) . '
' . ''; } private static function draft( array $try ): string { $id = 'rl-asg-' . (int) $try['id']; $html = ''; if ( null !== $try['deadline_at'] ) { $html .= '' . esc_html__( 'Time left:', 'rooster-lms' ) . ' ' . esc_html( self::clock( (int) max( 0, (int) strtotime( $try['deadline_at'] . ' UTC' ) - Clock::timestamp() ) ) ) . '
'; } $html .= ''; return $html; } private static function result( array $try ): string { $passed = 'passed' === $try['status']; $html = Ui::notice( $passed ? __( 'Your assignment has passed.', 'rooster-lms' ) : __( 'Your assignment did not pass.', 'rooster-lms' ), $passed ? 'success' : 'warning' ); if ( null !== $try['score'] ) { /* translators: %s: a percentage. */ $html .= '' . esc_html( sprintf( __( 'Score: %s', 'rooster-lms' ), Format::percent( (int) round( (float) $try['score'] ) ) ) ) . '
'; } if ( '' !== trim( (string) $try['feedback'] ) ) { $html .= '