Submissions API

New in version 0.9.80

The Submissions module provides a complete API for managing student assignments, file uploads, review workflows, and notifications. This guide covers the public APIs, hooks, and filters for extending submission functionality.

Checking Module Status

use BaselMedia\BricksMembers\Core\ModuleRegistry;

if ( ModuleRegistry::is_active( 'submissions' ) ) {
    // Submissions functionality available
}

Helper Services

SubmissionsAdminBridge::get_submission_post_type()

Get the configured post type slug for submissions.

/**
 * @return string Post type slug (default: 'brm_submission')
 */
$post_type = \BaselMedia\BricksMembers\Modules\Submissions\SubmissionsAdminBridge::get_instance()->get_submission_post_type();

SubmissionsAdminBridge::get_submission_reviewer_roles()

Get the roles allowed to review submissions.

/**
 * @return array Array of role slugs
 */
$roles = \BaselMedia\BricksMembers\Modules\Submissions\SubmissionsAdminBridge::get_instance()->get_submission_reviewer_roles();
// Default: array( 'administrator' )

SubmissionsService Class

use BaselMedia\BricksMembers\Modules\Submissions\SubmissionsService;

$service = SubmissionsService::get_instance();

Creating Submissions

$submission_id = $service->create_submission(
    $user_id,     // Student user ID
    $post_id,     // Assignment post ID
    $group_id,    // Group ID or null
    'mixed',      // Type: 'file', 'text', 'url', 'mixed'
    array(
        'text'           => 'My submission text...',
        'urls'           => array( 'https://example.com/my-project' ),
        'attachment_ids' => array( 123, 456 ),
    )
);

if ( false === $submission_id ) {
    // Submission failed (permission, validation, etc.)
}

Checking Submission Permissions

// Can user submit to this assignment?
$can_submit = $service->can_submit( $user_id, $assignment_post_id );

// Can user review this submission?
$can_review = $service->can_review( $user_id, $submission_id );

// Can user access files attached to this submission?
$can_access_file = $service->can_access_file( $user_id, $submission_id );

Getting Submissions

// Get a single submission
$submission = $service->get_submission( $submission_id );

// Get user's submissions for an assignment
$submissions = $service->get_submissions_for_user( $user_id, array(
    'post_id'  => $assignment_post_id,
    'per_page' => 20,
) );

// Get all submissions for an assignment
$submissions = $service->get_submissions_for_post( $assignment_post_id, array(
    'status'   => 'submitted', // or 'approved', 'rejected', 'resubmit'
    'group_id' => $group_id,   // optional group filter
) );

// Get submitted items for an assignment review queue
$review_queue = $service->get_submissions_for_post( $assignment_post_id, array(
    'status'   => 'submitted',
    'per_page' => 20,
) );

Submission Data Structure

$submission = array(
    'id'             => 123,              // Submission post ID
    'user_id'        => 45,               // Student user ID
    'assignment_id'  => 67,               // Assignment post ID
    'group_id'       => 89,               // Group ID or null
    'type'           => 'mixed',          // file, text, url, mixed
    'status'         => 'submitted',      // submitted, approved, rejected, resubmit
    'attempt'        => 1,                // Attempt number
    'content'        => '...',            // Text content
    'urls'           => array( '...' ),   // URL array
    'attachment_ids' => array( 123 ),     // File attachment IDs
    'submitted_at'   => '2026-03-04...',  // Timestamp
    'due_at'         => '2026-03-10...',  // Due date
    'reviewed_by'    => 78,               // Reviewer user ID
    'reviewed_at'    => '2026-03-05...',  // Review timestamp
    'score'          => 95,               // Score or null
    'feedback'       => '...',            // Reviewer feedback
);

Reviewing Submissions

// Approve submission
$service->review_submission( $submission_id, $reviewer_user_id, 'approved', 95, 'Great work!' );

// Reject submission
$service->review_submission( $submission_id, $reviewer_user_id, 'rejected', null, 'Does not meet requirements.' );

// Request resubmission
$service->review_submission( $submission_id, $reviewer_user_id, 'resubmit', null, 'Please revise section 3.' );

Counting Attempts

// Get attempt count
$attempts = $service->get_user_submission_attempt_count( $user_id, $assignment_post_id, $group_id );

// Check if max attempts reached
$assignment_max = get_post_meta( $assignment_post_id, '_brm_submission_max_attempts', true );
$can_resubmit = $attempts < (int) $assignment_max;

SubmissionFileHandler Class

Handles secure file uploads and downloads:

use BaselMedia\BricksMembers\Modules\Submissions\SubmissionFileHandler;

$handler = SubmissionFileHandler::get_instance();

Uploading Files

// Save one uploaded file payload after a submission exists
$attachment_id = SubmissionFileHandler::save_file( $submission_id, $_FILES['form-field-submission_file'] );

if ( is_wp_error( $attachment_id ) ) {
    // Handle upload error
}

Validating Files

// File extension and size validation happens inside save_file().
// Defaults come from brm_submission_allowed_file_types_default and
// brm_submission_max_file_size_default.

Getting Submission Files

// Get secure download URL
$download_url = SubmissionFileHandler::get_file_url( $attachment_id );

SubmissionDueDateResolver Class

Calculates due dates based on assignment settings. Current modes are none, fixed, after drip unlock when Drip is active, and after completion when Progress is active:

use BaselMedia\BricksMembers\Modules\Submissions\SubmissionDueDateResolver;

$resolver = SubmissionDueDateResolver::get_instance();

// Get the due date for a specific user
$due_at = $resolver->resolve_assignment_due_at( $assignment_post_id, $user_id );
// Returns: DateTime string or null

// Check if submission is late
$is_late = $resolver->is_submission_late( $submission_id );

Actions (Hooks)

Submission Lifecycle

// After submission created
add_action( 'brm_event_submission_created', function( \BaselMedia\BricksMembers\Core\Event $event ) {
    $submission_id = $event->get_resource_id();
    $user_id = $event->get_user_id();
    $assignment_id = $event->get_context_value( 'post_id' );

    // Custom notification, analytics, etc.
} );

// After resubmission
add_action( 'brm_event_submission_resubmitted', function( \BaselMedia\BricksMembers\Core\Event $event ) {
    // Same Event object shape as submission_created.
} );

// After submission reviewed
add_action( 'brm_event_submission_reviewed', function( \BaselMedia\BricksMembers\Core\Event $event ) {
    $submission_id = $event->get_resource_id();
    $reviewer_id = $event->get_context_value( 'reviewer_id' );
    $status = $event->get_context_value( 'status' ); // approved, rejected, resubmit
    $feedback = $event->get_context_value( 'feedback' );
} );

File Events

There is no separate public file-uploaded or file-downloaded hook in the current Submissions runtime. Use the submission lifecycle events above, or add a code-level extension point before documenting one as public API.

Filters

Submission Permissions

// Filter who can review
add_filter( 'brm_can_review_submission', function( $can_review, $user_id, $submission_id ) {
    // Allow group leaders to review their members' submissions
    // (Groups integration adds this automatically)
    return $can_review;
}, 10, 3 );

File Validation

// Configure global upload defaults with options or the Submissions settings UI.
update_option( 'brm_submission_allowed_file_types_default', 'pdf,doc,docx,ppt,pptx,jpg,png' );
update_option( 'brm_submission_max_file_size_default', 10 ); // MB

Notifications

// Control whether reviewer notification emails are sent.
// The reviewer notification uses the Emails notification key
// "brm_submission_reviewer_notify" through EmailDispatchService.
// Enable/disable it from Submissions settings; it is not a public filter.

// Customize student review notification body.
add_filter( 'brm_submission_review_email_template', function( $message, $event, $submission ) {
    return $message;
}, 10, 3 );

Due Date Calculation

// Fixed due date
update_post_meta( $assignment_id, '_brm_submission_due_at', '2026-03-15 23:59:59' );

// Due mode and offset are managed by the assignment submission settings.

Assignment Post Meta

Assignments store configuration in post meta:

// Get assignment settings
$submissions_enabled = get_post_meta( $post_id, '_brm_submissions_enabled', true );

$max_attempts = get_post_meta( $post_id, '_brm_submission_max_attempts', true );

$due_at = get_post_meta( $post_id, '_brm_submission_due_at', true );
// Format: Y-m-d H:i:s

$require_approval = get_post_meta( $post_id, '_brm_submission_require_approval', true );

Submission Post Meta

// Core submission data
$assignment_id = get_post_meta( $submission_id, '_brm_submission_assignment_id', true );
$status = get_post_meta( $submission_id, '_brm_submission_status', true );
$type = get_post_meta( $submission_id, '_brm_submission_type', true );
$attempt = get_post_meta( $submission_id, '_brm_submission_attempt', true );
$group_id = get_post_meta( $submission_id, '_brm_submission_group_id', true );

// Submission content
$urls = get_post_meta( $submission_id, '_brm_submission_urls', true ); // JSON array
$attachments = get_post_meta( $submission_id, '_brm_submission_attachment_ids', true ); // JSON array

// Review data
$reviewed_by = get_post_meta( $submission_id, '_brm_submission_reviewed_by', true );
$reviewed_at = get_post_meta( $submission_id, '_brm_submission_reviewed_at', true );
$feedback = get_post_meta( $submission_id, '_brm_submission_feedback', true );

// Timing
$submitted_at = get_post_meta( $submission_id, '_brm_submission_submitted_at', true );
$due_at = get_post_meta( $submission_id, '_brm_submission_due_at', true );

Transport Endpoints

Student submissions are handled by the Bricks form action brm_submit_assignment. Admin review and management use these AJAX actions:

  • wp_ajax_brm_review_submission - Submit review
  • wp_ajax_brm_submissions_bulk_action - Bulk moderation
  • wp_ajax_brm_submissions_search_students - Student filter search
  • wp_ajax_brm_save_submission_settings - Save settings

Use a Bricks Form element with action Submit Assignment (BRM) for frontend assignment submission instead of posting directly to admin-ajax.

Groups Integration

When both Groups and Submissions modules are active:

// Group leaders can review their members' submissions
// This is handled automatically via the brm_can_review_submission filter

// To check if a user can review a submission through group role permissions:
$submission = $service->get_submission( $submission_id );
$author_id = $submission['user_id'];
$group_id = $submission['group_id'];

if ( $group_id ) {
    $can_review = \BaselMedia\BricksMembers\Modules\Groups\GroupRolePermissionService::get_instance()
        ->viewer_can_review_target_user_submission(
            $reviewer_user_id,
            $author_id
        );
}

Building Custom Submission UIs

// In a Bricks template or custom template

// Check if submissions are enabled for the current assignment post
$submissions_enabled = filter_var(
    get_post_meta( get_the_ID(), '_brm_submissions_enabled', true ),
    FILTER_VALIDATE_BOOLEAN
);

if ( $submissions_enabled && is_user_logged_in() ) {
    $service = SubmissionsService::get_instance();
    $user_id = get_current_user_id();
    $post_id = get_the_ID();

    // Get existing submissions
    $submissions = $service->get_submissions_for_user( $user_id, array(
        'post_id'  => $post_id,
        'per_page' => 20,
    ) );

    // Check if can submit
    $can_submit = $service->can_submit( $user_id, $post_id );

    if ( $can_submit ) {
        // Render submission form
    }

    if ( ! empty( $submissions ) ) {
        // Render submission history
        foreach ( $submissions as $submission ) {
            echo 'Attempt ' . $submission['attempt'] . ': ' . $submission['status'];
        }
    }
}

Bricks Conditions

  • brm_can_review - Show review UI inside a submission loop or assignment context only when the current user can review that submission.
  • brm_user_can_review_submissions - Show page-level reviewer dashboards, or the matching access-required state, based on the current user's reviewer access.
  • brm_submission_status - Show status-specific badges or panels for submitted, approved, rejected, and resubmit states.

Best Practices

  1. Always verify permissions before creating or reviewing submissions
  2. Use the service layer rather than direct post meta for complex operations
  3. Handle file uploads securely through SubmissionFileHandler
  4. Check due dates via the resolver, not raw post meta
  5. Emit events for custom workflows (service methods do this automatically)
Get BricksMembers

Start Building Your Membership Site Today

Create, sell, and manage your content without limits. BricksMembers gives you everything you need to build membership and LMS sites with Bricks Builder.

Lifetime updates & bug fixes • Premium support • 0% transaction fees • 60-day money-back guarantee