Skip to content

Commit

Permalink
New notes panel (#13225)
Browse files Browse the repository at this point in the history
* New notes panel

* fixes

* Any further improvements after table rework

* most stupid test

* Math, formathing, bans button
  • Loading branch information
volas committed Jun 30, 2024
1 parent 38b4ea8 commit 7041bec
Show file tree
Hide file tree
Showing 8 changed files with 246 additions and 298 deletions.
226 changes: 1 addition & 225 deletions code/modules/admin/admin.dm
Original file line number Diff line number Diff line change
Expand Up @@ -201,235 +201,11 @@ var/global/BSACooldown = 0

feedback_add_details("admin_verb","SPP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!


#define PLAYER_INFO_MISSING_CONTENT_TEXT "Missing Data"
#define PLAYER_INFO_MISSING_AUTHOR_TEXT "N/A"
#define PLAYER_INFO_MISSING_RANK_TEXT "N/A"
#define PLAYER_INFO_MISSING_TIMESTAMP_TEXT "N/A"
#define PLAYER_INFO_MISSING_JOB_TEXT "N/A"
#define PLAYER_INFO_MISSING_ROUND_ID_TEXT "N/A"

/datum/player_info
var/author = PLAYER_INFO_MISSING_AUTHOR_TEXT // admin who authored the information
var/content = PLAYER_INFO_MISSING_CONTENT_TEXT // text content of the information
var/timestamp = PLAYER_INFO_MISSING_TIMESTAMP_TEXT // Because this is bloody annoying
var/days_timestamp = 0 // number of day after 1 Jan 2000
var/round_id = PLAYER_INFO_MISSING_ROUND_ID_TEXT
var/ingameage = 0

/datum/player_info/proc/get_days_timestamp()
return isnum(days_timestamp) ? days_timestamp : 0

/datum/admins/proc/show_player_notes(key)
if(!(check_rights(R_LOG) && check_rights(R_BAN)))
return

key = ckey(key)

if(!key || !config.sql_enabled)
return

if(!establish_db_connection("erro_messages", "erro_ban"))
to_chat(usr, "Notes [key] from DB don't available.")
return

//Display player age and player warn bans
var/p_age
var/p_ingame_age
for(var/client/C in clients)
if(C.ckey == key)
p_age = C.player_age
p_ingame_age = C.player_ingame_age

// Gather data
var/list/db_messages = load_info_player_db_messages(key)
var/list/db_bans = load_info_player_db_bans(key)
// Start render info page
var/dat = ""
dat +="<span style='color:#000000; font-weight: bold'>Player age: [p_age] / In-game age: [p_ingame_age]</span><hr>"

if(!length(db_messages) && !length(db_bans))
dat += "No information found on the given key.<br>"
else
var/list/infos = generalized_players_info(db_messages, db_bans)
for(var/datum/player_info/I in infos)
dat += "<font color=#008800>[I.content]</font> <i>by [I.author]</i> on <i><font color=blue>#[I.round_id], [I.timestamp] ([I.ingameage] player minutes)</i></font> "
dat += "<br><br>"
dat += "<br>"
dat += "<A href='?src=\ref[src];add_player_info=[key]'>Add Comment</A><br>"

var/datum/browser/popup = new(usr, "window=adminplayerinfo", "Info on [key]", 480, 480, ntheme = CSS_THEME_LIGHT)
popup.set_content(dat)
popup.open()

/datum/admins/proc/generalized_players_info(list/file_notes, list/db_notes)
var/list/datum/player_info/merged = list()
if(length(file_notes))
merged += file_notes
if(length(db_notes))
merged += db_notes
merged = sortMerge(merged, GLOBAL_PROC_REF(cmp_days_timestamp), FALSE)
return merged

/proc/cmp_days_timestamp(datum/player_info/a, datum/player_info/b)
return a.get_days_timestamp() - b.get_days_timestamp()

/datum/admins/proc/load_info_player_db_messages(player_ckey)
// Get player ckey and generate list of players_notes
// Return null if errors
var/list/db_player_notes = list()
var/timestamp_format = "%a, %M %D of %Y" // we don't really need it now because both bans and notes use normal timestamp, but i'm little tired
var/days_ago_start_date = "1999-12-31" // to make changes here ang test, and anyway we will rewrite it completely
var/list/sql_fields = list(
"adminckey",
"text",
"DATE_FORMAT(timestamp, '[timestamp_format]')",
"DATEDIFF(timestamp, '[days_ago_start_date]')",
"round_id",
"ingameage"
)
var/DBQuery/query = dbcon.NewQuery("SELECT " + sql_fields.Join(", ") + " FROM erro_messages WHERE (targetckey = '[ckey(player_ckey)]') AND (deleted = 0) ORDER BY id LIMIT 100")
if(!query.Execute())
return
while(query.NextRow())
var/datum/player_info/notes_record = new()

var/a_ckey = query.item[1]
var/text = query.item[2]
var/timestamp = query.item[3]
var/days_ago = text2num(query.item[4])
var/rid = text2num(query.item[5])
var/ingameage = text2num(query.item[6])

if(length(a_ckey))
notes_record.author = a_ckey
if(length(text))
notes_record.content = text
if(length(timestamp))
notes_record.timestamp = timestamp
if(days_ago)
notes_record.days_timestamp = days_ago
if(rid)
notes_record.round_id = rid
if(ingameage)
notes_record.ingameage = ingameage

db_player_notes += notes_record

return db_player_notes

/datum/admins/proc/load_info_player_db_bans(player_ckey)
// Get player ckey and generate list of players_notes
// Return null if errors
var/list/db_player_notes = list()
var/timestamp_format = "%a, %M %D of %Y"
var/days_ago_start_date = "1999-12-31"
var/list/sql_fields = list(
"a_ckey",
"bantype",
"reason",
"DATE_FORMAT(bantime, '[timestamp_format]')",
"ip",
"computerid",
"duration",
"job",
"DATEDIFF(bantime, '[days_ago_start_date]')",
"unbanned",
"DATE_FORMAT(unbanned_datetime, '[timestamp_format]')",
"DATEDIFF(unbanned_datetime, '[days_ago_start_date]')",
"unbanned_ckey",
"round_id",
"ingameage"
)
var/DBQuery/query = dbcon.NewQuery("SELECT " + sql_fields.Join(", ") + " FROM erro_ban WHERE (ckey = '[ckey(player_ckey)]') ORDER BY id LIMIT 100")
if(!query.Execute())
return
while(query.NextRow())
var/datum/player_info/notes_record = new()
var/datum/player_info/unban_notes_record
var/list/ip_cid = list()
var/a_ckey = query.item[1]
var/bantype = query.item[2]
var/reason = query.item[3]
var/timestamp = query.item[4]
if(query.item[5])
ip_cid += query.item[5]
if(query.item[6])
ip_cid += query.item[6]
var/duration = text2num(query.item[7])
var/job = query.item[8] ? query.item[8] : PLAYER_INFO_MISSING_JOB_TEXT
var/days_ago = text2num(query.item[9])
var/is_unbanned = query.item[10] ? TRUE : FALSE
var/unbanned_timestamp = query.item[11]
var/unbanned_days_ago = text2num(query.item[12])
var/unbanned_a_ckey = query.item[13]
var/rid = text2num(query.item[14])
var/ingameage = text2num(query.item[15])

if(rid)
notes_record.round_id = rid

if(ingameage)
notes_record.round_id = ingameage

// -1 = perma, duration in minutes come
if(!duration)
duration = "N/A"
else if(duration < 0)
duration = "infinity"
else
duration = DisplayTimeText((duration MINUTE), 1)

// Ban Record creating
if(length(a_ckey))
notes_record.author = a_ckey
var/description = "([ip_cid.Join(", ")]): [reason]"
switch(bantype)
if (BANTYPE_JOB_PERMA)
// notes_record.content = "Permanent JOB BAN [job] [description]"
// already in notes by Adminbot
continue
if (BANTYPE_JOB_TEMP)
// notes_record.content = "Temporal JOB BAN [job] for [duration] [description]"
continue
if (BANTYPE_PERMA)
notes_record.content = "Permanent BAN [description]"
if (BANTYPE_TEMP)
notes_record.content = "Temporal BAN for [duration] [description]"
if(length(timestamp))
notes_record.timestamp = timestamp
if(days_ago)
notes_record.days_timestamp = days_ago
db_player_notes += notes_record

// Unban record creating
if(is_unbanned)
unban_notes_record = new()
if(length(unbanned_a_ckey))
unban_notes_record.author = unbanned_a_ckey
switch(bantype)
if(BANTYPE_JOB_PERMA)
unban_notes_record.content = "Unban. Permanent JOB BAN [job] was [timestamp]"
if(BANTYPE_JOB_TEMP)
unban_notes_record.content = "Unban. Temporal JOB BAN [job] was [timestamp]"
if(BANTYPE_PERMA)
unban_notes_record.content = "Unban. Permanent BAN was [timestamp]"
if(BANTYPE_TEMP)
unban_notes_record.content = "Unban. Temporal BAN was [timestamp]"
if(length(unbanned_timestamp))
unban_notes_record.timestamp = unbanned_timestamp
if(unbanned_days_ago)
unban_notes_record.days_timestamp = unbanned_days_ago
db_player_notes += unban_notes_record
return db_player_notes

#undef PLAYER_INFO_MISSING_ROUND_ID_TEXT
#undef PLAYER_INFO_MISSING_CONTENT_TEXT
#undef PLAYER_INFO_MISSING_AUTHOR_TEXT
#undef PLAYER_INFO_MISSING_RANK_TEXT
#undef PLAYER_INFO_MISSING_TIMESTAMP_TEXT
#undef PLAYER_INFO_MISSING_JOB_TEXT

notes_panel(key)

/datum/admins/proc/access_news_network() //MARKER
set category = "Fun"
Expand Down
2 changes: 1 addition & 1 deletion code/modules/admin/admin_verbs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,7 @@ var/global/list/admin_verbs_hideable = list(
if(!warned_ckey || !reason)
return

notes_add(warned_ckey, "ADMINWARN: " + reason, src, secret = 0)
notes_add(warned_ckey, "ADMINWARN: " + reason, admin_key = src.ckey, secret = 0)

var/client/C = directory[warned_ckey]
reason = sanitize(reason)
Expand Down
121 changes: 121 additions & 0 deletions code/modules/admin/notes_panel.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/proc/notes_panel(ckey) // change to proc player_access = FALSE
if(!(check_rights(R_LOG) && check_rights(R_BAN)))
return

if(!establish_db_connection("erro_messages", "erro_player"))
return

var/sql_ckey = ckey(ckey)

if(!sql_ckey)
return

var/html = ""

var/player_ingame_age
var/player_age
var/offline = TRUE

if(global.directory[ckey])
var/client/C = global.directory[ckey]
player_ingame_age = C.player_ingame_age
player_age = C.player_age
offline = FALSE
else
var/DBQuery/player_query = dbcon.NewQuery("SELECT datediff(Now(), firstseen) as age, ingameage FROM erro_player WHERE ckey = '[sql_ckey]'")
player_query.Execute()

while(player_query.NextRow())
player_age = text2num(player_query.item[1])
player_ingame_age = text2num(player_query.item[2])
break

html += {"
<div style='color: #000; font-weight: bold;'>
<a style='float: right;' href='?_src_=holder;notes_add=[sql_ckey]'>Add new message</a>
[offline ? "<span style='color: red;'>Offline</span>" : "<span style='color: green;'>Online</span>"] /
Player age: [player_age] / In-game age: [player_ingame_age]
<br/><hr>
</div>
"}

// todo: use mysql DATE_FORMAT(timestamp, '%d.%m.%Y %H:%i:%s') after bans table rework (consistent column names, also need to allow job as null)
var/DBQuery/query = dbcon.NewQuery({"
SELECT id as message_id, type AS message_type, text AS message, timestamp, ingameage, adminckey AS author, round_id FROM erro_messages WHERE targetckey = '[sql_ckey]' AND deleted != 1
UNION ALL
SELECT NULL as message_id, bantype AS message_type, CONCAT_WS(' | Job: ', reason, NULLIF(job,'')) AS message, bantime AS timestamp, ingameage, a_ckey AS author, round_id FROM erro_ban WHERE ckey = '[sql_ckey]'
ORDER by timestamp DESC
LIMIT 50;
"}) // todo: pager

if(!query.Execute())
return

var/message_id
var/message_type
var/message
var/timestamp
var/ingameage
var/author
var/round_id

var/age_temperature
var/border_color
var/static/list/type_hex_colors = list(
"note" = "#00ffff",
lowertext(BANTYPE_PERMA) = "#b00000",
lowertext(BANTYPE_TEMP) = "#ff0000",
lowertext(BANTYPE_JOB_PERMA) = "#ff8c00",
lowertext(BANTYPE_JOB_TEMP) = "#ffa500",
)

var/buttons

while(query.NextRow())
message_id = query.item[1]
message_type = lowertext(query.item[2])
message = query.item[3]
timestamp = query.item[4]
ingameage = text2num(query.item[5])
author = query.item[6]
round_id = query.item[7] ? "#"+query.item[7] : ""

// heat color for recent messages
if(player_ingame_age && ingameage)
// if diff 5000 minutes or more - green
// if diff close to 0 - red
age_temperature = clamp(floor(((player_ingame_age - ingameage) * 100) / 5000), 0, 100)
else
age_temperature = 100

if(type_hex_colors[message_type])
border_color = type_hex_colors[message_type]
else
border_color = null

if(message_type == "note" && message_id)
buttons = {"
<div style='float: right'>
<a title='Edit' href='?_src_=holder;notes_edit=[sql_ckey];index=[message_id]'>E</a> <a title='Remove' href='?_src_=holder;notes_delete=[sql_ckey];index=[message_id]'>R</a>
</div>
"}
else // bans
buttons = {"
<div style='float: right'>
<a title='View bans' href='?_src_=holder;dbsearchckey=[sql_ckey];index=[message_id]'>V</a>
</div>
"}

// todo: move styles to own css
html += {"
<div style='padding: 8px; margin-top: 8px; background: #d1d1d1; border: 2px solid #444; [border_color ? "border-left: 6px solid [border_color]" : ""]'>
[buttons]
<span style='font-style: italic; color: #008800; font-size: 140%;'>[message]</span><br/>
<hr>
<b>Type:</b> [message_type]; <b>Date:</b> [timestamp] [round_id];<br/> <b>Minutes:</b> <span style='font-weight: bold; background: black; color:hsl([age_temperature], 100%, 50%);'>[ingameage]</span>; <b>By:</b> [author]
</div>
"}

var/datum/browser/popup = new(usr, "[sql_ckey]_notes_history", "[ckey] notes history", 700, 700, ntheme = CSS_THEME_LIGHT)
popup.set_content(html)
popup.open()
Loading

0 comments on commit 7041bec

Please sign in to comment.