From 186049b3ed33f025eeb87eb34c19a28e1d5ba70a Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Tue, 2 Oct 2012 01:05:51 +0200 Subject: restructure code, switch from GTK to Web based UI - move barcode generation scripts into generation/ - move code to src/ - remove database analysis from invoice/graph - put database creation sql files into sql/ - remove glade builder file - add new templates/ directory, which contains files used by the Web-UI --- src/web.vala | 592 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 592 insertions(+) create mode 100644 src/web.vala (limited to 'src/web.vala') diff --git a/src/web.vala b/src/web.vala new file mode 100644 index 0000000..562a1ba --- /dev/null +++ b/src/web.vala @@ -0,0 +1,592 @@ +/* Copyright 2012, Sebastian Reichel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +public class WebServer { + private Soup.Server srv; + + void handler_default(Soup.Server server, Soup.Message msg, string path, GLib.HashTable? query, Soup.ClientContext client) { + try { + var l = new WebSession(server, msg, path, query, client); + var t = new WebTemplate("index.html", l); + t.replace("TITLE", "KtT Shop System"); + t.menu_set_active("home"); + msg.set_response("text/html", Soup.MemoryUse.COPY, t.data); + } catch(TemplateError e) { + stderr.printf(e.message+"\n"); + handler_404(server, msg, path, query, client); + } + } + + void handler_logout(Soup.Server server, Soup.Message msg, string path, GLib.HashTable? query, Soup.ClientContext client) { + try { + var l = new WebSession(server, msg, path, query, client); + l.logout(); + var t = new WebTemplate("logout.html", l); + t.replace("TITLE", "KtT Shop System"); + t.menu_set_active("home"); + msg.set_response("text/html", Soup.MemoryUse.COPY, t.data); + } catch(TemplateError e) { + stderr.printf(e.message+"\n"); + handler_404(server, msg, path, query, client); + } + } + + void handler_users(Soup.Server server, Soup.Message msg, string path, GLib.HashTable? query, Soup.ClientContext client) { + string[] pathparts = path.split("/"); + + if(pathparts.length <= 2) { + handler_todo(server, msg, path, query, client); + } else { + int id = int.parse(pathparts[2]); + + if(pathparts.length <= 3) { + handler_user_entry(server, msg, path, query, client, id); + } else { + switch(pathparts[3]) { + case "invoice": + uint16 selectedyear = (pathparts.length >= 5 && pathparts[4] != "") ? (uint16) int.parse(pathparts[4]) : (uint16) (new DateTime.now_local()).get_year(); + uint8 selectedmonth = (pathparts.length >= 6 && pathparts[5] != "") ? (uint8) int.parse(pathparts[5]) : (uint8) (new DateTime.now_local()).get_month(); + uint8 selectedday = (pathparts.length >= 7 && pathparts[6] != "") ? (uint8) int.parse(pathparts[6]) : (uint8) (new DateTime.now_local()).get_day_of_month(); + handler_user_invoice(server, msg, path, query, client, id, selectedyear, selectedmonth, selectedday); + break; + case "stats": + handler_todo(server, msg, path, query, client); + break; + default: + handler_404(server, msg, path, query, client); + break; + } + } + } + } + + void handler_user_entry(Soup.Server server, Soup.Message msg, string path, GLib.HashTable? query, Soup.ClientContext client, int id) { + try { + var l = new WebSession(server, msg, path, query, client); + if(id != l.user && !l.superuser) { + handler_403(server, msg, path, query, client); + return; + } + var t = new WebTemplate("users/entry.html", l); + t.replace("TITLE", "KtT Shop System: User Info %llu".printf(id)); + t.menu_set_active("users"); + + var userinfo = db.get_user_info(id); + + t.replace("UID", "%d".printf(userinfo.id)); + t.replace("FIRSTNAME", userinfo.firstname); + t.replace("LASTNAME", userinfo.lastname); + t.replace("EMAIL", userinfo.email); + t.replace("GENDER", userinfo.gender); + t.replace("STREET", userinfo.street); + t.replace("POSTALCODE", "%d".printf(userinfo.plz)); + t.replace("CITY", userinfo.city); + + var userauth = db.get_user_auth(id); + t.replace("DISABLED", userauth.disabled ? "true" : "false"); + t.replace("SUPERUSER", userauth.superuser ? "true" : "false"); + + msg.set_response("text/html", Soup.MemoryUse.COPY, t.data); + } catch(TemplateError e) { + stderr.printf(e.message+"\n"); + handler_404(server, msg, path, query, client); + } + } + + void handler_user_invoice(Soup.Server server, Soup.Message msg, string path, GLib.HashTable? query, Soup.ClientContext client, int id, uint16 selectedyear, uint8 selectedmonth, uint8 selectedday) { + DateTime start, stop; + + DateYear y = (DateYear) selectedyear; + if(!y.valid() || y > 8000) { + selectedyear = (uint16) new DateTime.now_local().get_year(); + y = (DateYear) selectedyear; + } + + DateMonth m = (DateMonth) selectedmonth; + if(selectedmonth != 0 && !m.valid()) { + selectedmonth = (uint8) new DateTime.now_local().get_month(); + m = (DateMonth) selectedmonth; + } + + DateDay d = (DateDay) selectedday; + if(selectedday != 0 && !d.valid()) { + selectedday = (uint8) new DateTime.now_local().get_day_of_month(); + d = (DateDay) selectedday; + } + + try { + var l = new WebSession(server, msg, path, query, client); + if(id != l.user && !l.superuser) { + handler_403(server, msg, path, query, client); + return; + } + var t = new WebTemplate("users/invoice.html", l); + t.replace("TITLE", "KtT Shop System: User Invoice %llu".printf(id)); + t.menu_set_active("users"); + + /* years, in which something has been purchased by the user */ + var first = db.get_first_purchase(id); + var last = db.get_last_purchase(id); + string years = ""; + for(int i=first.get_year(); i <= last.get_year(); i++) { + years += @"
  • $i
  • "; + } + t.replace("YEARS", years); + t.replace("SELECTEDYEAR", @"$selectedyear"); + + /* months, in which something has been purchased by the user */ + string[] monthnames = { "All Months", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; + string months = @"
  • All Months
  • "; + for(int i=1; i 0 && i < first.get_month()) + months += @"
  • $(monthnames[i])
  • "; + else if(selectedyear < first.get_year()) + months += @"
  • $(monthnames[i])
  • "; + else if(last.get_year() == selectedyear && i > last.get_month()) + months += @"
  • $(monthnames[i])
  • "; + else if(selectedyear > last.get_year()) + months += @"
  • $(monthnames[i])
  • "; + else + months += @"
  • $(monthnames[i])
  • "; + } + t.replace("MONTHS", months); + t.replace("SELECTEDMONTH", @"$(monthnames[selectedmonth])"); + + int dim = m.valid() ? Date.get_days_in_month(m, y) : 0; + string days = @"
  • All Days
  • "; + for(int i=1; i<=dim; i++) { + if(first.get_year() == selectedyear && first.get_month() == selectedmonth && i < first.get_day_of_month()) + days += @"
  • $i
  • "; + else if(selectedyear < first.get_year() || (selectedyear == first.get_year() && selectedmonth < first.get_month())) + days += @"
  • $i
  • "; + else if(last.get_year() == selectedyear && last.get_month() == selectedmonth && i > last.get_day_of_month()) + days += @"
  • $i
  • "; + else if(selectedyear > last.get_year() || (selectedyear == last.get_year() && selectedmonth > last.get_month())) + days += @"
  • $i
  • "; + else + days += @"
  • $i
  • "; + } + t.replace("DAYS", days); + if(selectedday > 0) + t.replace("SELECTEDDAY", @"$selectedday"); + else + t.replace("SELECTEDDAY", "All Days"); + + if(selectedday != 0) { + start = new DateTime.local(selectedyear, selectedmonth, selectedday, 8, 0, 0); + stop = start.add_days(1); + } else if(selectedmonth != 0) { + start = new DateTime.local(selectedyear, selectedmonth, 16, 8, 0, 0); + stop = start.add_months(1); + } else { + start = new DateTime.local(selectedyear, 1, 16, 8, 0, 0); + stop = start.add_years(1); + } + + string table = ""; + Price sum = 0; + foreach(var e in db.get_invoice(id, start.to_unix(), stop.to_unix())) { + var timestamp = new DateTime.from_unix_utc(e.timestamp); + var date = timestamp.format("%d.%m.%Y"); + var time = timestamp.format("%H:%M:%S"); + var product = e.product.name; + var price = e.price; + table += @"$date$time$product$price€"; + sum += e.price; + } + + t.replace("DATA", table); + t.replace("SUM", @"$sum"); + + msg.set_response("text/html", Soup.MemoryUse.COPY, t.data); + } catch(TemplateError e) { + stderr.printf(e.message+"\n"); + handler_404(server, msg, path, query, client); + } + } + + void handler_products(Soup.Server server, Soup.Message msg, string path, GLib.HashTable? query, Soup.ClientContext client) { + string[] pathparts = path.split("/"); + + if(pathparts.length <= 2 || pathparts[2] == "") { + handler_product_list(server, msg, path, query, client); + } else { + uint64 id = uint64.parse(pathparts[2]); + + if(pathparts.length <= 3) { + handler_product_entry(server, msg, path, query, client, id); + } else { + switch(pathparts[3]) { + case "restock": + handler_product_restock(server, msg, path, query, client, id); + break; + default: + handler_product_entry(server, msg, path, query, client, id); + break; + } + } + } + } + + void handler_product_list(Soup.Server server, Soup.Message msg, string path, GLib.HashTable? query, Soup.ClientContext client) { + try { + var l = new WebSession(server, msg, path, query, client); + var t = new WebTemplate("products/index.html", l); + t.replace("TITLE", "KtT Shop System: Product List"); + t.menu_set_active("products"); + + string table = ""; + foreach(var e in db.get_stock()) { + table += "%s%s%d%s€%s€".printf( + e.id, e.id, e.id, e.name, e.amount, e.memberprice, e.guestprice + ); + } + + t.replace("DATA", table); + + if(l.superuser) + t.replace("NEWPRODUCT", "block"); + else + t.replace("NEWPRODUCT", "none"); + + msg.set_response("text/html", Soup.MemoryUse.COPY, t.data); + } catch(TemplateError e) { + stderr.printf(e.message+"\n"); + handler_404(server, msg, path, query, client); + } + } + + void handler_product_entry(Soup.Server server, Soup.Message msg, string path, GLib.HashTable? query, Soup.ClientContext client, uint64 id) { + try { + var l = new WebSession(server, msg, path, query, client); + var t = new WebTemplate("products/entry.html", l); + t.replace("TITLE", "KtT Shop System: Product %llu".printf(id)); + t.menu_set_active("products"); + + /* ean */ + t.replace("EAN", "%llu".printf(id)); + + /* name */ + string name = db.get_product_name(id); + t.replace("NAME", name); + + /* amount */ + t.replace("AMOUNT", "%d".printf(db.get_product_amount(id))); + + if(l.superuser) + t.replace("RESTOCK", "block"); + else + t.replace("RESTOCK", "none"); + + /* prices */ + string prices = ""; + foreach(var e in db.get_prices(id)) { + var time = new DateTime.from_unix_local(e.valid_from); + prices += @"%s$(e.memberprice)€$(e.guestprice)€".printf( + time.format("%Y-%m-%d %H:%M") + ); + } + t.replace("PRICES", prices); + + /* restocks */ + string restocks = ""; + foreach(var e in db.get_restocks(id)) { + var time = new DateTime.from_unix_local(e.timestamp); + restocks += "%s%d%s€".printf( + time.format("%Y-%m-%d %H:%M"), e.amount, e.price + ); + } + t.replace("RESTOCKS", restocks); + + msg.set_response("text/html", Soup.MemoryUse.COPY, t.data); + } catch(TemplateError e) { + stderr.printf(e.message+"\n"); + handler_404(server, msg, path, query, client); + } + } + + void handler_products_new(Soup.Server server, Soup.Message msg, string path, GLib.HashTable? query, Soup.ClientContext client) { + try { + var session = new WebSession(server, msg, path, query, client); + var template = new WebTemplate("products/new.html", session); + template.replace("TITLE", "KtT Shop System: New Product"); + template.menu_set_active("products"); + + if(!session.superuser) { + handler_403(server, msg, path, query, client); + return; + } + + if(query != null && query.contains("name") && query.contains("id") && query.contains("memberprice") && query.contains("guestprice")) { + var name = query["name"]; + var ean = uint64.parse(query["id"]); + Price memberprice = int.parse(query["memberprice"]); + Price guestprice = int.parse(query["guestprice"]); + + if(ean > 0 && memberprice > 0 && guestprice > 0 && db.new_product(ean, name, memberprice, guestprice)) { + template.replace("NAME", name); + template.replace("EAN", @"$ean"); + template.replace("MEMBERPRICE", @"$memberprice€"); + template.replace("GUESTPRICE", @"$guestprice€"); + template.replace("NEW.OK", "block"); + template.replace("NEW.FAIL", "none"); + } else { + template.replace("NAME", "..."); + template.replace("NEW.OK", "none"); + template.replace("NEW.FAIL", "block"); + } + } else { + template.replace("NAME", "..."); + template.replace("NEW.OK", "none"); + template.replace("NEW.FAIL", "block"); + } + + msg.set_response("text/html", Soup.MemoryUse.COPY, template.data); + } catch(TemplateError e) { + stderr.printf(e.message+"\n"); + handler_404(server, msg, path, query, client); + } + } + + void handler_product_restock(Soup.Server server, Soup.Message msg, string path, GLib.HashTable? query, Soup.ClientContext client, uint64 id) { + try { + var session = new WebSession(server, msg, path, query, client); + + if(!session.superuser) { + handler_403(server, msg, path, query, client); + return; + } + + var template = new WebTemplate("products/restock.html", session); + template.replace("TITLE", "KtT Shop System: Restock Product %llu".printf(id)); + template.replace("NAME", db.get_product_name(id)); + template.menu_set_active("products"); + + if(query != null && query.contains("amount") && query.contains("price")) { + int amount = int.parse(query["amount"]); + Price price = int.parse(query["price"]); + + if(amount >= 1 && price >= 1) { + if(db.restock(session.user, id, amount, price)) { + template.replace("AMOUNT", @"$amount"); + template.replace("PRICE", @"$price"); + template.replace("RESTOCK.OK", "block"); + template.replace("RESTOCK.FAIL", "none"); + msg.set_response("text/html", Soup.MemoryUse.COPY, template.data); + return; + } + } + } + + template.replace("RESTOCK.OK", "none"); + template.replace("RESTOCK.FAIL", "block"); + msg.set_response("text/html", Soup.MemoryUse.COPY, template.data); + return; + } catch(TemplateError e) { + stderr.printf(e.message+"\n"); + handler_404(server, msg, path, query, client); + } + } + + void handler_stats(Soup.Server server, Soup.Message msg, string path, GLib.HashTable? query, Soup.ClientContext client) { + try { + var l = new WebSession(server, msg, path, query, client); + var t = new WebTemplate("stats/index.html", l); + t.replace("TITLE", "KtT Shop System: Statistics"); + t.menu_set_active("stats"); + + var stats = db.get_stats_info(); + + t.replace("NUMBER_OF_ARTICLES", @"$(stats.count_articles)"); + t.replace("NUMBER_OF_USERS", @"$(stats.count_users)"); + t.replace("STOCK_VALUE", @"$(stats.stock_value)€"); + t.replace("TOTAL_SALES", @"$(stats.sales_total)€"); + t.replace("TOTAL_PROFIT", @"$(stats.profit_total)€"); + t.replace("SALES_TODAY", @"$(stats.sales_today)€"); + t.replace("PROFIT_TODAY", @"$(stats.profit_today)€"); + t.replace("SALES_THIS_MONTH", @"$(stats.sales_this_month)€"); + t.replace("PROFIT_THIS_MONTH", @"$(stats.profit_this_month)€"); + t.replace("SALES_PER_DAY", @"$(stats.sales_per_day)€"); + t.replace("PROFIT_PER_DAY", @"$(stats.profit_per_day)€"); + t.replace("SALES_PER_MONTH", @"$(stats.sales_per_month)€"); + t.replace("PROFIT_PER_MONTH", @"$(stats.profit_per_month)€"); + + msg.set_response("text/html", Soup.MemoryUse.COPY, t.data); + } catch(TemplateError e) { + stderr.printf(e.message+"\n"); + handler_404(server, msg, path, query, client); + } + } + + void handler_stats_stock(Soup.Server server, Soup.Message msg, string path, GLib.HashTable? query, Soup.ClientContext client) { + try { + var l = new WebSession(server, msg, path, query, client); + var t = new WebTemplate("stats/stock.html", l); + string data = db.get_stats_stock().json; + t.replace("DATA", data); + t.replace("TITLE", "KtT Shop System: Statistics: Stock"); + t.menu_set_active("stats"); + msg.set_response("text/html", Soup.MemoryUse.COPY, t.data); + } catch(TemplateError e) { + stderr.printf(e.message+"\n"); + handler_404(server, msg, path, query, client); + } + } + + void handler_stats_profit_per_day(Soup.Server server, Soup.Message msg, string path, GLib.HashTable? query, Soup.ClientContext client) { + try { + var l = new WebSession(server, msg, path, query, client); + var t = new WebTemplate("stats/profit_per_day.html", l); + string data = db.get_stats_profit_per_day().json; + t.replace("DATA", data); + t.replace("TITLE", "KtT Shop System: Statistics: Profit"); + t.menu_set_active("stats"); + msg.set_response("text/html", Soup.MemoryUse.COPY, t.data); + } catch(TemplateError e) { + stderr.printf(e.message+"\n"); + handler_404(server, msg, path, query, client); + } + } + + void handler_stats_profit_per_weekday(Soup.Server server, Soup.Message msg, string path, GLib.HashTable? query, Soup.ClientContext client) { + try { + var l = new WebSession(server, msg, path, query, client); + var t = new WebTemplate("stats/profit_per_weekday.html", l); + string data = db.get_stats_profit_per_weekday().json; + t.replace("DATA", data); + t.replace("TITLE", "KtT Shop System: Statistics: Profit/Weekday"); + t.menu_set_active("stats"); + msg.set_response("text/html", Soup.MemoryUse.COPY, t.data); + } catch(TemplateError e) { + stderr.printf(e.message+"\n"); + handler_404(server, msg, path, query, client); + } + } + + void handler_stats_profit_per_product(Soup.Server server, Soup.Message msg, string path, GLib.HashTable? query, Soup.ClientContext client) { + try { + var l = new WebSession(server, msg, path, query, client); + var t = new WebTemplate("stats/profit_per_product.html", l); + string data = db.get_stats_profit_per_products().json; + t.replace("DATA", data); + t.replace("TITLE", "KtT Shop System: Statistics: Profit/Product"); + t.menu_set_active("stats"); + msg.set_response("text/html", Soup.MemoryUse.COPY, t.data); + } catch(TemplateError e) { + stderr.printf(e.message+"\n"); + handler_404(server, msg, path, query, client); + } + } + + void handler_js(Soup.Server server, Soup.Message msg, string path, GLib.HashTable? query, Soup.ClientContext client) { + try { + var t = new WebTemplate.DATA(path); + msg.set_response("text/javascript", Soup.MemoryUse.COPY, t.data); + } catch(TemplateError e) { + stderr.printf(e.message+"\n"); + handler_404(server, msg, path, query, client); + } + } + + void handler_css(Soup.Server server, Soup.Message msg, string path, GLib.HashTable? query, Soup.ClientContext client) { + try { + var t = new WebTemplate.DATA(path); + msg.set_response("text/css", Soup.MemoryUse.COPY, t.data); + } catch(TemplateError e) { + stderr.printf(e.message+"\n"); + handler_404(server, msg, path, query, client); + } + } + + void handler_img(Soup.Server server, Soup.Message msg, string path, GLib.HashTable? query, Soup.ClientContext client) { + try { + var f = File.new_for_path("templates/"+path); + uint8[] data = null; + + if(f.query_exists() && f.load_contents(null, out data, null)) { + msg.set_response("image/png", Soup.MemoryUse.COPY, data); + return; + } + } catch(Error e) { + error("there has been some error: %s!\n", e.message); + } + + handler_404(server, msg, path, query, client); + return; + } + + void handler_404(Soup.Server server, Soup.Message msg, string path, GLib.HashTable? query, Soup.ClientContext client) { + string result = "Page not Found\n"; + msg.set_status(404); + msg.set_response("text/plain", Soup.MemoryUse.COPY, result.data); + } + + void handler_403(Soup.Server server, Soup.Message msg, string path, GLib.HashTable? query, Soup.ClientContext client) { + try { + var session = new WebSession(server, msg, path, query, client); + var template = new WebTemplate("errors/403.html", session); + template.replace("TITLE", "Access Denied"); + template.menu_set_active(""); + msg.set_status(403); + msg.set_response("text/html", Soup.MemoryUse.COPY, template.data); + } catch(TemplateError e) { + stderr.printf(e.message+"\n"); + handler_404(server, msg, path, query, client); + } } + + void handler_todo(Soup.Server server, Soup.Message msg, string path, GLib.HashTable? query, Soup.ClientContext client) { + try { + var session = new WebSession(server, msg, path, query, client); + var template = new WebTemplate("errors/todo.html", session); + template.replace("TITLE", "KtT Shop System: ToDo"); + template.menu_set_active(""); + msg.set_response("text/html", Soup.MemoryUse.COPY, template.data); + } catch(TemplateError e) { + stderr.printf(e.message+"\n"); + handler_404(server, msg, path, query, client); + } + } + + public WebServer(int port = 8080) { + srv = new Soup.Server(Soup.SERVER_PORT, port); + + /* index */ + srv.add_handler("/", handler_default); + + /* logout */ + srv.add_handler("/logout", handler_logout); + + /* data (js, css, img) */ + srv.add_handler("/js", handler_js); + srv.add_handler("/css", handler_css); + srv.add_handler("/img", handler_img); + + /* products */ + srv.add_handler("/products", handler_products); + srv.add_handler("/products/new", handler_products_new); + + /* stats */ + srv.add_handler("/stats", handler_stats); + srv.add_handler("/stats/stock", handler_stats_stock); + srv.add_handler("/stats/profit_per_day", handler_stats_profit_per_day); + srv.add_handler("/stats/profit_per_weekday", handler_stats_profit_per_weekday); + srv.add_handler("/stats/profit_per_product", handler_stats_profit_per_product); + + /* users */ + srv.add_handler("/users", handler_users); + + srv.run(); + } +} -- cgit v1.2.3