diff --git a/SQL/migrate/V016__poll_update.sql b/SQL/migrate/V016__poll_update.sql
new file mode 100644
index 00000000000..2f75fc584cf
--- /dev/null
+++ b/SQL/migrate/V016__poll_update.sql
@@ -0,0 +1,7 @@
+--
+-- Implemented in PR #4429
+--
+
+ALTER TABLE `ss13_poll_question`
+ ADD COLUMN `createdby_ckey` VARCHAR(50) NULL DEFAULT NULL AFTER `adminonly`,
+ ADD COLUMN `createdby_ip` VARCHAR(50) NULL DEFAULT NULL AFTER `createdby_ckey`;
diff --git a/aurorastation.dme b/aurorastation.dme
index 7d7e1bf3b53..503864878fd 100644
--- a/aurorastation.dme
+++ b/aurorastation.dme
@@ -1013,6 +1013,7 @@
#include "code\modules\admin\banjob.dm"
#include "code\modules\admin\create_mob.dm"
#include "code\modules\admin\create_object.dm"
+#include "code\modules\admin\create_poll.dm"
#include "code\modules\admin\create_turf.dm"
#include "code\modules\admin\holder2.dm"
#include "code\modules\admin\ipintel.dm"
diff --git a/code/datums/api.dm b/code/datums/api.dm
index e0a5b7d1af5..8c86dca5ebc 100644
--- a/code/datums/api.dm
+++ b/code/datums/api.dm
@@ -147,7 +147,7 @@ proc/api_update_command_database()
var/versionstring = null
//The Version Number follows SemVer http://semver.org/
version["major"] = 2 //Major Version Number --> Increment when implementing breaking changes
- version["minor"] = 1 //Minor Version Number --> Increment when adding features
+ version["minor"] = 2 //Minor Version Number --> Increment when adding features
version["patch"] = 0 //Patchlevel --> Increment when fixing bugs
versionstring = "[version["major"]].[version["minor"]].[version["patch"]]"
@@ -1030,7 +1030,7 @@ proc/api_update_command_database()
)
/datum/topic_command/cargo_reload/run_command(queryparams)
- var/force = sanitize(queryparams["force"])
+ var/force = text2num(queryparams["force"])
if(!SScargo.get_order_count())
SScargo.load_from_sql()
message_admins("Cargo has been reloaded via the API.")
@@ -1046,3 +1046,156 @@ proc/api_update_command_database()
statuscode = 500
response = "Orders have been placed. Use force parameter to overwrite."
return 1
+
+//Gets a overview of all polls (title, id, type)
+/datum/topic_command/get_polls
+ name = "get_polls"
+ description = "Gets a overview of all polls."
+ params = list(
+ "current_only" = list("name"="current_only","desc"="Only get information about the current polls","type"="int","req"=0),
+ "admin_only" = list("name"="admin_only","desc"="Only get information about the admin_only polls","type"="int","req"=0)
+ )
+
+/datum/topic_command/get_polls/run_command(queryparams)
+ var/current_only = text2num(queryparams["current_only"])
+ var/admin_only = text2num(queryparams["admin_only"])
+
+ if(!establish_db_connection(dbcon))
+ statuscode = 500
+ response = "DB-Connection unavailable"
+ return 1
+
+ var/list/polldata = list()
+
+ var/DBQuery/select_query = dbcon.NewQuery("SELECT id, polltype, starttime, endtime, question, multiplechoiceoptions, adminonly FROM ss13_poll_question [(current_only || admin_only) ? "WHERE" : ""] [(admin_only ? "adminonly = true " : "")][(current_only && admin_only ? "AND " : "")][(current_only ? "Now() BETWEEN starttime AND endtime" : "")]")
+ select_query.Execute()
+ while(select_query.NextRow())
+ polldata["[select_query.item[1]]"] = list(
+ "id"=select_query.item[1],
+ "polltype"=select_query.item[2],
+ "starttime"=select_query.item[3],
+ "endtime"=select_query.item[4],
+ "question"=select_query.item[5],
+ "multiplechoiceoptions"=select_query.item[6],
+ "adminonly"=select_query.item[7]
+ )
+
+ statuscode = 200
+ response = "Polldata sent"
+ data = polldata
+ return 1
+
+
+// Gets infos about a poll
+/datum/topic_command/get_poll_info
+ name = "get_poll_info"
+ description = "Gets Information about a poll."
+ params = list(
+ "poll_id" = list("name"="poll_id","desc"="The poll id that should be queried","type"="int","req"=1)
+ )
+
+/datum/topic_command/get_poll_info/run_command(queryparams)
+ var/poll_id = text2num(queryparams["poll_id"])
+
+ if(!establish_db_connection(dbcon))
+ statuscode = 500
+ response = "DB-Connection unavailable"
+ return 1
+
+ //Get general data about the poll
+ var/DBQuery/select_query = dbcon.NewQuery("SELECT id, polltype, starttime, endtime, question, multiplechoiceoptions, adminonly FROM ss13_poll_question WHERE id = :poll_id:")
+ select_query.Execute(list("poll_id"=poll_id))
+
+ //Check if the poll exists
+ if(!select_query.NextRow())
+ statuscode = 404
+ response = "The requested poll does not exist"
+ data = null
+ return 1
+ var/list/poll_data = list(
+ "id"=select_query.item[1],
+ "polltype"=select_query.item[2],
+ "starttime"=select_query.item[3],
+ "endtime"=select_query.item[4],
+ "question"=select_query.item[5],
+ "multiplechoiceoptions"=select_query.item[6],
+ "adminonly"=select_query.item[7]
+ )
+
+ var/list/result_data = list()
+
+ /** Return different data based on the poll type: */
+ //If we have a option or a multiple choice poll, return the number of options
+ if(poll_data["polltype"] == "OPTION" || poll_data["polltype"] == "MULTICHOICE")
+ var/DBQuery/result_query = dbcon.NewQuery({"SELECT ss13_poll_vote.optionid, ss13_poll_option.text, COUNT(*) as option_count
+ FROM ss13_poll_vote
+ LEFT JOIN ss13_poll_option ON ss13_poll_vote.optionid = ss13_poll_option.id
+ WHERE ss13_poll_vote.pollid = :poll_id:
+ GROUP BY ss13_poll_vote.optionid"})
+ result_query.Execute(list("poll_id"=poll_id))
+
+ while(result_query.NextRow())
+ result_data["[result_query.item[1]]"] = list(
+ "option_id"=result_query.item[1],
+ "option_question"=result_query.item[2],
+ "option_count"=result_query.item[3]
+ )
+ if(!length(result_data))
+ statuscode = 500
+ response = "No data returned by result query."
+ data = null
+ return 1
+
+ //If we have a numval poll, return the options with the min, max, and average
+ else if(poll_data["polltype"] == "NUMVAL")
+ var/DBQuery/result_query = dbcon.NewQuery({"SELECT ss13_poll_vote.optionid, ss13_poll_option.text, ss13_poll_option.minval, ss13_poll_option.maxval, ss13_poll_option.descmin, ss13_poll_option.descmid, ss13_poll_option.descmax, AVG(rating) as option_rating_avg, MIN(rating) as option_rating_min, MAX(rating) as option_rating_max
+ FROM ss13_poll_vote
+ LEFT JOIN ss13_poll_option ON ss13_poll_vote.optionid = ss13_poll_option.id
+ WHERE ss13_poll_vote.pollid = :poll_id:
+ GROUP BY ss13_poll_vote.optionid"})
+ result_query.Execute(list("poll_id"=poll_id))
+ while(result_query.NextRow())
+ result_data["[result_query.item[1]]"] = list(
+ "option_id"=result_query.item[1],
+ "option_question"=result_query.item[2],
+ "option_minval"=result_query.item[3],
+ "option_maxval"=result_query.item[4],
+ "option_descmin"=result_query.item[5],
+ "option_descmid"=result_query.item[6],
+ "option_descmax"=result_query.item[7],
+ "option_rating_min"=result_query.item[8],
+ "option_rating_max"=result_query.item[9],
+ "option_rating_avg"=result_query.item[10] //TODO: Expand that with MEDIAN once we upgrade mariadb
+ )
+ if(!length(result_data))
+ statuscode = 500
+ response = "No data returned by result query."
+ data = null
+ return 1
+
+ //If we have a textpoll, return the number of answers
+ else if(poll_data["polltype"] == "TEXT")
+ var/DBQuery/result_query = dbcon.NewQuery({"SELECT COUNT(*) as count FROM ss13_poll_textreply WHERE pollid = :poll_id:"})
+ result_query.Execute(list("poll_id"=poll_id))
+ if(result_query.NextRow())
+ result_data = list(
+ "response_count"=result_query.item[1]
+ )
+ else
+ statuscode = 500
+ response = "No data returned by result query."
+ data = null
+ return 1
+ else
+ statuscode = 500
+ response = "Unknown Poll Type"
+ data = poll_data
+ return 1
+
+
+ poll_data["results"] = result_data
+
+ statuscode = 200
+ response = "Poll data fetched"
+ data = poll_data
+ return 1
\ No newline at end of file
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 7f1d2241f4f..05a9f1ff36c 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -99,7 +99,8 @@ var/list/admin_verbs_admin = list(
/client/proc/clear_toxins,
/client/proc/wipe_ai, // allow admins to force-wipe AIs
/client/proc/fix_player_list,
- /client/proc/reset_openturf
+ /client/proc/reset_openturf,
+ /client/proc/create_poll //Allows to create polls
)
var/list/admin_verbs_ban = list(
/client/proc/unban_panel,
diff --git a/code/modules/admin/create_poll.dm b/code/modules/admin/create_poll.dm
new file mode 100644
index 00000000000..c379338fc6e
--- /dev/null
+++ b/code/modules/admin/create_poll.dm
@@ -0,0 +1,126 @@
+/client/proc/create_poll()
+ set category = "Special Verbs"
+ set name = "Create Poll"
+
+ if(!check_rights(R_ADMIN|R_DEV))
+ return
+ if(!establish_db_connection(dbcon))
+ to_chat(src,"Failed to establish database connection.")
+ return
+ var/polltype = input("Choose poll type.","Poll Type") in list("Single Option","Text Reply","Rating","Multiple Choice")
+ var/choice_amount = 0
+ switch(polltype)
+ if("Single Option")
+ polltype = "OPTION"
+ if("Text Reply")
+ polltype = "TEXT"
+ if("Rating")
+ polltype = "NUMVAL"
+ if("Multiple Choice")
+ polltype = "MULTICHOICE"
+ choice_amount = input("How many choices should be allowed?","Select choice amount") as num
+ var/starttime = SQLtime()
+ var/endtime = input("Set end time for poll as format YYYY-MM-DD HH:MM:SS. All times in server time. HH:MM:SS is optional and 24-hour. Must be later than starting time for obvious reasons.", "Set end time", SQLtime()) as text|null
+ if(!endtime)
+ return
+ endtime = sanitizeSQL(endtime)
+ var/DBQuery/query_validate_time = dbcon.NewQuery("SELECT STR_TO_DATE('[endtime]','%Y-%c-%d %T')")
+ if(!query_validate_time.Execute())
+ var/err = query_validate_time.ErrorMsg()
+ to_chat(src, "SQL ERROR validating endtime. Error : \[[err]\]\n")
+ return
+ if(query_validate_time.NextRow())
+ endtime = query_validate_time.item[1]
+ if(!endtime)
+ to_chat(src, "Datetime entered is invalid.")
+ return
+ var/DBQuery/query_time_later = dbcon.NewQuery("SELECT DATE('[endtime]') < NOW()")
+ if(!query_time_later.Execute())
+ var/err = query_time_later.ErrorMsg()
+ to_chat(src, "SQL ERROR comparing endtime to NOW(). Error : \[[err]\]\n")
+ return
+ if(query_time_later.NextRow())
+ var/checklate = text2num(query_time_later.item[1])
+ if(checklate)
+ to_chat(src, "Datetime entered is not later than current server time.")
+ return
+ var/adminonly
+ switch(alert("Admin only poll?",,"Yes","No","Cancel"))
+ if("Yes")
+ adminonly = 1
+ if("No")
+ adminonly = 0
+ else
+ return
+ var/sql_ckey = sanitizeSQL(ckey)
+ var/question = input("Write your question","Question") as message
+ if(!question)
+ return
+ question = sanitizeSQL(question)
+ var/DBQuery/query_polladd_question = dbcon.NewQuery("INSERT INTO ss13_poll_question (polltype, starttime, endtime, question, adminonly, multiplechoiceoptions, createdby_ckey, createdby_ip) VALUES ('[polltype]', '[starttime]', '[endtime]', '[question]', '[adminonly]', '[choice_amount]', '[sql_ckey]', '[address]')")
+ if(!query_polladd_question.Execute())
+ var/err = query_polladd_question.ErrorMsg()
+ to_chat(src,"SQL ERROR adding new poll question to table. Error : \[[err]\]\n")
+ return
+ var/pollid = 0
+ var/DBQuery/query_get_id = dbcon.NewQuery("SELECT id FROM ss13_poll_question WHERE question = '[question]' AND starttime = '[starttime]' AND endtime = '[endtime]' AND createdby_ckey = '[sql_ckey]' AND createdby_ip = '[address]'")
+ if(!query_get_id.Execute())
+ var/err = query_get_id.ErrorMsg()
+ to_chat(src,"SQL ERROR obtaining id from poll_question table. Error : \[[err]\]\n")
+ return
+ if(query_get_id.NextRow())
+ pollid = query_get_id.item[1]
+
+ log_admin("[key_name(src)] created the poll with id [pollid].")
+ message_admins("[key_name_admin(src)] created the poll with id [pollid].")
+
+ var/add_option = 1
+ if(polltype == "TEXT")
+ add_option = 0
+ while(add_option)
+ var/option = input("Write your option","Option") as message
+ if(!option)
+ return
+ option = sanitizeSQL(option)
+ var/percentagecalc
+ switch(alert("Calculate option results as percentage?",,"Yes","No","Cancel"))
+ if("Yes")
+ percentagecalc = 1
+ if("No")
+ percentagecalc = 0
+ else
+ return
+ var/minval = 0
+ var/maxval = 0
+ var/descmin = ""
+ var/descmid = ""
+ var/descmax = ""
+ if(polltype == "NUMVAL")
+ minval = input("Set minimum rating value.","Minimum rating") as num
+ if(!minval)
+ return
+ maxval = input("Set maximum rating value.","Maximum rating") as num
+ if(!maxval)
+ return
+ if(minval >= maxval)
+ to_chat(src, "Minimum rating value can't be more than maximum rating value")
+ return
+ descmin = input("Optional: Set description for minimum rating","Minimum rating description") as message
+ if(descmin)
+ descmin = sanitizeSQL(descmin)
+ descmid = input("Optional: Set description for median rating","Median rating description") as message
+ if(descmid)
+ descmid = sanitizeSQL(descmid)
+ descmax = input("Optional: Set description for maximum rating","Maximum rating description") as message
+ if(descmax)
+ descmax = sanitizeSQL(descmax)
+ var/DBQuery/query_polladd_option = dbcon.NewQuery("INSERT INTO ss13_poll_option (pollid, text, percentagecalc, minval, maxval, descmin, descmid, descmax) VALUES ('[pollid]', '[option]', '[percentagecalc]', '[minval]', '[maxval]', '[descmin]', '[descmid]', '[descmax]')")
+ if(!query_polladd_option.Execute())
+ var/err = query_polladd_option.ErrorMsg()
+ to_chat(src, "SQL ERROR adding new poll option to table. Error : \[[err]\]\n")
+ return
+ switch(alert(" ",,"Add option","Finish"))
+ if("Add option")
+ add_option = 1
+ if("Finish")
+ add_option = 0
\ No newline at end of file