Files
Bubberstation/code/modules/admin/admin_ranks.dm
carnie bbe7354265 Fixes #1147 - type-mismatch
Added helper: /proc/findchar(haystack, needles, start=1, end=0)
works like findtext except it finds the first occurrence of one of the characters from the needles string, within haystack.

Permissions can now be removed as well as added, by replacing the + with a -. e.g. +BAN adds bans permissions, -BAN removes them. This applies to admin_ranks.txt and the permissions panel.

Verb overrides are now possible for admins_ranks. Specific verbs can be forced-on, or forced-off via keywords like +/client/proc/mimespeak or -/client/verb/ooc. This applies both to admin_ranks.txt and the permissions panel. SQL system is not compatible.

admin_ranks are now datums. This means admin_ranks actually behave more like permission groups as intended.
When you temporarily modify an admin's permissions via the permissions panel, their rank_name is appended with "([ckey])". This prevents modifications to their rights affecting everyone.

admin rank names now support - _ and @ characters (since ckeyEx() does not strip these).

SQL permissions system was modified. SQL databases will have to be updated or they will no longer work.

WARNING: admin_ranks.txt and admins.txt format has changed slightly! You will need to redo those txt files!
2013-08-14 18:10:26 +01:00

284 lines
9.4 KiB
Plaintext

var/list/admin_ranks = list() //list of all admin_rank datums
/datum/admin_rank
var/name = "NoRank"
var/rights = 0
var/list/adds
var/list/subs
/datum/admin_rank/New(init_name, init_rights, list/init_adds, list/init_subs)
name = init_name
switch(name)
if("Removed",null,"")
error("invalid admin-rank name. datum deleted")
del(src)
if(init_rights) rights = init_rights
if(!init_adds) init_adds = list()
if(!init_subs) init_subs = list()
adds = init_adds
subs = init_subs
/datum/admin_rank/proc/process_keyword(word, previous_rights=0)
var/flag = 0
switch(ckey(word))
if("buildmode","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","rights") flag = R_PERMISSIONS
if("possess") flag = R_POSSESS
if("stealth") flag = R_STEALTH
if("rejuv","rejuvinate") flag = R_REJUVINATE
if("varedit") flag = R_VAREDIT
if("everything","host","all") flag = 65535
if("sound","sounds") flag = R_SOUNDS
if("spawn","create") flag = R_SPAWN
if("@","prev") flag = previous_rights
else
//isn't a keyword so maybe it's a verbpath?
var/path = text2path(copytext(word,2,findtext(word," ",2,0)))
if(path)
switch(text2ascii(word,1))
if(43)
if(!subs.Remove(path))
adds += path //+
if(45)
if(!adds.Remove(path))
subs += path //-
return
switch(text2ascii(word,1))
if(43) rights |= flag //+
if(45) rights &= ~flag //-
return
//load our rank - > rights associations
/proc/load_admin_ranks()
admin_ranks.Cut()
if(config.admin_legacy_system)
var/previous_rights = 0
//load text from file and process each line seperately
for(var/line in file2list("config/admin_ranks.txt"))
if(!line) continue
if(findtextEx(line,"#",1,2)) continue
var/next = findtext(line, "=")
var/datum/admin_rank/R = new(ckeyEx(copytext(line, 1, next)))
if(!R) continue
admin_ranks += R
var/prev = findchar(line, "+-", next, 0)
while(prev)
next = findchar(line, "+-", prev+1, 0)
R.process_keyword(copytext(line, prev, next), previous_rights)
prev = next
previous_rights = R.rights
else
establish_db_connection()
if(!dbcon.IsConnected())
world.log << "Failed to connect to database in load_admin_ranks(). Reverting to legacy system."
diary << "Failed to connect to database in load_admin_ranks(). Reverting to legacy system."
config.admin_legacy_system = 1
load_admin_ranks()
return
var/DBQuery/query = dbcon.NewQuery("SELECT rank, flags FROM erro_admin_ranks")
query.Execute()
while(query.NextRow())
var/rank_name = ckeyEx(query.item[1])
var/flags = query.item[2]
if(istext(flags)) flags = text2num(flags)
var/datum/admin_rank/R = new(rank_name, flags)
if(!R) continue
admin_ranks += R
#ifdef TESTING
var/msg = "Permission Sets Built:\n"
for(var/datum/admin_rank/R in admin_ranks)
msg += "\t[R.name]"
var/rights = rights2text(R.rights,"\n\t\t",R.adds,R.subs)
if(rights) msg += "\t\t[rights]\n"
testing(msg)
#endif
/proc/load_admins()
//clear the datums references
admin_datums.Cut()
for(var/client/C in admins)
C.remove_admin_verbs()
C.holder = null
admins.Cut()
load_admin_ranks()
var/list/rank_names = list()
for(var/datum/admin_rank/R in admin_ranks)
rank_names[R.name] = R
if(config.admin_legacy_system)
//load text from file
var/list/Lines = file2list("config/admins.txt")
//process each line seperately
for(var/line in Lines)
if(!length(line)) continue
if(findtextEx(line,"#",1,2)) continue
//Split the line at every "="
var/list/List = text2list(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])
var/datum/admins/D = new(rank_names[rank], ckey) //create the admin datum and store it for later use
if(!D) continue //will occur if an invalid rank is provided
D.associate(directory[ckey]) //find the client for a ckey if they are connected and associate them with the new admin datum
else
establish_db_connection()
if(!dbcon.IsConnected())
world.log << "Failed to connect to database in load_admins(). Reverting to legacy system."
diary << "Failed to connect to database in load_admins(). Reverting to legacy system."
config.admin_legacy_system = 1
load_admins()
return
var/DBQuery/query = dbcon.NewQuery("SELECT ckey, rank FROM erro_admin")
query.Execute()
while(query.NextRow())
var/ckey = ckey(query.item[1])
var/rank = ckeyEx(query.item[2])
var/datum/admins/D = new(rank, ckey) //create the admin datum and store it for later use
if(!D) continue //will occur if an invalid rank is provided
D.associate(directory[ckey]) //find the client for a ckey if they are connected and associate them with the new admin datum
#ifdef TESTING
var/msg = "Admins Built:\n"
for(var/ckey in admin_datums)
var/datum/admins/D = admin_datums[ckey]
msg += "\t[ckey] - [D.rank.name]\n"
testing(msg)
#endif
#ifdef TESTING
/client/verb/changerank(newrank in admin_ranks)
if(holder)
holder.rank = newrank
else
holder = new /datum/admins(newrank,ckey)
remove_admin_verbs()
holder.associate(src)
/client/verb/changerights(newrights as num)
if(holder)
holder.rank.rights = newrights
else
holder = new /datum/admins("testing",newrights,ckey)
remove_admin_verbs()
holder.associate(src)
#endif
/datum/admins/proc/edit_rights_topic(list/href_list)
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"]
switch(task)
if("add")
var/new_ckey = ckey(input(usr,"New admin's ckey","Admin ckey", null) as text|null)
if(!new_ckey) return
if(new_ckey in admin_datums)
usr << "<font color='red'>Error: Topic 'editrights': [new_ckey] is already an admin</font>"
return
adm_ckey = new_ckey
task = "rank"
else
adm_ckey = ckey(href_list["ckey"])
if(!adm_ckey)
usr << "<font color='red'>Error: Topic 'editrights': No valid ckey</font>"
return
var/datum/admins/D = admin_datums[adm_ckey]
switch(task)
if("remove")
if(alert("Are you sure you want to remove [adm_ckey]?","Message","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")
if("rank")
var/datum/admin_rank/R
var/list/rank_names = list("*New Rank*")
for(R in admin_ranks)
rank_names[R.name] = R
var/new_rank = input("Please select a rank", "New rank", null, null) as null|anything in rank_names
switch(new_rank)
if(null) return
if("*New Rank*")
new_rank = ckeyEx(input("Please input a new rank", "New custom rank", null, null) as null|text)
if(!new_rank) return
R = rank_names[new_rank]
if(!R) //rank with that name doesn't exist yet - make it
if(D) R = new(new_rank, D.rank.rights, D.rank.adds, D.rank.subs) //duplicate our previous admin_rank but with a new name
else R = new(new_rank) //blank new admin_rank
admin_ranks += R
if(D) //they were previously an admin
D.disassociate() //existing admin needs to be disassociated
D.rank = R //set the admin_rank as our rank
else
D = new(R,adm_ckey) //new admin
var/client/C = 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)
if("permissions")
if(!D) return //they're not an admin!
var/keyword = input("Input permission keyword (one at a time):\ne.g. +BAN or -FUN or +/client/proc/someverb", "Permission toggle", null, null) as null|text
if(!keyword) return
D.disassociate()
if(!findtext(D.rank.name, "([adm_ckey])")) //not a modified subrank, need to duplicate the admin_rank datum to prevent modifying others too
D.rank = new("[D.rank.name]([adm_ckey])", D.rank.rights, D.rank.adds, D.rank.subs) //duplicate our previous admin_rank but with a new name
//we don't add this clone to the admin_ranks list, as it is unique to that ckey
D.rank.process_keyword(keyword)
var/client/C = 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(usr)] added keyword [keyword] to permission of [adm_ckey]")
log_admin("[key_name(usr)] added keyword [keyword] to permission of [adm_ckey]")
log_admin_permission_modification(adm_ckey, D.rank.rights)
edit_admin_permissions()