Welcome Screen (#535)

The idea is to consolidate all of the spam that you see on the lower right panel into one concrete, semi-persistent pop-up window. Utilizing bootstrap, it'll show you a neat welcome screen, the message of the day, staff memos (if accessible), and a personalized set of notifications. The system is set up for easy future expansion, as well.
This commit is contained in:
skull132
2016-07-11 16:48:58 +03:00
committed by GitHub
parent 8428a94b99
commit 3663ddcfed
25 changed files with 741 additions and 103 deletions

View File

@@ -277,6 +277,8 @@ CREATE TABLE `ss13_player_preferences` (
`UI_style_alpha` int(11) NOT NULL, `UI_style_alpha` int(11) NOT NULL,
`be_special` int(11) NOT NULL, `be_special` int(11) NOT NULL,
`asfx_togs` int(11) NOT NULL, `asfx_togs` int(11) NOT NULL,
`lastmotd` text NOT NULL,
`lastmemo` text NOT NULL,
PRIMARY KEY (`ckey`), PRIMARY KEY (`ckey`),
CONSTRAINT `player_preferences_fk_ckey` FOREIGN KEY (`ckey`) REFERENCES `ss13_player` (`ckey`) ON DELETE CASCADE ON UPDATE CASCADE CONSTRAINT `player_preferences_fk_ckey` FOREIGN KEY (`ckey`) REFERENCES `ss13_player` (`ckey`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1; ) ENGINE=InnoDB DEFAULT CHARSET=latin1;

View File

@@ -134,6 +134,7 @@
#include "code\datums\modules.dm" #include "code\datums\modules.dm"
#include "code\datums\organs.dm" #include "code\datums\organs.dm"
#include "code\datums\recipe.dm" #include "code\datums\recipe.dm"
#include "code\datums\server_greeting.dm"
#include "code\datums\sun.dm" #include "code\datums\sun.dm"
#include "code\datums\supplypacks.dm" #include "code\datums\supplypacks.dm"
#include "code\datums\diseases\appendicitis.dm" #include "code\datums\diseases\appendicitis.dm"
@@ -811,8 +812,8 @@
#include "code\modules\admin\admin.dm" #include "code\modules\admin\admin.dm"
#include "code\modules\admin\admin_attack_log.dm" #include "code\modules\admin\admin_attack_log.dm"
#include "code\modules\admin\admin_investigate.dm" #include "code\modules\admin\admin_investigate.dm"
#include "code\modules\admin\admin_memo.dm"
#include "code\modules\admin\admin_ranks.dm" #include "code\modules\admin\admin_ranks.dm"
#include "code\modules\admin\admin_server_greeting.dm"
#include "code\modules\admin\admin_verbs.dm" #include "code\modules\admin\admin_verbs.dm"
#include "code\modules\admin\banjob.dm" #include "code\modules\admin\banjob.dm"
#include "code\modules\admin\create_mob.dm" #include "code\modules\admin\create_mob.dm"
@@ -896,6 +897,7 @@
#include "code\modules\client\preferences_ambience.dm" #include "code\modules\client\preferences_ambience.dm"
#include "code\modules\client\preferences_factions.dm" #include "code\modules\client\preferences_factions.dm"
#include "code\modules\client\preferences_gear.dm" #include "code\modules\client\preferences_gear.dm"
#include "code\modules\client\preferences_notification.dm"
#include "code\modules\client\preferences_save_migration.dm" #include "code\modules\client\preferences_save_migration.dm"
#include "code\modules\client\preferences_savefile.dm" #include "code\modules\client\preferences_savefile.dm"
#include "code\modules\client\preferences_spawnpoints.dm" #include "code\modules\client\preferences_spawnpoints.dm"

View File

@@ -0,0 +1,209 @@
/*
* Server greeting datum.
* Contains the following information:
* - hashes for the message of the day and staff memos
* - the current message of the day and staff memos
* - the fully parsed welcome screen HTML data
*/
#define MEMOFILE "data/greeting.sav"
#define OUTDATED_MOTD 1
#define OUTDATED_MEMO 2
#define OUTDATED_NOTE 4
/datum/server_greeting
// Hashes to figure out if we need to display the greeting message.
// These correspond to motd_hash and memo_hash on /datum/preferences for each client.
var/motd_hash = ""
var/memo_hash = ""
// The stored strings of general subcomponents.
var/motd = ""
var/memo_list[] = list()
var/memo = ""
var/raw_data_user = ""
var/raw_data_staff = ""
// The near-final string to be displayed.
// Only one placeholder remains: <!--notifications-->.
var/user_data = ""
var/staff_data = ""
/datum/server_greeting/New()
..()
load_from_file()
prepare_data()
/*
* Populates variables from save file, and loads the raw HTML data.
* Needs to be called at least once for successful initialization.
*/
/datum/server_greeting/proc/load_from_file()
var/savefile/F = new(MEMOFILE)
if (F)
if (F["motd"])
F["motd"] >> motd
if (F["memo"])
F["memo"] >> memo_list
raw_data_staff = file2text('html/templates/welcome_screen.html')
// This is a lazy way, but it disables the user from being able to see the memo button.
var/staff_button = "<li role=\"presentation\"><a href=\"#memo\" aria-controls=\"memo\" role=\"tab\" data-toggle=\"tab\">Staff Memos</a></li>"
raw_data_user = replacetextEx(raw_data_staff, staff_button, "")
/*
* Generates hashes, placeholders, and reparses var/memo.
* Then updates staff_data and user_data with the new contents.
* To be called after load_from_file or update_value.
*/
/datum/server_greeting/proc/prepare_data()
if (!motd)
motd = "<center>No new announcements to showcase.</center>"
motd_hash = ""
else
motd_hash = md5(motd)
memo = ""
if (memo_list.len)
for (var/ckey in memo_list)
var/data = {"
<p><b>[ckey]</b> wrote on [memo_list[ckey]["date"]]:<br>
[memo_list[ckey]["content"]]</p>
"}
memo += data
memo_hash = md5(memo)
else
memo = "<center>No memos have been posted.</center>"
memo_hash = ""
var/html_one = raw_data_staff
html_one = replacetextEx(html_one, "<!--motd-->", motd)
html_one = replacetextEx(html_one, "<!--memo-->", memo)
staff_data = html_one
var/html_two = raw_data_user
html_two = replacetextEx(html_two, "<!--motd-->", motd)
user_data = html_two
return
/*
* Helper to update the MoTD or memo contents.
* Args:
* - var/change string
* - var/new_value mixed
* Returns:
* - 1 upon success
* - 0 upon failure
*/
/datum/server_greeting/proc/update_value(var/change, var/new_value)
if (!change || !new_value)
return 0
switch (change)
if ("motd")
motd = new_value
motd_hash = md5(new_value)
if ("memo_write")
memo_list[new_value[1]] = list("date" = time2text(world.realtime, "DD-MMM-YYYY"), "content" = new_value[2])
if ("memo_delete")
if (memo_list[new_value])
memo_list -= new_value
else
return 0
else
return 0
var/savefile/F = new(MEMOFILE)
F["motd"] << motd
F["memo"] << memo_list
prepare_data()
return 1
/*
* Helper proc to determine whether or not we need to show the greeting window to a user.
* Args:
* - var/user client
* Returns:
* - int
*/
/datum/server_greeting/proc/find_outdated_info(var/client/user)
if (!user || !user.prefs)
return 0
var/outdated_info = 0
if (motd_hash && user.prefs.motd_hash != motd_hash)
outdated_info |= OUTDATED_MOTD
if (user.holder && memo_hash && user.prefs.memo_hash != memo_hash)
outdated_info |= OUTDATED_MEMO
if (user.prefs.notifications.len)
outdated_info |= OUTDATED_NOTE
return outdated_info
/*
* Composes the final message and displays it to the user.
* Also clears the user's notifications, should he have any.
*/
/datum/server_greeting/proc/display_to_client(var/client/user, var/outdated_info = 0)
if (!user)
return
var/notifications = "<div class=\"row\"><div class=\"alert alert-info\">You do not have any notifications to show.</div></div>"
var/list/outdated_tabs = list()
var/save_prefs = 0
if (outdated_info & OUTDATED_NOTE)
outdated_tabs += "#note-tab"
notifications = ""
for (var/datum/client_notification/a in user.prefs.notifications)
notifications += a.get_html()
if (outdated_info & OUTDATED_MEMO)
outdated_tabs += "#memo-tab"
user.prefs.memo_hash = memo_hash
save_prefs = 1
if (outdated_info & OUTDATED_MOTD)
outdated_tabs += "#motd-tab"
user.prefs.motd_hash = motd_hash
save_prefs = 1
var/data = user_data
if (user.holder)
data = staff_data
data = replacetextEx(data, "<!--note-->", notifications)
if (outdated_tabs.len)
var/tab_string = json_encode(outdated_tabs)
data = replacetextEx(data, "var updated_tabs = \[\]", "var updated_tabs = [tab_string]")
user << browse(data, "window=welcome_screen;size=640x500")
if (save_prefs)
user.prefs.handle_preferences_save(user)
#undef OUTDATED_NOTE
#undef OUTDATED_MEMO
#undef OUTDATED_MOTD
#undef MEMOFILE

View File

@@ -179,7 +179,7 @@ var/datum/moduletypes/mods = new()
var/wavesecret = 0 var/wavesecret = 0
var/gravity_is_on = 1 var/gravity_is_on = 1
var/join_motd = null var/datum/server_greeting/server_greeting = null
var/forceblob = 0 var/forceblob = 0
var/datum/nanomanager/nanomanager = new() // NanoManager, the manager for Nano UIs. var/datum/nanomanager/nanomanager = new() // NanoManager, the manager for Nano UIs.

View File

@@ -113,8 +113,6 @@
var/DBQuery/query = dbcon.NewQuery("SELECT ban_mirror_id, player_ckey, ban_mirror_ip, ban_mirror_computerid, date(ban_mirror_datetime) as datetime FROM ss13_ban_mirrors WHERE ban_id = :ban_id") var/DBQuery/query = dbcon.NewQuery("SELECT ban_mirror_id, player_ckey, ban_mirror_ip, ban_mirror_computerid, date(ban_mirror_datetime) as datetime FROM ss13_ban_mirrors WHERE ban_id = :ban_id")
query.Execute(list(":ban_id" = ban_id)) query.Execute(list(":ban_id" = ban_id))
testing("Ban ID: [ban_id]")
var/mirrors[] = list() var/mirrors[] = list()
while (query.NextRow()) while (query.NextRow())
var/items[] = list() var/items[] = list()

View File

@@ -1,54 +0,0 @@
#define MEMOFILE "data/memo.sav" //where the memos are saved
#define ENABLE_MEMOS 1 //using a define because screw making a config variable for it. This is more efficient and purty.
//switch verb so we don't spam up the verb lists with like, 3 verbs for this feature.
/client/proc/admin_memo(task in list("write","show","delete"))
set name = "Memo"
set category = "Server"
if(!ENABLE_MEMOS) return
if(!check_rights(0)) return
switch(task)
if("write") admin_memo_write()
if("show") admin_memo_show()
if("delete") admin_memo_delete()
//write a message
/client/proc/admin_memo_write()
var/savefile/F = new(MEMOFILE)
if(F)
var/memo = sanitize(input(src,"Type your memo\n(Leaving it blank will delete your current memo):","Write Memo",null) as null|message, extra = 0)
switch(memo)
if(null)
return
if("")
F.dir.Remove(ckey)
src << "<b>Memo removed</b>"
return
if( findtext(memo,"<script",1,0) )
return
F[ckey] << "[key] on [time2text(world.realtime,"(DDD) DD MMM hh:mm")]<br>[memo]"
message_admins("[key] set an admin memo:<br>[memo]")
//show all memos
/client/proc/admin_memo_show()
if(ENABLE_MEMOS)
var/savefile/F = new(MEMOFILE)
if(F)
for(var/ckey in F.dir)
src << "<center><span class='motd'><b>Admin Memo</b><i> by [F[ckey]]</i></span></center>"
//delete your own or somebody else's memo
/client/proc/admin_memo_delete()
var/savefile/F = new(MEMOFILE)
if(F)
var/ckey
if(check_rights(R_SERVER,0)) //high ranking admins can delete other admin's memos
ckey = input(src,"Whose memo shall we remove?","Remove Memo",null) as null|anything in F.dir
else
ckey = src.ckey
if(ckey)
F.dir.Remove(ckey)
src << "<b>Removed Memo created by [ckey].</b>"
#undef MEMOFILE
#undef ENABLE_MEMOS

View File

@@ -0,0 +1,80 @@
/*
* A file containing the admin commands for interfacing with the server_greeting datum.
*/
/client/proc/admin_edit_motd()
set name = "Edit MotD"
set category = "Server"
if (!check_rights(R_SERVER))
return
var/new_message = input(usr, "Please edit the Message of the Day as necessary.", "Message of the Day", server_greeting.motd) as message
if (!new_message)
new_message = "<center>This is a palceholder. Pester your staff to change it!</center>"
server_greeting.update_value("motd", new_message)
message_admins("[ckey] has edited the message of the day:<br>[html_encode(new_message)]")
/client/proc/admin_memo_control(task in list("write", "delete"))
set name = "Edit Memos"
set category = "Server"
if (!check_rights(R_ADMIN))
return
switch (task)
if ("write")
admin_memo_write()
if ("delete")
admin_memo_delete()
/client/proc/admin_memo_write()
var/current_memo = ""
if (server_greeting.memo_list.len && server_greeting.memo_list[ckey])
current_memo = server_greeting.memo_list[ckey]
var/new_memo = input(usr, "Please write your memo.", "Memo", current_memo) as message
if (server_greeting.update_value("memo_write", list(ckey, new_memo)))
src << "<span class='notice'>Operation carried out successfully.</span>"
message_admins("[ckey] wrote a new memo:<br>[html_encode(new_memo)]")
else
src << "<span class='danger'>Error carrying out desired operation.</span>"
return
/client/proc/admin_memo_delete()
if (!server_greeting.memo_list.len)
src << "<span class='notice'>No memos are currently saved.</span>"
return
if (!check_rights(R_SERVER))
if (!server_greeting.memo_list[ckey])
src << "<span class='warning'>You do not have a memo saved. Cancelling.</span>"
else if (alert("Do you wish to delete your own memo, written on [server_greeting.memo_list[ckey]["date"]]?", "Choices", "Yes", "No") == "Yes")
if (server_greeting.update_value("memo_delete", ckey))
src << "<span class='notice'>Operation carried out successfully.</span>"
message_admins("[ckey] has deleted their own memo.")
else
src << "<span class='danger'>Error carrying out desired operation.</span>"
else
src << "<span class='notice'>Cancelled.</span>"
return
else
var/input = input(usr, "Whose memo shall we delete?", "Remove Memo", null) as null|anything in server_greeting.memo_list
if (!input)
src << "<span class='notice'>Cancelled.</span>"
return
if (server_greeting.update_value("memo_delete", input))
src << "<span class='notice'>Operation carried out successfully.</span>"
message_admins("[ckey] has deleted the memo of [input].")
else
src << "<span class='danger'>Error carrying out desired operation.</span>"
return

View File

@@ -50,7 +50,7 @@ var/list/admin_verbs_admin = list(
/client/proc/rename_silicon, /*properly renames silicons*/ /client/proc/rename_silicon, /*properly renames silicons*/
/client/proc/manage_silicon_laws, /* Allows viewing and editing silicon laws. */ /client/proc/manage_silicon_laws, /* Allows viewing and editing silicon laws. */
/client/proc/check_antagonists, /client/proc/check_antagonists,
/client/proc/admin_memo, /*admin memo system. show/delete/write. +SERVER needed to delete admin memos of others*/ /client/proc/admin_memo_control, /*admin memo system. show/delete/write. +SERVER needed to delete admin memos of others*/
/client/proc/dsay, /*talk in deadchat using our ckey/fakekey*/ /client/proc/dsay, /*talk in deadchat using our ckey/fakekey*/
/client/proc/toggleprayers, /*toggles prayers on/off*/ /client/proc/toggleprayers, /*toggles prayers on/off*/
// /client/proc/toggle_hear_deadcast, /*toggles whether we hear deadchat*/ // /client/proc/toggle_hear_deadcast, /*toggles whether we hear deadchat*/
@@ -155,7 +155,8 @@ var/list/admin_verbs_server = list(
/datum/admins/proc/toggle_space_ninja, /datum/admins/proc/toggle_space_ninja,
/client/proc/toggle_random_events, /client/proc/toggle_random_events,
/client/proc/check_customitem_activity, /client/proc/check_customitem_activity,
/client/proc/nanomapgen_DumpImage /client/proc/nanomapgen_DumpImage,
/client/proc/admin_edit_motd
) )
var/list/admin_verbs_debug = list( var/list/admin_verbs_debug = list(
/client/proc/getruntimelog, /*allows us to access runtime logs to somebody*/ /client/proc/getruntimelog, /*allows us to access runtime logs to somebody*/

View File

@@ -183,11 +183,10 @@
warnings_check() warnings_check()
/* /*
* A proc to alert you if you have unacknowledged warnings. * A proc to gather notifications regarding your warnings.
* Called in /client/New (client procs.dm) * Called by /datum/preferences/proc/gather_notifications() in preferences.dm
*/ */
/client/proc/warnings_gather()
/client/proc/warnings_alert()
var/count = 0 var/count = 0
var/count_expire = 0 var/count_expire = 0
@@ -210,12 +209,13 @@
while (query.NextRow()) while (query.NextRow())
count++ count++
var/list/data = list("unread" = "", "expired" = "")
if (count) if (count)
src << "<br>" data["unread"] = "You have <b>[count] unread [count > 1 ? "warnings" : "warning"]!</b> Click <a href='?JSlink=warnings;notification=:src_ref'>here</a> to review and acknowledge them!"
src << "<font color=red><b>You have [count] unread [count > 1 ? "warnings" : "warning"]! Click <a href='byond://?src=\ref[src];warnview=1'>here</a> to review and acknowledge them!</b></font>"
if (count_expire) if (count_expire)
src << "<br>" data["expired"] = "[count_expire] of your warnings have expired."
src << "<font color=blue><b>[count_expire] of your warnings expired.</b></font>"
return data
/* /*
* A proc for an admin/moderator to look up a member's warnings. * A proc for an admin/moderator to look up a member's warnings.

View File

@@ -135,6 +135,36 @@
return return
// JSlink switch.
if (href_list["JSlink"])
switch (href_list["JSlink"])
if ("warnings")
src.warnings_check()
if ("linking")
src.check_linking_requests()
if ("dismiss")
if (href_list["notification"])
var/datum/client_notification/a = locate(href_list["notification"])
if (a && isnull(a.gcDestroyed))
a.dismiss()
if ("github")
if (!config.githuburl)
src << "<span class='danger'>Github URL not set in the config. Unable to open the site.</span>"
else if (alert("This will open the issue tracker in your browser. Are you sure?",, "Yes", "No") == "Yes")
src << link(config.githuburl)
if ("forums")
src.forum()
if ("wiki")
src.wiki()
if ("webint")
src.open_webint()
..() //redirect to hsrc.() ..() //redirect to hsrc.()
/client/proc/handle_spam_prevention(var/message, var/mute_type) /client/proc/handle_spam_prevention(var/message, var/mute_type)
@@ -207,6 +237,8 @@
if(!prefs) if(!prefs)
prefs = new /datum/preferences(src) prefs = new /datum/preferences(src)
preferences_datums[ckey] = prefs preferences_datums[ckey] = prefs
prefs.gather_notifications(src)
prefs.last_ip = address //these are gonna be used for banning prefs.last_ip = address //these are gonna be used for banning
prefs.last_id = computer_id //these are gonna be used for banning prefs.last_id = computer_id //these are gonna be used for banning
@@ -223,18 +255,6 @@
else else
del(src) del(src)
return 0 return 0
else if (byond_version < config.client_warn_version)
src << "<span class='danger'><b>Your version of BYOND may be out of date!</b></span>"
src << config.client_warn_message
src << "Your version: [byond_version]."
src << "Required version to remove this message: [config.client_warn_version] or later."
src << "Visit http://www.byond.com/download/ to get the latest version of BYOND."
if(custom_event_msg && custom_event_msg != "")
src << "<h1 class='alert'>Custom Event</h1>"
src << "<h2 class='alert'>A custom event is taking place. OOC Info:</h2>"
src << "<span class='alert'>[custom_event_msg]</span>"
src << "<br>"
if( (world.address == address || !address) && !host ) if( (world.address == address || !address) && !host )
host = key host = key
@@ -242,7 +262,6 @@
if(holder) if(holder)
add_admin_verbs() add_admin_verbs()
admin_memo_show()
// Forcibly enable hardware-accelerated graphics, as we need them for the lighting overlays. // Forcibly enable hardware-accelerated graphics, as we need them for the lighting overlays.
// (but turn them off first, since sometimes BYOND doesn't turn them on properly otherwise) // (but turn them off first, since sometimes BYOND doesn't turn them on properly otherwise)
@@ -252,20 +271,13 @@
sleep(2) // wait a bit more, possibly fixes hardware mode not re-activating right sleep(2) // wait a bit more, possibly fixes hardware mode not re-activating right
winset(src, null, "command=\".configure graphics-hwmode on\"") winset(src, null, "command=\".configure graphics-hwmode on\"")
warnings_alert()
check_linking_requests()
send_resources() send_resources()
nanomanager.send_resources(src) nanomanager.send_resources(src)
if(prefs.lastchangelog != changelog_hash) //bolds the changelog button on the interface so we know there are updates. var/outdated_greeting_info = server_greeting.find_outdated_info(src)
src << "<span class='info'>You have unread updates in the changelog.</span>"
winset(src, "rpane.changelog", "background-color=#eaeaea;font-style=bold")
if(config.aggressive_changelog)
src.changes()
if (outdated_greeting_info)
server_greeting.display_to_client(src, outdated_greeting_info)
////////////// //////////////
//DISCONNECT// //DISCONNECT//
@@ -384,6 +396,11 @@
'html/images/loading.gif', 'html/images/loading.gif',
'html/images/ntlogo.png', 'html/images/ntlogo.png',
'html/images/talisman.png', 'html/images/talisman.png',
'html/bootstrap/css/bootstrap.min.css',
'html/bootstrap/js/bootstrap.min.js',
'html/bootstrap/js/html5shiv.min.js',
'html/bootstrap/js/respond.min.js',
'html/jquery/jquery-2.0.0.min.js',
'icons/pda_icons/pda_atmos.png', 'icons/pda_icons/pda_atmos.png',
'icons/pda_icons/pda_back.png', 'icons/pda_icons/pda_back.png',
'icons/pda_icons/pda_bell.png', 'icons/pda_icons/pda_bell.png',
@@ -424,7 +441,6 @@
'icons/spideros_icons/sos_14.png' 'icons/spideros_icons/sos_14.png'
) )
/mob/proc/MayRespawn() /mob/proc/MayRespawn()
return 0 return 0
@@ -481,6 +497,23 @@
src << browse(dat, "window=LinkingRequests") src << browse(dat, "window=LinkingRequests")
return return
/client/proc/gather_linking_requests()
if (!config.webint_url || !config.sql_enabled)
return
establish_db_connection(dbcon)
if (!dbcon.IsConnected())
return
var/DBQuery/select_query = dbcon.NewQuery("SELECT COUNT(*) AS request_count FROM ss13_player_linking WHERE status = 'new' AND player_ckey = :ckey AND deleted_at IS NULL")
select_query.Execute(list(":ckey" = ckey))
if (select_query.NextRow())
if (text2num(select_query.item[1]) > 0)
return "You have [select_query.item[1]] account linking requests pending review. Click <a href='?JSlink=linking;notification=:src_ref'>here</a> to see them!"
return null
/client/proc/process_webint_link(var/route, var/attributes) /client/proc/process_webint_link(var/route, var/attributes)
if (!route) if (!route)
return return
@@ -523,3 +556,9 @@
src << link(linkURL) src << link(linkURL)
return return
/client/verb/show_greeting()
set name = "Open Greeting"
set category = "OOC"
server_greeting.display_to_client(src, server_greeting.find_outdated_info(src))

View File

@@ -40,6 +40,7 @@ datum/preferences
var/muted = 0 var/muted = 0
var/last_ip var/last_ip
var/last_id var/last_id
var/list/notifications = list() //A list of datums, for the dynamic server greeting window.
//game-preferences //game-preferences
var/lastchangelog = "" //Saved changlog filesize to detect if there was a change var/lastchangelog = "" //Saved changlog filesize to detect if there was a change
@@ -50,6 +51,8 @@ datum/preferences
var/asfx_togs = ASFX_DEFAULT var/asfx_togs = ASFX_DEFAULT
var/UI_style_color = "#ffffff" var/UI_style_color = "#ffffff"
var/UI_style_alpha = 255 var/UI_style_alpha = 255
var/motd_hash = "" //Hashes for the new server greeting window.
var/memo_hash = ""
//character preferences //character preferences
var/real_name //our character's name var/real_name //our character's name

View File

@@ -0,0 +1,198 @@
/*
* A simple datum for storing notifications for dispaly.
*/
/datum/client_notification
var/datum/preferences/owner = null
var/list/note_wrapper = list()
var/note_text = ""
var/proc_src = null
var/proc_name = ""
var/proc_args = null
var/persistent = 0
/datum/client_notification/New(var/datum/preferences/prefs, var/list/new_wrapper, var/new_text, var/new_persistence)
if (!prefs)
qdel(src)
return
if (!new_wrapper || new_wrapper.len != 2)
qdel(src)
return
if (!new_text)
qdel(src)
return
owner = prefs
note_wrapper = new_wrapper
note_text = new_text
note_text = replacetextEx(note_text, ":src_ref", "\ref[src]")
note_wrapper[1] = replacetextEx(note_wrapper[1], ":src_ref", "\ref[src]")
if (new_persistence)
persistent = new_persistence
/datum/client_notification/Destroy()
if (owner)
owner.notifications -= src
owner = null
..()
/*
* Associates a callback to be executed whenever a notification is dismissed.
*/
/datum/client_notification/proc/tie_callback(var/dismiss_proc_src, var/dismiss_proc, var/dismiss_proc_args)
if (!dismiss_proc_src || !dismiss_proc)
return
proc_src = dismiss_proc_src
proc_name = dismiss_proc
if (dismiss_proc_args)
proc_args = dismiss_proc_args
/*
* Returns the HTML required to display this alert.
*/
/datum/client_notification/proc/get_html()
var/html = "<div class=\"row\">[note_wrapper[1]]\n"
if (!persistent)
html += "<a href=\"#\" class=\"close\" data-dismiss=\"alert\" aria-label=\"close\">&times;</a>\n"
html += note_text
html += "\n[note_wrapper[2]]</div>"
return html
/*
* Dismisses the notification, executing the proc that was set up as necessary.
*/
/datum/client_notification/proc/dismiss()
if (proc_src && proc_name)
call(proc_src, proc_name)(proc_args)
if (!persistent)
qdel(src)
/*
* Adds a new notification datum for later processing.
*/
/datum/preferences/proc/new_notification(var/type, var/text, var/persistent, var/callback_src, var/callback_proc, var/callback_args)
if (!text)
return
var/list/wrapper
switch (type)
if ("success")
wrapper = list("<div class=\"alert alert-success\" notification=\":src_ref\">", "</div>")
if ("info")
wrapper = list("<div class=\"alert alert-info\" notification=\":src_ref\">", "</div>")
if ("warning")
wrapper = list("<div class=\"alert alert-warning\" notification=\":src_ref\">", "</div>")
if ("danger")
wrapper = list("<div class=\"alert alert-danger\" notification=\":src_ref\">", "</div>")
else
wrapper = list("<div class=\"alert alert-info\" notification=\":src_ref\">", "</div>")
var/datum/client_notification/note = new(src, wrapper, text, persistent)
if (callback_src && callback_proc)
note.tie_callback(callback_src, callback_proc, callback_args)
notifications += note
/*
* Gathers all notifications relevant to the client.
*/
/datum/preferences/proc/gather_notifications(var/client/user)
if (!user)
return
if (user.byond_version < config.client_warn_version)
var/version_warn = ""
version_warn += "<b>Your version of BYOND may be out of date!</b><br>"
version_warn += config.client_warn_message
version_warn += "Your version: [user.byond_version].<br>"
version_warn += "Required version to remove this message: [config.client_warn_version] or later.<br>"
version_warn += "Visit http://www.byond.com/download/ to get the latest version of BYOND."
new_notification("danger", version_warn)
if (custom_event_msg && custom_event_msg != "")
var/custom_event_warn = "<b><center>A custom event is taking place!</center></b><br>"
custom_event_warn += "<b>OOC Info:</b><br>[custom_event_msg]"
new_notification("danger", custom_event_warn)
if (lastchangelog != changelog_hash)
winset(user, "rpane.changelog", "background-color=#eaeaea;font-style=bold")
if (config.aggressive_changelog)
new_notification("info", "You have unread updates in the changelog.", callback_src = user, callback_proc = "changes")
else
new_notification("info", "You have unread updates in the changelog.")
if (config.sql_enabled)
var/list/warnings = user.warnings_gather()
if (warnings["unread"])
new_notification("danger", warnings["unread"], 1)
if (warnings["expired"])
new_notification("info", warnings["expired"])
var/linking = user.gather_linking_requests()
if (linking)
new_notification("info", linking, callback_src = user, callback_proc = "check_linking_requests")
var/cciaa_actions = count_ccia_actions(user)
if (cciaa_actions)
new_notification("info", cciaa_actions)
/*
* Helper proc for getting a count of active CCIA actions against the player's character.
*/
/datum/preferences/proc/count_ccia_actions(var/client/user)
if (!user)
return null
if (!establish_db_connection(dbcon))
error("Error initiatlizing database connection while counting CCIA actions.")
return null
var/DBQuery/prep_query = dbcon.NewQuery("SELECT id FROM ss13_characters WHERE ckey = :ckey")
prep_query.Execute(list(":ckey" = user.ckey))
var/list/chars = list()
while (prep_query.NextRow())
chars += text2num(prep_query.item[1])
if (!chars.len)
return null
var/DBQuery/query = dbcon.NewQuery({"SELECT
COUNT(act_chr.action_id) AS action_count
FROM ss13_ccia_action_char act_chr
JOIN ss13_characters chr ON act_chr.char_id = chr.id
JOIN ss13_ccia_actions act ON act_chr.action_id = act.id
WHERE
act_chr.char_id IN :char_id AND
(act.expires_at IS NULL OR act.expires_at >= CURRENT_DATE()) AND
act.deleted_at IS NULL;"})
query.Execute(list(":char_id" = chars))
if (query.NextRow())
var/action_count = text2num(query.item[1])
if (action_count == 0)
return null
var/string = "There are [action_count] active CCIA actions currently active against your character(s)."
return string
return null

View File

@@ -57,10 +57,14 @@
S["UI_style_color"] >> UI_style_color S["UI_style_color"] >> UI_style_color
S["UI_style_alpha"] >> UI_style_alpha S["UI_style_alpha"] >> UI_style_alpha
S["asfx_togs"] >> asfx_togs S["asfx_togs"] >> asfx_togs
S["motd_hash"] >> motd_hash
S["memo_hash"] >> memo_hash
//Sanitize //Sanitize
ooccolor = sanitize_hexcolor(ooccolor, initial(ooccolor)) ooccolor = sanitize_hexcolor(ooccolor, initial(ooccolor))
lastchangelog = sanitize_text(lastchangelog, initial(lastchangelog)) lastchangelog = sanitize_text(lastchangelog, initial(lastchangelog))
motd_hash = sanitize_text(motd_hash, initial(motd_hash))
memo_hash = sanitize_text(memo_hash, initial(memo_hash))
UI_style = sanitize_inlist(UI_style, list("White", "Midnight","Orange","old"), initial(UI_style)) UI_style = sanitize_inlist(UI_style, list("White", "Midnight","Orange","old"), initial(UI_style))
be_special = sanitize_integer(be_special, 0, 65535, initial(be_special)) be_special = sanitize_integer(be_special, 0, 65535, initial(be_special))
default_slot = sanitize_integer(default_slot, 1, config.character_slots, initial(default_slot)) default_slot = sanitize_integer(default_slot, 1, config.character_slots, initial(default_slot))
@@ -87,6 +91,8 @@
S["default_slot"] << default_slot S["default_slot"] << default_slot
S["toggles"] << toggles S["toggles"] << toggles
S["asfx_togs"] << asfx_togs S["asfx_togs"] << asfx_togs
S["motd_hash"] << motd_hash
S["memo_hash"] << memo_hash
return 1 return 1

View File

@@ -11,7 +11,9 @@
UI_style_color, UI_style_color,
UI_style_alpha, UI_style_alpha,
be_special, be_special,
asfx_togs asfx_togs,
motd_hash,
memo_hash
FROM ss13_player_preferences FROM ss13_player_preferences
WHERE ckey = :ckey"}) WHERE ckey = :ckey"})
query.Execute(list(":ckey" = C.ckey)) query.Execute(list(":ckey" = C.ckey))
@@ -28,10 +30,14 @@
UI_style_alpha = text2num(query.item[7]) UI_style_alpha = text2num(query.item[7])
be_special = text2num(query.item[8]) be_special = text2num(query.item[8])
asfx_togs = text2num(query.item[9]) asfx_togs = text2num(query.item[9])
motd_hash = query.item[10]
memo_hash = query.item[11]
//Sanitize //Sanitize
ooccolor = sanitize_hexcolor(ooccolor, initial(ooccolor)) ooccolor = sanitize_hexcolor(ooccolor, initial(ooccolor))
lastchangelog = sanitize_text(lastchangelog, initial(lastchangelog)) lastchangelog = sanitize_text(lastchangelog, initial(lastchangelog))
motd_hash = sanitize_text(motd_hash, initial(motd_hash))
memo_hash = sanitize_text(memo_hash, initial(memo_hash))
UI_style = sanitize_inlist(UI_style, list("White", "Midnight","Orange","old"), initial(UI_style)) UI_style = sanitize_inlist(UI_style, list("White", "Midnight","Orange","old"), initial(UI_style))
be_special = sanitize_integer(be_special, 0, 65535, initial(be_special)) be_special = sanitize_integer(be_special, 0, 65535, initial(be_special))
default_slot = sanitize_integer(default_slot, 1, config.character_slots, initial(default_slot)) default_slot = sanitize_integer(default_slot, 1, config.character_slots, initial(default_slot))
@@ -61,7 +67,9 @@
UI_style_color = :ui_color, UI_style_color = :ui_color,
UI_style_alpha = :ui_alpha, UI_style_alpha = :ui_alpha,
be_special = :be_special, be_special = :be_special,
asfx_togs = :asfx_togs asfx_togs = :asfx_togs,
motd_hash = :motd_hash,
memo_hash = :memo_hash
WHERE ckey = :ckey"}) WHERE ckey = :ckey"})
update_query.Execute(get_prefs_update_insert_params(C)) update_query.Execute(get_prefs_update_insert_params(C))
@@ -71,8 +79,8 @@
if (!C) if (!C)
return 0 return 0
var/DBQuery/query = dbcon.NewQuery({"INSERT INTO ss13_player_preferences (ckey, ooccolor, lastchangelog, UI_style, current_character, toggles, UI_style_color, UI_style_alpha, be_special, asfx_togs) var/DBQuery/query = dbcon.NewQuery({"INSERT INTO ss13_player_preferences (ckey, ooccolor, lastchangelog, UI_style, current_character, toggles, UI_style_color, UI_style_alpha, be_special, asfx_togs, motd_hash, memo_hash)
VALUES (:ckey, :ooccolor, :lastchangelog, :ui_style, :current_character, :toggles, :ui_color, :ui_alpha, :be_special, :asfx_togs);"}) VALUES (:ckey, :ooccolor, :lastchangelog, :ui_style, :current_character, :toggles, :ui_color, :ui_alpha, :be_special, :asfx_togs, :motd_hash, :memo_hash);"})
query.Execute(get_prefs_update_insert_params(C)) query.Execute(get_prefs_update_insert_params(C))
return 1 return 1
@@ -92,6 +100,8 @@
params[":ui_alpha"] = UI_style_alpha params[":ui_alpha"] = UI_style_alpha
params[":be_special"] = be_special params[":be_special"] = be_special
params[":asfx_togs"] = asfx_togs params[":asfx_togs"] = asfx_togs
params[":motd_hash"] = motd_hash
params[":memo_hash"] = memo_hash
return params return params

View File

@@ -1,7 +1,5 @@
/mob/new_player/Login() /mob/new_player/Login()
update_Login_details() //handles setting lastKnownIP and computer_id for use by the ban systems as well as checking for multikeying update_Login_details() //handles setting lastKnownIP and computer_id for use by the ban systems as well as checking for multikeying
if(join_motd)
src << "<div class=\"motd\">[join_motd]</div>"
if(!mind) if(!mind)
mind = new /datum/mind(key) mind = new /datum/mind(key)

View File

@@ -45,7 +45,7 @@
/* /*
* /proc/webint_start_singlesignon() * /proc/webint_start_singlesignon()
* Used to insert a token into the web_sso database and to enable a user to navigate to a page on the website and be automatically logged in. Hashes the user's save file for a unique token. Additional security managed on the website's end. * Used to insert a token into the web_sso database and to enable a user to navigate to a page on the website and be automatically logged in. Generates a hash algorithmically. Additional security managed on the website's end.
* *
* Arguments: * Arguments:
* - var/user - Must be a mob or a client. The player object that's going to be using the request. * - var/user - Must be a mob or a client. The player object that's going to be using the request.

View File

@@ -522,12 +522,12 @@ var/world_topic_spam_protect_time = world.timeofday
F << the_mode F << the_mode
/hook/startup/proc/loadMOTD() /hook/startup/proc/initialize_greeting()
world.load_motd() world.initialize_greeting()
return 1 return 1
/world/proc/load_motd() /world/proc/initialize_greeting()
join_motd = file2text("config/motd.txt") server_greeting = new()
/proc/load_configuration() /proc/load_configuration()

File diff suppressed because one or more lines are too long

6
html/bootstrap/css/bootstrap.min.css vendored Normal file

File diff suppressed because one or more lines are too long

7
html/bootstrap/js/bootstrap.min.js vendored Normal file

File diff suppressed because one or more lines are too long

4
html/bootstrap/js/html5shiv.min.js vendored Normal file
View File

@@ -0,0 +1,4 @@
/**
* @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
*/
!function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x<style>"+b+"</style>",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="<xyz></xyz>",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document);

5
html/bootstrap/js/respond.min.js vendored Normal file
View File

@@ -0,0 +1,5 @@
/*! Respond.js v1.4.2: min/max-width media query polyfill * Copyright 2013 Scott Jehl
* Licensed under https://github.com/scottjehl/Respond/blob/master/LICENSE-MIT
* */
!function(a){"use strict";a.matchMedia=a.matchMedia||function(a){var b,c=a.documentElement,d=c.firstElementChild||c.firstChild,e=a.createElement("body"),f=a.createElement("div");return f.id="mq-test-1",f.style.cssText="position:absolute;top:-100em",e.style.background="none",e.appendChild(f),function(a){return f.innerHTML='&shy;<style media="'+a+'"> #mq-test-1 { width: 42px; }</style>',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";function b(){u(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))};if(c.ajax=f,c.queue=d,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\([\s]*min\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/,maxw:/\([\s]*max\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var g,h,i,j=a.document,k=j.documentElement,l=[],m=[],n=[],o={},p=30,q=j.getElementsByTagName("head")[0]||k,r=j.getElementsByTagName("base")[0],s=q.getElementsByTagName("link"),t=function(){var a,b=j.createElement("div"),c=j.body,d=k.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=j.createElement("body"),c.style.background="none"),k.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&k.insertBefore(c,k.firstChild),a=b.offsetWidth,f?k.removeChild(c):c.removeChild(b),k.style.fontSize=d,e&&(c.style.fontSize=e),a=i=parseFloat(a)},u=function(b){var c="clientWidth",d=k[c],e="CSS1Compat"===j.compatMode&&d||j.body[c]||d,f={},o=s[s.length-1],r=(new Date).getTime();if(b&&g&&p>r-g)return a.clearTimeout(h),h=a.setTimeout(u,p),void 0;g=r;for(var v in l)if(l.hasOwnProperty(v)){var w=l[v],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?i||t():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?i||t():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(m[w.rules]))}for(var C in n)n.hasOwnProperty(C)&&n[C]&&n[C].parentNode===q&&q.removeChild(n[C]);n.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=j.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,q.insertBefore(E,o.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(j.createTextNode(F)),n.push(E)}},v=function(a,b,d){var e=a.replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var g=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},h=!f&&d;b.length&&(b+="/"),h&&(f=1);for(var i=0;f>i;i++){var j,k,n,o;h?(j=d,m.push(g(a))):(j=e[i].match(c.regex.findStyles)&&RegExp.$1,m.push(RegExp.$2&&g(RegExp.$2))),n=j.split(","),o=n.length;for(var p=0;o>p;p++)k=n[p],l.push({media:k.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:m.length-1,hasquery:k.indexOf("(")>-1,minw:k.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:k.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}u()},w=function(){if(d.length){var b=d.shift();f(b.href,function(c){v(c,b.href,b.media),o[b.href]=!0,a.setTimeout(function(){w()},0)})}},x=function(){for(var b=0;b<s.length;b++){var c=s[b],e=c.href,f=c.media,g=c.rel&&"stylesheet"===c.rel.toLowerCase();e&&g&&!o[e]&&(c.styleSheet&&c.styleSheet.rawCssText?(v(c.styleSheet.rawCssText,e,f),o[e]=!0):(!/^([a-zA-Z:]*\/\/)/.test(e)&&!r||e.replace(RegExp.$1,"").split("/")[0]===a.location.host)&&("//"===e.substring(0,2)&&(e=a.location.protocol+e),d.push({href:e,media:f})))}w()};x(),c.update=x,c.getEmValue=t,a.addEventListener?a.addEventListener("resize",b,!1):a.attachEvent&&a.attachEvent("onresize",b)}}(this);

View File

@@ -0,0 +1,13 @@
author: Skull132
# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
delete-after: True
# Any changes you've made. See valid prefix list above.
# INDENT WITH TWO SPACES. NOT TABS. SPACES.
# SCREW THIS UP AND IT WON'T WORK.
# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries.
# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog.
changes:
- rscadd: "Added a new \"Server Greeting\" system to replace the massive garbled dump of info people get in the lower right panel. Coloured tabs indicated things that need attention. The window can be opened from the OOC tab as well, via the \"Open Greeting\" button."
- rscadd: "Admins (with R_SERVER flag) can now edit the message of the day from within the game, with the \"Edit MotD\" button in the Server tab. Memos can be edited by any admin from the \"Edit Memo\" button in the same tab."

6
html/jquery/jquery-2.0.0.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,99 @@
<!DOCTYPE html>
<html>
<head>
<title>Aurorastation Welcome Screen</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Bootstrap -->
<link href="bootstrap.min.css" rel="stylesheet" media="screen">
<style media="screen">
.nav-tabs>li.updated>a, .nav-tabs>li.updated>a:hover, .nav-tabs>li.updated>a:focus {
background-color: #fcf8e3;
}
</style>
</head>
<body>
<div class="container-fluid">
<nav>
<ul class="nav nav-tabs" id="myTab" role="tablist">
<li role="presentation" class="active" id="home-tab"><a href="#home" aria-controls="home" role="tab" data-toggle="tab">Home</a></li>
<li role="presentation" id="motd-tab"><a href="#motd" aria-controls="motd" role="tab" data-toggle="tab">Announcements</a></li>
<li role="presentation" id="memo-tab"><a href="#memo" aria-controls="memo" role="tab" data-toggle="tab">Staff Memos</a></li>
<li role="presentation" id="note-tab"><a href="#note" aria-controls="note" role="tab" data-toggle="tab">Notifications</a></li>
<li role="presentation"><a href="#webint">My Profile</a></li>
</ul>
</nav>
<div class="panel-body">
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="home">
<!-- Don't forget to update the JSlink switch in client/Topic if you edit this! -->
<center>
<h1>Welcome to Aurorastation!</h1>
</center>
<p>
Aurorastation is a SS13 heavy roleplay server with antagonists enabled, that's been running since 2013. We utilize a customized build of the Baystation12 codebase, which can be found <a href="?JSlink=github">here, on Github.</a>
</p>
<p>
New players should take care to read <a href="?JSlink=rules">the rules.</a> Specially if you're new to roleplay. Should any questions arise, you are expected to adminhelp (F1) them and ask!
</p>
<p>
Further information about the server can be found at the following places:
<br>
<ul>
<li><a href="?JSlink=forums">The forums</a> - For whitelist applications, announcements, general discussion, etcetera.</li>
<li><a href="?JSlink=wiki">The wiki</a> - Primarily for lore reference, and gameplay guides. Note that the Baystation12 wiki linked there can be referenced for general mechanics.</li>
<li><a href="?JSlink=webint">The web-interface</a> - A custom website which adds additional functionality to the game. Anything from syndicate contracts, to reviewing your whitelists, bans, etcetera.</li>
</ul>
</p>
</div>
<div role="tabpanel" class="tab-pane" id="motd"><!--motd--></div>
<div role="tabpanel" class="tab-pane" id="memo"><!--memo--></div>
<div role="tabpanel" class="tab-pane" id="note"><!--note--></div>
</div>
</div>
</div>
</body>
<script src="jquery-2.0.0.min.js"></script>
<script src="bootstrap.min.js"></script>
<script src="html5shiv.min.js"></script>
<script src="respond.min.js"></script>
<script>
$(document).ready(function () {
// Deal with updating the tabs if any need an updated marker.
var updated_tabs = [];
if (updated_tabs.length > 0) {
var changes_needed = updated_tabs.length;
for (var i = 0; i < changes_needed; i++) {
$(updated_tabs[i]).addClass('updated');
}
}
// Handle tabs (mostly bootstrap).
$('#myTab a').click(function (e) {
e.preventDefault();
// One special exception for the web interface.
if ($(this).attr('href') == '#webint') {
window.location = '?routeWebInt=interface/login/sso_server;routeAttributes=location=user_dashboard;';
} else {
$(this).tab('show');
}
// Handle clicking of updated classes.
if ($(this).parent().hasClass('updated') == 1) {
$(this).parent().removeClass('updated');
}
});
// Dismiss an alert properly.
$('.alert').on('close.bs.alert', function () {
var command = '?JSlink=dismiss;notification=' + $(this).attr('notification');
window.location = command;
});
});
</script>
</html>