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 reviewwp_ajax_brm_submissions_bulk_action- Bulk moderationwp_ajax_brm_submissions_search_students- Student filter searchwp_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
- Always verify permissions before creating or reviewing submissions
- Use the service layer rather than direct post meta for complex operations
- Handle file uploads securely through SubmissionFileHandler
- Check due dates via the resolver, not raw post meta
- Emit events for custom workflows (service methods do this automatically)