diff --git a/foftickets.js b/foftickets.js
index 559058e..bf00ff5 100644
--- a/foftickets.js
+++ b/foftickets.js
@@ -27,9 +27,11 @@ const QRSalt ="!SaltyMagic5392370662";
// + Claim Any Tickets
// + Purge Revoked Function (Admin)
// + Issue Tickets from Camp Admin Page
-// Make one Update button on manytickets (User)
-// Make one Update button on editcamp (Admin)
-// Send email when new tickets are issued/offered
+// + Send email when new tickets are issued/offered
+// + Make one Update button on editcamp (Admin)
+// + Make one Update button on manytickets (User)
+// Turn ticket use on/off from Settings (Admin)
+// Turn email on/off from Settings (Admin)
// Magic-link Login System
// Deactivate individual magic links (User)
// Deactivate individual magic links (Admin)
@@ -39,8 +41,9 @@ const QRSalt ="!SaltyMagic5392370662";
// + Use a templating engine
// + Store password hashed and salted
// Make all HTML look nice
-// Logging and Replay system
+// Logging and Replay system(?)
// Stripe Integration
+// More efficent data structure: TicketsByCamp, TicketsByOffered, TicketsByOwner
//
//
@@ -110,6 +113,7 @@ function MagicLinkValid(email,hash) {
const users={};
const tickets={};
const camps={};
+const settings={};
function InitDatabase() {
for (const key in users ) delete users[key];
@@ -206,6 +210,7 @@ app.post("/camps",requireSuperUser,(req,res) => {
app.get('/editcamp', requireSuperUser, (req,res) => {
let campname=req.query.campname;
+ if (!camps[campname]) return res.redirect("/");
const edit={ username:req.session.username, superuser:req.session.superuser, campname:campname, leader:camps[campname].leader, tickets: {} };
for (const t in tickets) {
const parts=t.split("-");
@@ -328,6 +333,27 @@ app.post("/updateoffered2", requireLogin, (req,res) => {
})
+app.post("/updateoffered2su", requireSuperUser, (req,res) => {
+ const emaillist={};
+ for (name in req.body) {
+ let ticket=0;
+ if (name.endsWith("-owner" )) { ticket=name.slice(0,-6); tickets[ticket].owner =req.body[name]; }
+ else if (name.endsWith("-status" )) { ticket=name.slice(0,-7); tickets[ticket].status =req.body[name]; }
+ else if (name.endsWith("-offered")) {
+ ticket=name.slice(0,-8);
+ if (tickets[ticket].offered!=req.body[name]) {
+ tickets[ticket].offered=req.body[name];
+ emaillist[req.body[name]]=1;
+ }
+ }
+ }
+ for (email in emaillist) EmailTickets(email);
+ const referer = req.get('Referer');
+ if (referer) return res.redirect(referer);
+ else return res.redirect('/');
+ })
+
+
app.post("/updateticketsu", requireSuperUser, (req,res) => {
const ticket=req.body.ticket;
const owner=req.body.owner;
@@ -344,13 +370,16 @@ app.get("/useticket",(req,res) => {
if (!tickets[ticket]) return res.status(500).send("Ticket "+ticket+" not found.");
if (hashQR(ticket,tickets[ticket].owner)!=hash) return res.status(500).send("Ticket "+ticket+" was transferred to "+tickets[ticket].owner);
if (tickets[ticket].status!="i") return res.status(500).send("Ticket "+ticket+" has already been used.");
- tickets[ticket].status="u"; // LOG
- return res.send("
Welcome, "+tickets[ticket].owner+" to Falls on Fire! Ticket "+ticket+" has now been used.
");
-})
+ if (settings['enable-email']) {
+ tickets[ticket].status="u"; // LOG
+ return res.send("Welcome, "+tickets[ticket].owner+" to Falls on Fire! Ticket "+ticket+" has now been used.
");
+ } else return res.send("Your ticket is good, "+tickets[ticket].owner+", but the server is not in Event Mode, so Ticket "+ticket+" is still valid.");
+ })
async function EmailTickets(email) {
+ if (!settings['enable-email']) return;
let offered=0;
for (const ticket in tickets) if (tickets[ticket].offered==email) offered++;
if (offered==0) return;
@@ -505,6 +534,11 @@ app.post('/purge',requireSuperUser, (req,res) => {
res.render('settings',{ username:req.session.username, superuser:req.session.superuser, message: "Purged "+count+" revoked tickets" })
});
+app.post('/update-setting', requireSuperUser, (req, res) => {
+ settings[req.body.name]=req.body.checked;
+ res.json({ success: true, message: 'Checkbox state updated successfully' });
+});
+
// Start the server
app.listen(PORT, () => {
diff --git a/views/editcamp.ejs b/views/editcamp.ejs
index 83b4731..ff248f3 100644
--- a/views/editcamp.ejs
+++ b/views/editcamp.ejs
@@ -13,12 +13,10 @@
@@ -59,66 +60,28 @@
// JavaScript to change the form element
const blankimage=document.getElementById("QRCodeImage").src;
-const ResponseDisplay = document.getElementById("server-response");
-let ResponseStack = 0;
-let ResponseError="";
-let ooEdits={}; // Owner or Offer has been edited
-
-function UpdateSR(delta) {
- ResponseStack+=delta;
- if (ResponseError!="") ResponseDisplay.textContent=ResponseError;
- else if (ResponseStack>0) ResponseDisplay.textContent="Waiting for Server";
- else ResponseDisplay.textContent="Server Ready";
- }
-
-
-function changeStatus(el) {
- console.log("Change Status: ",el.target.value);
- const id=el.target.id.slice(0,-7);
- const js=JSON.stringify( { ticket: id, status: el.target.value } );
- const fetchtable={ method:'POST', headers: { 'Content-Type': 'application/json' }, body: js };
- UpdateSR(1);
- fetch('/changestatus',fetchtable)
- .then( response => { if (!response.ok) throw new Error(`Server responded with status ${response.status}`); else return response.json(); } )
- .then( data => UpdateSR(-1) )
- .catch( error => { console.log("Here is the error!"+error); ResponseError=error; UpdateSR(-1); })
- }
document.body.addEventListener("click", event => {
const id=event.target.id;
console.log("Click event on id ",id);
if (id.endsWith("-action")) {
const id0=id.slice(0,-7);
- if (ooEdits[id0]) {
- UpdateSR(1);
- const ownerEdit=document.getElementById(id0+"-owner");
- const offeredEdit=document.getElementById(id0+"-offered");
- const js=JSON.stringify( { ticket: id0, owner:ownerEdit.value, offered: offeredEdit.value } );
- const fetchtable={ method:'POST', headers: { 'Content-Type': 'application/json' }, body: js };
- fetch('/updateticketsu',fetchtable)
- .then( response => { if (!response.ok) throw new Error(`Server responded with status ${response.status}`); else return response.json(); } )
- .then( data => { UpdateSR(-1); event.target.textContent="QRCode"; ooEdits[id0]=false; } )
- .catch( error => { console.log("Here is the error!"+error); ResponseError=error; UpdateSR(-1); })
- } else {
- const modal = document.getElementById('QRShow');
- const qrbanner=document.getElementById('QRBanner');
- const qrcodeimage=document.getElementById('QRCodeImage');
- const closeModalSpan = document.querySelector('.close');
- modal.style.display = 'flex';
- const js=JSON.stringify( { ticket: id0 } );
- const fetchtable={ method:'POST', headers: { 'Content-Type': 'application/json' }, body: js };
- qrcodeimage.src=blankimage;
- fetch('/qrcodesu',fetchtable)
- .then( response => { if (!response.ok) throw new Error(`Server responded with status ${response.status}`); else return response.json(); } )
- .then( data => {
- console.log("Data is: ",data);
- qrbanner.innerText="Ticket: "+id0+" Owner: "+data.owner;
- qrcodeimage.src=data.qrcode;
- } )
- .catch( error => { console.log("Here is the error!"+error); ResponseError=error; UpdateSR(-1); })
-
- console.log("QRCode ",id0);
- }
+ const modal = document.getElementById('QRShow');
+ const qrbanner=document.getElementById('QRBanner');
+ const qrcodeimage=document.getElementById('QRCodeImage');
+ const closeModalSpan = document.querySelector('.close');
+ modal.style.display = 'flex';
+ const js=JSON.stringify( { ticket: id0 } );
+ const fetchtable={ method:'POST', headers: { 'Content-Type': 'application/json' }, body: js };
+ qrcodeimage.src=blankimage;
+ fetch('/qrcodesu',fetchtable)
+ .then( response => { if (!response.ok) throw new Error(`Server responded with status ${response.status}`); else return response.json(); } )
+ .then( data => {
+ console.log("Data is: ",data);
+ qrbanner.innerText="Ticket: "+id0+" Owner: "+data.owner;
+ qrcodeimage.src=data.qrcode;
+ } )
+ .catch( error => { console.log("Here is the error!"+error); ResponseError=error; })
} else if (["QRShow","QRCodeImage","QRBackground","QRBanner"].includes(id)) {
const modal = document.getElementById('QRShow');
modal.style.display="none";
@@ -128,21 +91,14 @@ document.body.addEventListener("click", event => {
-const pulldowns = document.querySelectorAll("[id$=-status]");
-pulldowns.forEach(el => {
- const pd = document.getElementById(el.id);
- pd.addEventListener("change",changeStatus);
- });
+const MessageArea=document.getElementById("message");
+const pulldowns = document.getElementsByClassName("status");
+for (let i=0; i MessageArea.textContent="Be sure to use the Update Tickets button.");
+const owners = document.getElementsByClassName("owner");
+for (let i=0; iMessageArea.textContent= "Be sure to use the Update Tickets button.");
+const offereds = document.getElementsByClassName("offered");
+for (let i=0; iMessageArea.textContent= "Be sure to use the Update Tickets button.");
-const owners = document.querySelectorAll("[id$=-owner]");
-owners.forEach(el => {
- const id0=el.id.slice(0,-6);
- const OwnerEdit = document.getElementById(id0+"-owner" );
- const OfferedEdit = document.getElementById(id0+"-offered");
- const ActionButton = document.getElementById(id0+"-action" );
- OwnerEdit .addEventListener('input', () => { ActionButton.textContent = "Update"; ooEdits[id0]=true; console.log("Changed OfferedEdit:",OfferedEdit.value); });
- OfferedEdit.addEventListener('input', () => { ActionButton.textContent = "Update"; ooEdits[id0]=true; console.log("Changed OfferedEdit:",OfferedEdit.value); });
- });