Modernizes SQL admin loading (#35264)

* updates sqladmins with excluded flags and tempminning

* rank, editing and permission protections; json backup

* fixes

* reverted the wrong file

* fix slidecolor cascading and disabled switches not being sent in form

* removes debug uncommenting

* whoops this too

* commas fix + versions for changelog entry headers since 3.0

* actually account for can_edit_flags and use of @ with - or *

* fixes and rearrangement of admin > localhost > autoadmin precedence

* in case you want to not have autologin autoadmins
This commit is contained in:
Jordie
2018-03-02 11:38:26 +11:00
committed by oranges
parent 109e376f14
commit 23a45889ed
25 changed files with 723 additions and 539 deletions

View File

@@ -0,0 +1,93 @@
#Python 3+ Script for importing admins.txt and admin_ranks.txt made by Jordie0608
#
#Before starting ensure you have installed the mysqlclient package https://github.com/PyMySQL/mysqlclient-python
#It can be downloaded from command line with pip:
#pip install mysqlclient
#And that you have run the most recent commands listed in database_changelog.txt
#
#To view the parameters for this script, execute it with the argument --help
#All the positional arguments are required, remember to include prefixes in your table names if you use them
#An example of the command used to execute this script from powershell:
#python admin_import_2018-02-03.py "localhost" "root" "password" "feedback" "SS13_admin" "SS13_admin_ranks"
#
#This script performs no error-correction, improper configurations of admins.txt or admin_ranks.txt will cause either breaking exceptions or invalid table rows
#It's safe to run this script with your game server(s) active.
import MySQLdb
import argparse
import re
import sys
def parse_text_flags(text, previous):
flag_values = {"BUILDMODE":1, "BUILD":1, "ADMIN":2, "REJUVINATE":2, "REJUV":2, "BAN":4, "FUN":8, "SERVER":16, "DEBUG":32, "POSSESS":64, "PERMISSIONS":128, "RIGHTS":128, "STEALTH":256, "POLL":512, "VAREDIT":1024, "SOUNDS":2048, "SOUND":2048, "SPAWN":4096, "CREATE":4096, "AUTOLOGIN":8192, "AUTOADMIN":8192, "DBRANKS":16384}
flags_int = 8192
exclude_flags_int = 0
can_edit_flags_int = 0
flags = text.split(" ")
if flags:
for flag in flags:
sign = flag[:1]
if flag[1:] in ("@", "prev"):
if sign is "+":
flags_int = previous[0]
elif sign is "-":
exclude_flags_int = previous[1]
elif sign is "*":
can_edit_flags_int = previous[2]
continue
if flag[1:] in ("EVERYTHING", "HOST", "ALL"):
if sign is "+":
flags_int = 65535
elif sign is "-":
exclude_flags_int = 65535
elif sign is "*":
can_edit_flags_int = 65535
continue
if flag[1:] in flag_values:
if sign is "+":
flags_int += flag_values[flag[1:]]
elif sign is "-":
exclude_flags_int += flag_values[flag[1:]]
elif sign is "*":
can_edit_flags_int += flag_values[flag[1:]]
flags_int = max(min(65535, flags_int), 0)
exclude_flags_int = max(min(65535, exclude_flags_int), 0)
can_edit_flags_int = max(min(65535, can_edit_flags_int), 0)
return flags_int, exclude_flags_int, can_edit_flags_int
if sys.version_info[0] < 3:
raise Exception("Python must be at least version 3 for this script.")
parser = argparse.ArgumentParser()
parser.add_argument("address", help="MySQL server address (use localhost for the current computer)")
parser.add_argument("username", help="MySQL login username")
parser.add_argument("password", help="MySQL login username")
parser.add_argument("database", help="Database name")
parser.add_argument("admintable", help="Name of the current admin table (remember prefixes if you use them)")
parser.add_argument("rankstable", help="Name of the current admin ranks (remember prefixes)")
args = parser.parse_args()
db=MySQLdb.connect(host=args.address, user=args.username, passwd=args.password, db=args.database)
cursor=db.cursor()
ranks_table = args.rankstable
admin_table = args.admintable
with open("..\\config\\admin_ranks.txt") as rank_file:
previous = 0
for line in rank_file:
if line.strip():
if line.startswith("#"):
continue
matches = re.match("(.+)\\b\\s+=\\s*(.*)", line)
flags = parse_text_flags(matches.group(2), previous)
previous = flags
cursor.execute("INSERT INTO {0} (rank, flags, exclude_flags, can_edit_flags) VALUES ('{1}', {2}, {3}, {4})".format(ranks_table, matches.group(1), flags[0], flags[1], flags[2]))
with open("..\\config\\admins.txt") as admins_file:
previous = 0
for line in admins_file:
if line.strip():
if line.startswith("#"):
continue
matches = re.match("(.+)\\b\\s+=\\s+(.+)", line)
cursor.execute("INSERT INTO {0} (ckey, rank) VALUES ('{1}', '{2}')".format(admin_table, matches.group(1).lower(), matches.group(2)))
db.commit()
cursor.close()
print("Import complete.")

View File

@@ -1,16 +1,50 @@
Any time you make a change to the schema files, remember to increment the database schema version. Generally increment the minor number, major should be reserved for significant changes to the schema. Both values go up to 255.
The latest database version is 4.0; The query to update the schema revision table is:
The latest database version is 4.1; The query to update the schema revision table is:
INSERT INTO `schema_revision` (`major`, `minor`) VALUES (4, 0);
INSERT INTO `schema_revision` (`major`, `minor`) VALUES (4, 1);
or
INSERT INTO `SS13_schema_revision` (`major`, `minor`) VALUES (4, 0);
INSERT INTO `SS13_schema_revision` (`major`, `minor`) VALUES (4, 1);
In any query remember to add a prefix to the table names if you use one.
----------------------------------------------------
12 November 2017, by Jordie0608
Version 4.1, 3 February 2018, by Jordie0608
Modified tables 'admin', 'admin_log' and 'admin_rank', removing unnecessary columns and adding support for excluding rights flags from admin ranks.
This change was made to enable use of sql-based admin loading.
To import your existing admins and ranks run the included script 'admin_import_2018-02-03.py', see the file for use instructions.
Legacy file-based admin loading is still supported, if you want to continue using it the script doesn't need to be run.
ALTER TABLE `admin`
CHANGE COLUMN `rank` `rank` VARCHAR(32) NOT NULL AFTER `ckey`,
DROP COLUMN `id`,
DROP COLUMN `level`,
DROP COLUMN `flags`,
DROP COLUMN `email`,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`ckey`);
ALTER TABLE `admin_log`
CHANGE COLUMN `datetime` `datetime` DATETIME NOT NULL AFTER `id`,
CHANGE COLUMN `adminckey` `adminckey` VARCHAR(32) NOT NULL AFTER `datetime`,
CHANGE COLUMN `adminip` `adminip` INT(10) UNSIGNED NOT NULL AFTER `adminckey`,
ADD COLUMN `operation` ENUM('add admin','remove admin','change admin rank','add rank','remove rank','change rank flags') NOT NULL AFTER `adminip`,
CHANGE COLUMN `log` `log` VARCHAR(1000) NOT NULL AFTER `operation`;
ALTER TABLE `admin_ranks`
CHANGE COLUMN `rank` `rank` VARCHAR(32) NOT NULL FIRST,
CHANGE COLUMN `flags` `flags` SMALLINT UNSIGNED NOT NULL AFTER `rank`,
ADD COLUMN `exclude_flags` SMALLINT UNSIGNED NOT NULL AFTER `flags`,
ADD COLUMN `can_edit_flags` SMALLINT(5) UNSIGNED NOT NULL AFTER `exclude_flags`,
DROP COLUMN `id`,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`rank`);
----------------------------------------------------
Version 4.0, 12 November 2017, by Jordie0608
Modified feedback table to use json, a python script is used to migrate data to this new format.
See the file 'feedback_conversion_2017-11-12.py' for instructions on how to use the script.
@@ -29,7 +63,7 @@ CREATE TABLE `feedback` (
----------------------------------------------------
28 August 2017, by MrStonedOne
Version 3.4, 28 August 2017, by MrStonedOne
Modified table 'messages', adding a deleted column and editing all indexes to include it
ALTER TABLE `messages`
@@ -43,7 +77,7 @@ ADD INDEX `idx_msg_type_ckey_time_odr` (`type`,`targetckey`,`timestamp`, `delete
----------------------------------------------------
25 August 2017, by Jordie0608
Version 3.3, 25 August 2017, by Jordie0608
Modified tables 'connection_log', 'legacy_population', 'library', 'messages' and 'player' to add additional 'round_id' tracking in various forms and 'server_ip' and 'server_port' to the table 'messages'.
@@ -55,7 +89,7 @@ ALTER TABLE `player` ADD COLUMN `firstseen_round_id` INT(11) UNSIGNED NOT NULL A
----------------------------------------------------
18 August 2017, by Cyberboss and nfreader
Version 3.2, 18 August 2017, by Cyberboss and nfreader
Modified table 'death', adding the columns `last_words` and 'suicide'.
@@ -67,7 +101,7 @@ Remember to add a prefix to the table name if you use them.
----------------------------------------------------
20th July 2017, by Shadowlight213
Version 3.1, 20th July 2017, by Shadowlight213
Added role_time table to track time spent playing departments.
Also, added flags column to the player table.
@@ -79,7 +113,7 @@ Remember to add a prefix to the table name if you use them.
----------------------------------------------------
28 June 2017, by oranges
Version 3.0, 28 June 2017, by oranges
Added schema_revision to store the current db revision, why start at 3.0?
because:
@@ -319,7 +353,7 @@ Remember to add prefix to the table name if you use them.
Modified table 'memo', removing 'id' column and making 'ckey' primary.
ALTER TABLE `memo` DROP COLUMN `id`, DROP PRIMARY KEY, ADD PRIMARY KEY (`ckey`)
ALTER TABLE `memo` DROP COLUMN `id`, DROP PRIMARY KEY, ADD PRIMARY KEY (`ckey`)
Remember to add prefix to the table name if you use them.

View File

@@ -17,13 +17,9 @@ DROP TABLE IF EXISTS `admin`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `admin` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ckey` varchar(32) NOT NULL,
`rank` varchar(32) NOT NULL DEFAULT 'Administrator',
`level` int(2) NOT NULL DEFAULT '0',
`flags` int(16) NOT NULL DEFAULT '0',
`email` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
`rank` varchar(32) NOT NULL,
PRIMARY KEY (`ckey`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
@@ -38,8 +34,9 @@ CREATE TABLE `admin_log` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`datetime` datetime NOT NULL,
`adminckey` varchar(32) NOT NULL,
`adminip` varchar(18) NOT NULL,
`log` text NOT NULL,
`adminip` int(10) unsigned NOT NULL,
`operation` enum('add admin','remove admin','change admin rank','add rank','remove rank','change rank flags') NOT NULL,
`log` varchar(1000) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
@@ -52,11 +49,12 @@ DROP TABLE IF EXISTS `admin_ranks`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `admin_ranks` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`rank` varchar(40) NOT NULL,
`flags` int(16) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=latin1;
`rank` varchar(32) NOT NULL,
`flags` smallint(5) unsigned NOT NULL,
`exclude_flags` smallint(5) unsigned NOT NULL,
`can_edit_flags` smallint(5) unsigned NOT NULL,
PRIMARY KEY (`rank`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -271,11 +269,11 @@ DROP TABLE IF EXISTS `role_time`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `role_time`
CREATE TABLE `role_time`
( `ckey` VARCHAR(32) NOT NULL ,
`job` VARCHAR(32) NOT NULL ,
`minutes` INT UNSIGNED NOT NULL,
PRIMARY KEY (`ckey`, `job`)
PRIMARY KEY (`ckey`, `job`)
) ENGINE = InnoDB;
--

View File

@@ -17,13 +17,9 @@ DROP TABLE IF EXISTS `SS13_admin`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `SS13_admin` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ckey` varchar(32) NOT NULL,
`rank` varchar(32) NOT NULL DEFAULT 'Administrator',
`level` int(2) NOT NULL DEFAULT '0',
`flags` int(16) NOT NULL DEFAULT '0',
`email` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
`rank` varchar(32) NOT NULL,
PRIMARY KEY (`ckey`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
@@ -38,8 +34,9 @@ CREATE TABLE `SS13_admin_log` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`datetime` datetime NOT NULL,
`adminckey` varchar(32) NOT NULL,
`adminip` varchar(18) NOT NULL,
`log` text NOT NULL,
`adminip` int(10) unsigned NOT NULL,
`operation` enum('add admin','remove admin','change admin rank','add rank','remove rank','change rank flags') NOT NULL,
`log` varchar(1000) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
@@ -52,11 +49,12 @@ DROP TABLE IF EXISTS `SS13_admin_ranks`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `SS13_admin_ranks` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`rank` varchar(40) NOT NULL,
`flags` int(16) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=latin1;
`rank` varchar(32) NOT NULL,
`flags` smallint(5) unsigned NOT NULL,
`exclude_flags` smallint(5) unsigned NOT NULL,
`can_edit_flags` smallint(5) unsigned NOT NULL,
PRIMARY KEY (`rank`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -271,11 +269,11 @@ DROP TABLE IF EXISTS `SS13_role_time`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `SS13_role_time`
CREATE TABLE `SS13_role_time`
( `ckey` VARCHAR(32) NOT NULL ,
`job` VARCHAR(32) NOT NULL ,
`minutes` INT UNSIGNED NOT NULL,
PRIMARY KEY (`ckey`, `job`)
PRIMARY KEY (`ckey`, `job`)
) ENGINE = InnoDB;
--

View File

@@ -34,6 +34,7 @@
#define R_SOUNDS 0x800
#define R_SPAWN 0x1000
#define R_AUTOLOGIN 0x2000
#define R_DBRANKS 0x4000
#define R_DEFAULT R_AUTOLOGIN

View File

@@ -1,7 +1,7 @@
//Update this whenever the db schema changes
//make sure you add an update to the schema_version stable in the db changelog
#define DB_MAJOR_VERSION 4
#define DB_MINOR_VERSION 0
#define DB_MINOR_VERSION 1
//Timing subsystem
//Don't run if there is an identical unique timer active

View File

@@ -500,3 +500,28 @@
objective_parts += "<b>Objective #[count]</b>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
count++
return objective_parts.Join("<br>")
/datum/controller/subsystem/ticker/proc/save_admin_data()
if(CONFIG_GET(flag/admin_legacy_system)) //we're already using legacy system so there's nothing to save
return
else if(load_admins()) //returns true if there was a database failure and the backup was loaded from
return
var/datum/DBQuery/query_admin_rank_update = SSdbcore.NewQuery("UPDATE [format_table_name("player")] p INNER JOIN [format_table_name("admin")] a ON p.ckey = a.ckey SET p.lastadminrank = a.rank")
query_admin_rank_update.Execute()
//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 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/i in GLOB.admin_datums+GLOB.deadmins)
var/datum/admins/A = GLOB.admin_datums[i]
if(!A)
A = GLOB.deadmins[i]
if (!A)
continue
file_data["admins"]["[i]"] = A.rank.name
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))

View File

@@ -182,38 +182,40 @@
return ICON_OVERLAY
//Converts a rights bitfield into a string
/proc/rights2text(rights, seperator="", list/adds, list/subs)
/proc/rights2text(rights, seperator="", prefix = "+")
seperator += prefix
if(rights & R_BUILDMODE)
. += "[seperator]+BUILDMODE"
. += "[seperator]BUILDMODE"
if(rights & R_ADMIN)
. += "[seperator]+ADMIN"
. += "[seperator]ADMIN"
if(rights & R_BAN)
. += "[seperator]+BAN"
. += "[seperator]BAN"
if(rights & R_FUN)
. += "[seperator]+FUN"
. += "[seperator]FUN"
if(rights & R_SERVER)
. += "[seperator]+SERVER"
. += "[seperator]SERVER"
if(rights & R_DEBUG)
. += "[seperator]+DEBUG"
. += "[seperator]DEBUG"
if(rights & R_POSSESS)
. += "[seperator]+POSSESS"
. += "[seperator]POSSESS"
if(rights & R_PERMISSIONS)
. += "[seperator]+PERMISSIONS"
. += "[seperator]PERMISSIONS"
if(rights & R_STEALTH)
. += "[seperator]+STEALTH"
. += "[seperator]STEALTH"
if(rights & R_POLL)
. += "[seperator]+POLL"
. += "[seperator]POLL"
if(rights & R_VAREDIT)
. += "[seperator]+VAREDIT"
. += "[seperator]VAREDIT"
if(rights & R_SOUNDS)
. += "[seperator]+SOUND"
. += "[seperator]SOUND"
if(rights & R_SPAWN)
. += "[seperator]+SPAWN"
for(var/verbpath in adds)
. += "[seperator]+[verbpath]"
for(var/verbpath in subs)
. += "[seperator]-[verbpath]"
. += "[seperator]SPAWN"
if(rights & R_AUTOLOGIN)
. += "[seperator]AUTOLOGIN"
if(rights & R_DBRANKS)
. += "[seperator]DBRANKS"
if(!.)
. = "NONE"
return .
/proc/ui_style2icon(ui_style)

View File

@@ -22,5 +22,6 @@ GLOBAL_LIST_EMPTY(player_details) // ckey -> /datum/player_details
GLOBAL_LIST_INIT(bitfields, list(
"obj_flags" = list("EMAGGED" = EMAGGED, "IN_USE" = IN_USE, "CAN_BE_HIT" = CAN_BE_HIT, "BEING_SHOCKED" = BEING_SHOCKED, "DANGEROUS_POSSESSION" = DANGEROUS_POSSESSION, "ON_BLUEPRINTS" = ON_BLUEPRINTS, "UNIQUE_RENAME" = UNIQUE_RENAME),
"datum_flags" = list("DF_USE_TAG" = DF_USE_TAG, "DF_VAR_EDITED" = DF_VAR_EDITED),
"item_flags" = list("BEING_REMOVED" = BEING_REMOVED, "IN_INVENTORY" = IN_INVENTORY, "FORCE_STRING_OVERRIDE" = FORCE_STRING_OVERRIDE, "NEEDS_PERMIT" = NEEDS_PERMIT)
"item_flags" = list("BEING_REMOVED" = BEING_REMOVED, "IN_INVENTORY" = IN_INVENTORY, "FORCE_STRING_OVERRIDE" = FORCE_STRING_OVERRIDE, "NEEDS_PERMIT" = NEEDS_PERMIT),
"admin_flags" = list("BUILDMODE" = R_BUILDMODE, "ADMIN" = R_ADMIN, "BAN" = R_BAN, "FUN" = R_FUN, "SERVER" = R_SERVER, "DEBUG" = R_DEBUG, "POSSESS" = R_POSSESS, "PERMISSIONS" = R_PERMISSIONS, "STEALTH" = R_STEALTH, "POLL" = R_POLL, "VAREDIT" = R_VAREDIT, "SOUNDS" = R_SOUNDS, "SPAWN" = R_SPAWN, "AUTOLOGIN" = R_AUTOLOGIN, "DBRANKS" = R_DBRANKS)
))

View File

@@ -120,6 +120,15 @@
/datum/config_entry/flag/admin_legacy_system //Defines whether the server uses the legacy admin system with admins.txt or the SQL system
protection = CONFIG_ENTRY_LOCKED
/datum/config_entry/flag/protect_legacy_admins //Stops any admins loaded by the legacy system from having their rank edited by the permissions panel
protection = CONFIG_ENTRY_LOCKED
/datum/config_entry/flag/protect_legacy_ranks //Stops any ranks loaded by the legacy system from having their flags edited by the permissions panel
protection = CONFIG_ENTRY_LOCKED
/datum/config_entry/flag/enable_localhost_rank //Gives the !localhost! rank to any client connecting from 127.0.0.1 or ::1
protection = CONFIG_ENTRY_LOCKED
/datum/config_entry/string/hostedby
/datum/config_entry/flag/norespawn

View File

@@ -612,6 +612,7 @@ SUBSYSTEM_DEF(ticker)
/datum/controller/subsystem/ticker/Shutdown()
gather_newscaster() //called here so we ensure the log is created even upon admin reboot
save_admin_data()
if(!round_end_sound)
round_end_sound = pick(\
'sound/roundend/newroundsexy.ogg',

View File

@@ -223,25 +223,27 @@
/datum/browser/modal/listpicker
var/valueslist = list()
/datum/browser/modal/listpicker/New(User,Message,Title,Button1="Ok",Button2,Button3,StealFocus = 1, Timeout = FALSE,list/values,inputtype="checkbox")
/datum/browser/modal/listpicker/New(User,Message,Title,Button1="Ok",Button2,Button3,StealFocus = 1, Timeout = FALSE,list/values,inputtype="checkbox", width, height, slidecolor)
if (!User)
return
var/output = {"<form><input type="hidden" name="src" value="[REF(src)]"><ul class="sparse">"}
if (inputtype == "checkbox" || inputtype == "radio")
for (var/i in values)
var/div_slider = slidecolor
if(!i["allowed_edit"])
div_slider = "locked"
output += {"<li>
<label class="switch">
<input type="[inputtype]" value="1" name="[i["name"]]"[i["checked"] ? " checked" : ""]>
<div class="slider"></div>
<span>[i["name"]]</span>
</label>
</li>"}
<label class="switch">
<input type="[inputtype]" value="1" name="[i["name"]]"[i["checked"] ? " checked" : ""][i["allowed_edit"] ? "" : " onclick='return false' onkeydown='return false'"]>
<div class="slider [div_slider ? "[div_slider]" : ""]"></div>
<span>[i["name"]]</span>
</label>
</li>"}
else
for (var/i in values)
output += {"<li><input id="name="[i["name"]]"" style="width: 50px" type="[type]" name="[i["name"]]" value="[i["value"]]">
<label for="[i["name"]]">[i["name"]]</label></li>"}
<label for="[i["name"]]">[i["name"]]</label></li>"}
output += {"</ul><div style="text-align:center">
<button type="submit" name="button" value="1" style="font-size:large;float:[( Button2 ? "left" : "right" )]">[Button1]</button>"}
@@ -252,7 +254,7 @@
output += {"<button type="submit" name="button" value="3" style="font-size:large;float:right">[Button3]</button>"}
output += {"</form></div>"}
..(User, ckey("[User]-[Message]-[Title]-[world.time]-[rand(1,10000)]"), Title, 350, 350, src, StealFocus, Timeout)
..(User, ckey("[User]-[Message]-[Title]-[world.time]-[rand(1,10000)]"), Title, width, height, src, StealFocus, Timeout)
set_content(output)
/datum/browser/modal/listpicker/Topic(href,href_list)
@@ -272,30 +274,32 @@
opentime = 0
close()
/proc/presentpicker(var/mob/User,Message, Title, Button1="Ok", Button2, Button3, StealFocus = 1,Timeout = 6000,list/values, inputtype = "checkbox")
/proc/presentpicker(var/mob/User,Message, Title, Button1="Ok", Button2, Button3, StealFocus = 1,Timeout = 6000,list/values, inputtype = "checkbox", width, height, slidecolor)
if (!istype(User))
if (istype(User, /client/))
var/client/C = User
User = C.mob
else
return
var/datum/browser/modal/listpicker/A = new(User, Message, Title, Button1, Button2, Button3, StealFocus,Timeout, values, inputtype)
var/datum/browser/modal/listpicker/A = new(User, Message, Title, Button1, Button2, Button3, StealFocus,Timeout, values, inputtype, width, height, slidecolor)
A.open()
A.wait()
if (A.selectedbutton)
return list("button" = A.selectedbutton, "values" = A.valueslist)
/proc/input_bitfield(var/mob/User, title, bitfield, current_value)
/proc/input_bitfield(var/mob/User, title, bitfield, current_value, nwidth = 350, nheight = 350, nslidecolor, allowed_edit_list = null)
if (!User || !(bitfield in GLOB.bitfields))
return
var/list/pickerlist = list()
for (var/i in GLOB.bitfields[bitfield])
var/can_edit = 1
if(!isnull(allowed_edit_list) && !(allowed_edit_list & GLOB.bitfields[bitfield][i]))
can_edit = 0
if (current_value & GLOB.bitfields[bitfield][i])
pickerlist += list(list("checked" = 1, "value" = GLOB.bitfields[bitfield][i], "name" = i))
pickerlist += list(list("checked" = 1, "value" = GLOB.bitfields[bitfield][i], "name" = i, "allowed_edit" = can_edit))
else
pickerlist += list(list("checked" = 0, "value" = GLOB.bitfields[bitfield][i], "name" = i))
var/list/result = presentpicker(User, "", title, Button1="Save", Button2 = "Cancel", Timeout=FALSE, values = pickerlist)
pickerlist += list(list("checked" = 0, "value" = GLOB.bitfields[bitfield][i], "name" = i, "allowed_edit" = can_edit))
var/list/result = presentpicker(User, "", title, Button1="Save", Button2 = "Cancel", Timeout=FALSE, values = pickerlist, width = nwidth, height = nheight, slidecolor = nslidecolor)
if (islist(result))
if (result["button"] == 2) // If the user pressed the cancel button
return

View File

@@ -1,13 +1,17 @@
GLOBAL_LIST_EMPTY(admin_ranks) //list of all admin_rank datums
GLOBAL_PROTECT(admin_ranks)
GLOBAL_LIST_EMPTY(protected_ranks) //admin ranks loaded from txt
GLOBAL_PROTECT(protected_ranks)
/datum/admin_rank
var/name = "NoRank"
var/rights = R_DEFAULT
var/list/adds
var/list/subs
var/exclude_rights = 0
var/include_rights = 0
var/can_edit_rights = 0
/datum/admin_rank/New(init_name, init_rights, list/init_adds, list/init_subs)
/datum/admin_rank/New(init_name, init_rights, init_exclude_rights, init_edit_rights)
if(IsAdminAdvancedProcCall())
var/msg = " has tried to elevate permissions!"
message_admins("[key_name_admin(usr)][msg]")
@@ -17,19 +21,18 @@ GLOBAL_PROTECT(admin_ranks)
CRASH("Admin proc call creation of admin datum")
return
name = init_name
switch(name)
if("Removed",null,"")
QDEL_IN(src, 0)
throw EXCEPTION("invalid admin-rank name")
return
if(!name)
qdel(src)
throw EXCEPTION("Admin rank created without name.")
return
if(init_rights)
rights = init_rights
if(!init_adds)
init_adds = list()
if(!init_subs)
init_subs = list()
adds = init_adds
subs = init_subs
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
/datum/admin_rank/Destroy()
if(IsAdminAdvancedProcCall())
@@ -78,6 +81,8 @@ GLOBAL_PROTECT(admin_ranks)
flag = R_SPAWN
if("autologin", "autoadmin")
flag = R_AUTOLOGIN
if("dbranks")
flag = R_DBRANKS
if("@","prev")
flag = previous_rights
if("rejuv","rejuvinate")
@@ -85,9 +90,6 @@ GLOBAL_PROTECT(admin_ranks)
flag = R_ADMIN
return flag
/proc/admin_keyword_to_path(word) //use this with verb keywords eg +/client/proc/blah
return text2path(copytext(word, 2, findtext(word, " ", 2, 0)))
// Adds/removes rights to this admin_rank
/datum/admin_rank/proc/process_keyword(word, previous_rights=0)
if(IsAdminAdvancedProcCall())
@@ -100,157 +102,156 @@ GLOBAL_PROTECT(admin_ranks)
switch(text2ascii(word,1))
if(43)
rights |= flag //+
include_rights |= flag
if(45)
rights &= ~flag //-
else
//isn't a keyword so maybe it's a verbpath?
var/path = admin_keyword_to_path(word)
if(path)
switch(text2ascii(word,1))
if(43)
if(!subs.Remove(path))
adds += path //+
if(45)
if(!adds.Remove(path))
subs += path //-
exclude_rights |= flag
if(42)
can_edit_rights |= flag //*
// Checks for (keyword-formatted) rights on this admin
/datum/admins/proc/check_keyword(word)
var/flag = admin_keyword_to_flag(word)
if(flag)
return ((rank.rights & flag) == flag) //true only if right has everything in flag
else
var/path = admin_keyword_to_path(word)
for(var/i in owner.verbs) //this needs to be a foreach loop for some reason. in operator and verbs.Find() don't work
if(i == path)
return 1
return 0
//load our rank - > rights associations
/proc/load_admin_ranks()
/proc/load_admin_ranks(dbfail)
if(IsAdminAdvancedProcCall())
to_chat(usr, "<span class='admin prefix'>Admin Reload blocked: Advanced ProcCall detected.</span>")
return
GLOB.admin_ranks.Cut()
if(CONFIG_GET(flag/admin_legacy_system))
var/previous_rights = 0
//load text from file and process each line separately
for(var/line in world.file2list("[global.config.directory]/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
GLOB.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
if(!SSdbcore.Connect())
if(CONFIG_GET(flag/sql_enabled))
var/msg = "Failed to connect to database in load_admin_ranks(). Reverting to legacy system."
log_world(msg)
WRITE_FILE(GLOB.world_game_log, msg)
CONFIG_SET(flag/admin_legacy_system, TRUE)
load_admin_ranks()
return
var/datum/DBQuery/query_load_admin_ranks = SSdbcore.NewQuery("SELECT rank, flags FROM [format_table_name("admin_ranks")]")
GLOB.protected_ranks.Cut()
var/previous_rights = 0
//load text from file and process each line separately
for(var/line in world.file2list("[global.config.directory]/admin_ranks.txt"))
if(!line || findtextEx(line,"#",1,2))
continue
var/next = findtext(line, "=")
var/datum/admin_rank/R = new(ckeyEx(copytext(line, 1, next)))
if(!R)
continue
GLOB.admin_ranks += R
GLOB.protected_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
if(!CONFIG_GET(flag/admin_legacy_system) || dbfail)
var/datum/DBQuery/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 = 1
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
//load ranks from backup file
if(dbfail)
var/backup_file = file("data/admins_backup.json")
if(!fexists(backup_file))
log_world("Unable to locate admins backup file.")
return
while(query_load_admin_ranks.NextRow())
var/rank_name = ckeyEx(query_load_admin_ranks.item[1])
var/flags = query_load_admin_ranks.item[2]
if(istext(flags))
flags = text2num(flags)
var/datum/admin_rank/R = new(rank_name, flags)
var/list/json = json_decode(file2text(backup_file))
for(var/J in json["ranks"])
for(var/datum/admin_rank/R in GLOB.admin_ranks)
if(R.name == "[J]") //this rank was already loaded from txt override
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 1
#ifdef TESTING
var/msg = "Permission Sets Built:\n"
for(var/datum/admin_rank/R in GLOB.admin_ranks)
msg += "\t[R.name]"
var/rights = rights2text(R.rights,"\n\t\t",R.adds,R.subs)
var/rights = rights2text(R.rights,"\n\t\t")
if(rights)
msg += "\t\t[rights]\n"
testing(msg)
#endif
/proc/load_admins()
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 = 1
//clear the datums references
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()
load_admin_ranks()
dbfail = load_admin_ranks(dbfail)
//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
if(CONFIG_GET(flag/admin_legacy_system))
//load text from file
var/list/lines = world.file2list("[global.config.directory]/admins.txt")
//process each line separately
for(var/line in lines)
if(!length(line))
continue
if(findtextEx(line, "#", 1, 2))
continue
var/list/entry = splittext(line, "=")
if(entry.len < 2)
continue
var/ckey = ckey(entry[1])
var/rank = ckeyEx(entry[2])
if(!ckey || !rank)
continue
new /datum/admins(rank_names[rank], ckey)
else
if(!SSdbcore.Connect())
log_world("Failed to connect to database in load_admins(). Reverting to legacy system.")
WRITE_FILE(GLOB.world_game_log, "Failed to connect to database in load_admins(). Reverting to legacy system.")
CONFIG_SET(flag/admin_legacy_system, TRUE)
load_admins()
return
//ckeys listed in admins.txt are always made admins before sql loading is attempted
var/list/lines = world.file2list("[global.config.directory]/admins.txt")
for(var/line in lines)
if(!length(line) || findtextEx(line, "#", 1, 2))
continue
var/list/entry = splittext(line, "=")
if(entry.len < 2)
continue
var/ckey = ckey(entry[1])
var/rank = ckeyEx(entry[2])
if(!ckey || !rank)
continue
new /datum/admins(rank_names[rank], ckey, 0, 1)
if(!CONFIG_GET(flag/admin_legacy_system) || dbfail)
var/datum/DBQuery/query_load_admins = SSdbcore.NewQuery("SELECT ckey, rank FROM [format_table_name("admin")]")
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 = query_load_admins.item[1]
var/admin_rank = query_load_admins.item[2]
var/skip
if(rank_names[admin_rank] == null)
message_admins("[admin_ckey] loaded with invalid admin rank [admin_rank].")
log_sql("[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)
new /datum/admins(rank_names[admin_rank], admin_ckey)
//load admins from backup file
if(dbfail)
var/backup_file = file("data/admins_backup.json")
if(!fexists(backup_file))
log_world("Unable to locate admins backup file.")
return
while(query_load_admins.NextRow())
var/ckey = ckey(query_load_admins.item[1])
var/rank = ckeyEx(query_load_admins.item[2])
if(rank_names[rank] == null)
WARNING("Admin rank ([rank]) does not exist.")
continue
new /datum/admins(rank_names[rank], ckey)
var/list/json = json_decode(file2text(backup_file))
for(var/J in json["admins"])
for(var/A in GLOB.admin_datums + GLOB.deadmins)
if(A == "[J]") //this admin was already loaded from txt override
continue
new /datum/admins(rank_names[json["admins"]["[J]"]], "[J]")
#ifdef TESTING
var/msg = "Admins Built:\n"
for(var/ckey in GLOB.admin_datums)
@@ -258,7 +259,7 @@ GLOBAL_PROTECT(admin_ranks)
msg += "\t[ckey] - [D.rank.name]\n"
testing(msg)
#endif
return dbfail
#ifdef TESTING
/client/verb/changerank(newrank in GLOB.admin_ranks)
@@ -277,149 +278,3 @@ GLOBAL_PROTECT(admin_ranks)
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
if(IsAdminAdvancedProcCall())
to_chat(usr, "<span class='admin prefix'>Admin Edit blocked: Advanced ProcCall detected.</span>")
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 GLOB.admin_datums)
to_chat(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)
to_chat(usr, "<font color='red'>Error: Topic 'editrights': No valid ckey</font>")
return
var/datum/admins/D = GLOB.admin_datums[adm_ckey]
if (!D)
D = GLOB.deadmins[adm_ckey]
switch(task)
if("remove")
if(alert("Are you sure you want to remove [adm_ckey]?","Message","Yes","Cancel") == "Yes")
if(!D)
return
if(!check_if_greater_rights_than_holder(D))
message_admins("[key_name_admin(usr)] attempted to remove [adm_ckey] from the admins list without sufficient rights.")
log_admin("[key_name(usr)] attempted to remove [adm_ckey] from the admins list without sufficient rights.")
return
GLOB.admin_datums -= adm_ckey
GLOB.deadmins -= adm_ckey
D.disassociate()
updateranktodb(adm_ckey, "player")
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 GLOB.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
if(D)
if(!check_if_greater_rights_than_holder(D))
message_admins("[key_name_admin(usr)] attempted to change the rank of [adm_ckey] to [new_rank] without sufficient rights.")
log_admin("[key_name(usr)] attempted to change the rank of [adm_ckey] to [new_rank] without sufficient rights.")
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
GLOB.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
D.associate()
else
D = new(R, adm_ckey, TRUE) //new admin
updateranktodb(adm_ckey, new_rank)
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
if(!check_keyword(keyword) || !check_if_greater_rights_than_holder(D))
message_admins("[key_name_admin(usr)] attempted to give [adm_ckey] the keyword [keyword] without sufficient rights.")
log_admin("[key_name(usr)] attempted to give [adm_ckey] the keyword [keyword] without sufficient rights.")
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 = 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(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)
if("activate") //forcefully readmin
if(!D || !D.deadmined)
return
D.activate()
message_admins("[key_name_admin(usr)] forcefully readmined [adm_ckey]")
log_admin("[key_name(usr)] forcefully readmined [adm_ckey]")
if("deactivate") //forcefully deadmin
if(!D || D.deadmined)
return
message_admins("[key_name_admin(usr)] forcefully deadmined [adm_ckey]")
log_admin("[key_name(usr)] forcefully deadmined [adm_ckey]")
D.deactivate() //after logs so the deadmined admin can see the message.
edit_admin_permissions()
/datum/admins/proc/updateranktodb(ckey,newrank)
if(!SSdbcore.Connect())
return
var/sql_ckey = sanitizeSQL(ckey)
var/sql_admin_rank = sanitizeSQL(newrank)
var/datum/DBQuery/query_admin_rank_update = SSdbcore.NewQuery("UPDATE [format_table_name("player")] SET lastadminrank = '[sql_admin_rank]' WHERE ckey = '[sql_ckey]'")
query_admin_rank_update.Execute()

View File

@@ -265,11 +265,6 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list(
if(rights & R_SPAWN)
verbs += GLOB.admin_verbs_spawn
for(var/path in holder.rank.adds)
verbs += path
for(var/path in holder.rank.subs)
verbs -= path
/client/proc/remove_admin_verbs()
verbs.Remove(
GLOB.admin_verbs_default,
@@ -304,8 +299,6 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list(
/client/proc/cmd_admin_areatest_station,
/client/proc/readmin
)
if(holder)
verbs.Remove(holder.rank.adds)
/client/proc/hide_most_verbs()//Allows you to keep some functionality while hiding some verbs
set name = "Adminverbs - Hide Most"

View File

@@ -1,5 +1,7 @@
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)
@@ -26,7 +28,7 @@ GLOBAL_PROTECT(href_token)
var/deadmined
/datum/admins/New(datum/admin_rank/R, ckey, force_active = FALSE)
/datum/admins/New(datum/admin_rank/R, ckey, force_active = FALSE, protected)
if(IsAdminAdvancedProcCall())
var/msg = " has tried to elevate permissions!"
message_admins("[key_name_admin(usr)][msg]")
@@ -51,6 +53,8 @@ GLOBAL_PROTECT(href_token)
if(R.rights & R_DEBUG) //grant profile access
world.SetConfig("APP/admin", ckey, "role=admin")
//only admins with +ADMIN start admined
if(protected)
GLOB.protected_admins[target] = src
if (force_active || (R.rights & R_AUTOLOGIN))
activate()
else

View File

@@ -0,0 +1,274 @@
/client/proc/edit_admin_permissions()
set category = "Admin"
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/list/output = list({"<!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:150px;text-align:right;'>CKEY <a class='small' href='?src=[REF(src)];[HrefToken()];editrights=add'>\[+\]</a></th>
<th style='width:125px;'>RANK</th>
<th style='width:40%;'>PERMISSIONS</th>
<th style='width:20%;'>DENIED</th>
<th style='width:40%;'>ALLOWED TO EDIT</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.deadmined)
deadminlink = " <a class='small' href='?src=[REF(src)];[HrefToken()];editrights=activate;ckey=[adm_ckey]'>\[RA\]</a>"
else
deadminlink = " <a class='small' href='?src=[REF(src)];[HrefToken()];editrights=deactivate;ckey=[adm_ckey]'>\[DA\]</a>"
output += "<tr>"
output += "<td style='text-align:right;'>[adm_ckey] [deadminlink]<a class='small' href='?src=[REF(src)];[HrefToken()];editrights=remove;ckey=[adm_ckey]'>\[-\]</a></td>"
output += "<td><a href='?src=[REF(src)];[HrefToken()];editrights=rank;ckey=[adm_ckey]'>[D.rank.name]</a></td>"
output += "<td><a class='small' href='?src=[REF(src)];[HrefToken()];editrights=permissions;ckey=[adm_ckey]'>[rights2text(D.rank.include_rights," ")]</a></td>"
output += "<td><a class='small' href='?src=[REF(src)];[HrefToken()];editrights=permissions;ckey=[adm_ckey]'>[rights2text(D.rank.exclude_rights," ", "-")]</a></td>"
output += "<td><a class='small' href='?src=[REF(src)];[HrefToken()];editrights=permissions;ckey=[adm_ckey]'>[rights2text(D.rank.can_edit_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(jointext(output, ""),"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>")
return
var/datum/asset/permissions_assets = get_asset_datum(/datum/asset/simple/permissions)
permissions_assets.send(src)
var/admin_ckey = ckey(href_list["ckey"])
var/datum/admins/D = GLOB.admin_datums[admin_ckey]
var/use_db
var/task = href_list["editrights"]
var/skip
if(task == "activate" || task == "deactivate")
skip = 1
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>")
return
if(!CONFIG_GET(flag/admin_legacy_system) && CONFIG_GET(flag/protect_legacy_ranks) && task == "permissions")
if(D.rank in GLOB.protected_ranks)
to_chat(usr, "<span class='admin prefix'>Editing the flags of this rank is blocked by server configuration.</span>")
return
if(check_rights(R_DBRANKS, 0))
if(!skip)
if(!SSdbcore.Connect())
to_chat(usr, "<span class='danger'>Unable to connect to database, changes are temporary only.</span>")
use_db = "Temporary"
if(!use_db)
use_db = alert("Permanent changes are saved to the database for future rounds, temporary changes will affect only the current round", "Permanent or Temporary?", "Permanent", "Temporary", "Cancel")
if(use_db == "Cancel")
return
if(use_db == "Permanent")
use_db = 1
admin_ckey = sanitizeSQL(admin_ckey)
else
use_db = 0
if(task != "add")
D = GLOB.admin_datums[admin_ckey]
if(!D)
D = GLOB.deadmins[admin_ckey]
if(!D)
return
if(!check_if_greater_rights_than_holder(D))
message_admins("[key_name_admin(usr)] attempted to change the rank of [admin_ckey] without sufficient rights.")
log_admin("[key_name(usr)] attempted to change the rank of [admin_ckey] without sufficient rights.")
switch(task)
if("add")
admin_ckey = add_admin(use_db)
if(!admin_ckey)
return
change_admin_rank(admin_ckey, use_db)
if("remove")
remove_admin(admin_ckey, use_db, D)
if("rank")
change_admin_rank(admin_ckey, use_db, D)
if("permissions")
change_admin_flags(admin_ckey, use_db, D)
if("activate")
force_readmin(admin_ckey, D)
if("deactivate")
force_deadmin(admin_ckey, D)
edit_admin_permissions()
/datum/admins/proc/add_admin(use_db)
. = sanitizeSQL(ckey(input("New admin's ckey","Admin ckey") as text|null))
if(!.)
return 0
if(. in GLOB.admin_datums+GLOB.deadmins)
to_chat(usr, "<span class='danger'>[.] is already an admin.</span>")
return 0
if(use_db)
var/datum/DBQuery/query_add_admin = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin")] (ckey, rank) VALUES ('[.]', 'NEW ADMIN')")
if(!query_add_admin.warn_execute())
return 0
var/datum/DBQuery/query_add_admin_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, adminckey, adminip, operation, log) VALUES ('[SQLtime()]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'add admin', 'New admin added: [.]')")
if(!query_add_admin_log.warn_execute())
return 0
/datum/admins/proc/remove_admin(admin_ckey, use_db, datum/admins/D)
if(alert("Are you sure you want to remove [admin_ckey]?","Confirm Removal","Do it","Cancel") == "Do it")
GLOB.admin_datums -= admin_ckey
GLOB.deadmins -= admin_ckey
D.disassociate()
if(use_db)
var/datum/DBQuery/query_add_rank = SSdbcore.NewQuery("DELETE FROM [format_table_name("admin")] WHERE ckey = '[admin_ckey]'")
if(!query_add_rank.warn_execute())
return
var/datum/DBQuery/query_add_rank_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, adminckey, adminip, operation, log) VALUES ('[SQLtime()]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'remove admin', 'Admin removed: [admin_ckey]')")
if(!query_add_rank_log.warn_execute())
return
message_admins("[key_name_admin(usr)] removed [admin_ckey] from the admins list [use_db ? "permanently" : "temporarily"]")
log_admin("[key_name(usr)] removed [admin_ckey] from the admins list [use_db ? "permanently" : "temporarily"]")
/datum/admins/proc/force_readmin(admin_ckey, datum/admins/D)
if(!D || !D.deadmined)
return
D.activate()
message_admins("[key_name_admin(usr)] forcefully readmined [admin_ckey]")
log_admin("[key_name(usr)] forcefully readmined [admin_ckey]")
/datum/admins/proc/force_deadmin(admin_ckey, datum/admins/D)
if(!D || D.deadmined)
return
message_admins("[key_name_admin(usr)] forcefully deadmined [admin_ckey]")
log_admin("[key_name(usr)] forcefully deadmined [admin_ckey]")
D.deactivate() //after logs so the deadmined admin can see the message.
/datum/admins/proc/change_admin_rank(admin_ckey, use_db, datum/admins/D)
var/datum/admin_rank/R
var/list/rank_names = list("*New Rank*")
for(R in GLOB.admin_ranks)
if((R.rights & usr.client.holder.rank.can_edit_rights) == R.rights)
rank_names[R.name] = R
var/new_rank = input("Please select a rank", "New rank") as null|anything in rank_names
if(new_rank == "*New Rank*")
new_rank = sanitizeSQL(ckeyEx(input("Please input a new rank", "New custom rank") as text|null))
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) //duplicate our previous admin_rank but with a new name
else
R = new(new_rank) //blank new admin_rank
GLOB.admin_ranks += R
if(use_db)
if(!R)
var/datum/DBQuery/query_add_rank = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_ranks")] (rank, flags, exclude_flags, can_edit_rights) VALUES ('[new_rank]', '0', '0', '0')")
if(!query_add_rank.warn_execute())
return
var/datum/DBQuery/query_add_rank_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, adminckey, adminip, operation, log) VALUES ('[SQLtime()]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'add rank', 'New rank added: [admin_ckey]')")
if(!query_add_rank_log.warn_execute())
return
var/old_rank
var/datum/DBQuery/query_get_rank = SSdbcore.NewQuery("SELECT rank FROM [format_table_name("admin")] WHERE ckey = '[admin_ckey]'")
if(!query_get_rank.warn_execute())
return
if(query_get_rank.NextRow())
old_rank = query_get_rank.item[1]
var/datum/DBQuery/query_change_rank = SSdbcore.NewQuery("UPDATE [format_table_name("admin")] SET rank = '[new_rank]' WHERE ckey = '[admin_ckey]'")
if(!query_change_rank.warn_execute())
return
var/datum/DBQuery/query_change_rank_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, adminckey, adminip, operation, log) VALUES ('[SQLtime()]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'change admin rank', 'Rank of [admin_ckey] changed from [old_rank] to [new_rank]')")
if(!query_change_rank_log.warn_execute())
return
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
D.associate()
else
D = new(R, admin_ckey, TRUE) //new admin
message_admins("[key_name_admin(usr)] edited the admin rank of [admin_ckey] to [new_rank] [use_db ? "permanently" : "temporarily"]")
log_admin("[key_name(usr)] edited the admin rank of [admin_ckey] to [new_rank] [use_db ? "permanently" : "temporarily"]")
/datum/admins/proc/change_admin_flags(admin_ckey, use_db, datum/admins/D)
var/new_flags = input_bitfield(usr, "Include permission flags<br>[use_db ? "This will affect ALL admins with this rank." : "This will affect only the current admin [admin_ckey]"]", "admin_flags", D.rank.include_rights, 350, 590, allowed_edit_list = usr.client.holder.rank.can_edit_rights)
if(isnull(new_flags))
return
var/new_exclude_flags = input_bitfield(usr, "Exclude permission flags<br>Flags enabled here will be removed from a rank.<br>Note these take precedence over included flags.<br>[use_db ? "This will affect ALL admins with this rank." : "This will affect only the current admin [admin_ckey]"]", "admin_flags", D.rank.exclude_rights, 350, 660, "red", usr.client.holder.rank.can_edit_rights)
if(isnull(new_exclude_flags))
return
var/new_can_edit_flags = input_bitfield(usr, "Editable permission flags<br>These are the flags this rank is allowed to edit if they have access to the permissions panel.<br>They will be unable to modify admins to a rank that has a flag not included here.<br>[use_db ? "This will affect ALL admins with this rank." : "This will affect only the current admin [admin_ckey]"]", "admin_flags", D.rank.can_edit_rights, 350, 710, allowed_edit_list = usr.client.holder.rank.can_edit_rights)
if(isnull(new_can_edit_flags))
return
if(use_db)
var/old_flags
var/old_exclude_flags
var/old_can_edit_flags
var/datum/DBQuery/query_get_rank_flags = SSdbcore.NewQuery("SELECT flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")] WHERE rank = '[D.rank.name]'")
if(!query_get_rank_flags.warn_execute())
return
if(query_get_rank_flags.NextRow())
old_flags = text2num(query_get_rank_flags.item[1])
old_exclude_flags = text2num(query_get_rank_flags.item[2])
old_can_edit_flags = text2num(query_get_rank_flags.item[3])
var/datum/DBQuery/query_change_rank_flags = SSdbcore.NewQuery("UPDATE [format_table_name("admin_ranks")] SET flags = '[new_flags]', exclude_flags = '[new_exclude_flags]', can_edit_flags = '[new_can_edit_flags]' WHERE rank = '[D.rank.name]'")
if(!query_change_rank_flags.warn_execute())
return
var/datum/DBQuery/query_change_rank_flags_log = SSdbcore.NewQuery("INSERT INTO [format_table_name("admin_log")] (datetime, adminckey, adminip, operation, log) VALUES ('[SQLtime()]', '[sanitizeSQL(usr.ckey)]', INET_ATON('[sanitizeSQL(usr.client.address)]'), 'change rank flags', 'Permissions of [admin_ckey] changed from[rights2text(old_flags," ")][rights2text(old_exclude_flags," ", "-")][rights2text(old_can_edit_flags," ", "*")] to[rights2text(new_flags," ")][rights2text(new_exclude_flags," ", "-")][rights2text(new_can_edit_flags," ", "*")]')")
if(!query_change_rank_flags_log.warn_execute())
return
for(var/datum/admin_rank/R in GLOB.admin_ranks)
if(R.name != D.rank.name)
continue
R.rights = new_flags &= ~new_exclude_flags
R.exclude_rights = new_exclude_flags
R.include_rights = new_flags
R.can_edit_rights = new_can_edit_flags
for(var/i in GLOB.admin_datums+GLOB.deadmins)
var/datum/admins/A = GLOB.admin_datums[i]
if(!A)
A = GLOB.deadmins[i]
if (!A)
continue
if(A.rank.name != D.rank.name)
continue
var/client/C = GLOB.directory[A.target]
A.disassociate()
A.associate(C)
else
D.disassociate()
if(!findtext(D.rank.name, "([admin_ckey])")) //not a modified subrank, need to duplicate the admin_rank datum to prevent modifying others too
D.rank = new("[D.rank.name]([admin_ckey])", new_flags, new_exclude_flags, new_can_edit_flags) //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
else
D.rank.rights = new_flags &= ~new_exclude_flags
D.rank.include_rights = new_flags
D.rank.exclude_rights = new_exclude_flags
var/client/C = GLOB.directory[admin_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 permissions of [use_db ? " rank [D.rank.name] permanently" : "[admin_ckey] temporarily"]")
log_admin("[key_name(usr)] edited the permissions of [use_db ? " rank [D.rank.name] permanently" : "[admin_ckey] temporarily"]")

View File

@@ -1,145 +0,0 @@
/client/proc/edit_admin_permissions()
set category = "Admin"
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/list/output = list({"<!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='?src=[REF(src)];[HrefToken()];editrights=add'>\[+\]</a></th>
<th style='width:125px;'>RANK</th>
<th style='width:375px;'>PERMISSIONS</th>
<th style='width:100%;'>VERB-OVERRIDES</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/rights = rights2text(D.rank.rights," ")
if(!rights)
rights = "*none*"
var/deadminlink = ""
if (D.deadmined)
deadminlink = " <a class='small' href='?src=[REF(src)];[HrefToken()];editrights=activate;ckey=[adm_ckey]'>\[RA\]</a>"
else
deadminlink = " <a class='small' href='?src=[REF(src)];[HrefToken()];editrights=deactivate;ckey=[adm_ckey]'>\[DA\]</a>"
output += "<tr>"
output += "<td style='text-align:right;'>[adm_ckey] [deadminlink]<a class='small' href='?src=[REF(src)];[HrefToken()];editrights=remove;ckey=[adm_ckey]'>\[-\]</a></td>"
output += "<td><a href='?src=[REF(src)];[HrefToken()];editrights=rank;ckey=[adm_ckey]'>[D.rank.name]</a></td>"
output += "<td><a class='small' href='?src=[REF(src)];[HrefToken()];editrights=permissions;ckey=[adm_ckey]'>[rights]</a></td>"
output += "<td><a class='small' href='?src=[REF(src)];[HrefToken()];editrights=permissions;ckey=[adm_ckey]'>[rights2text(0," ",D.rank.adds,D.rank.subs)]</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(jointext(output, ""),"window=editrights;size=900x650")
/datum/admins/proc/log_admin_rank_modification(adm_ckey, new_rank)
if(CONFIG_GET(flag/admin_legacy_system))
return
if(!usr.client)
return
if (!check_rights(R_PERMISSIONS))
return
if(!SSdbcore.Connect())
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
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/DBQuery/query_get_admin = SSdbcore.NewQuery("SELECT id FROM [format_table_name("admin")] WHERE ckey = '[adm_ckey]'")
if(!query_get_admin.warn_execute())
return
var/new_admin = 1
var/admin_id
while(query_get_admin.NextRow())
new_admin = 0
admin_id = text2num(query_get_admin.item[1])
if(new_admin)
var/datum/DBQuery/query_add_admin = SSdbcore.NewQuery("INSERT INTO `[format_table_name("admin")]` (`id`, `ckey`, `rank`, `level`, `flags`) VALUES (null, '[adm_ckey]', '[new_rank]', -1, 0)")
if(!query_add_admin.warn_execute())
return
var/datum/DBQuery/query_add_admin_log = SSdbcore.NewQuery("INSERT INTO `[format_table_name("admin_log")]` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Added new admin [adm_ckey] to rank [new_rank]');")
if(!query_add_admin_log.warn_execute())
return
to_chat(usr, "<span class='adminnotice'>New admin added.</span>")
else
if(!isnull(admin_id) && isnum(admin_id))
var/datum/DBQuery/query_change_admin = SSdbcore.NewQuery("UPDATE `[format_table_name("admin")]` SET rank = '[new_rank]' WHERE id = [admin_id]")
if(!query_change_admin.warn_execute())
return
var/datum/DBQuery/query_change_admin_log = SSdbcore.NewQuery("INSERT INTO `[format_table_name("admin_log")]` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Edited the rank of [adm_ckey] to [new_rank]');")
if(!query_change_admin_log.warn_execute())
return
to_chat(usr, "<span class='adminnnotice'>Admin rank changed.</span>")
/datum/admins/proc/log_admin_permission_modification(adm_ckey, new_permission)
if(CONFIG_GET(flag/admin_legacy_system))
return
if(!usr.client)
return
if(check_rights(R_PERMISSIONS))
return
if(!SSdbcore.Connect())
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
return
if(!adm_ckey || !istext(adm_ckey) || !isnum(new_permission))
return
var/datum/DBQuery/query_get_perms = SSdbcore.NewQuery("SELECT id, flags FROM [format_table_name("admin")] WHERE ckey = '[adm_ckey]'")
if(!query_get_perms.warn_execute())
return
var/admin_id
while(query_get_perms.NextRow())
admin_id = text2num(query_get_perms.item[1])
if(!admin_id)
return
var/datum/DBQuery/query_change_perms = SSdbcore.NewQuery("UPDATE `[format_table_name("admin")]` SET flags = [new_permission] WHERE id = [admin_id]")
if(!query_change_perms.warn_execute())
return
var/datum/DBQuery/query_change_perms_log = SSdbcore.NewQuery("INSERT INTO `[format_table_name("admin_log")]` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Edit permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]');")
query_change_perms_log.warn_execute()

View File

@@ -97,7 +97,7 @@ You can set verify to TRUE if you want send() to sleep until the client has the
if(!verify) // Can't access the asset cache browser, rip.
client.cache += unreceived
return 1
client.sending |= unreceived
var/job = ++client.last_asset_job
@@ -135,7 +135,7 @@ You can set verify to TRUE if you want send() to sleep until the client has the
else
concurrent_tracker++
send_asset(client, file, verify=FALSE)
stoplag(0) //queuing calls like this too quickly can cause issues in some client versions
//This proc "registers" an asset, it adds it to the cache for further use, you cannot touch it from this point on or you'll fuck things up.
@@ -350,6 +350,11 @@ GLOBAL_LIST_EMPTY(asset_datums)
"browserOutput.css" = 'code/modules/goonchat/browserassets/css/browserOutput.css',
)
/datum/asset/simple/permissions
assets = list(
"padlock.png" = 'html/padlock.png'
)
//this exists purely to avoid meta by pre-loading all language icons.
/datum/asset/language/register()
for(var/path in typesof(/datum/language))

View File

@@ -6,7 +6,7 @@
GLOBAL_LIST_INIT(blacklisted_builds, list(
"1407" = "bug preventing client display overrides from working leads to clients being able to see things/mobs they shouldn't be able to see",
"1408" = "bug preventing client display overrides from working leads to clients being able to see things/mobs they shouldn't be able to see",
))
#define LIMITER_SIZE 5
@@ -162,12 +162,14 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
GLOB.ahelp_tickets.ClientLogin(src)
var/connecting_admin = FALSE //because de-admined admins connecting should be treated like admins.
//Admin Authorisation
var/localhost_addresses = list("127.0.0.1", "::1")
if(address && (address in localhost_addresses))
var/datum/admin_rank/localhost_rank = new("!localhost!", 65535)
if(localhost_rank)
var/datum/admins/localhost_holder = new(localhost_rank, ckey)
localhost_holder.associate(src)
holder = GLOB.admin_datums[ckey]
if(holder)
GLOB.admins |= src
holder.owner = src
connecting_admin = TRUE
else if(GLOB.deadmins[ckey])
verbs += /client/proc/readmin
connecting_admin = TRUE
if(CONFIG_GET(flag/autoadmin))
if(!GLOB.admin_datums[ckey])
var/datum/admin_rank/autorank
@@ -178,18 +180,12 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
if(!autorank)
to_chat(world, "Autoadmin rank not found")
else
var/datum/admins/D = new(autorank, ckey)
GLOB.admin_datums[ckey] = D
holder = GLOB.admin_datums[ckey]
if(holder)
GLOB.admins |= src
holder.owner = src
connecting_admin = TRUE
else if(GLOB.deadmins[ckey])
verbs += /client/proc/readmin
connecting_admin = TRUE
new /datum/admins(autorank, ckey)
if(CONFIG_GET(flag/enable_localhost_rank) && !connecting_admin)
var/localhost_addresses = list("127.0.0.1", "::1")
if(address && (address in localhost_addresses))
var/datum/admin_rank/localhost_rank = new("!localhost!", 65535, 16384, 65535) //+EVERYTHING -DBRANKS *EVERYTHING
new /datum/admins(localhost_rank, ckey, 1, 1)
//preferences datum - also holds some persistent data for the client (because we may as well keep these datums to a minimum)
prefs = GLOB.preferences_datums[ckey]
if(!prefs)
@@ -239,7 +235,7 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
to_chat(src, "<span class='danger'>Please download a new version of byond. if [byond_build] is the latest, you can go to http://www.byond.com/download/build/ to download other versions.</span>")
if(connecting_admin)
to_chat(src, "As an admin, you are being allowed to continue using this version, but please consider changing byond versions")
else
else
qdel(src)
return
#endif
@@ -537,10 +533,10 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
tokens[ckey] = cid_check_reconnect()
sleep(15 SECONDS) //Longer sleep here since this would trigger if a client tries to reconnect manually because the inital reconnect failed
//we sleep after telling the client to reconnect, so if we still exist something is up
log_access("Forced disconnect: [key] [computer_id] [address] - CID randomizer check")
qdel(src)
return TRUE
@@ -583,10 +579,10 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
tokens[ckey] = cid_check_reconnect()
sleep(5 SECONDS) //browse is queued, we don't want them to disconnect before getting the browse() command.
//we sleep after telling the client to reconnect, so if we still exist something is up
log_access("Forced disconnect: [key] [computer_id] [address] - CID randomizer check")
qdel(src)
return TRUE

View File

@@ -4,12 +4,13 @@
# Rank is CASE-SENSITIVE, all punctuation save for '-', '_' and '@' will be stripped so spaces don't matter. #
# You can then define permissions for each rank by adding a '=' followed by keywords #
# These keywords represent groups of verbs and abilities. #
# keywords are preceded by either a '+' or a '-', + adds permissions, - takes them away. #
# keywords are preceded by a '+', '-' or '*' + adds permissions, - takes them away. #
# * is used only with SQL-based admin loading to denote what permissions the rank is allowed to edit #
# +@ (or +prev) is a special shorthand which adds all the rights of the rank above it. #
# You can also specify verbs like so +/client/proc/some_added_verb or -/client/proc/some_restricted_verb #
# Ranks with no keywords will just be given the most basic verbs and abilities ~Carn #
##############################################################################################################
# PLEASE NOTE: depending on config options, some abilities will be unavailable regardless if you have permission to use them!
# If SQL-based admin loading is enabled, ranks and their keywords listed here will be loaded first and override any with the same name loaded from the database.
# Follow the format below when documenting new keywords so the server tools may parse it
@@ -28,6 +29,7 @@
# +SOUND (or +SOUNDS) = allows you to upload and play sounds
# +SPAWN (or +CREATE) = mob transformations, spawning of most atoms including mobs (high-risk atoms, e.g. blackholes, will require the +FUN flag too)
# +AUTOLOGIN = admin gains powers upon connect. This defaults to on, you can use -AUTOLOGIN to make a role require using the readmin verb to gain powers. (this does not effect the admin's ability to walk past bans or other on-connect limitations like panic bunker or pop limit.)
# +DBRANKS = when sql-based admin loading is enabled, allows for non-temporary changes in the permissions panel to be saved (requires DB)
# +EVERYTHING (or +HOST or +ALL) = Simply gives you everything without having to type every flag
# END_KEYWORDS
@@ -37,9 +39,9 @@ Admin Candidate = +@
Trial Admin = +@ +SPAWN +VAREDIT +BAN
Badmin = +@ +POSSESS +POLL +BUILDMODE +SERVER +FUN
Game Admin = +@ +STEALTH +SOUNDS +DEBUG
Game Master = +EVERYTHING
Lazy Master = +EVERYTHING -AUTOLOGIN
Game Master = +EVERYTHING *EVERYTHING
Lazy Master = +EVERYTHING -AUTOLOGIN *EVERYTHING
Host = +EVERYTHING
Host = +EVERYTHING *EVERYTHING
Coder = +DEBUG +VAREDIT +SERVER +SPAWN +POLL -AUTOLOGIN

View File

@@ -6,6 +6,8 @@
# Ranks can be anything defined in admin_ranks.txt #
# NOTE: if the rank-name cannot be found in admin_ranks.txt, they will not be adminned! ~Carn #
# NOTE: syntax was changed to allow hyphenation of ranknames, since spaces are stripped. #
# If SQL-based admin loading is enabled, admins listed here will always be loaded first #
# and will override any duplicate entries in the database. #
###############################################################################################
Optimumtact = Host
NewSta = Game Master
@@ -32,7 +34,6 @@ agouri = Game Master
errorage = Game Master
superxpdude = Game Master
petethegoat = Game Master
korphaeron = Game Master
nodrak = Game Master
carnwennan = Game Master
ikarrus = Game Master
@@ -60,7 +61,6 @@ s0ldi3rkr4s0 = Game Master
ergovisavi = Game Master
vistapowa = Game Master
miauw62 = Game Master
kazeespada = Game Master
rumia29 = Game Master
bobylein = Game Master
sirbayer = Game Master
@@ -131,4 +131,4 @@ JStheguy = Game Master
excessiveuseofcobby = Game Master
Plizzard = Game Master
octareenroon91 = Game Master
Serpentarium = Game Master
Serpentarium = Game Master

View File

@@ -35,9 +35,18 @@ ROUND_END_COUNTDOWN 90
## This flag is automatically enabled if SQL_ENABLED isn't
ADMIN_LEGACY_SYSTEM
#Uncomment this to stop any admins loaded by the legacy system from having their rank edited by the permissions panel
#PROTECT_LEGACY_ADMINS
#Uncomment this to stop any ranks loaded by the legacy system from having their flags edited by the permissions panel
#PROTECT_LEGACY_RANKS
## Comment this out if you want to use the SQL based banning system. The legacy systems use the files in the data folder. You need to set up your database to use the SQL based system.
BAN_LEGACY_SYSTEM
## Comment this out to stop locally connected clients from being given the almost full access !localhost! admin rank
ENABLE_LOCALHOST_RANK
## Uncomment this entry to have certain jobs require your account to be at least a certain number of days old to select. You can configure the exact age requirement for different jobs by editing
## the minimal_player_age variable in the files in folder /code/game/jobs/job/.. for the job you want to edit. Set minimal_player_age to 0 to disable age requirement for that job.
## REQUIRES the database set up to work. Keep it hashed if you don't have a database set up.
@@ -56,7 +65,7 @@ BAN_LEGACY_SYSTEM
## Leave this commented out to use the values defined in the job datums. Values in the datums are stored as minutes.
#USE_EXP_RESTRICTIONS_HEADS_HOURS 3
## Unhash this to change head jobs' playtime requirements so that they're based on department playtime, rather than crew playtime.
#USE_EXP_RESTRICTIONS_HEADS_DEPARTMENT
#USE_EXP_RESTRICTIONS_HEADS_DEPARTMENT
## Unhash this to enable playtime requirements for certain non-head jobs, like Engineer and Scientist.
#USE_EXP_RESTRICTIONS_OTHER
## Allows admins to bypass job playtime requirements.
@@ -209,7 +218,7 @@ CHECK_RANDOMIZER
## Github repo id
##This can be found by going to https://api.github.com/users/<user name here>/repos
##Or https://api.github.com/orgs/<org name here>/repos if the repo owner is an organization
##Or https://api.github.com/orgs/<org name here>/repos if the repo owner is an organization
# GITHUBREPOID 3234987
## Ban appeals URL - usually for a forum or wherever people should go to contact your admins.
@@ -387,7 +396,7 @@ DEBUG_ADMIN_HREFS
## Setting this to 0 will prevent the Master Controller from ticking
BASE_MC_TICK_RATE 1
##High population MC tick rate
##High population MC tick rate
## Byond rounds timer values UP, but the tick rate is modified with heuristics during lag spites so setting this to something like 2
## will make it run every 2 byond ticks, but will also double the effect of anti-lag heuristics. You can instead set it to something like
## 1.1 to make it run every 2 byond ticks, but only increase the effect of anti-lag heuristics by 10%. or 1.5 for 50%.

View File

@@ -337,14 +337,39 @@ div.notice
transition: .4s;
}
.slider.red:before {
background-color: #d6858b;
}
.slider.locked:before {
content: url("padlock.png");
background-color: #b4b4b4;
}
input:checked + .slider {
background-color: #40628a;
}
input:checked + .slider.red {
background-color: #a92621;
}
input:checked + .slider.locked {
background-color: #707070;
}
input:focus + .slider {
box-shadow: 0 0 1px #2196F3;
}
input:focus + .slider.red {
box-shadow: 0 0 1px #f3212d;
}
input:focus + .slider.locked {
box-shadow: 0 0 1px #979797;
}
input:checked + .slider:before {
transform: translateX(24px);
}

BIN
html/padlock.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 B

View File

@@ -1003,6 +1003,7 @@
#include "code\modules\admin\ipintel.dm"
#include "code\modules\admin\IsBanned.dm"
#include "code\modules\admin\NewBan.dm"
#include "code\modules\admin\permissionedit.dm"
#include "code\modules\admin\player_panel.dm"
#include "code\modules\admin\secrets.dm"
#include "code\modules\admin\sound_emitter.dm"
@@ -1011,7 +1012,6 @@
#include "code\modules\admin\topic.dm"
#include "code\modules\admin\whitelist.dm"
#include "code\modules\admin\DB_ban\functions.dm"
#include "code\modules\admin\permissionverbs\permissionedit.dm"
#include "code\modules\admin\verbs\adminhelp.dm"
#include "code\modules\admin\verbs\adminjump.dm"
#include "code\modules\admin\verbs\adminpm.dm"