Commit 161a5929 authored by Robert Czechowski's avatar Robert Czechowski
Browse files

Add database field 'active' for 'taskgroup'. Reset it before contest/task...

Add database field 'active' for 'taskgroup'. Reset it before contest/task updating, so taskgroups can be removed from contests again. Fixes #12.
parent 75a08bfc
Pipeline #476 failed with stages
in 30 minutes and 40 seconds
ALTER TABLE taskgroup ADD COLUMN active BOOL;
ALTER TABLE taskgroup ADD COLUMN active INTEGER;
......@@ -74,15 +74,15 @@ impl MedalObject<Connection> for Taskgroup {
let id = match self.get_id() {
Some(id) => {
let query = "UPDATE taskgroup
SET contest = $1, name = $2, positionalnumber = $3
WHERE id = $4";
conn.execute(query, &[&self.contest, &self.name, &self.positionalnumber, &id]).unwrap();
SET contest = $1, name = $2, active = $3, positionalnumber = $4
WHERE id = $5";
conn.execute(query, &[&self.contest, &self.name, &self.active, &self.positionalnumber, &id]).unwrap();
id
}
None => {
let query = "INSERT INTO taskgroup (contest, name, positionalnumber)
VALUES ($1, $2, $3)";
conn.execute(query, &[&self.contest, &self.name, &self.positionalnumber]).unwrap();
let query = "INSERT INTO taskgroup (contest, name, active, positionalnumber)
VALUES ($1, $2, $3, $4)";
conn.execute(query, &[&self.contest, &self.name, &self.active, &self.positionalnumber]).unwrap();
conn.get_last_id().unwrap()
}
};
......@@ -613,9 +613,10 @@ impl MedalConnection for Connection {
let query = "SELECT id, name
FROM taskgroup
WHERE contest = $1
ORDER BY id ASC";
AND active = $2
ORDER BY positionalnumber";
let tasknames: Vec<(i32, String)> =
self.query_map_many(query, &[&contest_id], |row| (row.get(0), row.get(1))).unwrap();
self.query_map_many(query, &[&contest_id, &true], |row| (row.get(0), row.get(1))).unwrap();
let mut taskindex: ::std::collections::BTreeMap<i32, usize> = ::std::collections::BTreeMap::new();
......@@ -633,9 +634,10 @@ impl MedalConnection for Connection {
JOIN usergroup ON student.managed_by = usergroup.id
WHERE usergroup.admin = $1
AND taskgroup.contest = $2
ORDER BY usergroup.id, student.id, taskgroup.id ASC";
AND taskgroup.active = $3
ORDER BY usergroup.id, student.id, taskgroup.positionalnumber";
let gradeinfo =
self.query_map_many(query, &[&session_id, &contest_id], |row| {
self.query_map_many(query, &[&session_id, &contest_id, &true], |row| {
(Grade { taskgroup: row.get(0), user: row.get(1), grade: row.get(2), validated: row.get(3) },
Group { id: Some(row.get(4)),
name: row.get(5),
......@@ -692,9 +694,10 @@ impl MedalConnection for Connection {
let query = "SELECT id, name
FROM taskgroup
WHERE contest = $1
ORDER BY id ASC";
AND active = $2
ORDER BY positionalnumber";
let tasknames: Vec<(i32, String)> =
self.query_map_many(query, &[&contest_id], |row| (row.get(0), row.get(1))).unwrap();
self.query_map_many(query, &[&contest_id, &true], |row| (row.get(0), row.get(1))).unwrap();
let mut taskindex: ::std::collections::BTreeMap<i32, usize> = ::std::collections::BTreeMap::new();
let n_tasks = tasknames.len();
......@@ -708,9 +711,10 @@ impl MedalConnection for Connection {
JOIN session ON session.id = grade.session
WHERE session.session_token = $1
AND taskgroup.contest = $2
ORDER BY taskgroup.id ASC";
AND taskgroup.active = $3
ORDER BY taskgroup.positionalnumber";
let gradeinfo =
self.query_map_many(query, &[&session_token, &contest_id], |row| Grade { taskgroup: row.get(0),
self.query_map_many(query, &[&session_token, &contest_id, &true], |row| Grade { taskgroup: row.get(0),
user: row.get(1),
grade: row.get(2),
validated: row.get(3) })
......@@ -789,9 +793,10 @@ impl MedalConnection for Connection {
JOIN taskgroup ON contest.id = taskgroup.contest
JOIN task ON taskgroup.id = task.taskgroup
WHERE contest.id = $1
AND taskgroup.active = $2
ORDER BY taskgroup.positionalnumber";
let taskgroupcontest =
self.query_map_many(query, &[&contest_id], |row| {
self.query_map_many(query, &[&contest_id, &true], |row| {
(Contest { id: Some(contest_id),
location: row.get(0),
filename: row.get(1),
......@@ -807,6 +812,7 @@ impl MedalConnection for Connection {
Taskgroup { id: Some(row.get(9)),
contest: contest_id,
name: row.get(10),
active: true,
positionalnumber: None,
tasks: Vec::new() },
Task { id: Some(row.get(11)), taskgroup: row.get(9), location: row.get(12), stars: row.get(13) })
......@@ -834,8 +840,9 @@ impl MedalConnection for Connection {
taskgroup.name
FROM contest
JOIN taskgroup ON contest.id = taskgroup.contest
WHERE contest.id = $1";
let taskgroupcontest = self.query_map_many(query, &[&contest_id], |row| {
WHERE contest.id = $1
AND taskgroup.active = $2";
let taskgroupcontest = self.query_map_many(query, &[&contest_id, &true], |row| {
(Contest { id: Some(contest_id),
location: row.get(0),
filename: row.get(1),
......@@ -851,6 +858,7 @@ impl MedalConnection for Connection {
Taskgroup { id: Some(row.get(9)),
contest: contest_id,
name: row.get(10),
active: true,
positionalnumber: None,
tasks: Vec::new() })
})
......@@ -910,9 +918,9 @@ impl MedalConnection for Connection {
.unwrap()
}
fn get_task_by_id_complete(&self, task_id: i32) -> (Task, Taskgroup, Contest) {
let query = "SELECT task.location, task.stars, taskgroup.id, taskgroup.name, contest.id, contest.location,
contest.filename, contest.name, contest.duration, contest.public, contest.start_date,
contest.end_date, contest.min_grade, contest.max_grade
let query = "SELECT task.location, task.stars, taskgroup.id, taskgroup.name, taskgroup.active, contest.id,
contest.location, contest.filename, contest.name, contest.duration, contest.public,
contest.start_date, contest.end_date, contest.min_grade, contest.max_grade
FROM contest
JOIN taskgroup ON taskgroup.contest = contest.id
JOIN task ON task.taskgroup = taskgroup.id
......@@ -922,18 +930,19 @@ impl MedalConnection for Connection {
Taskgroup { id: Some(row.get(2)),
contest: row.get(4),
name: row.get(3),
active: row.get(4),
positionalnumber: None,
tasks: Vec::new() },
Contest { id: Some(row.get(4)),
location: row.get(5),
filename: row.get(6),
name: row.get(7),
duration: row.get(8),
public: row.get(9),
start: row.get(10),
end: row.get(11),
min_grade: row.get(12),
max_grade: row.get(13),
Contest { id: Some(row.get(5)),
location: row.get(6),
filename: row.get(7),
name: row.get(8),
duration: row.get(9),
public: row.get(10),
start: row.get(11),
end: row.get(12),
min_grade: row.get(13),
max_grade: row.get(14),
positionalnumber: None,
taskgroups: Vec::new() })
})
......@@ -1112,4 +1121,5 @@ impl MedalConnection for Connection {
}
fn reset_all_contest_visibilities(&self) { self.execute("UPDATE contest SET public = $1", &[&false]).unwrap(); }
fn reset_all_taskgroup_visibilities(&self) { self.execute("UPDATE taskgroup SET active = $1", &[&false]).unwrap(); }
}
......@@ -53,6 +53,7 @@ pub trait MedalConnection {
fn get_debug_information(&self) -> String;
fn reset_all_contest_visibilities(&self);
fn reset_all_taskgroup_visibilities(&self);
}
pub trait MedalObject<T: MedalConnection> {
......
......@@ -170,15 +170,15 @@ impl MedalObject<Connection> for Taskgroup {
let id = match self.get_id() {
Some(id) => {
let query = "UPDATE taskgroup
SET contest = $1, name = $2, positionalnumber = $3
WHERE id = $4";
conn.execute(query, &[&self.contest, &self.name, &self.positionalnumber, &id]).unwrap();
SET contest = $1, name = $2, active = $3, positionalnumber = $4
WHERE id = $5";
conn.execute(query, &[&self.contest, &self.name, &self.active, &self.positionalnumber, &id]).unwrap();
id
}
None => {
let query = "INSERT INTO taskgroup (contest, name, positionalnumber)
VALUES ($1, $2, $3)";
conn.execute(query, &[&self.contest, &self.name, &self.positionalnumber]).unwrap();
let query = "INSERT INTO taskgroup (contest, name, active, positionalnumber)
VALUES ($1, $2, $3, $4)";
conn.execute(query, &[&self.contest, &self.name, &self.active, &self.positionalnumber]).unwrap();
conn.get_last_id().unwrap()
}
};
......@@ -709,9 +709,10 @@ impl MedalConnection for Connection {
let query = "SELECT id, name
FROM taskgroup
WHERE contest = $1
ORDER BY id ASC";
AND active = $2
ORDER BY positionalnumber";
let tasknames: Vec<(i32, String)> =
self.query_map_many(query, &[&contest_id], |row| (row.get(0), row.get(1))).unwrap();
self.query_map_many(query, &[&contest_id, &true], |row| (row.get(0), row.get(1))).unwrap();
let mut taskindex: ::std::collections::BTreeMap<i32, usize> = ::std::collections::BTreeMap::new();
......@@ -729,9 +730,10 @@ impl MedalConnection for Connection {
JOIN usergroup ON student.managed_by = usergroup.id
WHERE usergroup.admin = $1
AND taskgroup.contest = $2
ORDER BY usergroup.id, student.id, taskgroup.id ASC";
AND taskgroup.active = $3
ORDER BY usergroup.id, student.id, taskgroup.positionalnumber";
let gradeinfo =
self.query_map_many(query, &[&session_id, &contest_id], |row| {
self.query_map_many(query, &[&session_id, &contest_id, &true], |row| {
(Grade { taskgroup: row.get(0), user: row.get(1), grade: row.get(2), validated: row.get(3) },
Group { id: Some(row.get(4)),
name: row.get(5),
......@@ -788,9 +790,10 @@ impl MedalConnection for Connection {
let query = "SELECT id, name
FROM taskgroup
WHERE contest = $1
ORDER BY id ASC";
AND active = $2
ORDER BY positionalnumber";
let tasknames: Vec<(i32, String)> =
self.query_map_many(query, &[&contest_id], |row| (row.get(0), row.get(1))).unwrap();
self.query_map_many(query, &[&contest_id, &true], |row| (row.get(0), row.get(1))).unwrap();
let mut taskindex: ::std::collections::BTreeMap<i32, usize> = ::std::collections::BTreeMap::new();
let n_tasks = tasknames.len();
......@@ -804,12 +807,13 @@ impl MedalConnection for Connection {
JOIN session ON session.id = grade.session
WHERE session.session_token = $1
AND taskgroup.contest = $2
ORDER BY taskgroup.id ASC";
AND taskgroup.active = $3
ORDER BY taskgroup.positionalnumber";
let gradeinfo =
self.query_map_many(query, &[&session_token, &contest_id], |row| Grade { taskgroup: row.get(0),
user: row.get(1),
grade: row.get(2),
validated: row.get(3) })
self.query_map_many(query, &[&session_token, &contest_id, &true], |row| Grade { taskgroup: row.get(0),
user: row.get(1),
grade: row.get(2),
validated: row.get(3) })
.unwrap();
let gradeinfo_iter = gradeinfo.iter();
......@@ -885,9 +889,10 @@ impl MedalConnection for Connection {
JOIN taskgroup ON contest.id = taskgroup.contest
JOIN task ON taskgroup.id = task.taskgroup
WHERE contest.id = $1
AND taskgroup.active = $2
ORDER BY taskgroup.positionalnumber";
let taskgroupcontest =
self.query_map_many(query, &[&contest_id], |row| {
self.query_map_many(query, &[&contest_id, &true], |row| {
(Contest { id: Some(contest_id),
location: row.get(0),
filename: row.get(1),
......@@ -903,6 +908,7 @@ impl MedalConnection for Connection {
Taskgroup { id: Some(row.get(9)),
contest: contest_id,
name: row.get(10),
active: true,
positionalnumber: None,
tasks: Vec::new() },
Task { id: Some(row.get(11)), taskgroup: row.get(9), location: row.get(12), stars: row.get(13) })
......@@ -930,8 +936,9 @@ impl MedalConnection for Connection {
taskgroup.name
FROM contest
JOIN taskgroup ON contest.id = taskgroup.contest
WHERE contest.id = $1";
let taskgroupcontest = self.query_map_many(query, &[&contest_id], |row| {
WHERE contest.id = $1
AND taskgroup.active = $2";
let taskgroupcontest = self.query_map_many(query, &[&contest_id, &true], |row| {
(Contest { id: Some(contest_id),
location: row.get(0),
filename: row.get(1),
......@@ -947,6 +954,7 @@ impl MedalConnection for Connection {
Taskgroup { id: Some(row.get(9)),
contest: contest_id,
name: row.get(10),
active: true,
positionalnumber: None,
tasks: Vec::new() })
})
......@@ -1006,9 +1014,9 @@ impl MedalConnection for Connection {
.unwrap()
}
fn get_task_by_id_complete(&self, task_id: i32) -> (Task, Taskgroup, Contest) {
let query = "SELECT task.location, task.stars, taskgroup.id, taskgroup.name, contest.id, contest.location,
contest.filename, contest.name, contest.duration, contest.public, contest.start_date,
contest.end_date, contest.min_grade, contest.max_grade
let query = "SELECT task.location, task.stars, taskgroup.id, taskgroup.name, taskgroup.active, contest.id,
contest.location, contest.filename, contest.name, contest.duration, contest.public,
contest.start_date, contest.end_date, contest.min_grade, contest.max_grade
FROM contest
JOIN taskgroup ON taskgroup.contest = contest.id
JOIN task ON task.taskgroup = taskgroup.id
......@@ -1018,18 +1026,19 @@ impl MedalConnection for Connection {
Taskgroup { id: Some(row.get(2)),
contest: row.get(4),
name: row.get(3),
active: row.get(4),
positionalnumber: None,
tasks: Vec::new() },
Contest { id: Some(row.get(4)),
location: row.get(5),
filename: row.get(6),
name: row.get(7),
duration: row.get(8),
public: row.get(9),
start: row.get(10),
end: row.get(11),
min_grade: row.get(12),
max_grade: row.get(13),
Contest { id: Some(row.get(5)),
location: row.get(6),
filename: row.get(7),
name: row.get(8),
duration: row.get(9),
public: row.get(10),
start: row.get(11),
end: row.get(12),
min_grade: row.get(13),
max_grade: row.get(14),
positionalnumber: None,
taskgroups: Vec::new() })
})
......@@ -1208,4 +1217,5 @@ impl MedalConnection for Connection {
}
fn reset_all_contest_visibilities(&self) { self.execute("UPDATE contest SET public = $1", &[&false]).unwrap(); }
fn reset_all_taskgroup_visibilities(&self) { self.execute("UPDATE taskgroup SET active = $1", &[&false]).unwrap(); }
}
......@@ -170,15 +170,15 @@ impl MedalObject<Connection> for Taskgroup {
let id = match self.get_id() {
Some(id) => {
let query = "UPDATE taskgroup
SET contest = ?1, name = ?2, positionalnumber = ?3
WHERE id = ?4";
conn.execute(query, &[&self.contest, &self.name, &self.positionalnumber, &id]).unwrap();
SET contest = ?1, name = ?2, active = ?3, positionalnumber = ?4
WHERE id = ?5";
conn.execute(query, &[&self.contest, &self.name, &self.active, &self.positionalnumber, &id]).unwrap();
id
}
None => {
let query = "INSERT INTO taskgroup (contest, name, positionalnumber)
VALUES (?1, ?2, ?3)";
conn.execute(query, &[&self.contest, &self.name, &self.positionalnumber]).unwrap();
let query = "INSERT INTO taskgroup (contest, name, active, positionalnumber)
VALUES (?1, ?2, ?3, ?4)";
conn.execute(query, &[&self.contest, &self.name, &self.active, &self.positionalnumber]).unwrap();
conn.get_last_id().unwrap()
}
};
......@@ -709,9 +709,10 @@ impl MedalConnection for Connection {
let query = "SELECT id, name
FROM taskgroup
WHERE contest = ?1
ORDER BY id ASC";
AND active = ?2
ORDER BY positionalnumber";
let tasknames: Vec<(i32, String)> =
self.query_map_many(query, &[&contest_id], |row| (row.get(0), row.get(1))).unwrap();
self.query_map_many(query, &[&contest_id, &true], |row| (row.get(0), row.get(1))).unwrap();
let mut taskindex: ::std::collections::BTreeMap<i32, usize> = ::std::collections::BTreeMap::new();
......@@ -729,9 +730,10 @@ impl MedalConnection for Connection {
JOIN usergroup ON student.managed_by = usergroup.id
WHERE usergroup.admin = ?1
AND taskgroup.contest = ?2
ORDER BY usergroup.id, student.id, taskgroup.id ASC";
AND taskgroup.active = ?3
ORDER BY usergroup.id, student.id, taskgroup.positionalnumber";
let gradeinfo =
self.query_map_many(query, &[&session_id, &contest_id], |row| {
self.query_map_many(query, &[&session_id, &contest_id, &true], |row| {
(Grade { taskgroup: row.get(0), user: row.get(1), grade: row.get(2), validated: row.get(3) },
Group { id: Some(row.get(4)),
name: row.get(5),
......@@ -788,9 +790,10 @@ impl MedalConnection for Connection {
let query = "SELECT id, name
FROM taskgroup
WHERE contest = ?1
ORDER BY id ASC";
AND active = ?2
ORDER BY positionalnumber";
let tasknames: Vec<(i32, String)> =
self.query_map_many(query, &[&contest_id], |row| (row.get(0), row.get(1))).unwrap();
self.query_map_many(query, &[&contest_id, &true], |row| (row.get(0), row.get(1))).unwrap();
let mut taskindex: ::std::collections::BTreeMap<i32, usize> = ::std::collections::BTreeMap::new();
let n_tasks = tasknames.len();
......@@ -804,12 +807,13 @@ impl MedalConnection for Connection {
JOIN session ON session.id = grade.session
WHERE session.session_token = ?1
AND taskgroup.contest = ?2
ORDER BY taskgroup.id ASC";
AND taskgroup.active = ?3
ORDER BY taskgroup.positionalnumber";
let gradeinfo =
self.query_map_many(query, &[&session_token, &contest_id], |row| Grade { taskgroup: row.get(0),
user: row.get(1),
grade: row.get(2),
validated: row.get(3) })
self.query_map_many(query, &[&session_token, &contest_id, &true], |row| Grade { taskgroup: row.get(0),
user: row.get(1),
grade: row.get(2),
validated: row.get(3) })
.unwrap();
let gradeinfo_iter = gradeinfo.iter();
......@@ -885,9 +889,10 @@ impl MedalConnection for Connection {
JOIN taskgroup ON contest.id = taskgroup.contest
JOIN task ON taskgroup.id = task.taskgroup
WHERE contest.id = ?1
AND taskgroup.active = ?2
ORDER BY taskgroup.positionalnumber";
let taskgroupcontest =
self.query_map_many(query, &[&contest_id], |row| {
self.query_map_many(query, &[&contest_id, &true], |row| {
(Contest { id: Some(contest_id),
location: row.get(0),
filename: row.get(1),
......@@ -903,6 +908,7 @@ impl MedalConnection for Connection {
Taskgroup { id: Some(row.get(9)),
contest: contest_id,
name: row.get(10),
active: true,
positionalnumber: None,
tasks: Vec::new() },
Task { id: Some(row.get(11)), taskgroup: row.get(9), location: row.get(12), stars: row.get(13) })
......@@ -930,8 +936,9 @@ impl MedalConnection for Connection {
taskgroup.name
FROM contest
JOIN taskgroup ON contest.id = taskgroup.contest
WHERE contest.id = ?1";
let taskgroupcontest = self.query_map_many(query, &[&contest_id], |row| {
WHERE contest.id = ?1
AND taskgroup.active = ?2";
let taskgroupcontest = self.query_map_many(query, &[&contest_id, &true], |row| {
(Contest { id: Some(contest_id),
location: row.get(0),
filename: row.get(1),
......@@ -947,6 +954,7 @@ impl MedalConnection for Connection {
Taskgroup { id: Some(row.get(9)),
contest: contest_id,
name: row.get(10),
active: true,
positionalnumber: None,
tasks: Vec::new() })
})
......@@ -1006,9 +1014,9 @@ impl MedalConnection for Connection {
.unwrap()
}
fn get_task_by_id_complete(&self, task_id: i32) -> (Task, Taskgroup, Contest) {
let query = "SELECT task.location, task.stars, taskgroup.id, taskgroup.name, contest.id, contest.location,
contest.filename, contest.name, contest.duration, contest.public, contest.start_date,
contest.end_date, contest.min_grade, contest.max_grade
let query = "SELECT task.location, task.stars, taskgroup.id, taskgroup.name, taskgroup.active, contest.id,
contest.location, contest.filename, contest.name, contest.duration, contest.public,
contest.start_date, contest.end_date, contest.min_grade, contest.max_grade
FROM contest
JOIN taskgroup ON taskgroup.contest = contest.id
JOIN task ON task.taskgroup = taskgroup.id
......@@ -1018,18 +1026,19 @@ impl MedalConnection for Connection {
Taskgroup { id: Some(row.get(2)),
contest: row.get(4),
name: row.get(3),
active: row.get(4),
positionalnumber: None,
tasks: Vec::new() },
Contest { id: Some(row.get(4)),
location: row.get(5),
filename: row.get(6),
name: row.get(7),
duration: row.get(8),
public: row.get(9),
start: row.get(10),
end: row.get(11),
min_grade: row.get(12),
max_grade: row.get(13),
Contest { id: Some(row.get(5)),
location: row.get(6),
filename: row.get(7),
name: row.get(8),
duration: row.get(9),
public: row.get(10),
start: row.get(11),
end: row.get(12),
min_grade: row.get(13),
max_grade: row.get(14),
positionalnumber: None,
taskgroups: Vec::new() })
})
......@@ -1208,4 +1217,5 @@ impl MedalConnection for Connection {
}
fn reset_all_contest_visibilities(&self) { self.execute("UPDATE contest SET public = ?1", &[&false]).unwrap(); }
fn reset_all_taskgroup_visibilities(&self) { self.execute("UPDATE taskgroup SET active = ?1", &[&false]).unwrap(); }
}
......@@ -80,6 +80,7 @@ pub struct Taskgroup {
pub id: Option<i32>,
pub contest: i32,
pub name: String,
pub active: bool,
pub positionalnumber: Option<i32>,
pub tasks: Vec<Task>,
}
......@@ -272,7 +273,12 @@ impl SessionUser {
impl Taskgroup {
pub fn new(name: String, positionalnumber: Option<i32>) -> Self {
Taskgroup { id: None, contest: 0, name: name, positionalnumber: positionalnumber, tasks: Vec::new() }
Taskgroup { id: None,
contest: 0,
name: name,
active: true,
positionalnumber: positionalnumber,
tasks: Vec::new() }
}
}
......
......@@ -96,6 +96,8 @@ fn refresh_all_contests<C>(conn: &mut C)
db_objects::Contest: db_conn::MedalObject<C>
{
conn.reset_all_contest_visibilities();
conn.reset_all_taskgroup_visibilities();
let v = get_all_contest_info("tasks/");
for mut contest_info in v {
......
......@@ -481,7 +481,7 @@ fn contestresults_download<C>(req: &mut Request) -> IronResult<Response>
let disable_contest_results = config.disable_results_page.unwrap_or(false);
println!("test");
if disable_contest_results {
let mut resp = Response::new();
resp.set_mut(Template::new(&"nocontestresults", 2)).set_mut(status::Locked);
......@@ -500,7 +500,7 @@ fn contestresults_download<C>(req: &mut Request) -> IronResult<Response>
Charset::Ext("Utf-8".to_string()), // The character set for the bytes of the filename
None, // The optional language tag (see `language-tag` crate)
format!("{}.csv", data.get("contestname").unwrap().as_str().unwrap()).as_bytes().to_vec(), // the actual bytes of the filename
// TODO: The name should be returned by core::show_contest_results directly
// TODO: The name should be returned by core::show_contest_results directly
)] };
let mut resp = Response::new();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment