Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

admin: Poll voting #6361

Merged
merged 6 commits into from
Jan 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions SQL/paradise_schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -652,3 +652,96 @@ CREATE TABLE `budget`
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Table structure for table `poll_question`
--
DROP TABLE IF EXISTS `poll_question`;
CREATE TABLE IF NOT EXISTS `poll_question` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`polltype` enum('Single Option','Text Reply','Rating','Multiple Choice') NOT NULL,
`created_datetime` datetime NOT NULL,
`starttime` datetime NOT NULL,
`endtime` datetime NOT NULL,
`question` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`subtitle` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`adminonly` tinyint(1) unsigned NOT NULL,
`multiplechoiceoptions` int(2) DEFAULT NULL,
`createdby_ckey` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,
`dontshow` tinyint(1) unsigned NOT NULL,
`minimum_playtime` int(4) NOT NULL,
`allow_revoting` tinyint(1) unsigned NOT NULL,
`deleted` tinyint(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `idx_pquest_question_time_ckey` (`question`,`starttime`,`endtime`,`createdby_ckey`),
KEY `idx_pquest_time_deleted_id` (`starttime`,`endtime`, `deleted`, `id`),
KEY `idx_pquest_id_time_type_admin` (`id`,`starttime`,`endtime`,`polltype`,`adminonly`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

--
-- Table structure for table `poll_option`
--
DROP TABLE IF EXISTS `poll_option`;
CREATE TABLE IF NOT EXISTS `poll_option` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`pollid` int(11) NOT NULL,
`text` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`minval` int(3) DEFAULT NULL,
`maxval` int(3) DEFAULT NULL,
`descmin` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`descmid` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`descmax` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`default_percentage_calc` tinyint(1) unsigned NOT NULL DEFAULT '1',
`deleted` tinyint(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `idx_pop_pollid` (`pollid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

--
-- Table structure for table `poll_textreply`
--
DROP TABLE IF EXISTS `poll_textreply`;
CREATE TABLE IF NOT EXISTS `poll_textreply` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`datetime` datetime NOT NULL,
`pollid` int(11) NOT NULL,
`ckey` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,
`replytext` varchar(2048) COLLATE utf8mb4_unicode_ci NOT NULL,
`adminrank` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,
`deleted` tinyint(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `idx_ptext_pollid_ckey` (`pollid`,`ckey`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

--
-- Table structure for table `poll_vote`
--
DROP TABLE IF EXISTS `poll_vote`;
CREATE TABLE IF NOT EXISTS `poll_vote` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`datetime` datetime NOT NULL,
`pollid` int(11) NOT NULL,
`optionid` int(11) NOT NULL,
`ckey` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,
`adminrank` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,
`rating` int(2) DEFAULT NULL,
`deleted` tinyint(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `idx_pvote_pollid_ckey` (`pollid`,`ckey`),
KEY `idx_pvote_optionid_ckey` (`optionid`,`ckey`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

DELIMITER $$
DROP PROCEDURE IF EXISTS `set_poll_deleted`;
CREATE PROCEDURE `set_poll_deleted`(
IN `poll_id` INT
)
SQL SECURITY INVOKER
BEGIN
UPDATE `poll_question` SET deleted = 1 WHERE id = poll_id;
UPDATE `poll_option` SET deleted = 1 WHERE pollid = poll_id;
UPDATE `poll_vote` SET deleted = 1 WHERE pollid = poll_id;
UPDATE `poll_textreply` SET deleted = 1 WHERE pollid = poll_id;
END
$$
DELIMITER ;
93 changes: 93 additions & 0 deletions SQL/updates/33-34.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Adds poll related. Poll question as body, options inside body, textreply and vote as player answers
--
-- Table structure for table `poll_question`
--
DROP TABLE IF EXISTS `poll_question`;
CREATE TABLE IF NOT EXISTS `poll_question` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`polltype` enum('Single Option','Text Reply','Rating','Multiple Choice') NOT NULL,
`created_datetime` datetime NOT NULL,
`starttime` datetime NOT NULL,
`endtime` datetime NOT NULL,
`question` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`subtitle` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`adminonly` tinyint(1) unsigned NOT NULL,
`multiplechoiceoptions` int(2) DEFAULT NULL,
`createdby_ckey` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,
`dontshow` tinyint(1) unsigned NOT NULL,
`minimum_playtime` int(4) NOT NULL,
`allow_revoting` tinyint(1) unsigned NOT NULL,
`deleted` tinyint(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `idx_pquest_question_time_ckey` (`question`,`starttime`,`endtime`,`createdby_ckey`),
KEY `idx_pquest_time_deleted_id` (`starttime`,`endtime`, `deleted`, `id`),
KEY `idx_pquest_id_time_type_admin` (`id`,`starttime`,`endtime`,`polltype`,`adminonly`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

--
-- Table structure for table `poll_option`
--
DROP TABLE IF EXISTS `poll_option`;
CREATE TABLE IF NOT EXISTS `poll_option` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`pollid` int(11) NOT NULL,
`text` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`minval` int(3) DEFAULT NULL,
`maxval` int(3) DEFAULT NULL,
`descmin` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`descmid` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`descmax` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`default_percentage_calc` tinyint(1) unsigned NOT NULL DEFAULT '1',
`deleted` tinyint(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `idx_pop_pollid` (`pollid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

--
-- Table structure for table `poll_textreply`
--
DROP TABLE IF EXISTS `poll_textreply`;
CREATE TABLE IF NOT EXISTS `poll_textreply` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`datetime` datetime NOT NULL,
`pollid` int(11) NOT NULL,
`ckey` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,
`replytext` varchar(2048) COLLATE utf8mb4_unicode_ci NOT NULL,
`adminrank` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,
`deleted` tinyint(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `idx_ptext_pollid_ckey` (`pollid`,`ckey`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

--
-- Table structure for table `poll_vote`
--
DROP TABLE IF EXISTS `poll_vote`;
CREATE TABLE IF NOT EXISTS `poll_vote` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`datetime` datetime NOT NULL,
`pollid` int(11) NOT NULL,
`optionid` int(11) NOT NULL,
`ckey` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,
`adminrank` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,
`rating` int(2) DEFAULT NULL,
`deleted` tinyint(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `idx_pvote_pollid_ckey` (`pollid`,`ckey`),
KEY `idx_pvote_optionid_ckey` (`optionid`,`ckey`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

DELIMITER $$
DROP PROCEDURE IF EXISTS `set_poll_deleted`;
CREATE PROCEDURE `set_poll_deleted`(
IN `poll_id` INT
)
SQL SECURITY INVOKER
BEGIN
UPDATE `poll_question` SET deleted = 1 WHERE id = poll_id;
UPDATE `poll_option` SET deleted = 1 WHERE pollid = poll_id;
UPDATE `poll_vote` SET deleted = 1 WHERE pollid = poll_id;
UPDATE `poll_textreply` SET deleted = 1 WHERE pollid = poll_id;
END
$$
DELIMITER ;
2 changes: 1 addition & 1 deletion code/__DEFINES/misc.dm
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@
#define EXPLOSION_BLOCK_PROC -1

// The SQL version required by this version of the code
#define SQL_VERSION 33
#define SQL_VERSION 34

// Vending machine stuff
#define CAT_NORMAL 1
Expand Down
25 changes: 25 additions & 0 deletions code/__DEFINES/polls.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

//unmagic-strings for types of polls, used by SQL don't change these
#define POLLTYPE_OPTION "Single Option"
#define POLLTYPE_TEXT "Text Reply"
#define POLLTYPE_RATING "Rating"
#define POLLTYPE_MULTI "Multiple Choice"

#define POLL_SECOND "SECOND"
#define POLL_MINUTE "MINUTE"
#define POLL_HOUR "HOUR"
#define POLL_DAY "DAY"
#define POLL_WEEK "WEEK"
#define POLL_MONTH "MONTH"
#define POLL_YEAR "YEAR"

///The message sent when you sign up to a poll.
#define POLL_RESPONSE_SIGNUP "signup"
///The message sent when you've already signed up for a poll and are trying to sign up again.
#define POLL_RESPONSE_ALREADY_SIGNED "already_signed"
///The message sent when you are not signed up for a poll.
#define POLL_RESPONSE_NOT_SIGNED "not_signed"
///The message sent when you are too late to unregister from a poll.
#define POLL_RESPONSE_TOO_LATE_TO_UNREGISTER "failed_unregister"
///The message sent when you successfully unregister from a poll.
#define POLL_RESPONSE_UNREGISTERED "unregistered"
11 changes: 11 additions & 0 deletions code/_globalvars/misc.dm
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,14 @@ GLOBAL_VAR_INIT(pacifism_after_gt, FALSE)

GLOBAL_VAR_INIT(communications_blackout, FALSE)

///All currently running polls held as datums
GLOBAL_LIST_EMPTY(polls)
GLOBAL_PROTECT(polls)
///Active polls
GLOBAL_LIST_EMPTY(active_polls)
GLOBAL_PROTECT(active_polls)


///All poll option datums of running polls
GLOBAL_LIST_EMPTY(poll_options)
GLOBAL_PROTECT(poll_options)
67 changes: 67 additions & 0 deletions code/controllers/subsystem/dbcore.dm
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,73 @@ SUBSYSTEM_DEF(dbcore)
return FALSE
return new /datum/db_query(connection, sql_query, arguments)

/*
* Takes a list of rows (each row being an associated list of column => value) and inserts them via a single mass query.
* Rows missing columns present in other rows will resolve to SQL NULL
* You are expected to do your own escaping of the data, and expected to provide your own quotes for strings.
* The duplicate_key arg can be true to automatically generate this part of the query
* or set to a string that is appended to the end of the query
* Ignore_errors instructes mysql to continue inserting rows if some of them have errors.
* the erroneous row(s) aren't inserted and there isn't really any way to know why or why errored
*/
/datum/controller/subsystem/dbcore/proc/MassInsert(table, list/rows, duplicate_key = FALSE, ignore_errors = FALSE, warn = FALSE, async = TRUE, special_columns = null)
if (!table || !rows || !istype(rows))
return

// Prepare column list
var/list/columns = list()
var/list/has_question_mark = list()
for (var/list/row in rows)
for (var/column in row)
columns[column] = "?"
has_question_mark[column] = TRUE
for (var/column in special_columns)
columns[column] = special_columns[column]
has_question_mark[column] = findtext(special_columns[column], "?")

// Prepare SQL query full of placeholders
var/list/query_parts = list("INSERT")
if (ignore_errors)
query_parts += " IGNORE"
query_parts += " INTO "
query_parts += table
query_parts += "\n([columns.Join(", ")])\nVALUES"

var/list/arguments = list()
var/has_row = FALSE
for (var/list/row in rows)
if (has_row)
query_parts += ","
query_parts += "\n ("
var/has_col = FALSE
for (var/column in columns)
if (has_col)
query_parts += ", "
if (has_question_mark[column])
var/name = "p[arguments.len]"
query_parts += replacetext(columns[column], "?", ":[name]")
arguments[name] = row[column]
else
query_parts += columns[column]
has_col = TRUE
query_parts += ")"
has_row = TRUE

if (duplicate_key == TRUE)
var/list/column_list = list()
for (var/column in columns)
column_list += "[column] = VALUES([column])"
query_parts += "\nON DUPLICATE KEY UPDATE [column_list.Join(", ")]"
else if (duplicate_key != FALSE)
query_parts += duplicate_key

var/datum/db_query/Query = NewQuery(query_parts.Join(), arguments)
if (warn)
. = Query.warn_execute(async)
else
. = Query.Execute(async)
qdel(Query)

/**
* Handler to allow many queries to be executed en masse
*
Expand Down
1 change: 1 addition & 0 deletions code/controllers/subsystem/non-firing/titlescreen.dm
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ SUBSYSTEM_DEF(title)
<a class="menu_button" href='byond://?src=[player.UID()];game_preferences=1'>Настройки игры</a>
<hr>
<a class="menu_button" href='byond://?src=[player.UID()];sound_options=1'>Настройки громкости</a>
<a class="menu_button" href='byond://?src=[player.UID()];poll_panel=1'>Открыть голосование</a>
BeebBeebBoob marked this conversation as resolved.
Show resolved Hide resolved
"}
// html += "<a class="menu_button" href='byond://?src=[player.UID()];swap_server=1'>Сменить сервер</a>" // TODO: add this after regis merge
if(!viewer.prefs.discord_id || (viewer.prefs.discord_id && length(viewer.prefs.discord_id) == 32))
Expand Down
1 change: 1 addition & 0 deletions code/game/world.dm
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ GLOBAL_DATUM(test_runner, /datum/test_runner)
// Right off the bat, load up the DB
SSdbcore.CheckSchemaVersion() // This doesnt just check the schema version, it also connects to the db! This needs to happen super early! I cannot stress this enough!
SSdbcore.SetRoundID() // Set the round ID here
load_poll_data()

// Setup all log paths and stamp them with startups, including round IDs
SetupLogs()
Expand Down
2 changes: 2 additions & 0 deletions code/modules/admin/admin_verbs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ GLOBAL_LIST_INIT(admin_verbs_server, list(
/datum/admins/proc/toggleaban,
/datum/admins/proc/toggleenter, /*toggles whether people can join the current game*/
/datum/admins/proc/toggleguests, /*toggles whether guests can join the current game*/
/datum/admins/proc/open_poll_list,
/client/proc/select_next_map,
/client/proc/toggle_log_hrefs,
/client/proc/toggle_twitch_censor,
Expand Down Expand Up @@ -165,6 +166,7 @@ GLOBAL_LIST_INIT(admin_verbs_debug, list(
/client/proc/admin_serialize,
/client/proc/jump_to_ruin,
/client/proc/clear_dynamic_transit,
/client/proc/cmd_reload_polls,
/client/proc/toggle_medal_disable,
/client/proc/uid_log,
/client/proc/visualise_active_turfs,
Expand Down
Loading
Loading