Overview
The problem_controller handles all operations related to problems including CRUD operations, test case management, import/export functionality, and problem metadata editing.
Constructor
public function __construct()
Applies the following middleware:
auth - Requires user authentication
read_only_archive - Makes controller read-only when app is in archived mode
ip_white_listing - IP whitelist validation
CRUD Methods
index()
public function index(Request $request)
Displays a paginated listing of problems with filtering and search capabilities.
Search term to filter problems by name
Array of tag IDs to filter problems
Array of usernames to filter by problem owner
Access Control:
- Admin: Can view all problems
- Others: Can only view problems available to them
Returns: Paginated view with:
- Problem list with assignments and languages
- Total submissions count per problem
- Accepted submissions count per problem
- Acceptance ratio
- All tags for filtering
- All problem owners for filtering
Example from controller:
if (Auth::user()->role->name == "admin") {
$all_problem = Problem::latest();
} else {
$all_problem = Problem::available(Auth::user()->id)->latest();
}
// Apply filters
if ($request->get("search") != "") {
$all_problem->where("name", "like", "%" . trim($request->get("search")) . "%");
}
if ($request->get("tag_id") != null) {
$all_problem->whereHas("tags", function (Builder $query) use ($request) {
$query->whereIn("tag_id", $request->get("tag_id"));
});
}
create()
Shows the form for creating a new problem.
Access Control:
- Admin, head_instructor, and instructor only
- Aborts with 404 for other roles
Returns: View with:
- All available languages (ordered by sorting)
- Maximum file uploads limit
- All available tags
- Empty problem instance
store()
public function store(Request $request)
Stores a newly created problem in the database.
Problem name (max 255 characters)
Administrative notes about the problem
Allow problem to appear in practice mode
Allow other instructors to use this problem
Allow students to download input test cases
Allow students to download output test cases
Array of tag IDs or new tag names (prefixed with #)
Array of 1/0 indicating which languages are enabled
Array of language IDs corresponding to enable array
Array of time limits (seconds) for each language
Array of memory limits (KB) for each language
ZIP file containing test cases (in/ and out/ folders)
Array of individual test files to upload
Whether to rename test files sequentially when extracting ZIP
Validation Rules:
name: required, max 255 characters
Returns: Redirect to problems index with messages
Example from controller:
$langs = [];
foreach ($request->input("enable") as $i => $lang) {
if ($lang == 1) {
$langs += [
$request->input("language_id")[$i] => [
"time_limit" => $request->input("time_limit")[$i],
"memory_limit" => $request->input("memory_limit")[$i],
],
];
}
}
$problem["user_id"] = Auth::user()->id;
$p = Problem::create($problem);
$p->languages()->sync($langs);
show()
public function show($id)
Attempts to display a problem directly (not through assignment).
Note: This method always aborts with 404 because problems can only be viewed through assignments or practice mode.
edit()
public function edit(Problem $problem)
Shows the form for editing an existing problem.
Problem model instance to edit
Access Control:
- Calls
_can_edit_or_404() to verify permissions
Returns: View with:
- Problem data
- Problem’s languages with pivot data
- Problem’s tags
- All available languages and tags
- Directory tree dump of test cases
Example from controller:
$lang_of_problems = $problem->languages->keyBy("id");
$tags = $problem->tags->keyBy("id");
return view("problems.create", [
"problem" => $problem,
"languages" => $lang_of_problems,
"tags" => $tags,
"tree_dump" => shell_exec("tree -h " . $problem->get_directory_path()),
]);
update()
public function update(Request $request, Problem $problem)
Updates the specified problem in storage.
Problem model instance to update
Problem name (max 255 characters)
Validation Rules:
name: required, max 255 characters
editorial: nullable, must be valid URL
Process:
- Updates problem metadata
- Syncs tags (creates new tags if needed)
- Replaces language configurations
- Processes test file uploads if provided
Returns: Redirect to problems index with messages
destroy()
public function destroy($id = null)
Removes the specified problem from storage.
Access Control:
- Admin, head_instructor, and instructor only
- Calls
_can_edit_or_404() to verify ownership
Deletion Rules:
- Cannot delete if problem appears in assignments
- Cannot delete if problem has submissions
Returns: JSON response:
{
"done": 1,
"message": "Optional error message"
}
Test Case Methods
downloadtestsdesc()
public function downloadtestsdesc($id)
Downloads the problem description file (desc.html).
Note: Route is defined but implementation not shown in provided code.
Route: /problems/downloadtestsdesc/{id}
download_testcases()
public function download_testcases(Problem $problem, Assignment $assignment, $type = null)
Downloads input or output test cases for a problem.
Assignment model instance (for permission check)
Type of files to download: “in” or “out”
Access Control:
- User must be able to submit to the assignment
- Input download requires
allow_input_download flag
- Output download requires
allow_output_download flag
Returns: ZIP file download with automatic deletion
Route: /problems/downloadtestcases/{problem}/{assignment}/{type}
Example from controller:
$check = $assignment->can_submit(Auth::user(), $problem);
if (!$check->can_submit) {
abort(403, $check->error_message);
}
if ($type == "in" && $problem->allow_input_download == false) {
abort(403, "This problem does not allow input download");
}
$pathdir = $assignments_root . "/problems/" . $problem->id . "/" . $type . "/";
shell_exec("cd $pathdir && zip -r $zipFile *");
return response()->download($zipFile)->deleteFileAfterSend();
Import/Export Methods
export()
public function export(Request $request)
Exports one or more problems as a ZIP file with metadata.
Comma-separated list of problem IDs to export
Access Control:
- Admin: Can export any problem
- Others: Can only export sharable problems or their own problems
Export Includes:
- Test cases (in/ and out/ folders)
- Problem description (desc.html)
- Metadata JSON file (problem.wecode.metadata.json)
- PDF files if present
- Templates if present
Returns: ZIP file download with automatic deletion
Route: /problems/export (GET)
Example from controller:
$ids = explode(",", $request->input("ids"));
$probs = Problem::whereIn("id", $ids)->get()->load("user");
if (!in_array(Auth::user()->role->name, ["admin"])) {
$probs = $probs->reject(fn(Problem $prob, int $key)
=> !($prob->sharable && Auth::user()->role->name != "student")
&& $prob->user->id != Auth::user()->id
);
}
$probs->load("languages")->load("tags");
foreach ($probs as $prob) {
$pathdir = $prob->get_directory_path();
$metadata_file = $pathdir . "/problem.wecode.metadata.json";
file_put_contents($metadata_file, $prob->toJSON(JSON_PRETTY_PRINT));
shell_exec("cd $pathdir/.. && zip -r $zipFile " . $prob->id . "/*");
unlink($metadata_file);
}
import()
public function import(Request $request)
Imports problems from a ZIP file exported by the export function.
ZIP file containing exported problems
Access Control:
- Admin and head_instructor only
Validation Rules:
Import Process:
- Extracts ZIP to temporary directory
- Reads metadata JSON from each problem folder
- Creates new problem with metadata
- Maps language names to IDs
- Creates new tags if they don’t exist
- Copies test case files to problem directory
- Updates admin_note with import information
Returns: Redirect to problems index with error messages if any
Route: /problems/import (POST)
Example from controller:
$lang_to_id = Language::all()->pluck("id", "name");
$tag_to_id = Tag::all()->pluck('id', 'text');
foreach ($storage->directories($tmp_dir) as $prob_folder) {
$metadata = json_decode(
$storage->get("$prob_folder/problem.wecode.metadata.json")
);
$problem = new Problem((array) $metadata);
$problem->id = null;
$problem->user_id = Auth::user()->id;
$problem->admin_note .= sprintf(
"\nIMPORTED: original user %s(%s), updated %s",
$metadata->user->username,
$metadata->user->email,
new Carbon\Carbon($metadata->updated_at)
);
$problem->save();
// Map languages and sync
$langs = [];
foreach ($metadata->languages as $lang) {
if (!isset($lang_to_id[$lang->name])) continue;
$langs[$lang_to_id[$lang->name]] = [
"time_limit" => $lang->pivot->time_limit,
"memory_limit" => $lang->pivot->memory_limit,
];
}
$problem->languages()->sync($langs);
}
Description Methods
edit_description()
public function edit_description(Request $request, Problem $problem)
Updates the problem description (desc.html file).
HTML content for problem description
Access Control:
- Admin, head_instructor, and instructor only
Returns: Plain text response:
"success" - Description saved successfully
"error" - Failed to save description
Route: /problems/edit_description/{problem} (POST)
Example from controller:
private function save_problem_description(Problem $problem, $text, $type = "html") {
$problem_dir = $problem->get_directory_path();
if (file_put_contents("$problem_dir/desc.html", $text)) {
return true;
} else {
return false;
}
}
Toggle Methods
toggle_practice()
public function toggle_practice(Request $request, string $query)
Toggles the practice mode or sharable status of a problem.
Format: “practice.” or “sharable.”
Access Control:
- Calls
_can_edit_or_404() to verify permissions
Returns: Boolean value (true/false) of new state
Route: /problems/toggle_practice/{query?} (POST)
Example from controller:
$a = explode(".", $query);
$task = $a[0];
$id = $a[1];
$problem = Problem::find($id);
if ($task == "practice") {
$problem->allow_practice = !$problem->allow_practice;
$problem->save();
return $problem->allow_practice;
} else {
$problem->sharable = !$problem->sharable;
$problem->save();
return $problem->sharable;
}
public function edit_tags(Request $request, Problem $problem)
Updates the tags associated with a problem.
Array of tag IDs or new tag names (prefixed with #)
Access Control:
- Calls
_can_edit_or_404() to verify permissions
Process:
- Creates new tags if they don’t exist (tags prefixed with #)
- Syncs problem’s tags with provided list
Returns: JSON response:
{
"all_tags": [...],
"new_tags": [...]
}
Route: /problems/edit_tags/{problem?} (POST)
Example from controller:
private function _add_missing_tags($tags) {
foreach ($tags as $i => $tag) {
if (Tag::find($tag) == null) {
$tag = substr($tag, 1); // Remove '#' from new tag
if (Tag::where("text", $tag)->first() == []) {
$new_tag = new Tag();
$new_tag->text = $tag;
$new_tag->save();
$tags[$i] = (string) $new_tag->id;
}
}
}
return $tags;
}
Test File Upload
Test cases can be uploaded in two formats:
Structure:
problem.zip
├── desc.html (optional)
├── desc.pdf (optional)
├── in/
│ ├── input1.txt
│ ├── input2.txt
│ └── ...
└── out/
├── output1.txt
├── output2.txt
└── ...
Notes:
- Input files must be named
input{N}.txt where N starts from 1
- Output files must be named
output{N}.txt matching input numbers
- Can enable
rename_zip to automatically rename files sequentially
Upload individual files:
desc.html - Problem description (required)
input1.txt, input2.txt, … - Input test cases
output1.txt, output2.txt, … - Output test cases
- Problem: Main model for problems
- Language: Programming languages supported
- Tag: Tags for categorizing problems
- Assignment: Assignments that include problems
- Submission: Student submissions for problems
- User: Problem creator/owner