From 535d4fb33b299fce1575947b533dd6277bd703f0 Mon Sep 17 00:00:00 2001 From: mochi Date: Wed, 23 Sep 2020 22:28:07 +0200 Subject: [PATCH 1/8] [TGUI] Security Records --- code/datums/datacore.dm | 8 +- code/game/machinery/computer/security.dm | 844 ++++++++---------- tgui/packages/tgui/index.js | 1 + .../tgui/interfaces/SecurityRecords.js | 437 +++++++++ .../tgui/interfaces/common/ComplexModal.js | 4 +- .../styles/interfaces/SecurityRecords.scss | 36 + tgui/packages/tgui/styles/main.scss | 1 + .../packages/tgui/styles/themes/security.scss | 26 + 8 files changed, 876 insertions(+), 481 deletions(-) create mode 100644 tgui/packages/tgui/interfaces/SecurityRecords.js create mode 100644 tgui/packages/tgui/styles/interfaces/SecurityRecords.scss create mode 100644 tgui/packages/tgui/styles/themes/security.scss diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm index 0e98179e2f9..f44edc6029c 100644 --- a/code/datums/datacore.dm +++ b/code/datums/datacore.dm @@ -281,8 +281,10 @@ GLOBAL_VAR_INIT(record_id_num, 1001) G.fields["photo-south"] = "'data:image/png;base64,[icon2base64(icon(G.fields["photo"], dir = SOUTH))]'" G.fields["photo-west"] = "'data:image/png;base64,[icon2base64(icon(G.fields["photo"], dir = WEST))]'" if(H.gen_record && !jobban_isbanned(H, "Records")) + G.fields["record"] = H.gen_record G.fields["notes"] = H.gen_record else + G.fields["record"] = "No notes found." G.fields["notes"] = "No notes found." general += G @@ -301,8 +303,10 @@ GLOBAL_VAR_INIT(record_id_num, 1001) M.fields["cdi"] = "None" M.fields["cdi_d"] = "No diseases have been diagnosed at the moment." if(H.med_record && !jobban_isbanned(H, "Records")) + M.fields["record"] = H.med_record M.fields["notes"] = H.med_record else + M.fields["record"] = "No notes found." M.fields["notes"] = "No notes found." medical += M @@ -317,9 +321,11 @@ GLOBAL_VAR_INIT(record_id_num, 1001) S.fields["ma_crim_d"] = "No major crime convictions." S.fields["notes"] = "No notes." if(H.sec_record && !jobban_isbanned(H, "Records")) + S.fields["record"] = H.sec_record S.fields["notes"] = H.sec_record else - S.fields["notes"] = "No notes." + S.fields["record"] = "No notes found." + S.fields["notes"] = "No notes found." LAZYINITLIST(S.fields["comments"]) security += S diff --git a/code/game/machinery/computer/security.dm b/code/game/machinery/computer/security.dm index 8c815198d31..6cb11f8ffb2 100644 --- a/code/game/machinery/computer/security.dm +++ b/code/game/machinery/computer/security.dm @@ -2,43 +2,74 @@ #define SEC_DATA_MAINT 2 // Records maintenance #define SEC_DATA_RECORD 3 // Record -/obj/machinery/computer/secure_data//TODO:SANITY +#define FIELD(N, V, E) list(field = N, value = V, edit = E) +#define SEC_FIELD(N, V, E, LB) list(field = N, value = V, edit = E, line_break = LB) + +/obj/machinery/computer/secure_data name = "security records" desc = "Used to view and edit personnel's security records." icon_keyboard = "security_key" icon_screen = "security" - req_one_access = list(ACCESS_SECURITY, ACCESS_FORENSICS_LOCKERS) circuit = /obj/item/circuitboard/secure_data - var/obj/item/card/id/scan = null - var/authenticated = null - var/rank = null - var/list/authcard_access = list() - var/screen = null - var/datum/data/record/active1 = null - var/datum/data/record/active2 = null - var/temp = null - var/printing = null - //Sorting Variables - var/sortBy = "name" - var/order = 1 // -1 = Descending - 1 = Ascending + /// The inserted ID card's access list. + var/list/inserted_id_access = null + /// The current page being viewed. + var/current_page = SEC_DATA_R_LIST + /// The current general record being viewed. + var/datum/data/record/record_general = null + /// The current security record being viewed. + var/datum/data/record/record_security = null + /// Whether the computer is currently printing a paper or not. + var/is_printing = FALSE + /// The editable fields and their associated question to display to the user. + var/static/list/field_edit_questions + /// The editable fields and their associated choices to display to the user. + var/static/list/field_edit_choices + /// The current temporary notice. + var/temp_notice light_color = LIGHT_COLOR_RED +/obj/machinery/computer/secure_data/Initialize(mapload) + . = ..() + req_one_access = list(ACCESS_SECURITY, ACCESS_FORENSICS_LOCKERS) + field_edit_questions = list( + // General + "name" = "Please input new name:", + "id" = "Please input new ID:", + "sex" = "Please select new sex:", + "age" = "Please input new age:", + "rank" = "Please input new assignment:", + "fingerprint" = "Please input new fingerprint hash:", + "p_stat" = "Please select new physical status:", + "m_stat" = "Please select new mental status:", + // Security + "criminal" = "Please select new criminal status:", + "mi_crim" = "Please input new minor crimes:", + "mi_crim_d" = "Please summarize minor crimes:", + "ma_crim" = "Please input new major crimes:", + "ma_crim_d" = "Please summarize major crimes:", + "notes" = "Please input new important notes:", + ) + field_edit_choices = list( + // General + "sex" = list("Male", "Female"), + "p_stat" = list("*Deceased*", "*SSD*", "Active", "Physically Unfit", "Disabled"), + "m_stat" = list("*Insane*", "*Unstable*", "*Watch*", "Stable"), + // Security + "criminal" = list(SEC_RECORD_STATUS_NONE, SEC_RECORD_STATUS_ARREST, SEC_RECORD_STATUS_EXECUTE, SEC_RECORD_STATUS_INCARCERATED, SEC_RECORD_STATUS_RELEASED, SEC_RECORD_STATUS_PAROLLED, SEC_RECORD_STATUS_DEMOTE, SEC_RECORD_STATUS_SEARCH, SEC_RECORD_STATUS_MONITOR), + ) + /obj/machinery/computer/secure_data/Destroy() - active1 = null - active2 = null + record_general = null + record_security = null return ..() /obj/machinery/computer/secure_data/attackby(obj/item/O, mob/user, params) - if(istype(O, /obj/item/card/id) && !scan) - user.drop_item() - O.forceMove(src) - scan = O - ui_interact(user) + if(tgui_login_attackby(O, user)) return return ..() -//Someone needs to break down the dat += into chunks instead of long ass lines. /obj/machinery/computer/secure_data/attack_hand(mob/user) if(..()) return @@ -46,267 +77,117 @@ to_chat(user, "Unable to establish a connection: You're too far away from the station!") return add_fingerprint(user) - ui_interact(user) + tgui_interact(user) -/obj/machinery/computer/secure_data/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/secure_data/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "secure_data.tmpl", name, 800, 800) + ui = new(user, src, ui_key, "SecurityRecords", name, 800, 800) ui.open() + ui.set_autoupdate(FALSE) -/obj/machinery/computer/secure_data/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) +/obj/machinery/computer/secure_data/tgui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] - data["temp"] = temp - data["scan"] = scan ? scan.name : null - data["authenticated"] = authenticated - data["screen"] = screen - if(authenticated) - switch(screen) + data["currentPage"] = current_page + data["isPrinting"] = is_printing + tgui_login_data(data, user) + data["modal"] = tgui_modal_data() + data["temp"] = temp_notice + if(data["loginState"]["logged_in"]) + switch(current_page) if(SEC_DATA_R_LIST) - if(!isnull(GLOB.data_core.general)) - for(var/datum/data/record/R in sortRecord(GLOB.data_core.general, sortBy, order)) - var/crimstat = "null" - for(var/datum/data/record/E in GLOB.data_core.security) - if(E.fields["name"] == R.fields["name"] && E.fields["id"] == R.fields["id"]) - crimstat = E.fields["criminal"] - break - var/background = "''" - switch(crimstat) - if(SEC_RECORD_STATUS_EXECUTE) - background = "'background-color:#5E0A1A'" - if(SEC_RECORD_STATUS_ARREST) - background = "'background-color:#890E26'" - if(SEC_RECORD_STATUS_SEARCH) - background = "'background-color:#999900'" - if(SEC_RECORD_STATUS_MONITOR) - background = "'background-color:#004C99'" - if(SEC_RECORD_STATUS_DEMOTE) - background = "'background-color:#C2A111'" - if(SEC_RECORD_STATUS_INCARCERATED) - background = "'background-color:#743B03'" - if(SEC_RECORD_STATUS_PAROLLED) - background = "'background-color:#743B03'" - if(SEC_RECORD_STATUS_RELEASED) - background = "'background-color:#216489'" - if(SEC_RECORD_STATUS_NONE) - background = "'background-color:#007f47'" - if("null") - crimstat = "No record." - data["records"] += list(list("ref" = "\ref[R]", "id" = R.fields["id"], "name" = R.fields["name"], "rank" = R.fields["rank"], "fingerprint" = R.fields["fingerprint"], "background" = background, "crimstat" = crimstat)) + // Prepare the list of security records to associate with the general ones. + // This is not ideal but datacore code sucks and needs to be rewritten. + var/list/sec_records_assoc[0] + for(var/datum/data/record/S in (GLOB.data_core.security || list())) + sec_records_assoc["[S.fields["name"]]|[S.fields["id"]]"] = S + // List the general records + var/list/records = list() + data["records"] = records + for(var/datum/data/record/G in (GLOB.data_core.general || list())) + var/datum/data/record/S = sec_records_assoc["[G.fields["name"]]|[G.fields["id"]]"] + var/list/record_line = list("uid_gen" = G.UID(), "id" = G.fields["id"], "name" = G.fields["name"], "rank" = G.fields["rank"], "fingerprint" = G.fields["fingerprint"]) + record_line["status"] = S?.fields["criminal"] || "No record" + record_line["uid_sec"] = S && S.UID() // So we don't have to perform the search through a for loop again later + records[++records.len] = record_line if(SEC_DATA_RECORD) var/list/general = list() data["general"] = general - if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1)) - var/list/fields = list() - general["fields"] = fields - fields[++fields.len] = list("field" = "Name:", "value" = active1.fields["name"], "edit" = "name") - fields[++fields.len] = list("field" = "ID:", "value" = active1.fields["id"], "edit" = "id") - fields[++fields.len] = list("field" = "Sex:", "value" = active1.fields["sex"], "edit" = "sex") - fields[++fields.len] = list("field" = "Age:", "value" = active1.fields["age"], "edit" = "age") - fields[++fields.len] = list("field" = "Rank:", "value" = active1.fields["rank"], "edit" = "rank") - fields[++fields.len] = list("field" = "Fingerprint:", "value" = active1.fields["fingerprint"], "edit" = "fingerprint") - fields[++fields.len] = list("field" = "Physical Status:", "value" = active1.fields["p_stat"], "edit" = null) - fields[++fields.len] = list("field" = "Mental Status:", "value" = active1.fields["m_stat"], "edit" = null) - var/list/photos = list() - general["photos"] = photos - photos[++photos.len] = list("photo" = active1.fields["photo-south"]) - photos[++photos.len] = list("photo" = active1.fields["photo-west"]) - general["has_photos"] += (active1.fields["photo-south"] || active1.fields["photo-west"] ? 1 : 0) - general["empty"] = 0 + if(record_general && GLOB.data_core.general.Find(record_general)) + var/list/gen_fields = record_general.fields + general["fields"] = list( + FIELD("Name", gen_fields["name"], "name"), + FIELD("ID", gen_fields["id"], "id"), + FIELD("Sex", gen_fields["sex"], "sex"), + FIELD("Age", gen_fields["age"], "age"), + FIELD("Assignment", gen_fields["rank"], "rank"), + FIELD("Fingerprint", gen_fields["fingerprint"], "fingerprint"), + FIELD("Physical Status", gen_fields["p_stat"], "p_stat"), + FIELD("Mental Status", gen_fields["m_stat"], "m_stat"), + FIELD("Important Notes", gen_fields["record"], null), + ) + general["photos"] = list( + gen_fields["photo-south"], + gen_fields["photo-west"], + ) + general["has_photos"] = (gen_fields["photo-south"] || gen_fields["photo-west"]) ? TRUE : FALSE + general["empty"] = FALSE else - general["empty"] = 1 + general["empty"] = TRUE var/list/security = list() data["security"] = security - if(istype(active2, /datum/data/record) && GLOB.data_core.security.Find(active2)) - var/list/fields = list() - security["fields"] = fields - fields[++fields.len] = list("field" = "Criminal Status:", "value" = active2.fields["criminal"], "edit" = "criminal", "line_break" = 1) - fields[++fields.len] = list("field" = "Minor Crimes:", "value" = active2.fields["mi_crim"], "edit" = "mi_crim", "line_break" = 0) - fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["mi_crim_d"], "edit" = "mi_crim_d", "line_break" = 1) - fields[++fields.len] = list("field" = "Major Crimes:", "value" = active2.fields["ma_crim"], "edit" = "ma_crim", "line_break" = 0) - fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["ma_crim_d"], "edit" = "ma_crim_d", "line_break" = 1) - fields[++fields.len] = list("field" = "Important Notes:", "value" = active2.fields["notes"], "edit" = "notes", "line_break" = 0) - if(!active2.fields["comments"] || !islist(active2.fields["comments"])) - active2.fields["comments"] = list() - security["comments"] = active2.fields["comments"] - security["empty"] = 0 + if(record_security && GLOB.data_core.security.Find(record_security)) + var/list/sec_fields = record_security.fields + security["fields"] = list( + SEC_FIELD("Criminal Status", sec_fields["criminal"], "criminal", TRUE), + SEC_FIELD("Minor Crimes", sec_fields["mi_crim"], "mi_crim", FALSE), + SEC_FIELD("Details", sec_fields["mi_crim_d"], "mi_crim_d", TRUE), + SEC_FIELD("Major Crimes", sec_fields["ma_crim"], "ma_crim", FALSE), + SEC_FIELD("Details", sec_fields["ma_crim_d"], "ma_crim_d", TRUE), + SEC_FIELD("Important Notes", sec_fields["notes"], null, FALSE), + ) + if(!islist(sec_fields["comments"])) + sec_fields["comments"] = list() + security["comments"] = sec_fields["comments"] + security["empty"] = FALSE else - security["empty"] = 1 + security["empty"] = TRUE + return data -/obj/machinery/computer/secure_data/Topic(href, href_list) +/obj/machinery/computer/secure_data/tgui_act(action, list/params) if(..()) - return 1 + return - if(!GLOB.data_core.general.Find(active1)) - active1 = null - if(!GLOB.data_core.security.Find(active2)) - active2 = null + . = TRUE + if(tgui_act_modal(action, params)) + return + if(tgui_login_act(action, params)) + return - if(href_list["temp"]) - temp = null - - if(href_list["temp_action"]) - var/temp_href = splittext(href_list["temp_action"], "=") - switch(temp_href[1]) - if("del_all2") - for(var/datum/data/record/R in GLOB.data_core.security) - qdel(R) - update_all_mob_security_hud() - setTemp("

All records deleted.

") - if("del_alllogs2") - if(GLOB.cell_logs.len) - setTemp("

All cell logs deleted.

") - GLOB.cell_logs.Cut() - else - to_chat(usr, "Error; No cell logs to delete.") - if("del_r2") - if(active2) - qdel(active2) - update_all_mob_security_hud() - if("del_rg2") - if(active1) - for(var/datum/data/record/R in GLOB.data_core.medical) - if(R.fields["name"] == active1.fields["name"] && R.fields["id"] == active1.fields["id"]) - qdel(R) - QDEL_NULL(active1) - QDEL_NULL(active2) - update_all_mob_security_hud() - screen = SEC_DATA_R_LIST - if("criminal") - if(active2) - var/t1 - if(temp_href[2] == "execute") - t1 = copytext(trim(sanitize(input("Explain why they are being executed. Include a list of their crimes, and victims.", "EXECUTION ORDER", null, null) as text)), 1, MAX_MESSAGE_LEN) - else - t1 = copytext(trim(sanitize(input("Enter Reason:", "Secure. records", null, null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1) - t1 = "(none)" - if(!set_criminal_status(usr, active2, temp_href[2], t1, rank, authcard_access)) - setTemp("

Error: permission denied.

") - return 1 - if("rank") - if(active1) - active1.fields["rank"] = temp_href[2] - if(temp_href[2] in GLOB.joblist) - active1.fields["real_rank"] = temp_href[2] - - if(href_list["scan"]) - if(scan) - scan.forceMove(loc) - if(ishuman(usr) && !usr.get_active_hand()) - usr.put_in_hands(scan) - scan = null - else - var/obj/item/I = usr.get_active_hand() - if(istype(I, /obj/item/card/id)) - usr.drop_item() - I.forceMove(src) - scan = I - - if(href_list["login"]) - if(isAI(usr)) - authenticated = usr.name - rank = "AI" - else if(isrobot(usr)) - authenticated = usr.name - var/mob/living/silicon/robot/R = usr - rank = "[R.modtype] [R.braintype]" - else if(istype(scan, /obj/item/card/id)) - if(check_access(scan)) - authenticated = scan.registered_name - rank = scan.assignment - authcard_access = scan.access - - if(authenticated) - active1 = null - active2 = null - screen = SEC_DATA_R_LIST - - if(authenticated) - if(href_list["logout"]) - authenticated = null - screen = null - active1 = null - active2 = null - authcard_access = list() - - else if(href_list["sort"]) - // Reverse the order if clicked twice - if(sortBy == href_list["sort"]) - if(order == 1) - order = -1 - else - order = 1 - else - sortBy = href_list["sort"] - order = initial(order) - - else if(href_list["screen"]) - screen = text2num(href_list["screen"]) - if(screen < 1) - screen = SEC_DATA_R_LIST - - active1 = null - active2 = null - - else if(href_list["d_rec"]) - var/datum/data/record/R = locate(href_list["d_rec"]) - var/datum/data/record/M = locate(href_list["d_rec"]) - if(!GLOB.data_core.general.Find(R)) - setTemp("

Record not found!

") - return 1 - for(var/datum/data/record/E in GLOB.data_core.security) - if(E.fields["name"] == R.fields["name"] && E.fields["id"] == R.fields["id"]) - M = E - active1 = R - active2 = M - screen = SEC_DATA_RECORD - - else if(href_list["del_all"]) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_all2=1", "status" = null) - buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null) - setTemp("

Are you sure you wish to delete all records?

", buttons) - - else if(href_list["del_alllogs"]) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_alllogs2=1", "status" = null) - buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null) - setTemp("

Are you sure you wish to delete all cell logs?

", buttons) - - else if(href_list["del_rg"]) - if(active1) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_rg2=1", "status" = null) - buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null) - setTemp("

Are you sure you wish to delete the record (ALL)?

", buttons) - - else if(href_list["del_r"]) - if(active1) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_r2=1", "status" = null) - buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null) - setTemp("

Are you sure you wish to delete the record (Security Portion Only)?

", buttons) - - else if(href_list["new_s"]) - if(istype(active1, /datum/data/record) && !istype(active2, /datum/data/record)) - var/datum/data/record/R = new /datum/data/record() - R.fields["name"] = active1.fields["name"] - R.fields["id"] = active1.fields["id"] - R.name = "Security Record #[R.fields["id"]]" - R.fields["criminal"] = "None" - R.fields["mi_crim"] = "None" - R.fields["mi_crim_d"] = "No minor crime convictions." - R.fields["ma_crim"] = "None" - R.fields["ma_crim_d"] = "No major crime convictions." - R.fields["notes"] = "No notes." - GLOB.data_core.security += R - active2 = R - screen = SEC_DATA_RECORD - - else if(href_list["new_g"]) + switch(action) + if("cleartemp") + temp_notice = null + if("page") // Select Page + var/page_num = clamp(text2num(params["page"]) || SEC_DATA_R_LIST, SEC_DATA_R_LIST, SEC_DATA_MAINT) // SEC_DATA_RECORD cannot be accessed through this act + current_page = page_num + record_general = null + record_security = null + if("view") // View Record + var/datum/data/record/G = locateUID(params["uid_gen"]) + var/datum/data/record/S = locateUID(params["uid_sec"]) + if(!istype(G)) // No general record! + set_temp("General record not found!", "danger") + return + if(istype(S) && !(G.fields["name"] == S.fields["name"] && G.fields["id"] == S.fields["id"])) // General and security records don't match! + S = null + record_general = G + record_security = S + current_page = SEC_DATA_RECORD + if("new_general") // New General Record + if(record_general) + return var/datum/data/record/G = new /datum/data/record() G.fields["name"] = "New Record" G.fields["id"] = "[add_zero(num2hex(rand(1, 1.6777215E7)), 6)]" @@ -319,228 +200,219 @@ G.fields["m_stat"] = "Stable" G.fields["species"] = "Human" GLOB.data_core.general += G - active1 = G - active2 = null + record_general = G + record_security = null + current_page = SEC_DATA_RECORD + if("new_security") // New Security Record + if(!record_general || record_security) + return + var/datum/data/record/S = new /datum/data/record() + S.fields["name"] = record_general.fields["name"] + S.fields["id"] = record_general.fields["id"] + S.name = "Security Record #[S.fields["id"]]" + S.fields["criminal"] = SEC_RECORD_STATUS_NONE + S.fields["mi_crim"] = "None" + S.fields["mi_crim_d"] = "No minor crime convictions." + S.fields["ma_crim"] = "None" + S.fields["ma_crim_d"] = "No major crime convictions." + S.fields["notes"] = "No notes." + GLOB.data_core.security += S + record_security = S + update_all_mob_security_hud() + if("delete_general") // Delete General, Security and Medical Records + if(!record_general) + return + for(var/datum/data/record/M in GLOB.data_core.medical) + if(M.fields["name"] == record_general.fields["name"] && M.fields["id"] == record_general.fields["id"]) + qdel(M) + QDEL_NULL(record_general) + QDEL_NULL(record_security) + update_all_mob_security_hud() + current_page = SEC_DATA_R_LIST + set_temp("General, Security and Medical records deleted.") + if("delete_security") // Delete Security Record + if(!record_security) + return + QDEL_NULL(record_security) + update_all_mob_security_hud() + set_temp("Security record deleted.") + if("delete_security_all") // Delete All Security Records + for(var/datum/data/record/S in GLOB.data_core.security) + qdel(S) + update_all_mob_security_hud() + set_temp("All security records deleted.") + if("delete_cell_logs") // Delete All Cell Logs + GLOB?.cell_logs.Cut() + set_temp("All cell logs deleted.") + if("comment_delete") // Delete Comment + var/index = text2num(params["id"] || "") + if(!index || !record_security) + return - else if(href_list["print_r"]) - if(!printing) - printing = 1 - playsound(loc, "sound/goonstation/machines/printer_dotmatrix.ogg", 50, 1) - sleep(50) - var/obj/item/paper/P = new /obj/item/paper(loc) - P.info = "
Security Record

" - if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1)) - P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]] -
\nSex: [active1.fields["sex"]] -
\nAge: [active1.fields["age"]] -
\nFingerprint: [active1.fields["fingerprint"]] -
\nPhysical Status: [active1.fields["p_stat"]] -
\nMental Status: [active1.fields["m_stat"]]
"} - else - P.info += "General Record Lost!
" - if(istype(active2, /datum/data/record) && GLOB.data_core.security.Find(active2)) - P.info += {"
\n
Security Data
-
\nCriminal Status: [active2.fields["criminal"]]
\n -
\nMinor Crimes: [active2.fields["mi_crim"]] -
\nDetails: [active2.fields["mi_crim_d"]]
\n -
\nMajor Crimes: [active2.fields["ma_crim"]] -
\nDetails: [active2.fields["ma_crim_d"]]
\n -
\nImportant Notes: -
\n\t[active2.fields["notes"]]
\n
\n
Comments/Log

"} - for(var/c in active2.fields["comments"]) - P.info += "[c]
" - else - P.info += "Security Record Lost!
" - P.info += "" - P.name = "paper - 'Security Record: [active1.fields["name"]]'" - printing = 0 + var/list/comments = record_security.fields["comments"] + index = clamp(index, 1, length(comments)) + if(comments[index]) + comments.Cut(index, index + 1) + if("print_record") + if(is_printing) + return + is_printing = TRUE + playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, TRUE) + addtimer(CALLBACK(src, .proc/print_record_finish), 5 SECONDS) + else + return FALSE -/* Removed due to BYOND issue - else if(href_list["print_p"]) - if(!printing) - printing = 1 - playsound(loc, "sound/goonstation/machines/printer_dotmatrix.ogg", 50, 1) - sleep(50) - if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) - create_record_photo(active1) - printing = 0 -*/ + add_fingerprint(usr) - else if(href_list["printlogs"]) - if(GLOB.cell_logs.len && !printing) - var/obj/item/paper/P = input(usr, "Select log to print", "Available Cell Logs") as null|anything in GLOB.cell_logs - if(!P) - return 0 - printing = 1 - playsound(loc, "sound/goonstation/machines/printer_dotmatrix.ogg", 50, 1) - to_chat(usr, "Printing file [P.name].") - sleep(50) - var/obj/item/paper/log = new /obj/item/paper(loc) - log.name = P.name - log.info = P.info - printing = 0 - return 1 - else - to_chat(usr, "[src] has no logs stored or is already printing.") - - - else if(href_list["add_c"]) - if(istype(active2, /datum/data/record)) - var/a2 = active2 - var/t1 = copytext(trim(sanitize(input("Add Comment:", "Secure. records", null, null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["comments"] += "Made by [authenticated] ([rank]) on [GLOB.current_date_string] [station_time_timestamp()]
[t1]" - - else if(href_list["del_c"]) - var/index = min(max(text2num(href_list["del_c"]) + 1, 1), length(active2.fields["comments"])) - if(istype(active2, /datum/data/record) && active2.fields["comments"][index]) - active2.fields["comments"] -= active2.fields["comments"][index] - - if(href_list["field"]) - if(..()) - return 1 - var/a1 = active1 - var/a2 = active2 - switch(href_list["field"]) - if("name") - if(istype(active1, /datum/data/record)) - var/t1 = reject_bad_name(clean_input("Please input name:", "Secure. records", active1.fields["name"], null)) - if(!t1 || !length(trim(t1)) || ..() || active1 != a1) - return 1 - active1.fields["name"] = t1 - if(istype(active2, /datum/data/record)) - active2.fields["name"] = t1 - if("id") - if(istype(active1, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input id:", "Secure. records", active1.fields["id"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active1 != a1) - return 1 - active1.fields["id"] = t1 - if(istype(active2, /datum/data/record)) - active2.fields["id"] = t1 - if("fingerprint") - if(istype(active1, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input fingerprint hash:", "Secure. records", active1.fields["fingerprint"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active1 != a1) - return 1 - active1.fields["fingerprint"] = t1 - if("sex") - if(istype(active1, /datum/data/record)) - if(active1.fields["sex"] == "Male") - active1.fields["sex"] = "Female" - else - active1.fields["sex"] = "Male" - if("age") - if(istype(active1, /datum/data/record)) - var/t1 = input("Please input age:", "Secure. records", active1.fields["age"], null) as num - if(!t1 || ..() || active1 != a1) - return 1 - active1.fields["age"] = t1 - if("mi_crim") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input minor crimes list:", "Secure. records", active2.fields["mi_crim"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["mi_crim"] = t1 - if("mi_crim_d") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please summarize minor crimes:", "Secure. records", active2.fields["mi_crim_d"], null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["mi_crim_d"] = t1 - if("ma_crim") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input major crimes list:", "Secure. records", active2.fields["ma_crim"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["ma_crim"] = t1 - if("ma_crim_d") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please summarize major crimes:", "Secure. records", active2.fields["ma_crim_d"], null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["ma_crim_d"] = t1 - if("notes") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(html_encode(trim(input("Please summarize notes:", "Secure. records", html_decode(active2.fields["notes"]), null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["notes"] = t1 - if("criminal") - if(istype(active2, /datum/data/record)) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "None", "icon" = "unlock", "href" = "criminal=none", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_NONE ? "selected" : null)) - buttons[++buttons.len] = list("name" = "*Arrest*", "icon" = "lock", "href" = "criminal=arrest", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_ARREST ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Search", "icon" = "lock", "href" = "criminal=search", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_SEARCH ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Monitor", "icon" = "unlock", "href" = "criminal=monitor", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_MONITOR ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Demote", "icon" = "lock", "href" = "criminal=demote", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_DEMOTE ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Incarcerated", "icon" = "lock", "href" = "criminal=incarcerated", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_INCARCERATED ? "selected" : null)) - buttons[++buttons.len] = list("name" = "*Execute*", "icon" = "lock", "href" = "criminal=execute", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_EXECUTE ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Parolled", "icon" = "unlock-alt", "href" = "criminal=parolled", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_PAROLLED ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Released", "icon" = "unlock", "href" = "criminal=released", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_RELEASED ? "selected" : null)) - setTemp("

Criminal Status

", buttons) - if("rank") - var/list/L = list("Head of Personnel", "Captain", "AI") - //This was so silly before the change. Now it actually works without beating your head against the keyboard. /N - if(istype(active1, /datum/data/record) && L.Find(rank)) - var/list/buttons = list() - for(var/rank in GLOB.joblist) - buttons[++buttons.len] = list("name" = rank, "icon" = null, "href" = "rank=[rank]", "status" = (active1.fields["rank"] == rank ? "selected" : null)) - setTemp("

Rank

", buttons) +/** + * Called in tgui_act() to process modal actions + * + * Arguments: + * * action - The action passed by tgui + * * params - The params passed by tgui + */ +/obj/machinery/computer/secure_data/proc/tgui_act_modal(action, list/params) + . = TRUE + var/id = params["id"] + var/list/arguments = istext(params["arguments"]) ? json_decode(params["arguments"]) : params["arguments"] + switch(tgui_modal_act(src, action, params)) + if(TGUI_MODAL_OPEN) + switch(id) + if("edit") + var/field = arguments["field"] + if(!length(field) || !field_edit_questions[field]) + return + var/question = field_edit_questions[field] + var/choices = field_edit_choices[field] + if(length(choices)) + tgui_modal_choice(src, id, question, arguments = arguments, value = arguments["value"], choices = choices) else - setTemp("

You do not have the required rank to do this!

") - if("species") - if(istype(active1, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please enter race:", "General records", active1.fields["species"], null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active1 != a1) - return 1 - active1.fields["species"] = t1 - return 1 + tgui_modal_input(src, id, question, arguments = arguments, value = arguments["value"]) + if("comment_add") + tgui_modal_input(src, id, "Please enter your message:") + if("print_cell_log") + if(is_printing) + return + if(!length(GLOB?.cell_logs)) + set_temp("There are no cell logs available to print.") + return + var/list/choices = list() + var/list/already_in[0] + for(var/obj/item/paper/P in GLOB.cell_logs) + if(already_in[P.name]) + continue + choices += P.name + already_in[P.name] = TRUE + tgui_modal_choice(src, id, "Please select the cell log you would like printed:", choices = choices) + else + return FALSE + if(TGUI_MODAL_ANSWER) + var/answer = params["answer"] + switch(id) + if("edit") + var/field = arguments["field"] + if(!length(field) || !field_edit_questions[field]) + return + var/list/choices = field_edit_choices[field] + if(length(choices) && !(answer in choices)) + return -/obj/machinery/computer/secure_data/proc/setTemp(text, list/buttons = list()) - temp = list("text" = text, "buttons" = buttons, "has_buttons" = buttons.len > 0) + if(field == "age") + var/new_age = text2num(answer) + if(new_age < AGE_MIN || new_age > AGE_MAX) + set_temp("Invalid age. It must be between [AGE_MIN] and [AGE_MAX].", "danger") + return + answer = new_age + else if(field == "criminal") + var/text = "Please enter a reason for the status change to [answer]:" + if(answer == SEC_RECORD_STATUS_EXECUTE) + text = "Please explain why they are being executed. Include a list of their crimes, and victims." + tgui_modal_input(src, "criminal_reason", text, arguments = list("status" = answer)) + return -/* Proc disabled due to BYOND Issue + if(record_security && (field in record_security.fields)) + record_security.fields[field] = answer + else if(record_general && (field in record_general.fields)) + record_general.fields[field] = answer + if("criminal_reason") + if(!record_security || !(arguments["status"] in field_edit_choices["criminal"])) + return + if(!set_criminal_status(usr, record_security, arguments["status"], answer, tgui_login_get().rank, inserted_id_access)) + set_temp("Required permissions to set this criminal status not found!", "danger") + if("comment_add") + var/datum/tgui_login/state = tgui_login_get() + if(!length(answer) || !record_security || !length(state.name)) + return + record_security.fields["comments"] += list(list( + header = "Made by [state.name] ([state.rank]) on [GLOB.current_date_string] [station_time_timestamp()]", + text = answer + )) + if("print_cell_log") + if(is_printing) + return + var/obj/item/paper/T + for(var/obj/item/paper/P in GLOB?.cell_logs) + if(P.name == answer) + T = P + break + if(!T) + set_temp("Cell log not found!", "danger") + return + is_printing = TRUE + playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, TRUE) + addtimer(CALLBACK(src, .proc/print_cell_log_finish, T.name, T.info), 5 SECONDS) + else + return FALSE + else + return FALSE -/obj/machinery/computer/secure_data/proc/create_record_photo(datum/data/record/R) - // basically copy-pasted from the camera code but different enough that it has to be redone - var/icon/photoimage = get_record_photo(R) - var/icon/small_img = icon(photoimage) - var/icon/tiny_img = icon(photoimage) - var/icon/ic = icon('icons/obj/items.dmi',"photo") - var/icon/pc = icon('icons/obj/bureaucracy.dmi', "photo") - small_img.Scale(8, 8) - tiny_img.Scale(4, 4) - ic.Blend(small_img, ICON_OVERLAY, 10, 13) - pc.Blend(tiny_img, ICON_OVERLAY, 12, 19) +/** + * Called when the print record timer finishes + */ +/obj/machinery/computer/secure_data/proc/print_record_finish() + var/obj/item/paper/P = new(loc) + P.info = "
Security Record

" + if(record_general && GLOB.data_core.general.Find(record_general)) + P.info += {"Name: [record_general.fields["name"]] ID: [record_general.fields["id"]] +
\nSex: [record_general.fields["sex"]] +
\nAge: [record_general.fields["age"]] +
\nFingerprint: [record_general.fields["fingerprint"]] +
\nPhysical Status: [record_general.fields["p_stat"]] +
\nMental Status: [record_general.fields["m_stat"]]
"} + P.name = "paper - 'Security Record: [record_general.fields["name"]]'" + else + P.info += "General Record Lost!
" + if(record_security && GLOB.data_core.security.Find(record_security)) + P.info += {"
\n
Security Data
+
\nCriminal Status: [record_security.fields["criminal"]]
\n +
\nMinor Crimes: [record_security.fields["mi_crim"]] +
\nDetails: [record_security.fields["mi_crim_d"]]
\n +
\nMajor Crimes: [record_security.fields["ma_crim"]] +
\nDetails: [record_security.fields["ma_crim_d"]]
\n +
\nImportant Notes: +
\n\t[record_security.fields["notes"]]
\n
\n
Comments/Log

"} + for(var/c in record_security.fields["comments"]) + P.info += "[c]
" + else + P.info += "Security Record Lost!
" + is_printing = FALSE + SStgui.update_uis(src) - var/datum/picture/P = new() - P.fields["name"] = "File Photo - [R.fields["name"]]" - P.fields["author"] = "Central Command" - P.fields["icon"] = ic - P.fields["tiny"] = pc - P.fields["img"] = photoimage - P.fields["desc"] = "You can see [R.fields["name"]] on the photo." - P.fields["pixel_x"] = rand(-10, 10) - P.fields["pixel_y"] = rand(-10, 10) - P.fields["size"] = 2 +/** + * Called when the print cell log timer finishes + */ +/obj/machinery/computer/secure_data/proc/print_cell_log_finish(name, info) + var/obj/item/paper/P = new(loc) + P.name = name + P.info = info + is_printing = FALSE + SStgui.update_uis(src) - var/obj/item/photo/PH = new/obj/item/photo(loc) - PH.construct(P) +/obj/machinery/computer/secure_data/tgui_login_on_login(datum/tgui_login/state) + inserted_id_access = state?.id.access -*/ - -/obj/machinery/computer/secure_data/proc/get_record_photo(datum/data/record/R) - // similar to the code to make a photo, but of course the actual rendering is completely different - var/icon/res = icon('icons/effects/96x96.dmi', "") - // will be 2x2 to fit the 2 directions - res.Scale(2 * 32, 2 * 32) - // transparent background (it's a plastic transparency, you see) with the front and side icons - res.Blend(icon(R.fields["photo"], dir = SOUTH), ICON_OVERLAY, 1, 17) - res.Blend(icon(R.fields["photo"], dir = WEST), ICON_OVERLAY, 33, 17) - - return res +/obj/machinery/computer/secure_data/tgui_login_on_logout(datum/tgui_login/state) + inserted_id_access = null /obj/machinery/computer/secure_data/emp_act(severity) if(stat & (BROKEN|NOPOWER)) @@ -548,8 +420,8 @@ return for(var/datum/data/record/R in GLOB.data_core.security) - if(prob(10/severity)) - switch(rand(1,6)) + if(prob(10 / severity)) + switch(rand(1, 6)) if(1) R.fields["name"] = "[pick(pick(GLOB.first_names_male), pick(GLOB.first_names_female))] [pick(GLOB.last_names)]" if(2) @@ -570,6 +442,18 @@ ..(severity) +/** + * Sets a temporary message to display to the user + * + * Arguments: + * * text - Text to display, null/empty to clear the message from the UI + * * style - The style of the message: (color name), info, success, warning, danger + */ +/obj/machinery/computer/secure_data/proc/set_temp(text = "", style = "info", update_now = FALSE) + temp_notice = list(text = text, style = style) + if(update_now) + SStgui.update_uis(src) + /obj/machinery/computer/secure_data/detective_computer icon = 'icons/obj/computer.dmi' icon_state = "messyfiles" @@ -580,8 +464,10 @@ icon_state = "laptop" icon_keyboard = "seclaptop_key" icon_screen = "seclaptop" - density = 0 + density = FALSE #undef SEC_DATA_R_LIST #undef SEC_DATA_MAINT #undef SEC_DATA_RECORD +#undef FIELD +#undef SEC_FIELD diff --git a/tgui/packages/tgui/index.js b/tgui/packages/tgui/index.js index 45012a7e956..af9d6c6d7be 100644 --- a/tgui/packages/tgui/index.js +++ b/tgui/packages/tgui/index.js @@ -24,6 +24,7 @@ import './styles/themes/hackerman.scss'; import './styles/themes/malfunction.scss'; import './styles/themes/ntos.scss'; import './styles/themes/retro.scss'; +import './styles/themes/security.scss'; import './styles/themes/syndicate.scss'; diff --git a/tgui/packages/tgui/interfaces/SecurityRecords.js b/tgui/packages/tgui/interfaces/SecurityRecords.js new file mode 100644 index 00000000000..7ce69f82c4c --- /dev/null +++ b/tgui/packages/tgui/interfaces/SecurityRecords.js @@ -0,0 +1,437 @@ +import { createSearch } from 'common/string'; +import { Fragment } from 'inferno'; +import { useBackend, useLocalState } from "../backend"; +import { Box, Button, Flex, Icon, Input, LabeledList, Section, Table, Tabs } from '../components'; +import { FlexItem } from '../components/Flex'; +import { Window } from "../layouts"; +import { ComplexModal, modalOpen } from './common/ComplexModal'; +import { LoginInfo } from './common/LoginInfo'; +import { LoginScreen } from './common/LoginScreen'; +import { TemporaryNotice } from './common/TemporaryNotice'; + +const statusStyles = { + "*Execute*": "execute", + "*Arrest*": "arrest", + "Incarcerated": "incarcerated", + "Parolled": "parolled", + "Released": "released", +}; + +const doEdit = (context, field) => { + modalOpen(context, 'edit', { + field: field.edit, + value: field.value, + }); +}; + +export const SecurityRecords = (properties, context) => { + const { act, data } = useBackend(context); + const { + loginState, + currentPage, + } = data; + + let body; + if (!loginState.logged_in) { + return ( + + + + + + ); + } else { + if (currentPage === 1) { + body = ; + } else if (currentPage === 2) { + body = ; + } else if (currentPage === 3) { + body = ; + } + } + + return ( + + + + + + +
+ {body} +
+
+
+ ); +}; + +const SecurityRecordsNavigation = (properties, context) => { + const { act, data } = useBackend(context); + const { + currentPage, + general, + } = data; + return ( + + act('page', { page: 1 })}> + + List Records + + act('page', { page: 2 })}> + + Record Maintenance + + {(currentPage === 3 && general && !general.empty) && ( + + + Record: {general.fields[0].value} + + )} + + ); +}; + +const SecurityRecordsPageList = (properties, context) => { + const { act, data } = useBackend(context); + const { + records, + } = data; + const [searchText, setSearchText] = useLocalState(context, "searchText", ""); + const [sortId, _setSortId] = useLocalState(context, "sortId", "name"); + const [sortOrder, _setSortOrder] = useLocalState(context, "sortOrder", true); + return ( + + +
+ + + Name + ID + Assignment + Fingerprint + Criminal Status + + {records + .filter(createSearch(searchText, record => { + return record.name + "|" + + record.id + "|" + + record.rank + "|" + + record.fingerprint + "|" + + record.status; + })) + .sort((a, b) => { + const i = sortOrder ? 1 : -1; + return a[sortId].localeCompare(b[sortId]) * i; + }) + .map(record => ( + act('view', { + uid_gen: record.uid_gen, + uid_sec: record.uid_sec, + })}> + {record.name} + {record.id} + {record.rank} + {record.fingerprint} + {record.status} + + ))} +
+
+
+ ); +}; + +const SortButton = (properties, context) => { + const [sortId, setSortId] = useLocalState(context, "sortId", "name"); + const [sortOrder, setSortOrder] = useLocalState(context, "sortOrder", true); + const { + id, + children, + } = properties; + return ( + + + + ); +}; + +const SecurityRecordsActions = (properties, context) => { + const { act, data } = useBackend(context); + const { + isPrinting, + } = data; + const [searchText, setSearchText] = useLocalState(context, "searchText", ""); + return ( + + +