[MAJOR CHANGE] Admin rank datum (#17133)

* Admin ranks update

* Permissions fix

* One missing thing

* Further rework to make database also fully work

* Adds admin rank bitflag calculator to tools

* ZA WARUDO

---------

Co-authored-by: C.L. <killer65311@gmail.com>
This commit is contained in:
Selis
2025-03-06 01:38:19 +01:00
committed by GitHub
parent caec59322e
commit 7a7ae89713
88 changed files with 1993 additions and 904 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
#ifndef OVERRIDE_BAN_SYSTEM
//Blocks an attempt to connect before even creating our client datum thing.
/world/IsBanned(key,address,computer_id)
if(ckey(key) in admin_datums)
if(ckey(key) in GLOB.admin_datums)
return ..()
//Guest Checking
+31 -8
View File
@@ -7,7 +7,7 @@ var/global/floorIsLava = 0
//log_adminwarn(msg) //log_and_message_admins is for this
for(var/client/C in GLOB.admins)
if((R_ADMIN|R_MOD) & C.holder.rights)
if(check_rights_for(C, (R_ADMIN|R_MOD|R_SERVER)))
to_chat(C,
type = MESSAGE_TYPE_ADMINLOG,
html = msg,
@@ -16,7 +16,7 @@ var/global/floorIsLava = 0
/proc/msg_admin_attack(var/text) //Toggleable Attack Messages
var/rendered = span_filter_attacklog(span_log_message(span_prefix("ATTACK:") + span_message("[text]")))
for(var/client/C in GLOB.admins)
if((R_ADMIN|R_MOD) & C.holder.rights)
if(check_rights_for(C, (R_ADMIN|R_MOD)))
if(C.prefs?.read_preference(/datum/preference/toggle/show_attack_logs))
var/msg = rendered
to_chat(C,
@@ -26,8 +26,16 @@ var/global/floorIsLava = 0
/proc/admin_notice(var/message, var/rights)
for(var/mob/M in mob_list)
if(check_rights(rights, 0, M))
to_chat(M,message)
var/C = M.client
if(!C)
return
if(!(istype(C, /client)))
return
if(check_rights_for(C, rights))
to_chat(C, message)
///////////////////////////////////////////////////////////////////////////////////////////////Panels
@@ -49,7 +57,7 @@ var/global/floorIsLava = 0
body += "<body>Options panel for" + span_bold("[M]")
if(M.client)
body += " played by " + span_bold("[M.client]")
body += "\[<A href='byond://?src=\ref[src];[HrefToken()];editrights=show'>[M.client.holder ? M.client.holder.rank : "Player"]</A>\]"
body += "\[<A href='byond://?src=\ref[src];[HrefToken()];editrights=show'>[M.client.holder ? M.client.holder.rank_names() : "Player"]</A>\]"
if(isnewplayer(M))
body += span_bold(" Hasn't Entered Game")
@@ -1353,7 +1361,7 @@ var/datum/announcement/minor/admin_min_announcer = new
if(istype(whom, /mob))
M = whom
C = M.client
if(R_HOST & C.holder.rights)
if(check_rights_for(C, R_HOST))
return 1
else
return 0
@@ -1562,12 +1570,12 @@ var/datum/announcement/minor/admin_min_announcer = new
if(P.sender) // sent as a reply
log_admin("[key_name(src.owner)] replied to a fax message from [key_name(P.sender)]")
for(var/client/C in GLOB.admins)
if((R_ADMIN | R_MOD | R_EVENT) & C.holder.rights)
if(check_rights_for(C, (R_ADMIN | R_MOD | R_EVENT)))
to_chat(C, span_log_message("[span_prefix("FAX LOG:")][key_name_admin(src.owner)] replied to a fax message from [key_name_admin(P.sender)] (<a href='byond://?_src_=holder;[HrefToken()];AdminFaxView=\ref[rcvdcopy]'>VIEW</a>)"))
else
log_admin("[key_name(src.owner)] has sent a fax message to [destination.department]")
for(var/client/C in GLOB.admins)
if((R_ADMIN | R_MOD | R_EVENT) & C.holder.rights)
if(check_rights_for(C, (R_ADMIN | R_MOD | R_EVENT)))
to_chat(C, span_log_message("[span_prefix("FAX LOG:")][key_name_admin(src.owner)] has sent a fax message to [destination.department] (<a href='byond://?_src_=holder;[HrefToken()];AdminFaxView=\ref[rcvdcopy]'>VIEW</a>)"))
var/plaintext_title = P.sender ? "replied to [key_name(P.sender)]'s fax" : "sent a fax message to [destination.department]"
@@ -1590,3 +1598,18 @@ var/datum/announcement/minor/admin_min_announcer = new
qdel(P)
faxreply = null
return
/datum/admins/proc/set_uplink(mob/living/carbon/human/H as mob)
set category = "Debug.Events"
set name = "Set Uplink"
set desc = "Allows admins to set up an uplink on a character. This will be required for a character to use telecrystals."
set popup_menu = FALSE
if(check_rights(R_ADMIN|R_DEBUG))
traitors.spawn_uplink(H)
H.mind.tcrystals = DEFAULT_TELECRYSTAL_AMOUNT
H.mind.accept_tcrystals = 1
var/msg = "[key_name(usr)] has given [H.ckey] an uplink."
message_admins(msg)
else
to_chat(usr, "You do not have access to this command.")
+368 -136
View File
@@ -1,166 +1,398 @@
var/list/admin_ranks = list() //list of all ranks with associated rights
GLOBAL_LIST_EMPTY(admin_ranks) //list of all admin_rank datums
GLOBAL_PROTECT(admin_ranks)
//load our rank - > rights associations
/proc/load_admin_ranks()
admin_ranks.Cut()
GLOBAL_LIST_EMPTY(protected_ranks) //admin ranks loaded from txt
GLOBAL_PROTECT(protected_ranks)
var/previous_rights = 0
/datum/admin_rank
var/name = "NoRank"
var/rights = R_DEFAULT
var/exclude_rights = NONE
var/include_rights = NONE
var/can_edit_rights = NONE
//Clear profile access
for(var/A in world.GetConfig("admin"))
world.SetConfig("APP/admin", A, null)
/datum/admin_rank/New(init_name, init_rights, init_exclude_rights, init_edit_rights)
if(IsAdminAdvancedProcCall())
alert_to_permissions_elevation_attempt(usr)
if (name == "NoRank") //only del if this is a true creation (and not just a New() proc call), other wise trialmins/coders could abuse this to deadmin other admins
QDEL_IN(src, 0)
CRASH("Admin proc call creation of admin datum")
return
name = init_name
if(!name)
qdel(src)
CRASH("Admin rank created without name.")
if(init_rights)
rights = init_rights
include_rights = rights
if(init_exclude_rights)
exclude_rights = init_exclude_rights
rights &= ~exclude_rights
if(init_edit_rights)
can_edit_rights = init_edit_rights
//load text from file
var/list/Lines = file2list("config/admin_ranks.txt")
/datum/admin_rank/Destroy()
if(IsAdminAdvancedProcCall())
alert_to_permissions_elevation_attempt(usr)
return QDEL_HINT_LETMELIVE
. = ..()
//process each line seperately
for(var/line in Lines)
if(!length(line)) continue
if(copytext(line,1,2) == "#") continue
/datum/admin_rank/vv_edit_var(var_name, var_value)
return FALSE
var/list/List = splittext(line,"+")
if(!List.len) continue
var/rank = ckeyEx(List[1])
switch(rank)
if(null,"") continue
if("Removed") continue //Reserved
var/rights = 0
for(var/i=2, i<=List.len, i++)
switch(ckey(List[i]))
if("@","prev") rights |= previous_rights
if("buildmode","build") rights |= R_BUILDMODE
if("admin") rights |= R_ADMIN
if("ban") rights |= R_BAN
if("fun") rights |= R_FUN
if("server") rights |= R_SERVER
if("debug") rights |= R_DEBUG
if("permissions","rights") rights |= R_PERMISSIONS
if("possess") rights |= R_POSSESS
if("stealth") rights |= R_STEALTH
if("rejuv","rejuvinate") rights |= R_REJUVINATE
if("varedit") rights |= R_VAREDIT
if("everything","host","all") rights |= (R_HOST | R_BUILDMODE | R_ADMIN | R_BAN | R_FUN | R_SERVER | R_DEBUG | R_PERMISSIONS | R_POSSESS | R_STEALTH | R_REJUVINATE | R_VAREDIT | R_SOUNDS | R_SPAWN | R_MOD| R_EVENT)
if("sound","sounds") rights |= R_SOUNDS
if("spawn","create") rights |= R_SPAWN
if("mod") rights |= R_MOD
if("event") rights |= R_EVENT
admin_ranks[rank] = rights
previous_rights = rights
// Adds/removes rights to this admin_rank
/datum/admin_rank/proc/process_keyword(group, group_count, datum/admin_rank/previous_rank)
if(IsAdminAdvancedProcCall())
alert_to_permissions_elevation_attempt(usr)
return
var/list/keywords = splittext(group, " ")
var/flag = 0
for(var/k in keywords)
switch(k)
if("BUILD")
flag = R_BUILDMODE
if("ADMIN")
flag = R_ADMIN
if("BAN")
flag = R_BAN
if("FUN")
flag = R_FUN
if("SERVER")
flag = R_SERVER
if("DEBUG")
flag = R_DEBUG
if("PERMISSIONS")
flag = R_PERMISSIONS
if("POSSESS")
flag = R_POSSESS
if("STEALTH")
flag = R_STEALTH
if("REJUVINATE")
flag = R_REJUVINATE
if("VAREDIT")
flag = R_VAREDIT
if("EVERYTHING")
flag = R_EVERYTHING
if("SOUND")
flag = R_SOUNDS
if("SPAWN")
flag = R_SPAWN
if("MOD")
flag = R_MOD
if("EVENT")
flag = R_EVENT
if("@")
if(previous_rank)
switch(group_count)
if(1)
flag = previous_rank.include_rights
if(2)
flag = previous_rank.exclude_rights
if(3)
flag = previous_rank.can_edit_rights
else
continue
switch(group_count)
if(1)
rights |= flag
include_rights |= flag
if(2)
rights &= ~flag
exclude_rights |= flag
if(3)
can_edit_rights |= flag
/// Loads admin ranks.
/// Return a list containing the backup data if they were loaded from the database backup json
/proc/load_admin_ranks(dbfail, no_update)
if(IsAdminAdvancedProcCall())
to_chat(usr, "<span class='admin prefix'>Admin Reload blocked: Advanced ProcCall detected.</span>", confidential = TRUE)
return
GLOB.admin_ranks.Cut()
GLOB.protected_ranks.Cut()
//load text from file and process each entry
var/ranks_text = file2text("[global.config.directory]/admin_ranks.txt")
var/datum/admin_rank/previous_rank
var/regex/admin_ranks_regex = new(@"^Name\s*=\s*(.+?)\s*\n+Include\s*=\s*([\l @]*?)\s*\n+Exclude\s*=\s*([\l @]*?)\s*\n+Edit\s*=\s*([\l @]*?)\s*\n*$", "gm")
while(admin_ranks_regex.Find(ranks_text))
var/datum/admin_rank/R = new(admin_ranks_regex.group[1])
if(!R)
continue
var/count = 1
for(var/i in admin_ranks_regex.group - admin_ranks_regex.group[1])
if(i)
R.process_keyword(i, count, previous_rank)
count++
GLOB.admin_ranks += R
GLOB.protected_ranks += R
previous_rank = R
if(!CONFIG_GET(flag/admin_legacy_system) && !dbfail)
if(CONFIG_GET(flag/load_legacy_ranks_only))
if(!no_update)
sync_ranks_with_db()
else
var/datum/db_query/query_load_admin_ranks = SSdbcore.NewQuery("SELECT `rank`, flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")]")
if(!query_load_admin_ranks.Execute())
message_admins("Error loading admin ranks from database. Loading from backup.")
log_sql("Error loading admin ranks from database. Loading from backup.")
dbfail = TRUE
else
while(query_load_admin_ranks.NextRow())
var/skip
var/rank_name = query_load_admin_ranks.item[1]
for(var/datum/admin_rank/R in GLOB.admin_ranks)
if(R.name == rank_name) //this rank was already loaded from txt override
skip = 1
break
if(!skip)
var/rank_flags = text2num(query_load_admin_ranks.item[2])
var/rank_exclude_flags = text2num(query_load_admin_ranks.item[3])
var/rank_can_edit_flags = text2num(query_load_admin_ranks.item[4])
var/datum/admin_rank/R = new(rank_name, rank_flags, rank_exclude_flags, rank_can_edit_flags)
if(!R)
continue
GLOB.admin_ranks += R
qdel(query_load_admin_ranks)
//load ranks from backup file
if(dbfail)
var/backup_file = file2text("data/admins_backup.json")
if(backup_file == null)
log_world("Unable to locate admins backup file.")
return FALSE
var/list/json = json_decode(backup_file)
for(var/J in json["ranks"])
var/skip
for(var/datum/admin_rank/R in GLOB.admin_ranks)
if(R.name == "[J]") //this rank was already loaded from txt override
skip = TRUE
if(skip)
continue
var/datum/admin_rank/R = new("[J]", json["ranks"]["[J]"]["include rights"], json["ranks"]["[J]"]["exclude rights"], json["ranks"]["[J]"]["can edit rights"])
if(!R)
continue
GLOB.admin_ranks += R
return json
#ifdef TESTING
var/msg = "Permission Sets Built:\n"
for(var/rank in admin_ranks)
msg += "\t[rank] - [admin_ranks[rank]]\n"
for(var/datum/admin_rank/R in GLOB.admin_ranks)
msg += "\t[R.name]"
var/rights = rights2text(R.rights,"\n\t\t")
if(rights)
msg += "\t\t[rights]\n"
testing(msg)
#endif
/hook/startup/proc/loadAdmins()
load_admins()
return 1
/// Converts a rank name (such as "Coder+Moth") into a list of /datum/admin_rank
/proc/ranks_from_rank_name(rank_name)
var/list/rank_names = splittext(rank_name, "+")
var/list/ranks = list()
/proc/load_admins()
for (var/datum/admin_rank/rank as anything in GLOB.admin_ranks)
if (rank.name in rank_names)
rank_names -= rank.name
ranks += rank
if (rank_names.len == 0)
break
if (rank_names.len > 0)
log_config("Admin rank names were invalid: [jointext(ranks, ", ")]")
return ranks
/// Takes a list of rank names and joins them with +
/proc/join_admin_ranks(list/datum/admin_rank/ranks)
var/list/names = list()
for (var/datum/admin_rank/rank as anything in ranks)
names += rank.name
return jointext(names, "+")
/// (Re)Loads the admin list.
/// returns TRUE if database admins had to be loaded from the backup json
/proc/load_admins(no_update)
var/dbfail
if(!CONFIG_GET(flag/admin_legacy_system) && !SSdbcore.Connect())
message_admins("Failed to connect to database while loading admins. Loading from backup.")
log_sql("Failed to connect to database while loading admins. Loading from backup.")
dbfail = TRUE
//clear the datums references
admin_datums.Cut()
GLOB.admin_datums.Cut()
for(var/client/C in GLOB.admins)
C.remove_admin_verbs()
C.holder = null
GLOB.admins.Cut()
GLOB.protected_admins.Cut()
GLOB.deadmins.Cut()
var/list/backup_file_json = load_admin_ranks(dbfail, no_update)
dbfail = backup_file_json != null
//Clear profile access
for(var/A in world.GetConfig("admin"))
world.SetConfig("APP/admin", A, null)
var/list/rank_names = list()
for(var/datum/admin_rank/R in GLOB.admin_ranks)
rank_names[R.name] = R
//ckeys listed in admins.txt are always made admins before sql loading is attempted
var/admins_text = file2text("[global.config.directory]/admins.txt")
var/regex/admins_regex = new(@"^(?!#)(.+?)\s+=\s+(.+)", "gm")
if(CONFIG_GET(flag/admin_legacy_system))
load_admin_ranks()
while(admins_regex.Find(admins_text))
var/admin_key = admins_regex.group[1]
var/admin_rank = admins_regex.group[2]
new /datum/admins(ranks_from_rank_name(admin_rank), ckey(admin_key), force_active = FALSE, protected = TRUE)
//load text from file
var/list/Lines = file2list("config/admins.txt")
if(!CONFIG_GET(flag/admin_legacy_system) && !dbfail)
var/datum/db_query/query_load_admins = SSdbcore.NewQuery("SELECT ckey, `rank`, feedback FROM [format_table_name("admin")] ORDER BY `rank`")
if(!query_load_admins.Execute())
message_admins("Error loading admins from database. Loading from backup.")
log_sql("Error loading admins from database. Loading from backup.")
dbfail = 1
else
while(query_load_admins.NextRow())
var/admin_ckey = ckey(query_load_admins.item[1])
var/admin_rank = query_load_admins.item[2]
var/admin_feedback = query_load_admins.item[3]
var/skip
//process each line seperately
for(var/line in Lines)
if(!length(line)) continue
if(copytext(line,1,2) == "#") continue
//Split the line at every "-"
var/list/List = splittext(line, "-")
if(!List.len) continue
//ckey is before the first "-"
var/ckey = ckey(List[1])
if(!ckey) continue
//rank follows the first "-"
var/rank = ""
if(List.len >= 2)
rank = ckeyEx(List[2])
//load permissions associated with this rank
var/rights = admin_ranks[rank]
//create the admin datum and store it for later use
var/datum/admins/D = new /datum/admins(rank, rights, ckey)
//find the client for a ckey if they are connected and associate them with the new admin datum
D.associate(GLOB.directory[ckey])
else
//The current admin system uses SQL
establish_db_connection()
if(!SSdbcore.IsConnected())
error("Failed to connect to database in load_admins(). Reverting to legacy system.")
log_misc("Failed to connect to database in load_admins(). Reverting to legacy system.")
CONFIG_SET(flag/admin_legacy_system, TRUE)
load_admins()
return
var/datum/db_query/query = SSdbcore.NewQuery("SELECT ckey, rank, level, flags FROM erro_admin")
query.Execute()
while(query.NextRow())
var/ckey = query.item[1]
var/rank = query.item[2]
if(rank == "Removed") continue //This person was de-adminned. They are only in the admin list for archive purposes.
var/rights = query.item[4]
if(istext(rights)) rights = text2num(rights)
var/datum/admins/D = new /datum/admins(rank, rights, ckey)
//find the client for a ckey if they are connected and associate them with the new admin datum
D.associate(GLOB.directory[ckey])
qdel(query)
if(!admin_datums)
error("The database query in load_admins() resulted in no admins being added to the list. Reverting to legacy system.")
log_misc("The database query in load_admins() resulted in no admins being added to the list. Reverting to legacy system.")
CONFIG_SET(flag/admin_legacy_system, TRUE)
load_admins()
return
var/list/admin_ranks = ranks_from_rank_name(admin_rank)
if(admin_ranks.len == 0)
message_admins("[admin_ckey] loaded with invalid admin rank [admin_rank].")
skip = 1
if(GLOB.admin_datums[admin_ckey] || GLOB.deadmins[admin_ckey])
skip = 1
if(!skip)
var/datum/admins/admin_holder = new(admin_ranks, admin_ckey)
admin_holder.cached_feedback_link = admin_feedback || NO_FEEDBACK_LINK
qdel(query_load_admins)
if (!no_update)
save_admin_backup()
sync_admins_with_db()
//load admins from backup file
if(dbfail)
if(!backup_file_json)
if(backup_file_json != null)
//already tried
return
var/backup_file = file2text("data/admins_backup.json")
if(backup_file == null)
log_world("Unable to locate admins backup file.")
return
backup_file_json = json_decode(backup_file)
for(var/backup_admin_ckey in backup_file_json["admins"])
var/skip
for(var/admin_ckey in GLOB.admin_datums + GLOB.deadmins)
if(ckey(admin_ckey) == ckey("[backup_admin_ckey]")) //this admin was already loaded from txt override
skip = TRUE
break
if(skip)
continue
new /datum/admins(ranks_from_rank_name(backup_file_json["admins"]["[backup_admin_ckey]"]), ckey("[backup_admin_ckey]"))
#ifdef TESTING
var/msg = "Admins Built:\n"
for(var/ckey in admin_datums)
var/rank
var/datum/admins/D = admin_datums[ckey]
if(D) rank = D.rank
msg += "\t[ckey] - [rank]\n"
for(var/ckey in GLOB.admin_datums)
var/datum/admins/D = GLOB.admin_datums[ckey]
msg += "\t[ckey] - [D.rank_names()]\n"
testing(msg)
#endif
return dbfail
#ifdef TESTING
/client/verb/changerank(newrank in admin_ranks)
if(holder)
holder.rank = newrank
holder.rights = admin_ranks[newrank]
else
holder = new /datum/admins(newrank,admin_ranks[newrank],ckey)
remove_admin_verbs()
holder.associate(src)
/proc/sync_ranks_with_db()
set waitfor = FALSE
/client/verb/changerights(newrights as num)
if(holder)
holder.rights = newrights
else
holder = new /datum/admins("testing",newrights,ckey)
remove_admin_verbs()
holder.associate(src)
if(IsAdminAdvancedProcCall())
to_chat(usr, "<span class='admin prefix'>Admin rank DB Sync blocked: Advanced ProcCall detected.</span>", confidential = TRUE)
return
#endif
var/list/sql_ranks = list()
for(var/datum/admin_rank/R as anything in GLOB.protected_ranks)
sql_ranks += list(list("rank" = R.name, "flags" = R.include_rights, "exclude_flags" = R.exclude_rights, "can_edit_flags" = R.can_edit_rights))
SSdbcore.MassInsert(format_table_name("admin_ranks"), sql_ranks, duplicate_key = TRUE)
update_everything_flag_in_db()
/proc/update_everything_flag_in_db()
for(var/datum/admin_rank/R as anything in GLOB.admin_ranks)
var/list/flags = list()
if(R.include_rights == R_EVERYTHING)
flags += "flags"
if(R.exclude_rights == R_EVERYTHING)
flags += "exclude_flags"
if(R.can_edit_rights == R_EVERYTHING)
flags += "can_edit_flags"
if(!flags.len)
continue
var/flags_to_check = flags.Join(" != [R_EVERYTHING] AND ") + " != [R_EVERYTHING]"
var/datum/db_query/query_check_everything_ranks = SSdbcore.NewQuery(
"SELECT flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")] WHERE rank = :rank AND ([flags_to_check])",
list("rank" = R.name)
)
if(!query_check_everything_ranks.Execute())
qdel(query_check_everything_ranks)
return
if(query_check_everything_ranks.NextRow()) //no row is returned if the rank already has the correct flag value
var/flags_to_update = flags.Join(" = [R_EVERYTHING], ") + " = [R_EVERYTHING]"
var/datum/db_query/query_update_everything_ranks = SSdbcore.NewQuery(
"UPDATE [format_table_name("admin_ranks")] SET [flags_to_update] WHERE rank = :rank",
list("rank" = R.name)
)
if(!query_update_everything_ranks.Execute())
qdel(query_update_everything_ranks)
return
qdel(query_update_everything_ranks)
qdel(query_check_everything_ranks)
/proc/sync_admins_with_db()
if(IsAdminAdvancedProcCall())
to_chat(usr, "<span class='admin prefix'>Admin rank DB Sync blocked: Advanced ProcCall detected.</span>")
return
if(CONFIG_GET(flag/admin_legacy_system) || !SSdbcore.IsConnected()) //we're already using legacy system so there's nothing to save
return
sync_ranks_with_db()
var/list/sql_admins = list()
for(var/holder_ckey in GLOB.protected_admins)
var/datum/admins/holder = GLOB.protected_admins[holder_ckey]
sql_admins += list(list("ckey" = holder.target, "rank" = holder.rank_names()))
SSdbcore.MassInsert(format_table_name("admin"), sql_admins, duplicate_key = TRUE)
var/datum/db_query/query_admin_rank_update = SSdbcore.NewQuery("UPDATE [format_table_name("player")] AS p INNER JOIN [format_table_name("admin")] AS a ON p.ckey = a.ckey SET p.lastadminrank = a.rank")
query_admin_rank_update.Execute()
qdel(query_admin_rank_update)
/proc/save_admin_backup()
if(IsAdminAdvancedProcCall())
to_chat(usr, "<span class='admin prefix'>Admin rank DB Sync blocked: Advanced ProcCall detected.</span>")
return
if(CONFIG_GET(flag/admin_legacy_system)) //we're already using legacy system so there's nothing to save
return
//json format backup file generation stored per server
var/json_file = file("data/admins_backup.json")
var/list/file_data = list(
"ranks" = list(),
"admins" = list()
)
for(var/datum/admin_rank/R as anything in GLOB.admin_ranks)
file_data["ranks"]["[R.name]"] = list()
file_data["ranks"]["[R.name]"]["include rights"] = R.include_rights
file_data["ranks"]["[R.name]"]["exclude rights"] = R.exclude_rights
file_data["ranks"]["[R.name]"]["can edit rights"] = R.can_edit_rights
for(var/admin_ckey in GLOB.admin_datums + GLOB.deadmins)
var/datum/admins/admin = GLOB.admin_datums[admin_ckey]
if(!admin)
admin = GLOB.deadmins[admin_ckey]
if (!admin)
continue
file_data["admins"][admin_ckey] = admin.rank_names()
//admin.backup_connections()
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data, JSON_PRETTY_PRINT))
+1 -1
View File
@@ -55,7 +55,7 @@ var/datum/admin_secrets/admin_secrets = new()
return name
/datum/admin_secret_item/proc/can_view(var/mob/user)
return check_rights(permissions, 0, user)
return check_rights_for(user.client, permissions)
/datum/admin_secret_item/proc/can_execute(var/mob/user)
if(can_view(user))
+2 -2
View File
@@ -3,7 +3,7 @@ var/list/admin_verbs_default = list(
/datum/admins/proc/show_player_panel, //shows an interface for individual players, with various links (links require additional flags,
/client/proc/player_panel_new, //shows an interface for all players, with links to various panels,
/client/proc/player_panel,
/client/proc/deadmin_self, //destroys our own admin datum so we can play as a regular player,
/client/proc/deadmin, //destroys our own admin datum so we can play as a regular player,
/client/proc/hide_verbs, //hides all our adminverbs,
/client/proc/hide_most_verbs, //hides all our hideable adminverbs,
/client/proc/debug_variables, //allows us to -see- the variables of any instance in the game. +VAREDIT needed to modify,
@@ -268,7 +268,7 @@ var/list/admin_verbs_rejuv = list(
//verbs which can be hidden - needs work
var/list/admin_verbs_hideable = list(
/client/proc/deadmin_self,
/client/proc/deadmin,
// /client/proc/deadchat,
/datum/admins/proc/show_traitor_panel,
/datum/admins/proc/toggleenter,
+18 -17
View File
@@ -3,7 +3,7 @@ var/list/admin_verbs_default = list(
// /datum/admins/proc/show_player_panel, //shows an interface for individual players, with various links (links require additional flags, //VOREStation Remove,
// /client/proc/player_panel_new, //shows an interface for all players, with links to various panels, //VOREStation Remove,
// /client/proc/player_panel, //VOREStation Remove,
/client/proc/deadmin_self, //destroys our own admin datum so we can play as a regular player,
/client/proc/deadmin, //destroys our own admin datum so we can play as a regular player,
/client/proc/cmd_admin_say, //VOREStation Add,
/client/proc/cmd_mod_say, //VOREStation Add,
/client/proc/cmd_event_say, //VOREStation Add,
@@ -311,7 +311,7 @@ var/list/admin_verbs_rejuv = list(
//verbs which can be hidden - needs work
var/list/admin_verbs_hideable = list(
/client/proc/deadmin_self,
/client/proc/deadmin,
// /client/proc/deadchat,
/datum/admins/proc/show_traitor_panel,
/datum/admins/proc/toggleenter,
@@ -580,24 +580,25 @@ var/list/admin_verbs_event_manager = list(
/client/proc/add_admin_verbs()
if(holder)
var/rights = holder.rank_flags()
add_verb(src, admin_verbs_default)
if(holder.rights & R_BUILDMODE) add_verb(src, /client/proc/togglebuildmodeself)
if(holder.rights & R_ADMIN) add_verb(src, admin_verbs_admin)
if(holder.rights & R_BAN) add_verb(src, admin_verbs_ban)
if(holder.rights & R_FUN) add_verb(src, admin_verbs_fun)
if(holder.rights & R_SERVER) add_verb(src, admin_verbs_server)
if(holder.rights & R_DEBUG)
if(rights & R_BUILDMODE) add_verb(src, /client/proc/togglebuildmodeself)
if(rights & R_ADMIN) add_verb(src, admin_verbs_admin)
if(rights & R_BAN) add_verb(src, admin_verbs_ban)
if(rights & R_FUN) add_verb(src, admin_verbs_fun)
if(rights & R_SERVER) add_verb(src, admin_verbs_server)
if(rights & R_DEBUG)
add_verb(src, admin_verbs_debug)
if(CONFIG_GET(flag/debugparanoid) && !(holder.rights & R_ADMIN))
if(CONFIG_GET(flag/debugparanoid) && !(rights & R_ADMIN))
remove_verb(src, admin_verbs_paranoid_debug) //Right now it's just callproc but we can easily add others later on.
if(holder.rights & R_POSSESS) add_verb(src, admin_verbs_possess)
if(holder.rights & R_PERMISSIONS) add_verb(src, admin_verbs_permissions)
if(holder.rights & R_STEALTH) add_verb(src, /client/proc/stealth)
if(holder.rights & R_REJUVINATE) add_verb(src, admin_verbs_rejuv)
if(holder.rights & R_SOUNDS) add_verb(src, admin_verbs_sounds)
if(holder.rights & R_SPAWN) add_verb(src, admin_verbs_spawn)
if(holder.rights & R_MOD) add_verb(src, admin_verbs_mod)
if(holder.rights & R_EVENT) add_verb(src, admin_verbs_event_manager)
if(rights & R_POSSESS) add_verb(src, admin_verbs_possess)
if(rights & R_PERMISSIONS) add_verb(src, admin_verbs_permissions)
if(rights & R_STEALTH) add_verb(src, /client/proc/stealth)
if(rights & R_REJUVINATE) add_verb(src, admin_verbs_rejuv)
if(rights & R_SOUNDS) add_verb(src, admin_verbs_sounds)
if(rights & R_SPAWN) add_verb(src, admin_verbs_spawn)
if(rights & R_MOD) add_verb(src, admin_verbs_mod)
if(rights & R_EVENT) add_verb(src, admin_verbs_event_manager)
/client/proc/remove_admin_verbs()
remove_verb(src, list(
+154 -27
View File
@@ -204,7 +204,7 @@
if(!check_rights(R_ADMIN)) return
if(!warned_ckey || !istext(warned_ckey)) return
if(warned_ckey in admin_datums)
if(warned_ckey in GLOB.admin_datums)
to_chat(usr, span_warning("Error: warn(): You can't warn admins."))
return
@@ -333,36 +333,22 @@
log_admin("[key_name(usr)] used 'kill air'.")
message_admins(span_blue("[key_name_admin(usr)] used 'kill air'."), 1)
/client/proc/readmin_self()
set name = "Re-Admin self"
/client/proc/deadmin()
set name = "DeAdmin"
set category = "Admin.Misc"
set desc = "Shed your admin powers."
if(deadmin_holder)
deadmin_holder.reassociate()
log_admin("[src] re-admined themself.")
message_admins("[src] re-admined themself.", 1)
to_chat(src, span_filter_system(span_interface("You now have the keys to control the planet, or at least a small space station")))
remove_verb(src, /client/proc/readmin_self)
if(isobserver(mob))
var/mob/observer/dead/our_mob = mob
our_mob.visualnet?.addVisibility(our_mob, src)
/client/proc/deadmin_self()
set name = "De-admin self"
set category = "Admin.Misc"
if(holder)
if(tgui_alert(usr, "Confirm self-deadmin for the round? You can't re-admin yourself without someone promoting you.","Deadmin",list("Yes","No")) == "Yes")
log_admin("[src] deadmined themself.")
message_admins("[src] deadmined themself.", 1)
deadmin()
to_chat(src, span_filter_system(span_interface("You are now a normal player.")))
add_verb(src, /client/proc/readmin_self)
if(isobserver(mob))
var/mob/observer/dead/our_mob = mob
our_mob.visualnet?.removeVisibility(our_mob, src)
src.holder.deactivate()
to_chat(src, span_interface("You are now a normal player."))
log_admin("[key_name(src)] deadminned themselves.")
message_admins("[key_name_admin(src)] deadminned themselves.")
//BLACKBOX_LOG_ADMIN_VERB("Deadmin")
feedback_add_details("admin_verb","DAS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
if(isobserver(mob))
var/mob/observer/dead/our_mob = mob
our_mob.visualnet?.removeVisibility(our_mob, src)
/client/proc/toggle_log_hrefs()
set name = "Toggle href logging"
set category = "Server.Config"
@@ -582,3 +568,144 @@
return
SSmotiontracker.hide_all = !SSmotiontracker.hide_all
log_admin("[key_name(usr)] changed the motion echo visibility to [SSmotiontracker.hide_all ? "hidden" : "visible"].")
/client/proc/adminorbit()
set category = "Fun.Event Kit"
set name = "Orbit Things"
set desc = "Makes something orbit around something else."
set popup_menu = FALSE
if(!check_rights(R_FUN))
return
var/center
var/atom/movable/orbiter
var/input
if(holder.marked_datum)
input = tgui_alert(usr, "You have \n[holder.marked_datum] marked, should this be the center of the orbit, or the orbiter?", "Orbit", list("Center", "Orbiter", "Neither"))
switch(input)
if("Center")
center = holder.marked_datum
if("Orbiter")
orbiter = holder.marked_datum
var/list/possible_things = list()
for(var/T as mob in view(view)) //Let's do mobs before objects
if(ismob(T))
possible_things |= T
for(var/T as obj in view(view))
if(isobj(T))
possible_things |= T
if(!center)
center = tgui_input_list(src, "What should act as the center of the orbit?", "Center", possible_things)
possible_things -= center
if(!orbiter)
orbiter = tgui_input_list(src, "What should act as the orbiter of the orbit?", "Orbiter", possible_things)
if(!center || !orbiter)
to_chat(usr, span_warning("A center of orbit and an orbiter must be configured. You can also do this by marking a target."))
return
if(center == orbiter)
to_chat(usr, span_warning("The center of the orbit cannot also be the orbiter."))
return
if(isturf(orbiter))
to_chat(usr, span_warning("The orbiter cannot be a turf. It can only be used as a center."))
return
var/distance = tgui_input_number(usr, "How large will their orbit radius be? (In pixels. 32 is 'near around a character)", "Orbit Radius", 32)
var/speed = tgui_input_number(usr, "How fast will they orbit (negative numbers spin clockwise)", "Orbit Speed", 20)
var/segments = tgui_input_number(usr, "How many segments will they have in their orbit? (3 is a triangle, 36 is a circle, etc)", "Orbit Segments", 36)
var/clock = FALSE
if(!distance)
distance = 32
if(!speed)
speed = 20
else if (speed < 0)
clock = TRUE
speed *= -1
if(!segments)
segments = 36
if(tgui_alert(usr, "\The [orbiter] will orbit around [center]. Is this okay?", "Confirm Orbit", list("Yes", "No")) == "Yes")
orbiter.orbit(center, distance, clock, speed, segments)
/client/proc/removetickets()
set name = "Security Tickets"
set category = "Admin.Investigate"
set desc = "Allows one to remove tickets from the global list."
if(!check_rights(R_ADMIN))
return
if(security_printer_tickets.len >= 1)
var/input = tgui_input_list(usr, "Which message?", "Security Tickets", security_printer_tickets)
if(!input)
return
if(tgui_alert(usr, "Do you want to remove the following message from the global list? \"[input]\"", "Remove Ticket", list("Yes", "No")) == "Yes")
security_printer_tickets -= input
log_and_message_admins("removed a security ticket from the global list: \"[input]\"", usr)
else
tgui_alert_async(usr, "The ticket list is empty.","Empty")
/client/proc/delbook()
set name = "Delete Book"
set desc = "Permamently deletes a book from the database."
set category = "Admin.Game"
if(!src.holder)
to_chat(src, "Only administrators may use this command.")
return
var/obj/machinery/librarycomp/our_comp
for(var/obj/machinery/librarycomp/l in world)
if(istype(l, /obj/machinery/librarycomp))
our_comp = l
break
if(!our_comp)
to_chat(usr, span_warning("Unable to locate a library computer to use for book deleting."))
return
var/dat = "<HEAD><TITLE>Book Inventory Management</TITLE></HEAD><BODY>\n"
dat += "<h3>ADMINISTRATIVE MANAGEMENT</h3>"
establish_db_connection()
if(!SSdbcore.IsConnected())
dat += span_red(span_bold("ERROR") + ": Unable to contact External Archive. Please contact your system administrator for assistance.")
else
dat += {"<A href='byond://?our_comp=\ref[our_comp];[HrefToken()];orderbyid=1'>(Order book by SS<sup>13</sup>BN)</A><BR><BR>
<table>
<tr><td><A href='byond://?our_comp=\ref[our_comp];[HrefToken()];sort=author>AUTHOR</A></td><td><A href='byond://?our_comp=\ref[our_comp];[HrefToken()];sort=title>TITLE</A></td><td><A href='byond://?our_comp=\ref[our_comp];[HrefToken()];sort=category>CATEGORY</A></td><td></td></tr>"}
var/datum/db_query/query = SSdbcore.NewQuery("SELECT id, author, title, category FROM library ORDER BY [our_comp.sortby]")
query.Execute()
var/show_admin_options = check_rights(R_ADMIN, show_msg = FALSE)
while(query.NextRow())
var/id = query.item[1]
var/author = query.item[2]
var/title = query.item[3]
var/category = query.item[4]
dat += "<tr><td>[author]</td><td>[title]</td><td>[category]</td><td>"
if(show_admin_options) // This isn't the only check, since you can just href-spoof press this button. Just to tidy things up.
dat += "<A href='byond://?our_comp=\ref[our_comp];[HrefToken()];delid=[id]'>\[Del\]</A>"
dat += "</td></tr>"
dat += "</table>"
qdel(query)
usr << browse("<html>[dat]</html>", "window=library")
onclose(usr, "library")
/client/proc/toggle_spawning_with_recolour()
set name = "Toggle Simple/Robot recolour verb"
set desc = "Makes it so new robots/simple_mobs spawn with a verb to recolour themselves for this round. You must set them separately."
set category = "Server.Game"
if(!check_rights(R_ADMIN|R_EVENT|R_FUN))
return
var/which = tgui_alert(usr, "Which do you want to toggle?", "Choose Recolour Toggle", list("Robot", "Simple Mob"))
switch(which)
if("Robot")
CONFIG_SET(flag/allow_robot_recolor, !CONFIG_GET(flag/allow_robot_recolor))
to_chat(usr, "You have [CONFIG_GET(flag/allow_robot_recolor) ? "enabled" : "disabled"] newly spawned cyborgs to spawn with the recolour verb")
if("Simple Mob")
CONFIG_SET(flag/allow_simple_mob_recolor, !CONFIG_GET(flag/allow_simple_mob_recolor))
to_chat(usr, "You have [CONFIG_GET(flag/allow_simple_mob_recolor) ? "enabled" : "disabled"] newly spawned simple mobs to spawn with the recolour verb")
-140
View File
@@ -1,140 +0,0 @@
/client/proc/adminorbit()
set category = "Fun.Event Kit"
set name = "Orbit Things"
set desc = "Makes something orbit around something else."
set popup_menu = FALSE
if(!check_rights(R_FUN))
return
var/center
var/atom/movable/orbiter
var/input
if(holder.marked_datum)
input = tgui_alert(usr, "You have \n[holder.marked_datum] marked, should this be the center of the orbit, or the orbiter?", "Orbit", list("Center", "Orbiter", "Neither"))
switch(input)
if("Center")
center = holder.marked_datum
if("Orbiter")
orbiter = holder.marked_datum
var/list/possible_things = list()
for(var/T as mob in view(view)) //Let's do mobs before objects
if(ismob(T))
possible_things |= T
for(var/T as obj in view(view))
if(isobj(T))
possible_things |= T
if(!center)
center = tgui_input_list(src, "What should act as the center of the orbit?", "Center", possible_things)
possible_things -= center
if(!orbiter)
orbiter = tgui_input_list(src, "What should act as the orbiter of the orbit?", "Orbiter", possible_things)
if(!center || !orbiter)
to_chat(usr, span_warning("A center of orbit and an orbiter must be configured. You can also do this by marking a target."))
return
if(center == orbiter)
to_chat(usr, span_warning("The center of the orbit cannot also be the orbiter."))
return
if(isturf(orbiter))
to_chat(usr, span_warning("The orbiter cannot be a turf. It can only be used as a center."))
return
var/distance = tgui_input_number(usr, "How large will their orbit radius be? (In pixels. 32 is 'near around a character)", "Orbit Radius", 32)
var/speed = tgui_input_number(usr, "How fast will they orbit (negative numbers spin clockwise)", "Orbit Speed", 20)
var/segments = tgui_input_number(usr, "How many segments will they have in their orbit? (3 is a triangle, 36 is a circle, etc)", "Orbit Segments", 36)
var/clock = FALSE
if(!distance)
distance = 32
if(!speed)
speed = 20
else if (speed < 0)
clock = TRUE
speed *= -1
if(!segments)
segments = 36
if(tgui_alert(usr, "\The [orbiter] will orbit around [center]. Is this okay?", "Confirm Orbit", list("Yes", "No")) == "Yes")
orbiter.orbit(center, distance, clock, speed, segments)
/client/proc/removetickets()
set name = "Security Tickets"
set category = "Admin.Investigate"
set desc = "Allows one to remove tickets from the global list."
if(!check_rights(R_ADMIN))
return
if(security_printer_tickets.len >= 1)
var/input = tgui_input_list(usr, "Which message?", "Security Tickets", security_printer_tickets)
if(!input)
return
if(tgui_alert(usr, "Do you want to remove the following message from the global list? \"[input]\"", "Remove Ticket", list("Yes", "No")) == "Yes")
security_printer_tickets -= input
log_and_message_admins("removed a security ticket from the global list: \"[input]\"", usr)
else
tgui_alert_async(usr, "The ticket list is empty.","Empty")
/client/proc/delbook()
set name = "Delete Book"
set desc = "Permamently deletes a book from the database."
set category = "Admin.Game"
if(!src.holder)
to_chat(src, "Only administrators may use this command.")
return
var/obj/machinery/librarycomp/our_comp
for(var/obj/machinery/librarycomp/l in world)
if(istype(l, /obj/machinery/librarycomp))
our_comp = l
break
if(!our_comp)
to_chat(usr, span_warning("Unable to locate a library computer to use for book deleting."))
return
var/dat = "<HEAD><TITLE>Book Inventory Management</TITLE></HEAD><BODY>\n"
dat += "<h3>ADMINISTRATIVE MANAGEMENT</h3>"
establish_db_connection()
if(!SSdbcore.IsConnected())
dat += span_red(span_bold("ERROR") + ": Unable to contact External Archive. Please contact your system administrator for assistance.")
else
dat += {"<A href='byond://?our_comp=\ref[our_comp];[HrefToken()];orderbyid=1'>(Order book by SS<sup>13</sup>BN)</A><BR><BR>
<table>
<tr><td><A href='byond://?our_comp=\ref[our_comp];[HrefToken()];sort=author>AUTHOR</A></td><td><A href='byond://?our_comp=\ref[our_comp];[HrefToken()];sort=title>TITLE</A></td><td><A href='byond://?our_comp=\ref[our_comp];[HrefToken()];sort=category>CATEGORY</A></td><td></td></tr>"}
var/datum/db_query/query = SSdbcore.NewQuery("SELECT id, author, title, category FROM library ORDER BY [our_comp.sortby]")
query.Execute()
var/show_admin_options = check_rights(R_ADMIN, show_msg = FALSE)
while(query.NextRow())
var/id = query.item[1]
var/author = query.item[2]
var/title = query.item[3]
var/category = query.item[4]
dat += "<tr><td>[author]</td><td>[title]</td><td>[category]</td><td>"
if(show_admin_options) // This isn't the only check, since you can just href-spoof press this button. Just to tidy things up.
dat += "<A href='byond://?our_comp=\ref[our_comp];[HrefToken()];delid=[id]'>\[Del\]</A>"
dat += "</td></tr>"
dat += "</table>"
qdel(query)
usr << browse("<html>[dat]</html>", "window=library")
onclose(usr, "library")
/client/proc/toggle_spawning_with_recolour()
set name = "Toggle Simple/Robot recolour verb"
set desc = "Makes it so new robots/simple_mobs spawn with a verb to recolour themselves for this round. You must set them separately."
set category = "Server.Game"
if(!check_rights(R_ADMIN|R_EVENT|R_FUN))
return
var/which = tgui_alert(usr, "Which do you want to toggle?", "Choose Recolour Toggle", list("Robot", "Simple Mob"))
switch(which)
if("Robot")
CONFIG_SET(flag/allow_robot_recolor, !CONFIG_GET(flag/allow_robot_recolor))
to_chat(usr, "You have [CONFIG_GET(flag/allow_robot_recolor) ? "enabled" : "disabled"] newly spawned cyborgs to spawn with the recolour verb")
if("Simple Mob")
CONFIG_SET(flag/allow_simple_mob_recolor, !CONFIG_GET(flag/allow_simple_mob_recolor))
to_chat(usr, "You have [CONFIG_GET(flag/allow_simple_mob_recolor) ? "enabled" : "disabled"] newly spawned simple mobs to spawn with the recolour verb")
-14
View File
@@ -1,14 +0,0 @@
/datum/admins/proc/set_uplink(mob/living/carbon/human/H as mob)
set category = "Debug.Events"
set name = "Set Uplink"
set desc = "Allows admins to set up an uplink on a character. This will be required for a character to use telecrystals."
set popup_menu = FALSE
if(check_rights(R_ADMIN|R_DEBUG))
traitors.spawn_uplink(H)
H.mind.tcrystals = DEFAULT_TELECRYSTAL_AMOUNT
H.mind.accept_tcrystals = 1
var/msg = "[key_name(usr)] has given [H.ckey] an uplink."
message_admins(msg)
else
to_chat(usr, "You do not have access to this command.")
+205 -78
View File
@@ -1,13 +1,18 @@
GLOBAL_LIST_EMPTY(admin_datums)
GLOBAL_PROTECT(admin_datums)
GLOBAL_LIST_EMPTY(protected_admins)
GLOBAL_PROTECT(protected_admins)
GLOBAL_VAR_INIT(href_token, GenerateToken())
GLOBAL_PROTECT(href_token)
var/list/admin_datums = list()
/datum/admins
var/rank = "Temporary Admin"
var/client/owner = null
var/rights = 0
var/fakekey = null
var/list/datum/admin_rank/ranks
var/target
var/name = "nobody's admin datum (no rank)" //Makes for better runtimes
var/client/owner = null
var/fakekey = null
var/datum/marked_datum
@@ -18,49 +23,194 @@ var/list/admin_datums = list()
var/href_token
/// Link from the database pointing to the admin's feedback forum
var/cached_feedback_link
/datum/admins/New(initial_rank = "Temporary Admin", initial_rights = 0, ckey)
if(!ckey)
error("Admin datum created without a ckey argument. Datum has been deleted")
qdel(src)
var/deadmined
var/given_profiling = FALSE
/datum/admins/New(list/datum/admin_rank/ranks, ckey, force_active = FALSE, protected)
if(IsAdminAdvancedProcCall())
alert_to_permissions_elevation_attempt(usr)
if (!target) //only del if this is a true creation (and not just a New() proc call), other wise trialmins/coders could abuse this to deadmin other admins
QDEL_IN(src, 0)
CRASH("Admin proc call creation of admin datum")
return
if(!ckey)
QDEL_IN(src, 0)
CRASH("Admin datum created without a ckey")
if(!istype(ranks))
QDEL_IN(src, 0)
CRASH("Admin datum created with invalid ranks: [ranks] ([json_encode(ranks)])")
target = ckey
name = "[ckey]'s admin datum ([join_admin_ranks(ranks)])"
src.ranks = ranks
admincaster_signature = "[using_map.company_name] Officer #[rand(0,9)][rand(0,9)][rand(0,9)]"
href_token = GenerateToken()
rank = initial_rank
rights = initial_rights
admin_datums[ckey] = src
if(rights & R_DEBUG) //grant profile access
world.SetConfig("APP/admin", ckey, "role=admin")
if(protected)
GLOB.protected_admins[target] = src
activate()
/datum/admins/proc/associate(client/C)
if(istype(C))
owner = C
owner.holder = src
owner.add_admin_verbs() //TODO
owner.init_verbs() //re-initialize the verb list
GLOB.admins |= C
/datum/admins/Destroy()
if(IsAdminAdvancedProcCall())
alert_to_permissions_elevation_attempt(usr)
return QDEL_HINT_LETMELIVE
. = ..()
/datum/admins/proc/activate()
if(IsAdminAdvancedProcCall())
alert_to_permissions_elevation_attempt(usr)
return
GLOB.deadmins -= target
GLOB.admin_datums[target] = src
deadmined = FALSE
//plane_debug = new(src)
if (GLOB.directory[target])
associate(GLOB.directory[target]) //find the client for a ckey if they are connected and associate them with us
/datum/admins/proc/deactivate()
if(IsAdminAdvancedProcCall())
alert_to_permissions_elevation_attempt(usr)
return
GLOB.deadmins[target] = src
GLOB.admin_datums -= target
//QDEL_NULL(plane_debug)
deadmined = TRUE
var/client/client = owner || GLOB.directory[target]
if (!isnull(client))
disassociate()
add_verb(client, /client/proc/readmin)
//client.disable_combo_hud()
//client.update_special_keybinds()
/datum/admins/proc/associate(client/client)
if(IsAdminAdvancedProcCall())
alert_to_permissions_elevation_attempt(usr)
return
if(!istype(client))
return
if(client?.ckey != target)
var/msg = " has attempted to associate with [target]'s admin datum"
message_admins("[key_name_admin(client)][msg]")
log_admin("[key_name(client)][msg]")
return
if (deadmined)
activate()
owner = client
owner.holder = src
owner.add_admin_verbs()
remove_verb(owner, /client/proc/readmin)
owner.init_verbs() //re-initialize the verb list
//owner.update_special_keybinds()
GLOB.admins |= client
try_give_profiling()
/datum/admins/proc/disassociate()
if(IsAdminAdvancedProcCall())
alert_to_permissions_elevation_attempt(usr)
return
if(owner)
GLOB.admins -= owner
owner.remove_admin_verbs()
owner.init_verbs() //re-initialize the verb list
owner.deadmin_holder = owner.holder
// owner.init_verbs() //re-initialize the verb list
owner.holder = null
owner = null
/datum/admins/proc/reassociate()
if(owner)
GLOB.admins += owner
owner.holder = src
owner.deadmin_holder = null
owner.add_admin_verbs()
/// Returns the feedback forum thread for the admin holder's owner, as according to DB.
/datum/admins/proc/feedback_link()
// This intentionally does not follow the 10-second maximum TTL rule,
// as this can be reloaded through the Reload-Admins verb.
if (cached_feedback_link == NO_FEEDBACK_LINK)
return null
if (!isnull(cached_feedback_link))
return cached_feedback_link
if (!SSdbcore.IsConnected())
return FALSE
var/datum/db_query/feedback_query = SSdbcore.NewQuery("SELECT feedback FROM [format_table_name("admin")] WHERE ckey = '[owner.ckey]'")
if(!feedback_query.Execute())
log_sql("Error retrieving feedback link for [src]")
qdel(feedback_query)
return FALSE
if(!feedback_query.NextRow())
qdel(feedback_query)
return FALSE // no feedback link exists
cached_feedback_link = feedback_query.item[1] || NO_FEEDBACK_LINK
qdel(feedback_query)
if (cached_feedback_link == NO_FEEDBACK_LINK) // Because we don't want to send fake clickable links.
return null
return cached_feedback_link
/datum/admins/proc/check_for_rights(rights_required)
if(rights_required && !(rights_required & rank_flags()))
return FALSE
return TRUE
/datum/admins/proc/check_if_greater_rights_than_holder(datum/admins/other)
if(!other)
return TRUE //they have no rights
if(rank_flags() == R_EVERYTHING)
return TRUE //we have all the rights
if(src == other)
return TRUE //you always have more rights than yourself
if(rank_flags() != other.rank_flags())
if( (rank_flags() & other.rank_flags()) == other.rank_flags() )
return TRUE //we have all the rights they have and more
return FALSE
/// Get the rank name of the admin
/datum/admins/proc/rank_names()
return join_admin_ranks(ranks)
/// Get the rank flags of the admin
/datum/admins/proc/rank_flags()
var/combined_flags = NONE
for (var/datum/admin_rank/rank as anything in ranks)
combined_flags |= rank.rights
return combined_flags
/// Get the permissions this admin is allowed to edit on other ranks
/datum/admins/proc/can_edit_rights_flags()
var/combined_flags = NONE
for (var/datum/admin_rank/rank as anything in ranks)
combined_flags |= rank.can_edit_rights
return combined_flags
/datum/admins/proc/try_give_profiling()
if (CONFIG_GET(flag/forbid_admin_profiling))
return
if (given_profiling)
return
if (!(rank_flags() & R_DEBUG))
return
given_profiling = TRUE
world.SetConfig("APP/admin", owner.ckey, "role=admin")
/datum/admins/vv_edit_var(var_name, var_value)
if(var_name == NAMEOF(src, rights) || var_name == NAMEOF(src, owner) || var_name == NAMEOF(src, rank))
return FALSE
return ..()
//TODO: Proccall guard, when all try/catch are removed and WrapAdminProccall is ported.
return FALSE //nice try trialmin
/*
checks if usr is an admin with at least ONE of the flags in rights_required. (Note, they don't need all the flags)
@@ -69,45 +219,36 @@ if it doesn't return 1 and show_msg=1 it will prints a message explaining why th
generally it would be used like so:
/proc/admin_proc()
if(!check_rights(R_ADMIN)) return
to_world("you have enough rights!")
if(!check_rights(R_ADMIN))
return
to_chat(world, "you have enough rights!", confidential = TRUE)
NOTE: It checks usr by default. Supply the "user" argument if you wish to check for a specific mob.
NOTE: it checks usr! not src! So if you're checking somebody's rank in a proc which they did not call
you will have to do something like if(client.rights & R_ADMIN) yourself.
*/
/proc/check_rights(rights_required, show_msg=1, var/client/C = usr)
if(ismob(C))
var/mob/M = C
C = M.client
if(!C)
return FALSE
if(!(istype(C, /client))) // If we still didn't find a client, something is wrong.
return FALSE
if(!C.holder)
if(show_msg)
to_chat(C, span_filter_adminlog(span_warning("Error: You are not an admin.")))
return FALSE
if(rights_required)
if(rights_required & C.holder.rights)
/proc/check_rights(rights_required, show_msg=1)
if(usr?.client)
if (check_rights_for(usr.client, rights_required))
return TRUE
else
if(show_msg)
to_chat(C, span_filter_adminlog(span_warning("Error: You do not have sufficient rights to do that. You require one of the following flags:[rights2text(rights_required," ")].")))
return FALSE
else
return TRUE
to_chat(usr, "<font color='red'>Error: You do not have sufficient rights to do that. You require one of the following flags:[rights2text(rights_required," ")].</font>", confidential = TRUE)
return FALSE
//probably a bit iffy - will hopefully figure out a better solution
/proc/check_if_greater_rights_than(client/other)
if(usr && usr.client)
if(usr?.client)
if(usr.client.holder)
if(!other || !other.holder)
return 1
if(usr.client.holder.rights != other.holder.rights)
if( (usr.client.holder.rights & other.holder.rights) == other.holder.rights )
return 1 //we have all the rights they have and more
to_chat(usr, span_filter_adminlog(span_warning("Error: Cannot proceed. They have more or equal rights to us.")))
return 0
return TRUE
return usr.client.holder.check_if_greater_rights_than_holder(other.holder)
return FALSE
//This proc checks whether subject has at least ONE of the rights specified in rights_required.
/proc/check_rights_for(client/subject, rights_required)
if(subject?.holder)
return subject.holder.check_for_rights(rights_required)
return FALSE
/client/proc/mark_datum(datum/D)
if(!holder)
@@ -122,20 +263,6 @@ NOTE: It checks usr by default. Supply the "user" argument if you wish to check
set name = "Mark Object"
mark_datum(D)
/client/proc/deadmin()
if(holder)
holder.disassociate()
//qdel(holder)
return 1
//This proc checks whether subject has at least ONE of the rights specified in rights_required.
/proc/check_rights_for(client/subject, rights_required)
if(subject && subject.holder)
if(rights_required && !(rights_required & subject.holder.rights))
return 0
return 1
return 0
/proc/GenerateToken()
. = ""
for(var/I in 1 to 32)
+561
View File
@@ -0,0 +1,561 @@
/client/proc/edit_admin_permissions()
set category = "Admin.Secrets"
set name = "Permissions Panel"
set desc = "Edit admin permissions"
if(!check_rights(R_PERMISSIONS))
return
usr.client.holder.edit_admin_permissions()
/datum/admins/proc/edit_admin_permissions(action, target, operation, page)
if(!check_rights(R_PERMISSIONS))
return
var/datum/asset/asset_cache_datum = get_asset_datum(/datum/asset/group/permissions)
asset_cache_datum.send(usr)
var/list/output = list("<link rel='stylesheet' type='text/css' href='[SSassets.transport.get_asset_url("panels.css")]'><a href='byond://?_src_=holder;[HrefToken()];editrightsbrowser=1'>\[Permissions\]</a>")
if(action)
output += " | <a href='byond://?_src_=holder;[HrefToken()];editrightsbrowserlog=1;editrightspage=0'>\[Log\]</a> | <a href='byond://?_src_=holder;[HrefToken()];editrightsbrowsermanage=1'>\[Management\]</a><hr style='background:#000000; border:0; height:3px'>"
else
output += "<br><a href='byond://?_src_=holder;[HrefToken()];editrightsbrowserlog=1;editrightspage=0'>\[Log\]</a><br><a href='byond://?_src_=holder;[HrefToken()];editrightsbrowsermanage=1'>\[Management\]</a>"
if(action == 1)
var/logcount = 0
var/logssperpage = 20
var/pagecount = 0
page = text2num(page)
var/datum/db_query/query_count_admin_logs = SSdbcore.NewQuery(
"SELECT COUNT(id) FROM [format_table_name("admin_log")] WHERE (:target IS NULL OR adminckey = :target) AND (:operation IS NULL OR operation = :operation)",
list("target" = target, "operation" = operation)
)
if(!query_count_admin_logs.warn_execute())
qdel(query_count_admin_logs)
return
if(query_count_admin_logs.NextRow())
logcount = text2num(query_count_admin_logs.item[1])
qdel(query_count_admin_logs)
if(logcount > logssperpage)
output += "<br><b>Page: </b>"
while(logcount > 0)
output += "|<a href='byond://?_src_=holder;[HrefToken()];editrightsbrowserlog=1;editrightstarget=[target];editrightsoperation=[operation];editrightspage=[pagecount]'>[pagecount == page ? "<b>\[[pagecount]\]</b>" : "\[[pagecount]\]"]</a>"
logcount -= logssperpage
pagecount++
output += "|"
var/datum/db_query/query_search_admin_logs = SSdbcore.NewQuery({"
SELECT
datetime,
round_id,
IFNULL((SELECT ckey FROM [format_table_name("erro_player")] WHERE ckey = adminckey), adminckey),
operation,
IF(ckey IS NULL, target, ckey),
log
FROM [format_table_name("admin_log")]
LEFT JOIN [format_table_name("erro_player")] ON target = ckey
WHERE (:target IS NULL OR ckey = :target) AND (:operation IS NULL OR operation = :operation)
ORDER BY datetime DESC
LIMIT :skip, :take
"}, list("target" = target, "operation" = operation, "skip" = logssperpage * page, "take" = logssperpage))
if(!query_search_admin_logs.warn_execute())
qdel(query_search_admin_logs)
return
while(query_search_admin_logs.NextRow())
var/datetime = query_search_admin_logs.item[1]
var/round_id = query_search_admin_logs.item[2]
var/admin_key = query_search_admin_logs.item[3]
operation = query_search_admin_logs.item[4]
target = query_search_admin_logs.item[5]
var/log = query_search_admin_logs.item[6]
output += "<p style='margin:0px'><b>[datetime] | Round ID [round_id] | Admin [admin_key] | Operation [operation] on [target]</b><br>[log]</p><hr style='background:#000000; border:0; height:3px'>"
qdel(query_search_admin_logs)
if(action == 2)
output += "<h3>Admin ckeys with invalid ranks</h3>"
var/datum/db_query/query_check_admin_errors = SSdbcore.NewQuery("SELECT IFNULL((SELECT ckey FROM [format_table_name("erro_player")] WHERE [format_table_name("erro_player")].ckey = [format_table_name("admin")].ckey), ckey), [format_table_name("admin")].`rank` FROM [format_table_name("admin")] LEFT JOIN [format_table_name("admin_ranks")] ON [format_table_name("admin_ranks")].`rank` = [format_table_name("admin")].`rank` WHERE [format_table_name("admin_ranks")].`rank` IS NULL")
if(!query_check_admin_errors.warn_execute())
qdel(query_check_admin_errors)
return
while(query_check_admin_errors.NextRow())
var/admin_key = query_check_admin_errors.item[1]
var/admin_rank = query_check_admin_errors.item[2]
output += "[admin_key] has non-existent rank [admin_rank] | <a href='byond://?_src_=holder;[HrefToken()];editrightsbrowsermanage=1;editrightschange=[admin_key]'>\[Change Rank\]</a> | <a href='byond://?_src_=holder;[HrefToken()];editrightsbrowsermanage=1;editrightsremove=[admin_key]'>\[Remove\]</a>"
output += "<hr style='background:#000000; border:0; height:1px'>"
qdel(query_check_admin_errors)
output += "<h3>Unused ranks</h3>"
var/datum/db_query/query_check_unused_rank = SSdbcore.NewQuery("SELECT [format_table_name("admin_ranks")].`rank`, flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")] LEFT JOIN [format_table_name("admin")] ON [format_table_name("admin")].`rank` = [format_table_name("admin_ranks")].`rank` WHERE [format_table_name("admin")].`rank` IS NULL")
if(!query_check_unused_rank.warn_execute())
qdel(query_check_unused_rank)
return
while(query_check_unused_rank.NextRow())
var/admin_rank = query_check_unused_rank.item[1]
output += {"Rank [admin_rank] is not held by any admin | <a href='byond://?_src_=holder;[HrefToken()];editrightsbrowsermanage=1;editrightsremoverank=[admin_rank]'>\[Remove\]</a>
<br>Permissions: [rights2text(text2num(query_check_unused_rank.item[2])," ")]
<br>Denied: [rights2text(text2num(query_check_unused_rank.item[3])," ", "-")]
<br>Allowed to edit: [rights2text(text2num(query_check_unused_rank.item[4])," ", "*")]
<hr style='background:#000000; border:0; height:1px'>"}
qdel(query_check_unused_rank)
else if(!action)
output += {"
<head>
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
<title>Permissions Panel</title>
<script type='text/javascript' src='[SSassets.transport.get_asset_url("search.js")]'></script>
</head>
<body onload='selectTextField();updateSearch();'>
<div id='main'><table id='searchable' cellspacing='0'>
<tr class='title'>
<th style='width:150px;'>CKEY <a class='small' href='byond://?src=[REF(src)];[HrefToken()];editrights=add'>\[+\]</a></th>
<th style='width:125px;'>RANK</th>
<th>PERMISSIONS</th>
</tr>
"}
for(var/adm_ckey in GLOB.admin_datums+GLOB.deadmins)
var/datum/admins/D = GLOB.admin_datums[adm_ckey]
if(!D)
D = GLOB.deadmins[adm_ckey]
if (!D)
continue
var/deadminlink = ""
if(D.owner)
adm_ckey = D.owner.key
if (D.deadmined)
deadminlink = " <a class='small' href='byond://?src=[REF(src)];[HrefToken()];editrights=activate;key=[adm_ckey]'>\[RA\]</a>"
else
deadminlink = " <a class='small' href='byond://?src=[REF(src)];[HrefToken()];editrights=deactivate;key=[adm_ckey]'>\[DA\]</a>"
var/verify_link = ""
//if (D.blocked_by_2fa)
// verify_link += " | <a class='small' href='byond://?src=[REF(src)];[HrefToken()];editrights=verify;key=[adm_ckey]'>\[2FA VERIFY\]</a>"
output += "<tr>"
output += "<td style='text-align:center;'>[adm_ckey]<br>[deadminlink]<a class='small' href='byond://?src=[REF(src)];[HrefToken()];editrights=remove;key=[adm_ckey]'>\[-\]</a><a class='small' href='byond://?src=[REF(src)];[HrefToken()];editrights=sync;key=[adm_ckey]'>\[SYNC TGDB\]</a>[verify_link]</td>"
output += "<td><a href='byond://?src=[REF(src)];[HrefToken()];editrights=rank;key=[adm_ckey]'>[D.rank_names()]</a></td>"
output += "<td><a class='small' href='byond://?src=[REF(src)];[HrefToken()];editrights=permissions;key=[adm_ckey]'>[rights2text(D.rank_flags(), " ")]</a></td>"
output += "</tr>"
output += "</table></div><div id='top'><b>Search:</b> <input type='text' id='filter' value='' style='width:70%;' onkeyup='updateSearch();'></div></body>"
if(QDELETED(usr))
return
usr << browse("<!DOCTYPE html><html>[jointext(output, "")]</html>","window=editrights;size=1000x650")
/datum/admins/proc/edit_rights_topic(list/href_list)
if(!check_rights(R_PERMISSIONS))
message_admins("[key_name_admin(usr)] attempted to edit admin permissions without sufficient rights.")
log_admin("[key_name(usr)] attempted to edit admin permissions without sufficient rights.")
return
if(IsAdminAdvancedProcCall())
to_chat(usr, "<span class='admin prefix'>Admin Edit blocked: Advanced ProcCall detected.</span>", confidential = TRUE)
return
var/datum/asset/permissions_assets = get_asset_datum(/datum/asset/simple/namespaced/common)
permissions_assets.send(usr.client)
var/admin_key = href_list["key"]
var/admin_ckey = ckey(admin_key)
var/task = href_list["editrights"]
var/datum/admins/target_admin_datum = GLOB.admin_datums[admin_ckey]
if(!target_admin_datum)
target_admin_datum = GLOB.deadmins[admin_ckey]
if (!target_admin_datum && task != "add")
return
var/use_db
var/skip
var/legacy_only
if(task == "activate" || task == "deactivate" || task == "sync" || task == "verify")
skip = TRUE
if(!CONFIG_GET(flag/admin_legacy_system) && CONFIG_GET(flag/protect_legacy_admins) && task == "rank")
if(admin_ckey in GLOB.protected_admins)
to_chat(usr, "<span class='admin prefix'>Editing the rank of this admin is blocked by server configuration.</span>", confidential = TRUE)
return
if(!CONFIG_GET(flag/admin_legacy_system) && CONFIG_GET(flag/protect_legacy_ranks) && task == "permissions")
if((target_admin_datum.ranks & GLOB.protected_ranks).len > 0)
to_chat(usr, "<span class='admin prefix'>Editing the flags of this rank is blocked by server configuration.</span>", confidential = TRUE)
return
if(CONFIG_GET(flag/load_legacy_ranks_only) && (task == "add" || task == "rank" || task == "permissions"))
to_chat(usr, "<span class='admin prefix'>Database rank loading is disabled, only temporary changes can be made to a rank's permissions and permanently creating a new rank is blocked.</span>", confidential = TRUE)
legacy_only = TRUE
//if(check_rights(R_DBRANKS, FALSE))
if(!skip)
if(!SSdbcore.Connect())
to_chat(usr, span_danger("Unable to connect to database, changes are temporary only."), confidential = TRUE)
use_db = FALSE
else
use_db = tgui_alert(usr,"Permanent changes are saved to the database for future rounds, temporary changes will affect only the current round", "Permanent or Temporary?", list("Permanent", "Temporary", "Cancel"))
if(use_db == "Cancel")
return
if(use_db == "Permanent")
use_db = TRUE
else
use_db = FALSE
if(QDELETED(usr))
return
if(target_admin_datum && (task != "sync" && task != "verify") && !check_if_greater_rights_than_holder(target_admin_datum))
message_admins("[key_name_admin(usr)] attempted to change the rank of [admin_key] without sufficient rights.")
log_admin("[key_name(usr)] attempted to change the rank of [admin_key] without sufficient rights.")
return
switch(task)
if("add")
admin_ckey = add_admin(admin_ckey, admin_key, use_db)
if(!admin_ckey)
return
if(!admin_key) // Prevents failures in logging admin rank changes.
admin_key = admin_ckey
change_admin_rank(admin_ckey, admin_key, use_db, null, legacy_only)
if("remove")
remove_admin(admin_ckey, admin_key, use_db, target_admin_datum)
if("rank")
change_admin_rank(admin_ckey, admin_key, use_db, target_admin_datum, legacy_only)
if("permissions")
change_admin_flags(admin_ckey, admin_key, target_admin_datum)
if("activate")
force_readmin(admin_key, target_admin_datum)
if("deactivate")
force_deadmin(admin_key, target_admin_datum)
if("sync")
sync_lastadminrank(admin_ckey, admin_key, target_admin_datum)
/*
if("verify")
var/msg = "has authenticated [admin_ckey]"
message_admins("[key_name_admin(usr)] [msg]")
log_admin("[key_name(usr)] [msg]")
target_admin_datum.bypass_2fa = TRUE
target_admin_datum.associate(GLOB.directory[admin_ckey])
*/
edit_admin_permissions()
/datum/admins/proc/add_admin(admin_ckey, admin_key, use_db)
if(admin_ckey)
. = admin_ckey
else
admin_key = input("New admin's key","Admin key") as text|null
. = ckey(admin_key)
if(!.)
return FALSE
if(!admin_ckey && (. in (GLOB.admin_datums+GLOB.deadmins)))
to_chat(usr, span_danger("[admin_key] is already an admin."), confidential = TRUE)
return FALSE
if(use_db)
//if an admin exists without a datum they won't be caught by the above
var/datum/db_query/query_admin_in_db = SSdbcore.NewQuery(
"SELECT 1 FROM [format_table_name("admin")] WHERE ckey = :ckey",
list("ckey" = .)
)
if(!query_admin_in_db.warn_execute())
qdel(query_admin_in_db)
return FALSE
if(query_admin_in_db.NextRow())
qdel(query_admin_in_db)
to_chat(usr, span_danger("[admin_key] already listed in admin database. Check the Management tab if they don't appear in the list of admins."), confidential = TRUE)
return FALSE
qdel(query_admin_in_db)
var/datum/db_query/query_add_admin = SSdbcore.NewQuery(
"INSERT INTO [format_table_name("admin")] (ckey, `rank`) VALUES (:ckey, 'NEW ADMIN')",
list("ckey" = .)
)
if(!query_add_admin.warn_execute())
qdel(query_add_admin)
return FALSE
qdel(query_add_admin)
var/datum/db_query/query_add_admin_log = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log)
VALUES (NOW(), :round_id, :adminckey, INET_ATON(:adminip), 'add admin', :target, CONCAT('New admin added: ', :target))
"}, list("round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "target" = .))
if(!query_add_admin_log.warn_execute())
qdel(query_add_admin_log)
return FALSE
qdel(query_add_admin_log)
/datum/admins/proc/remove_admin(admin_ckey, admin_key, use_db, datum/admins/D)
if(tgui_alert(usr,"Are you sure you want to remove [admin_ckey]?","Confirm Removal",list("Do it","Cancel")) == "Do it")
GLOB.admin_datums -= admin_ckey
GLOB.deadmins -= admin_ckey
if(D)
D.disassociate()
var/m1 = "[key_name_admin(usr)] removed [admin_key] from the admins list [use_db ? "permanently" : "temporarily"]"
var/m2 = "[key_name(usr)] removed [admin_key] from the admins list [use_db ? "permanently" : "temporarily"]"
if(use_db)
var/datum/db_query/query_add_rank = SSdbcore.NewQuery(
"DELETE FROM [format_table_name("admin")] WHERE ckey = :ckey",
list("ckey" = admin_ckey)
)
if(!query_add_rank.warn_execute())
qdel(query_add_rank)
return
qdel(query_add_rank)
var/datum/db_query/query_add_rank_log = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log)
VALUES (NOW(), :round_id, :adminckey, INET_ATON(:adminip), 'remove admin', :admin_ckey, CONCAT('Admin removed: ', :admin_ckey))
"}, list("round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "admin_ckey" = admin_ckey))
if(!query_add_rank_log.warn_execute())
qdel(query_add_rank_log)
return
qdel(query_add_rank_log)
sync_lastadminrank(admin_ckey, admin_key)
message_admins(m1)
log_admin(m2)
/datum/admins/proc/force_readmin(admin_key, datum/admins/D)
if(!D || !D.deadmined)
return
D.activate()
message_admins("[key_name_admin(usr)] forcefully readmined [admin_key]")
log_admin("[key_name(usr)] forcefully readmined [admin_key]")
/datum/admins/proc/force_deadmin(admin_key, datum/admins/D)
if(!D || D.deadmined)
return
message_admins("[key_name_admin(usr)] forcefully deadmined [admin_key]")
log_admin("[key_name(usr)] forcefully deadmined [admin_key]")
D.deactivate() //after logs so the deadmined admin can see the message.
#define RANK_DONE ":) I'm Done"
/datum/admins/proc/change_admin_rank(admin_ckey, admin_key, use_db, datum/admins/D, legacy_only)
if(!check_rights(R_PERMISSIONS))
return
var/list/rank_names = list()
if(!use_db || (use_db && !legacy_only))
rank_names += "*New Rank*"
for(var/datum/admin_rank/admin_rank as anything in GLOB.admin_ranks)
if((admin_rank.rights & usr.client.holder.can_edit_rights_flags()) == admin_rank.rights)
rank_names[admin_rank.name] = admin_rank
var/list/new_rank_names = list()
var/list/custom_ranks = list()
while (TRUE)
var/list/display_rank_names = list(RANK_DONE)
if (new_rank_names.len > 0)
display_rank_names += "** SELECTED **"
for (var/rank_name in new_rank_names)
display_rank_names += rank_name
display_rank_names += "---------"
for (var/rank_name in rank_names)
if (!(rank_name in display_rank_names))
display_rank_names += rank_name
var/next_rank = input("Please select a rank, or select [RANK_DONE] if you are finished.") as null|anything in display_rank_names
if (isnull(next_rank))
return
if (next_rank == RANK_DONE)
break
// They clicked "** SELECTED **" or something silly.
if (!(next_rank in rank_names))
continue
if (next_rank in new_rank_names)
new_rank_names -= next_rank
continue
if (next_rank == "*New Rank*")
var/new_rank_name = input("Please input a new rank", "New custom rank") as text|null
if (!new_rank_name)
return
var/datum/admin_rank/custom_rank = rank_names[new_rank_name]
if (isnull(custom_rank))
if (D)
custom_rank = new(new_rank_name, D.rank_flags())
else
custom_rank = new(new_rank_name)
GLOB.admin_ranks += custom_rank
custom_ranks += custom_rank
new_rank_names += new_rank_name
new_rank_names += next_rank
var/list/new_ranks = list()
for (var/datum/admin_rank/admin_rank as anything in GLOB.admin_ranks)
if (admin_rank.name in new_rank_names)
new_ranks += admin_rank
new_rank_names -= admin_rank.name
if (new_rank_names.len == 0)
break
var/joined_rank = join_admin_ranks(new_ranks)
var/m1 = "[key_name_admin(usr)] edited the admin rank of [admin_key] to [joined_rank] [use_db ? "permanently" : "temporarily"]"
var/m2 = "[key_name(usr)] edited the admin rank of [admin_key] to [joined_rank] [use_db ? "permanently" : "temporarily"]"
if(use_db)
//if a player was tempminned before having a permanent change made to their rank they won't yet be in the db
var/old_rank
var/datum/db_query/query_admin_in_db = SSdbcore.NewQuery(
"SELECT `rank` FROM [format_table_name("admin")] WHERE ckey = :admin_ckey",
list("admin_ckey" = admin_ckey)
)
if(!query_admin_in_db.warn_execute())
qdel(query_admin_in_db)
return
if(!query_admin_in_db.NextRow())
add_admin(admin_ckey, admin_key, TRUE)
old_rank = "NEW ADMIN"
else
old_rank = query_admin_in_db.item[1]
qdel(query_admin_in_db)
for (var/datum/admin_rank/custom_rank in custom_ranks)
//similarly if a temp rank is created it won't be in the db if someone is permanently changed to it
var/datum/db_query/query_rank_in_db = SSdbcore.NewQuery(
"SELECT 1 FROM [format_table_name("admin_ranks")] WHERE `rank` = :new_rank",
list("new_rank" = custom_rank.name)
)
if(!query_rank_in_db.warn_execute())
qdel(query_rank_in_db)
return
if(!query_rank_in_db.NextRow())
QDEL_NULL(query_rank_in_db)
var/datum/db_query/query_add_rank = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("admin_ranks")] (`rank`, flags, exclude_flags, can_edit_flags)
VALUES (:new_rank, '0', '0', '0')
"}, list("new_rank" = custom_rank.name))
if(!query_add_rank.warn_execute())
qdel(query_add_rank)
return
qdel(query_add_rank)
var/datum/db_query/query_add_rank_log = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log)
VALUES (NOW(), :round_id, :adminckey, INET_ATON(:adminip), 'add rank', :new_rank, CONCAT('New rank added: ', :new_rank))
"}, list("round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "new_rank" = custom_rank.name))
if(!query_add_rank_log.warn_execute())
qdel(query_add_rank_log)
return
qdel(query_add_rank_log)
qdel(query_rank_in_db)
var/datum/db_query/query_change_rank = SSdbcore.NewQuery(
"UPDATE [format_table_name("admin")] SET `rank` = :new_rank WHERE ckey = :admin_ckey",
list("new_rank" = joined_rank, "admin_ckey" = admin_ckey)
)
if(!query_change_rank.warn_execute())
qdel(query_change_rank)
return
qdel(query_change_rank)
var/datum/db_query/query_change_rank_log = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log)
VALUES (NOW(), :round_id, :adminckey, INET_ATON(:adminip), 'change admin rank', :target, CONCAT('Rank of ', :target, ' changed from ', :old_rank, ' to ', :new_rank))
"}, list("round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "target" = admin_ckey, "old_rank" = old_rank, "new_rank" = joined_rank))
if(!query_change_rank_log.warn_execute())
qdel(query_change_rank_log)
return
qdel(query_change_rank_log)
if(D) //they were previously an admin
D.disassociate() //existing admin needs to be disassociated
D.ranks = new_ranks //set the admin_rank as our rank
//D.bypass_2fa = TRUE // Another admin has cleared us
var/client/C = GLOB.directory[admin_ckey]
D.associate(C)
else
D = new(new_ranks, admin_ckey) //new admin
//D.bypass_2fa = TRUE // Another admin has cleared us
D.activate()
message_admins(m1)
log_admin(m2)
#undef RANK_DONE
/datum/admins/proc/change_admin_flags(admin_ckey, admin_key, datum/admins/admin_holder)
var/new_flags = input_bitfield(
usr,
"Admin rights<br>This will affect only the current admin [admin_key]",
"admin_flags",
admin_holder.rank_flags(),
350,
590,
allowed_edit_list = usr.client.holder.can_edit_rights_flags(),
)
admin_holder.disassociate()
if (findtext(admin_holder.rank_names(), "([admin_ckey])"))
var/datum/admin_rank/rank = admin_holder.ranks[1]
rank.rights = new_flags
rank.include_rights = new_flags
rank.exclude_rights = NONE
rank.can_edit_rights = rank.can_edit_rights
else
// Not a modified subrank, need to duplicate the admin_rank datum to prevent modifying others too.
var/datum/admin_rank/new_admin_rank = new(
/* init_name = */ "[admin_holder.rank_names()]([admin_ckey])",
/* init_rights = */ new_flags,
// rank_flags() includes the exclude rights, so we no longer need to handle them separately.
/* init_exclude_rights = */ NONE,
/* init_edit_rights = */ admin_holder.can_edit_rights_flags(),
)
admin_holder.ranks = list(new_admin_rank)
var/log = "[key_name(usr)] has updated the admin rights of [admin_ckey] into [rights2text(new_flags)]"
message_admins(log)
log_admin(log)
var/client/admin_client = GLOB.directory[admin_ckey]
admin_holder.associate(admin_client)
/datum/admins/proc/remove_rank(admin_rank)
if(!admin_rank)
return
for(var/datum/admin_rank/R in GLOB.admin_ranks)
if(R.name == admin_rank && (!(R.rights & usr.client.holder.can_edit_rights_flags()) == R.rights))
to_chat(usr, "<span class='admin prefix'>You don't have edit rights to all the rights this rank has, rank deletion not permitted.</span>", confidential = TRUE)
return
if(!CONFIG_GET(flag/admin_legacy_system) && CONFIG_GET(flag/protect_legacy_ranks) && (admin_rank in GLOB.protected_ranks))
to_chat(usr, "<span class='admin prefix'>Deletion of protected ranks is not permitted, it must be removed from admin_ranks.txt.</span>", confidential = TRUE)
return
if(CONFIG_GET(flag/load_legacy_ranks_only))
to_chat(usr, "<span class='admin prefix'>Rank deletion not permitted while database rank loading is disabled.</span>", confidential = TRUE)
return
var/datum/db_query/query_admins_with_rank = SSdbcore.NewQuery(
"SELECT 1 FROM [format_table_name("admin")] WHERE `rank` = :admin_rank",
list("admin_rank" = admin_rank)
)
if(!query_admins_with_rank.warn_execute())
qdel(query_admins_with_rank)
return
if(query_admins_with_rank.NextRow())
qdel(query_admins_with_rank)
to_chat(usr, span_danger("Error: Rank deletion attempted while rank still used; Tell a coder, this shouldn't happen."), confidential = TRUE)
return
qdel(query_admins_with_rank)
if(tgui_alert(usr,"Are you sure you want to remove [admin_rank]?","Confirm Removal",list("Do it","Cancel")) == "Do it")
var/m1 = "[key_name_admin(usr)] removed rank [admin_rank] permanently"
var/m2 = "[key_name(usr)] removed rank [admin_rank] permanently"
var/datum/db_query/query_add_rank = SSdbcore.NewQuery(
"DELETE FROM [format_table_name("admin_ranks")] WHERE `rank` = :admin_rank",
list("admin_rank" = admin_rank)
)
if(!query_add_rank.warn_execute())
qdel(query_add_rank)
return
qdel(query_add_rank)
var/datum/db_query/query_add_rank_log = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log)
VALUES (NOW(), :round_id, :adminckey, INET_ATON(:adminip), 'remove rank', :admin_rank, CONCAT('Rank removed: ', :admin_rank))
"}, list("round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "admin_rank" = admin_rank))
if(!query_add_rank_log.warn_execute())
qdel(query_add_rank_log)
return
qdel(query_add_rank_log)
message_admins(m1)
log_admin(m2)
/datum/admins/proc/sync_lastadminrank(admin_ckey, admin_key, datum/admins/D)
var/sqlrank = "Player"
if (D)
sqlrank = D.rank_names()
var/datum/db_query/query_sync_lastadminrank = SSdbcore.NewQuery(
"UPDATE [format_table_name("erro_player")] SET lastadminrank = :rank WHERE ckey = :ckey",
list("rank" = sqlrank, "ckey" = admin_ckey)
)
if(!query_sync_lastadminrank.warn_execute())
qdel(query_sync_lastadminrank)
return
qdel(query_sync_lastadminrank)
to_chat(usr, span_admin("Sync of [admin_key] successful."), confidential = TRUE)
@@ -1,158 +0,0 @@
/client/proc/edit_admin_permissions()
set category = "Admin.Secrets"
set name = "Permissions Panel"
set desc = "Edit admin permissions"
if(!check_rights(R_PERMISSIONS)) return
usr.client.holder.edit_admin_permissions()
/datum/admins/proc/edit_admin_permissions()
if(!check_rights(R_PERMISSIONS)) return
var/output = {"<!DOCTYPE html>
<html>
<head>
<title>Permissions Panel</title>
<script type='text/javascript' src='search.js'></script>
<link rel='stylesheet' type='text/css' href='panels.css'>
</head>
<body onload='selectTextField();updateSearch();'>
<div id='main'><table id='searchable' cellspacing='0'>
<tr class='title'>
<th style='width:125px;text-align:right;'>CKEY <a class='small' href='byond://?src=\ref[src];[HrefToken()];editrights=add'>\[+\]</a></th>
<th style='width:125px;'>RANK</th><th style='width:100%;'>PERMISSIONS</th>
</tr>
"}
for(var/adm_ckey in admin_datums)
var/datum/admins/D = admin_datums[adm_ckey]
if(!D) continue
var/rank = D.rank ? D.rank : "*none*"
var/rights = rights2text(D.rights," ")
if(!rights) rights = "*none*"
output += "<tr>"
output += "<td style='text-align:right;'>[adm_ckey] <a class='small' href='byond://?src=\ref[src];[HrefToken()];editrights=remove;ckey=[adm_ckey]'>\[-\]</a></td>"
output += "<td><a href='byond://?src=\ref[src];[HrefToken()];editrights=rank;ckey=[adm_ckey]'>[rank]</a></td>"
output += "<td><a class='small' href='byond://?src=\ref[src];[HrefToken()];editrights=permissions;ckey=[adm_ckey]'>[rights]</a></td>"
output += "</tr>"
output += {"
</table></div>
<div id='top'><b>Search:</b> <input type='text' id='filter' value='' style='width:70%;' onkeyup='updateSearch();'></div>
</body>
</html>"}
usr << browse(output,"window=editrights;size=600x500")
/datum/admins/proc/log_admin_rank_modification(var/adm_ckey, var/new_rank)
if(CONFIG_GET(flag/admin_legacy_system)) return
if(!usr.client)
return
if(!usr.client.holder || !(usr.client.holder.rights & R_PERMISSIONS))
to_chat(usr, span_filter_adminlog("[span_red("You do not have permission to do this!")]"))
return
establish_db_connection()
if(!SSdbcore.IsConnected())
to_chat(usr, span_filter_adminlog("[span_red("Failed to establish database connection")]"))
return
if(!adm_ckey || !new_rank)
return
adm_ckey = ckey(adm_ckey)
if(!adm_ckey)
return
if(!istext(adm_ckey) || !istext(new_rank))
return
var/datum/db_query/select_query = SSdbcore.NewQuery("SELECT id FROM erro_admin WHERE ckey = '[adm_ckey]'")
select_query.Execute()
var/new_admin = 1
var/admin_id
while(select_query.NextRow())
new_admin = 0
admin_id = text2num(select_query.item[1])
qdel(select_query)
if(new_admin)
var/datum/db_query/insert_query = SSdbcore.NewQuery("INSERT INTO `erro_admin` (`id`, `ckey`, `rank`, `level`, `flags`) VALUES (null, '[adm_ckey]', '[new_rank]', -1, 0)")
insert_query.Execute()
qdel(insert_query)
var/datum/db_query/log_query = SSdbcore.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Added new admin [adm_ckey] to rank [new_rank]');")
log_query.Execute()
qdel(log_query)
to_chat(usr, span_filter_adminlog("[span_blue("New admin added.")]"))
else
if(!isnull(admin_id) && isnum(admin_id))
var/datum/db_query/insert_query = SSdbcore.NewQuery("UPDATE `erro_admin` SET rank = '[new_rank]' WHERE id = [admin_id]")
insert_query.Execute()
qdel(insert_query)
var/datum/db_query/log_query = SSdbcore.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Edited the rank of [adm_ckey] to [new_rank]');")
log_query.Execute()
qdel(log_query)
to_chat(usr, span_filter_adminlog("[span_blue("Admin rank changed.")]"))
/datum/admins/proc/log_admin_permission_modification(var/adm_ckey, var/new_permission)
if(CONFIG_GET(flag/admin_legacy_system)) return
if(!usr.client)
return
if(!usr.client.holder || !(usr.client.holder.rights & R_PERMISSIONS))
to_chat(usr, span_filter_adminlog("[span_red(">You do not have permission to do this!")]"))
return
establish_db_connection()
if(!SSdbcore.IsConnected())
to_chat(usr, span_filter_adminlog("[span_red("Failed to establish database connection!")]"))
return
if(!adm_ckey || !new_permission)
return
adm_ckey = ckey(adm_ckey)
if(!adm_ckey)
return
if(istext(new_permission))
new_permission = text2num(new_permission)
if(!istext(adm_ckey) || !isnum(new_permission))
return
var/datum/db_query/select_query = SSdbcore.NewQuery("SELECT id, flags FROM erro_admin WHERE ckey = '[adm_ckey]'")
select_query.Execute()
var/admin_id
var/admin_rights
while(select_query.NextRow())
admin_id = text2num(select_query.item[1])
admin_rights = text2num(select_query.item[2])
qdel(select_query)
if(!admin_id)
return
if(admin_rights & new_permission) //This admin already has this permission, so we are removing it.
var/datum/db_query/insert_query = SSdbcore.NewQuery("UPDATE `erro_admin` SET flags = [admin_rights & ~new_permission] WHERE id = [admin_id]")
insert_query.Execute()
qdel(insert_query)
var/datum/db_query/log_query = SSdbcore.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Removed permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]');")
log_query.Execute()
qdel(log_query)
to_chat(usr, span_filter_adminlog("[span_blue("Permission removed.")]"))
else //This admin doesn't have this permission, so we are adding it.
var/datum/db_query/insert_query = SSdbcore.NewQuery("UPDATE `erro_admin` SET flags = '[admin_rights | new_permission]' WHERE id = [admin_id]")
insert_query.Execute()
qdel(insert_query)
var/datum/db_query/log_query = SSdbcore.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Added permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]')")
log_query.Execute()
qdel(log_query)
to_chat(usr, span_filter_adminlog("[span_blue("Permission added.")]"))
+1 -1
View File
@@ -27,7 +27,7 @@
var/datum/player_info/P = new
if (user)
P.author = user.key
P.rank = user.client.holder.rank
P.rank = user.client.holder.rank_names()
else
P.author = "Adminbot"
P.rank = "Friendly Robot"
+18 -94
View File
@@ -126,99 +126,23 @@
if((bantype == BANTYPE_PERMA || bantype == BANTYPE_TEMP) && playermob.client)
qdel(playermob.client)
else if(href_list["editrightsbrowser"])
edit_admin_permissions(0)
else if(href_list["editrightsbrowserlog"])
edit_admin_permissions(1, href_list["editrightstarget"], href_list["editrightsoperation"], href_list["editrightspage"])
if(href_list["editrightsbrowsermanage"])
if(href_list["editrightschange"])
change_admin_rank(ckey(href_list["editrightschange"]), href_list["editrightschange"], TRUE)
else if(href_list["editrightsremove"])
remove_admin(ckey(href_list["editrightsremove"]), href_list["editrightsremove"], TRUE)
else if(href_list["editrightsremoverank"])
remove_rank(href_list["editrightsremoverank"])
edit_admin_permissions(2)
else if(href_list["editrights"])
if(!check_rights(R_PERMISSIONS))
message_admins("[key_name_admin(usr)] attempted to edit the admin permissions without sufficient rights.")
log_admin("[key_name(usr)] attempted to edit the admin permissions without sufficient rights.")
return
var/adm_ckey
var/task = href_list["editrights"]
if(task == "add")
var/new_ckey = ckey(tgui_input_text(usr,"New admin's ckey","Admin ckey", null))
if(!new_ckey) return
if(new_ckey in admin_datums)
to_chat(usr, span_filter_adminlog(span_warning("Error: Topic 'editrights': [new_ckey] is already an admin")))
return
adm_ckey = new_ckey
task = "rank"
else if(task != "show")
adm_ckey = ckey(href_list["ckey"])
if(!adm_ckey)
to_chat(usr, span_filter_adminlog(span_warning("Error: Topic 'editrights': No valid ckey")))
return
var/datum/admins/D = admin_datums[adm_ckey]
if(task == "remove")
if(tgui_alert(usr, "Are you sure you want to remove [adm_ckey]?","Message",list("Yes","Cancel")) == "Yes")
if(!D) return
admin_datums -= adm_ckey
D.disassociate()
message_admins("[key_name_admin(usr)] removed [adm_ckey] from the admins list")
log_admin("[key_name(usr)] removed [adm_ckey] from the admins list")
log_admin_rank_modification(adm_ckey, "Removed")
else if(task == "rank")
var/new_rank
if(admin_ranks.len)
new_rank = tgui_input_list(usr, "Please select a rank", "New rank", (admin_ranks|"*New Rank*"))
else
new_rank = tgui_input_list(usr, "Please select a rank", "New rank", list("Game Master","Game Admin", "Trial Admin", "Admin Observer","*New Rank*"))
var/rights = 0
if(D)
rights = D.rights
switch(new_rank)
if(null,"") return
if("*New Rank*")
new_rank = tgui_input_text(usr, "Please input a new rank", "New custom rank")
if(CONFIG_GET(flag/admin_legacy_system))
new_rank = ckeyEx(new_rank)
if(!new_rank)
to_chat(usr, span_filter_adminlog(span_warning("Error: Topic 'editrights': Invalid rank")))
return
if(CONFIG_GET(flag/admin_legacy_system))
if(admin_ranks.len)
if(new_rank in admin_ranks)
rights = admin_ranks[new_rank] //we typed a rank which already exists, use its rights
else
admin_ranks[new_rank] = 0 //add the new rank to admin_ranks
else
if(CONFIG_GET(flag/admin_legacy_system))
new_rank = ckeyEx(new_rank)
rights = admin_ranks[new_rank] //we input an existing rank, use its rights
if(D)
D.disassociate() //remove adminverbs and unlink from client
D.rank = new_rank //update the rank
D.rights = rights //update the rights based on admin_ranks (default: 0)
else
D = new /datum/admins(new_rank, rights, adm_ckey)
var/client/C = GLOB.directory[adm_ckey] //find the client with the specified ckey (if they are logged in)
D.associate(C) //link up with the client and add verbs
message_admins("[key_name_admin(usr)] edited the admin rank of [adm_ckey] to [new_rank]")
log_admin("[key_name(usr)] edited the admin rank of [adm_ckey] to [new_rank]")
log_admin_rank_modification(adm_ckey, new_rank)
else if(task == "permissions")
if(!D) return
var/list/permissionlist = list()
for(var/i=1, i<=R_MAXPERMISSION, i<<=1) //that <<= is shorthand for i = i << 1. Which is a left bitshift
permissionlist[rights2text(i)] = i
var/new_permission = tgui_input_list(usr, "Select a permission to turn on/off", "Permission toggle", permissionlist)
if(!new_permission) return
D.rights ^= permissionlist[new_permission]
message_admins("[key_name_admin(usr)] toggled the [new_permission] permission of [adm_ckey]")
log_admin("[key_name(usr)] toggled the [new_permission] permission of [adm_ckey]")
log_admin_permission_modification(adm_ckey, permissionlist[new_permission])
edit_admin_permissions()
edit_rights_topic(href_list)
else if(href_list["call_shuttle"])
if(!check_rights(R_ADMIN|R_EVENT)) return
@@ -689,7 +613,7 @@
return
if(M != usr) //we can jobban ourselves
if(M.client && M.client.holder && (M.client.holder.rights & R_BAN)) //they can ban too. So we can't ban them
if(M.client && M.client.holder && (check_rights_for(M.client, R_BAN))) //they can ban too. So we can't ban them
tgui_alert_async(usr, "You cannot perform this action. You must be of a higher administrative rank!")
return
@@ -1357,7 +1281,7 @@
if(ismob(M))
var/take_msg = span_notice("<b>ADMINHELP</b>: <b>[key_name(usr.client)]</b> is attending to <b>[key_name(M)]'s</b> adminhelp, please don't dogpile them.")
for(var/client/X in GLOB.admins)
if((R_ADMIN|R_MOD|R_SERVER) & X.holder.rights) //VOREStation Edit
if(check_rights_for(X, (R_ADMIN|R_MOD|R_SERVER)))
to_chat(X, take_msg)
to_chat(M, span_filter_pm(span_boldnotice("Your adminhelp is being attended to by [usr.client]. Thanks for your patience!")))
// VoreStation Edit Start
@@ -0,0 +1,27 @@
GENERAL_PROTECT_DATUM(/datum/admin_verb)
/**
* This is the admin verb datum. It is used to store the verb's information and handle the verb's functionality.
* All of this is setup for you, and you should not be defining this manually.
* That means you reader.
*/
/datum/admin_verb
var/name //! The name of the verb.
var/description //! The description of the verb.
var/category //! The category of the verb.
var/permissions //! The permissions required to use the verb.
var/visibility_flag //! The flag that determines if the verb is visible.
VAR_PROTECTED/verb_path //! The path to the verb proc.
/datum/admin_verb/Destroy(force)
if(!force)
return QDEL_HINT_LETMELIVE
return ..()
/// Assigns the verb to the admin.
/datum/admin_verb/proc/assign_to_client(client/admin)
add_verb(admin, verb_path)
/// Unassigns the verb from the admin.
/datum/admin_verb/proc/unassign_from_client(client/admin)
remove_verb(admin, verb_path)
+2 -2
View File
@@ -257,7 +257,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
//send this msg to all admins
for(var/client/X in GLOB.admins)
if(!check_rights(R_ADMIN, 0, X))
if(!check_rights_for(X, R_ADMIN))
continue
if(X.prefs?.read_preference(/datum/preference/toggle/holder/play_adminhelp_ping))
X << 'sound/effects/adminhelp.ogg'
@@ -684,7 +684,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
. = list("total" = list(), "noflags" = list(), "afk" = list(), "stealth" = list(), "present" = list())
for(var/client/X in GLOB.admins)
.["total"] += X
if(requiredflags != 0 && !check_rights(rights_required = requiredflags, show_msg = FALSE, C = X))
if(requiredflags != 0 && !check_rights_for(X, requiredflags))
.["noflags"] += X
else if(X.is_afk())
.["afk"] += X
+2 -2
View File
@@ -203,14 +203,14 @@
if(irc)
log_admin("PM: [key_name(src)]->IRC: [rawmsg]")
for(var/client/X in GLOB.admins)
if(!check_rights(R_ADMIN, 0, X))
if(!check_rights_for(X, R_ADMIN))
continue
to_chat(X, span_admin_pm_notice(span_bold("PM: [key_name(src, X, 0)]-&gt;IRC:") + " [keywordparsedmsg]"))
else
log_admin("PM: [key_name(src)]->[key_name(recipient)]: [rawmsg]")
//we don't use message_admins here because the sender/receiver might get it too
for(var/client/X in GLOB.admins)
if(!check_rights(R_ADMIN, 0, X))
if(!check_rights_for(X, R_ADMIN))
continue
if(X.key!=key && X.key!=recipient.key) //check client/X is an admin and isn't the sender or recipient
to_chat(X, span_admin_pm_notice(span_bold("PM: [key_name(src, X, 0)]-&gt;[key_name(recipient, X, 0)]:") + " [keywordparsedmsg]"))
+1 -1
View File
@@ -12,7 +12,7 @@
log_adminsay(msg,src)
for(var/client/C in GLOB.admins)
if(check_rights(R_ADMIN, 0, C))
if(check_rights_for(C, R_ADMIN))
to_chat(C, span_admin_channel(create_text_tag("admin", "ADMIN:", C) + " " + span_name("[key_name(usr, 1)]") + "([admin_jump_link(mob, src)]): " + span_name("[msg]") ))
feedback_add_details("admin_verb","M") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+2 -2
View File
@@ -27,10 +27,10 @@
display_name = usr.client.holder.fakekey
// Name shown to other players. Admins whom are not also antags have their rank displayed.
var/player_display = (is_admin && !is_antag) ? "[display_name]([usr.client.holder.rank])" : display_name
var/player_display = (is_admin && !is_antag) ? "[display_name]([usr.client.holder.rank_names()])" : display_name
for(var/mob/M in mob_list)
if(check_rights(R_ADMIN|R_MOD|R_EVENT, 0, M)) // Staff can see AOOC unconditionally, and with more details.
if(check_rights_for(M.client, R_ADMIN|R_MOD|R_EVENT)) // Staff can see AOOC unconditionally, and with more details.
to_chat(M, span_ooc(span_aooc("[create_text_tag("aooc", "Antag-OOC:", M.client)] <EM>[get_options_bar(src, 0, 1, 1)]([admin_jump_link(usr, M.client.holder)]):</EM> " + span_message("[msg]"))))
else if(M.client) // Players can only see AOOC if observing, or if they are an antag type allowed to use AOOC.
var/datum/antagonist/A = null
+1 -1
View File
@@ -15,7 +15,7 @@
to_chat(src, span_warning("You have deadchat muted."))
return
var/stafftype = uppertext(holder.rank)
var/stafftype = uppertext(holder.rank_names())
msg = sanitize(msg)
log_admin("DSAY: [key_name(src)] : [msg]")
+3 -3
View File
@@ -16,7 +16,7 @@
var/msg = span_filter_pray(span_blue("[icon2html(cross, GLOB.admins)] <b>" + span_purple("PRAY: ") + "[key_name(src, 1)] [ADMIN_QUE(src)] [ADMIN_PP(src)] [ADMIN_VV(src)] [ADMIN_SM(src)] ([admin_jump_link(src, src)]) [ADMIN_CA(src)] [ADMIN_SC(src)] [ADMIN_SMITE(src)]:</b> [raw_msg]"))
for(var/client/C in GLOB.admins)
if(!check_rights(R_ADMIN, 0, C))
if(!check_rights_for(C, R_ADMIN))
continue
if(C.prefs?.read_preference(/datum/preference/toggle/show_chat_prayers))
to_chat(C, msg, type = MESSAGE_TYPE_PRAYER, confidential = TRUE)
@@ -29,7 +29,7 @@
/proc/CentCom_announce(var/msg, var/mob/Sender, var/iamessage)
msg = span_blue(span_bold(span_orange("[uppertext(using_map.boss_short)]M[iamessage ? " IA" : ""]:") + "[key_name(Sender, 1)] [ADMIN_PP(Sender)] [ADMIN_VV(Sender)] [ADMIN_SM(Sender)] ([admin_jump_link(Sender)]) [ADMIN_CA(Sender)] [ADMIN_BSA(Sender)] [ADMIN_CENTCOM_REPLY(Sender)]:") + " [msg]")
for(var/client/C in GLOB.admins) //VOREStation Edit - GLOB admins
if(!check_rights(R_ADMIN, 0, C))
if(!check_rights_for(C, R_ADMIN))
continue
to_chat(C,msg)
C << 'sound/machines/signal.ogg'
@@ -37,7 +37,7 @@
/proc/Syndicate_announce(var/msg, var/mob/Sender)
msg = span_blue(span_bold(span_crimson("ILLEGAL:") + "[key_name(Sender, 1)] [ADMIN_PP(Sender)] [ADMIN_VV(Sender)] [ADMIN_SM(Sender)] ([admin_jump_link(Sender)]) [ADMIN_CA(Sender)] [ADMIN_BSA(Sender)] [ADMIN_SYNDICATE_REPLY(Sender)]:") + " [msg]")
for(var/client/C in GLOB.admins) //VOREStation Edit - GLOB admins
if(!check_rights(R_ADMIN, 0, C))
if(!check_rights_for(C, R_ADMIN))
continue
to_chat(C,msg)
C << 'sound/machines/signal.ogg'
+30
View File
@@ -0,0 +1,30 @@
// Admin Verbs in this file are special and cannot use the AVD system for some reason or another.
/client/proc/readmin()
set name = "Readmin"
set category = "Admin.Misc"
set desc = "Regain your admin powers."
var/datum/admins/A = GLOB.deadmins[ckey]
if(!A)
A = GLOB.admin_datums[ckey]
if (!A)
var/msg = " is trying to readmin but they have no deadmin entry"
message_admins("[key_name_admin(src)][msg]")
log_admin_private("[key_name(src)][msg]")
return
A.associate(src)
if(!holder)
return //This can happen if an admin attempts to vv themself into somebody elses's deadmin datum by getting ref via brute force
to_chat(src, span_interface("You are now an admin."), confidential = TRUE)
message_admins("[src] re-adminned themselves.")
log_admin("[src] re-adminned themselves.")
//BLACKBOX_LOG_ADMIN_VERB("Readmin")
if(isobserver(mob))
var/mob/observer/dead/our_mob = mob
our_mob.visualnet?.addVisibility(our_mob, src)
+8 -5
View File
@@ -172,21 +172,24 @@
/client/VV_ckey_edit()
return list("key", "ckey")
/datum/proc/may_edit_var(var/user, var/var_to_edit)
/datum/proc/may_edit_var(var/user, var/var_to_edit) //User must be a CLIENT that is passed to this.
if(!user)
return FALSE
if(ismob(user)) //Failsafe catch in case someone feeds a mob into us.
var/mob/living = user
user = living.client
if(!(var_to_edit in vars))
to_chat(user, span_warning("\The [src] does not have a var '[var_to_edit]'"))
return FALSE
if(var_to_edit in VV_static())
return FALSE
if((var_to_edit in VV_secluded()) && !check_rights(R_ADMIN|R_DEBUG, FALSE, C = user))
if((var_to_edit in VV_secluded()) && !check_rights_for(user, R_ADMIN|R_DEBUG))
return FALSE
if((var_to_edit in VV_locked()) && !check_rights(R_DEBUG, C = user))
if((var_to_edit in VV_locked()) && !check_rights_for(user, R_DEBUG))
return FALSE
if((var_to_edit in VV_ckey_edit()) && !check_rights(R_SPAWN|R_DEBUG, C = user))
if((var_to_edit in VV_ckey_edit()) && !check_rights_for(user, R_SPAWN|R_DEBUG))
return FALSE
if((var_to_edit in VV_icon_edit_lock()) && !check_rights(R_FUN|R_DEBUG, C = user))
if((var_to_edit in VV_icon_edit_lock()) && !check_rights_for(user, R_FUN|R_DEBUG))
return FALSE
return TRUE