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 Functions
brm_get_submission_post_type()
Get the configured post type slug for submissions.
/**
* @return string Post type slug (default: 'brm_submission')
*/
$post_type = brm_get_submission_post_type();
brm_get_submission_reviewer_roles()
Get the roles allowed to review submissions.
/**
* @return array Array of role slugs
*/
$roles = brm_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 view this submission?
$can_view = $service->can_view_submission( $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_user_submissions( $user_id, $assignment_post_id );
// Get all submissions for an assignment
$submissions = $service->get_assignment_submissions( $assignment_post_id, array(
'status' => 'submitted', // or 'approved', 'rejected', 'resubmit'
'group_id' => $group_id, // optional group filter
) );
// Get pending submissions for review
$pending = $service->get_pending_submissions( $reviewer_user_id );
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
'text' => '...', // 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
'review_status' => 'approved', // Review result
'feedback' => '...', // Reviewer feedback
);
Reviewing Submissions
// Approve submission
$service->review_submission( $submission_id, $reviewer_user_id, 'approved', 'Great work!' );
// Reject submission
$service->review_submission( $submission_id, $reviewer_user_id, 'rejected', 'Does not meet requirements.' );
// Request resubmission
$service->review_submission( $submission_id, $reviewer_user_id, 'resubmit', '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
// Process uploaded files from $_FILES
$attachment_ids = $handler->process_uploads( $submission_id, $_FILES['submission_files'] );
if ( is_wp_error( $attachment_ids ) ) {
// Handle upload error
}
Validating Files
// Check file type
$allowed = $handler->is_allowed_file_type( 'document.pdf', $assignment_post_id );
// Check file size
$valid_size = $handler->is_valid_file_size( $file_size_bytes, $assignment_post_id );
Getting Submission Files
// Get all files for a submission
$files = $handler->get_submission_files( $submission_id );
// Get secure download URL
$download_url = $handler->get_download_url( $attachment_id, $submission_id );
SubmissionDueDateResolver Class
Calculates due dates based on assignment settings, drip rules, and groups:
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( $event_data ) {
$submission_id = $event_data['submission_id'];
$user_id = $event_data['user_id'];
$assignment_id = $event_data['assignment_id'];
// Custom notification, analytics, etc.
} );
// After resubmission
add_action( 'brm_event_submission_resubmitted', function( $event_data ) {
// Same structure as submission_created
} );
// After submission reviewed
add_action( 'brm_event_submission_reviewed', function( $event_data ) {
$submission_id = $event_data['submission_id'];
$reviewer_id = $event_data['reviewer_id'];
$status = $event_data['status']; // approved, rejected, resubmit
$feedback = $event_data['feedback'];
} );
File Events
// After file uploaded
add_action( 'brm_submission_file_uploaded', function( $attachment_id, $submission_id, $user_id ) {
// Virus scan, backup, etc.
}, 10, 3 );
// After file downloaded
add_action( 'brm_submission_file_downloaded', function( $attachment_id, $submission_id, $user_id ) {
// Track downloads
}, 10, 3 );
Filters
Submission Permissions
// Filter who can submit
add_filter( 'brm_can_submit', function( $can_submit, $user_id, $assignment_id ) {
// Custom logic (e.g., check if user completed prerequisites)
if ( ! user_completed_prerequisite( $user_id, $assignment_id ) ) {
return false;
}
return $can_submit;
}, 10, 3 );
// 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
// Custom allowed file types
add_filter( 'brm_submission_allowed_file_types', function( $types, $assignment_id ) {
// Add custom types for specific assignments
$types[] = 'psd';
$types[] = 'ai';
return $types;
}, 10, 2 );
// Custom max file size
add_filter( 'brm_submission_max_file_size', function( $bytes, $assignment_id ) {
// Allow larger files for video assignments
$is_video = get_post_meta( $assignment_id, '_is_video_assignment', true );
if ( $is_video ) {
return 100 * 1024 * 1024; // 100MB
}
return $bytes;
}, 10, 2 );
Notifications
// Custom notification recipients
add_filter( 'brm_submission_notification_recipients', function( $recipients, $submission_id, $type ) {
// Add course instructor
$assignment_id = get_post_meta( $submission_id, '_brm_submission_assignment_id', true );
$instructor_id = get_post_meta( $assignment_id, '_course_instructor', true );
if ( $instructor_id ) {
$recipients[] = $instructor_id;
}
return $recipients;
}, 10, 3 );
// Custom notification message
add_filter( 'brm_submission_notification_message', function( $message, $submission_id, $type, $recipient_id ) {
// Customize based on type: 'new_submission', 'review_complete', 'resubmit_request'
return $message;
}, 10, 4 );
Due Date Calculation
add_filter( 'brm_submission_due_date', function( $due_at, $assignment_id, $user_id ) {
// Custom due date logic
// e.g., extend deadline for certain users
if ( user_has_extension( $user_id, $assignment_id ) ) {
$due_at = date( 'Y-m-d H:i:s', strtotime( $due_at . ' +3 days' ) );
}
return $due_at;
}, 10, 3 );
Assignment Post Meta
Assignments store configuration in post meta:
// Check if post is an assignment
$is_assignment = get_post_meta( $post_id, '_brm_is_assignment', true );
// Get assignment settings
$submission_type = get_post_meta( $post_id, '_brm_submission_type', true );
// Values: 'file', 'text', 'url', 'mixed'
$max_attempts = get_post_meta( $post_id, '_brm_submission_max_attempts', true );
// Default: 1
$due_date = get_post_meta( $post_id, '_brm_submission_due_date', true );
// Format: Y-m-d H:i:s
$allowed_extensions = get_post_meta( $post_id, '_brm_submission_file_types', true );
// Comma-separated: 'pdf,doc,docx'
$max_file_size = get_post_meta( $post_id, '_brm_submission_max_file_size', true );
// In bytes
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 );
$review_status = get_post_meta( $submission_id, '_brm_submission_review_status', 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 );
AJAX Endpoints
The module registers these AJAX actions:
wp_ajax_brm_submit_assignment- Create new submissionwp_ajax_brm_review_submission- Submit reviewwp_ajax_brm_get_submission- Fetch submission datawp_ajax_brm_download_submission_file- Secure file download
Making AJAX Requests
// Submit assignment (from JS)
const formData = new FormData();
formData.append('action', 'brm_submit_assignment');
formData.append('nonce', brmSubmissions.nonce);
formData.append('assignment_id', assignmentId);
formData.append('text', textContent);
formData.append('files[]', fileInput.files[0]);
const response = await fetch(brmSubmissions.ajaxUrl, {
method: 'POST',
body: formData
});
const data = await response.json();
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 is a group leader for the submission's author:
$submission = $service->get_submission( $submission_id );
$author_id = $submission['user_id'];
$group_id = $submission['group_id'];
if ( $group_id ) {
$reviewer_role = brm_get_user_group_role( $group_id, $reviewer_user_id );
$author_role = brm_get_user_group_role( $group_id, $author_id );
$can_review = 'leader' === $reviewer_role && 'member' === $author_role;
}
Building Custom Submission UIs
// In a Bricks template or custom template
// Check if current post is an assignment
$is_assignment = get_post_meta( get_the_ID(), '_brm_is_assignment', true );
if ( $is_assignment && 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_user_submissions( $user_id, $post_id );
// 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'];
}
}
}
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)
Related Documentation
- Groups & Team Licenses API Reference — Group leader review integration
- Extending & Customizing — General extension patterns
- AJAX & REST Endpoints — Making AJAX requests
- Assignment Submissions (User Guide) — End-user documentation