diff options
author | Holger Cremer <HolgerCremer@gmail.com> | 2018-08-27 19:38:11 +0200 |
---|---|---|
committer | Holger Cremer <HolgerCremer@gmail.com> | 2018-08-27 19:38:11 +0200 |
commit | 8f2ba2050ee78d0e4a47f1277c6bc4422d06170c (patch) | |
tree | c008d2878905e03df7a8bf8bd3330762cc2d8f43 /src | |
parent | bb55e121576a5b5d225bfc68c5062f386cc32db9 (diff) | |
parent | 3fc3ea6c6df237dbdf48d14703118b747bf5d647 (diff) | |
download | serial-barcode-scanner-8f2ba2050ee78d0e4a47f1277c6bc4422d06170c.tar.bz2 |
Merge branch 'master' into better_inventory
Conflicts:
README
data/templates/products/entry.html
docker/Dockerfile
docker/init.sh
src/database/database.vala
src/database/db-interface.vala
src/pdf-stock/Makefile
src/pdf-stock/pdf-stock-interface.vala
src/pdf-stock/pdf-stock.vala
src/web/Makefile
src/web/main.vala
templates/menu.html
templates/products/index.html
Diffstat (limited to 'src')
87 files changed, 1566 insertions, 863 deletions
diff --git a/src/Makefile b/src/Makefile deleted file mode 100644 index d5bb40f..0000000 --- a/src/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -DAEMONS=audio backup cli config curses-ui database input-device invoice mail pdf-invoice pdf-stock pgp scanner-session serial-device input-device web - -all: - @$(foreach dir,$(DAEMONS),cd $(dir) && echo "Building $(dir)..." && make --no-print-directory all ; cd ..;) - -clean: - @$(foreach dir,$(DAEMONS),cd $(dir) && make --no-print-directory clean ; cd ..;) - -.PHONY: all clean diff --git a/src/audio/.gitignore b/src/audio/.gitignore deleted file mode 100644 index d5cc284..0000000 --- a/src/audio/.gitignore +++ /dev/null @@ -1 +0,0 @@ -audio diff --git a/src/audio/Makefile b/src/audio/Makefile deleted file mode 100644 index 950287a..0000000 --- a/src/audio/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -all: audio - @echo > /dev/null - -audio: main.vala audio.vala audio-interface.vala ../config/config-interface.vala - valac -X -w -o $@ --pkg gstreamer-1.0 --pkg gio-2.0 $^ - -clean: - rm -rf audio - -.PHONY: all clean diff --git a/src/audio/audio-interface.vala b/src/audio/audio-interface.vala index fe13af5..721a484 100644 --- a/src/audio/audio-interface.vala +++ b/src/audio/audio-interface.vala @@ -17,8 +17,16 @@ public interface AudioPlayer : Object { public abstract signal void end_of_stream(); - public abstract void play_system(string file) throws IOError; - public abstract string get_random_user_theme() throws IOError; - public abstract string[] get_user_themes() throws IOError; - public abstract void play_user(string theme, string type) throws IOError; + public abstract void play_system(string file) throws DBusError, IOError; + public abstract string get_random_user_theme() throws DBusError, IOError; + public abstract string[] get_user_themes() throws DBusError, IOError; + public abstract void play_user(string theme, string type) throws DBusError, IOError; +} + +public enum AudioType { + ERROR, + LOGIN, + LOGOUT, + PURCHASE, + INFO } diff --git a/src/audio/audio.vala b/src/audio/audio.vala index 9afb840..0d969e2 100644 --- a/src/audio/audio.vala +++ b/src/audio/audio.vala @@ -30,24 +30,31 @@ public class AudioPlayerImplementation { return true; } - public AudioPlayerImplementation(string path) throws IOError { + public AudioPlayerImplementation(string path) throws IOError, DBusError { this.path = path; var alsa = Gst.ElementFactory.make("alsasink", "alsa"); - if (alsa == null) - throw new GLib.IOError.FAILED("Cannot find alsa GStreamer plugin"); + if (alsa == null) { + var msg = _("Cannot find alsa GStreamer plugin"); + stderr.printf(msg); + throw new GLib.IOError.FAILED(msg); + } p = Gst.ElementFactory.make("playbin", "player"); - if (p == null) - throw new GLib.IOError.FAILED("Cannot find playbin2 GStreamer plugin"); + if (p == null) { + var msg = _("Cannot find playbin2 GStreamer plugin"); + stderr.printf(msg); + throw new GLib.IOError.FAILED(msg); + } p.set("audio-sink", alsa); p.get_bus().add_watch(Priority.DEFAULT, bus_callback); } - public void play_system(string file) { + public void play_system(string file) throws IOError, DBusError { p.set_state(Gst.State.NULL); - p.uri = "file://" + path + "system/" + file; + p.uri = "file://" + Path.build_filename(path, "system", file); + stdout.printf("Play: %s\n", p.uri); p.set_state(Gst.State.PLAYING); } @@ -76,18 +83,19 @@ public class AudioPlayerImplementation { return files[index]; } - public string get_random_user_theme() { - return get_random_file(path + "user/"); + public string get_random_user_theme() throws IOError, DBusError { + return get_random_file(Path.build_filename(path, "user")); } - public string[] get_user_themes() { - return get_files(path + "user/"); + public string[] get_user_themes() throws IOError, DBusError { + return get_files(Path.build_filename(path, "user")); } - public void play_user(string theme, string type) { + public void play_user(string theme, string type) throws IOError, DBusError { p.set_state(Gst.State.NULL); - var file = get_random_file(path + "user/" + theme+ "/" + type); - p.uri = "file://" + path + "user/" + theme+ "/" + type + "/" + file; + var file = get_random_file(Path.build_filename(path, "user", theme, type)); + p.uri = "file://" + Path.build_filename(path, "user", theme, type, file); + stdout.printf("Play: %s\n", p.uri); p.set_state(Gst.State.PLAYING); } } diff --git a/src/audio/main.vala b/src/audio/main.vala index ac64f52..2fdaf61 100644 --- a/src/audio/main.vala +++ b/src/audio/main.vala @@ -18,22 +18,28 @@ AudioPlayerImplementation player; public static int main(string[] args) { Gst.init(ref args); + Intl.setlocale(LocaleCategory.ALL, ""); + Intl.textdomain("shopsystem"); + Bus.own_name( BusType.SYSTEM, "io.mainframe.shopsystem.AudioPlayer", BusNameOwnerFlags.NONE, - on_bus_aquired, + on_bus_acquired, () => {}, - () => stderr.printf("Could not aquire name\n")); + () => stderr.printf(_("Could not acquire name\n"))); try { Config cfg = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Config", "/io/mainframe/shopsystem/config"); - var path = cfg.get_string("AUDIO", "path"); + var datapath = cfg.get_string("GENERAL", "datapath"); + var path = Path.build_filename(datapath, "sounds"); player = new AudioPlayerImplementation(path); } catch(IOError e) { - error("IOError: %s\n", e.message); + error(_("IO Error: %s\n"), e.message); } catch(KeyFileError e) { - error("Config Error: %s\n", e.message); + error(_("Config Error: %s\n"), e.message); + } catch(DBusError e) { + error(_("DBus Error: %s\n"), e.message); } new MainLoop().run(); @@ -41,10 +47,10 @@ public static int main(string[] args) { return 0; } -void on_bus_aquired(DBusConnection con) { +void on_bus_acquired(DBusConnection con) { try { con.register_object("/io/mainframe/shopsystem/audio", player); } catch(IOError e) { - stderr.printf("Could not register service\n"); + stderr.printf(_("Could not register service\n")); } } diff --git a/src/backup/.gitignore b/src/backup/.gitignore deleted file mode 100644 index ec76ec2..0000000 --- a/src/backup/.gitignore +++ /dev/null @@ -1 +0,0 @@ -backup diff --git a/src/backup/Makefile b/src/backup/Makefile deleted file mode 100644 index de8eee3..0000000 --- a/src/backup/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -all: backup - @echo > /dev/null - -backup: main.vala ../mail/mailer-interface.vala ../config/config-interface.vala - valac -X -w -o $@ --pkg gio-2.0 $^ - -clean: - rm -f backup - -.PHONY: all clean diff --git a/src/backup/main.vala b/src/backup/main.vala index 4aed71f..670e56a 100644 --- a/src/backup/main.vala +++ b/src/backup/main.vala @@ -14,6 +14,9 @@ */ public static int main(string[] args) { + Intl.setlocale(LocaleCategory.ALL, ""); + Intl.textdomain("shopsystem"); + try { Mailer mailer = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Mail", "/io/mainframe/shopsystem/mailer"); Config cfg = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Config", "/io/mainframe/shopsystem/config"); @@ -35,7 +38,7 @@ public static int main(string[] args) { mailer.send_mail(mailpath); } catch(Error e) { - stderr.printf("Error: %s\n", e.message); + stderr.printf(_("Error: %s\n"), e.message); } return 0; diff --git a/src/cli/.gitignore b/src/cli/.gitignore deleted file mode 100644 index 573c0c4..0000000 --- a/src/cli/.gitignore +++ /dev/null @@ -1 +0,0 @@ -cli diff --git a/src/cli/Makefile b/src/cli/Makefile deleted file mode 100644 index f042ca8..0000000 --- a/src/cli/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -all: cli - @echo > /dev/null - -cli: main.vala cli.vala cli-interface.vala ../config/config-interface.vala - valac -X -w -o $@ --pkg linux --pkg posix --pkg gio-2.0 $^ - - -clean: - rm -rf cli - -.PHONY: all clean diff --git a/src/cli/cli.vala b/src/cli/cli.vala index bc6fe9d..0d0e9d4 100644 --- a/src/cli/cli.vala +++ b/src/cli/cli.vala @@ -15,14 +15,13 @@ [DBus (name = "io.mainframe.shopsystem.Cli")] public class CliImpl { - public signal void received_barcode(string barcode); public CliImpl() { - } + } - public void send(string msg) { - stdout.printf("Sending: %s\n", msg); - received_barcode(msg); - } + public void send(string msg) throws IOError, DBusError { + stdout.printf(_("Sending: %s\n"), msg); + received_barcode(msg); + } } diff --git a/src/cli/main.vala b/src/cli/main.vala index a3208dd..aacb3ba 100644 --- a/src/cli/main.vala +++ b/src/cli/main.vala @@ -18,20 +18,23 @@ MainLoop ml; string[] commands; public static int main(string[] args) { + Intl.setlocale(LocaleCategory.ALL, ""); + Intl.textdomain("shopsystem"); + if (args.length == 1) { - stdout.printf("Nothing to send.\nUsage: %s <commnds to send...>\nExample: %s \"USER 1\" \"LOGOUT\"\n", args[0], args[0]); + stdout.printf(_("Nothing to send.\nUsage: %s <commands to send...>\nExample: %s \"USER 1\" \"LOGOUT\"\n"), args[0], args[0]); return 0; } commands = args[1:args.length]; - + cli = new CliImpl(); Bus.own_name( BusType.SYSTEM, "io.mainframe.shopsystem.Cli", BusNameOwnerFlags.NONE, - on_bus_aquired, - on_name_aquired, - () => stderr.printf("Could not aquire name\n")); + on_bus_acquired, + on_name_acquired, + () => stderr.printf(_("Could not acquire name\n"))); ml = new MainLoop(); @@ -40,9 +43,13 @@ public static int main(string[] args) { return 0; } -void on_name_aquired() { +void on_name_acquired() { foreach (string cmd in commands) { - cli.send(cmd); + try { + cli.send(cmd); + } catch (Error e) { + stderr.printf(_("Error sending command: %s"), e.message); + } } // wait a minimal amount of time, to ensure the event was sent @@ -51,15 +58,14 @@ void on_name_aquired() { ml.quit (); return false; }); - time.attach (ml.get_context ()); - + time.attach (ml.get_context ()); } -void on_bus_aquired(DBusConnection con) { +void on_bus_acquired(DBusConnection con) { try { - con.register_object("/io/mainframe/shopsystem/cli", cli); + con.register_object("/io/mainframe/shopsystem/cli", cli); } catch(IOError e) { - stderr.printf("Could not register service\n"); + stderr.printf(_("Could not register service\n")); } -}
\ No newline at end of file +} diff --git a/src/config/.gitignore b/src/config/.gitignore deleted file mode 100644 index 04204c7..0000000 --- a/src/config/.gitignore +++ /dev/null @@ -1 +0,0 @@ -config diff --git a/src/config/Makefile b/src/config/Makefile deleted file mode 100644 index d93d5df..0000000 --- a/src/config/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -all: config - @echo > /dev/null - -config: main.vala config.vala config-interface.vala - valac -X -w -o $@ --pkg gio-2.0 $^ - -clean: - rm -rf config - -.PHONY: all clean diff --git a/src/config/config-interface.vala b/src/config/config-interface.vala index c34e3a7..369071e 100644 --- a/src/config/config-interface.vala +++ b/src/config/config-interface.vala @@ -15,12 +15,12 @@ [DBus (name = "io.mainframe.shopsystem.Config")] public interface Config : Object { - public abstract bool has_group(string group_name) throws IOError, KeyFileError; - public abstract bool has_key(string group_name, string key) throws IOError, KeyFileError; - public abstract string get_string(string group_name, string key) throws IOError, KeyFileError; - public abstract bool get_boolean(string group_name, string key) throws IOError, KeyFileError; - public abstract int get_integer(string group_name, string key) throws IOError, KeyFileError; - public abstract int64 get_int64(string group_name, string key) throws IOError, KeyFileError; - public abstract uint64 get_uint64(string group_name, string key) throws IOError, KeyFileError; - public abstract double get_double(string group_name, string key) throws IOError, KeyFileError; + public abstract bool has_group(string group_name) throws DBusError, IOError, KeyFileError; + public abstract bool has_key(string group_name, string key) throws DBusError, IOError, KeyFileError; + public abstract string get_string(string group_name, string key) throws DBusError, IOError, KeyFileError; + public abstract bool get_boolean(string group_name, string key) throws DBusError, IOError, KeyFileError; + public abstract int get_integer(string group_name, string key) throws DBusError, IOError, KeyFileError; + public abstract int64 get_int64(string group_name, string key) throws DBusError, IOError, KeyFileError; + public abstract uint64 get_uint64(string group_name, string key) throws DBusError, IOError, KeyFileError; + public abstract double get_double(string group_name, string key) throws DBusError, IOError, KeyFileError; } diff --git a/src/config/config.vala b/src/config/config.vala index 60fd835..bc476f4 100644 --- a/src/config/config.vala +++ b/src/config/config.vala @@ -23,39 +23,39 @@ public class Cfg { this.file = new KeyFile(); this.file.load_from_file(file, KeyFileFlags.NONE); } catch(Error e) { - error("Could not load configuration file: %s", e.message); + error(_("Could not load configuration file: %s"), e.message); } } - public bool has_group(string group_name) throws KeyFileError { + public bool has_group(string group_name) throws DBusError, IOError, KeyFileError { return file.has_group(group_name); } - public bool has_key(string group_name, string key) throws KeyFileError { + public bool has_key(string group_name, string key) throws DBusError, IOError, KeyFileError { return file.has_key(group_name, key); } - public string get_string(string group_name, string key) throws KeyFileError { + public string get_string(string group_name, string key) throws DBusError, IOError, KeyFileError { return file.get_string(group_name, key); } - public bool get_boolean(string group_name, string key) throws KeyFileError { + public bool get_boolean(string group_name, string key) throws DBusError, IOError, KeyFileError { return file.get_boolean(group_name, key); } - public int get_integer(string group_name, string key) throws KeyFileError { + public int get_integer(string group_name, string key) throws DBusError, IOError, KeyFileError { return file.get_integer(group_name, key); } - public int64 get_int64(string group_name, string key) throws KeyFileError { + public int64 get_int64(string group_name, string key) throws DBusError, IOError, KeyFileError { return file.get_int64(group_name, key); } - public uint64 get_uint64(string group_name, string key) throws KeyFileError { + public uint64 get_uint64(string group_name, string key) throws DBusError, IOError, KeyFileError { return file.get_uint64(group_name, key); } - public double get_double(string group_name, string key) throws KeyFileError { + public double get_double(string group_name, string key) throws DBusError, IOError, KeyFileError { return file.get_double(group_name, key); } diff --git a/src/config/main.vala b/src/config/main.vala index 5ae15be..1a5328d 100644 --- a/src/config/main.vala +++ b/src/config/main.vala @@ -16,27 +16,28 @@ Cfg cfg; public static int main(string[] args) { - string binarylocation = File.new_for_path(args[0]).get_parent().get_path(); + Intl.setlocale(LocaleCategory.ALL, ""); + Intl.textdomain("shopsystem"); - cfg = new Cfg(binarylocation + "/../../ktt-shopsystem.cfg"); + cfg = new Cfg("/etc/shopsystem/config.ini"); Bus.own_name( BusType.SYSTEM, "io.mainframe.shopsystem.Config", BusNameOwnerFlags.NONE, - on_bus_aquired, + on_bus_acquired, () => {}, - () => stderr.printf("Could not aquire name\n")); + () => stderr.printf(_("Could not acquire name\n"))); new MainLoop().run(); return 0; } -void on_bus_aquired(DBusConnection con) { +void on_bus_acquired(DBusConnection con) { try { con.register_object("/io/mainframe/shopsystem/config", cfg); } catch(IOError e) { - stderr.printf("Could not register service\n"); + stderr.printf(_("Could not register service\n")); } } diff --git a/src/curses-ui/.gitignore b/src/curses-ui/.gitignore deleted file mode 100644 index b66c764..0000000 --- a/src/curses-ui/.gitignore +++ /dev/null @@ -1 +0,0 @@ -curses-ui diff --git a/src/curses-ui/Makefile b/src/curses-ui/Makefile deleted file mode 100644 index 1ca68cf..0000000 --- a/src/curses-ui/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -all: curses-ui - @echo > /dev/null - -curses-ui: *.vala ../audio/audio-interface.vala ../scanner-session/scannersession-interface.vala - valac -X -w -o $@ --pkg curses -X -lncursesw --pkg posix --pkg gio-2.0 $^ - -clean: - rm -rf curses-ui - -.PHONY: all clean diff --git a/src/curses-ui/clock.vala b/src/curses-ui/clock.vala index dd3ddcd..43ca48b 100644 --- a/src/curses-ui/clock.vala +++ b/src/curses-ui/clock.vala @@ -1,4 +1,5 @@ /* Copyright 2013, Sebastian Reichel <sre@ring0.de> + * Copyright 2018, Malte Modler <malte@malte-modler.de> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/src/curses-ui/curses-ui.vala b/src/curses-ui/curses-ui.vala index ab34787..539c959 100644 --- a/src/curses-ui/curses-ui.vala +++ b/src/curses-ui/curses-ui.vala @@ -1,4 +1,6 @@ /* Copyright 2013, Sebastian Reichel <sre@ring0.de> + * Copyright 2017-2018, Johannes Rudolph <johannes.rudolph@gmx.com> + * Copyright 2018, Malte Modler <malte@malte-modler.de> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -21,7 +23,7 @@ public class CursesUI { //StatusPanel statuswin; MessageBoxOverlay mbOverlay; - public CursesUI() { + public CursesUI(string configdir) { /* unicode support */ Intl.setlocale(LocaleCategory.CTYPE, ""); @@ -37,8 +39,8 @@ public class CursesUI { Curses.init_pair(1, Curses.Color.GREEN, Curses.Color.BLACK); Curses.init_pair(2, Curses.Color.WHITE, Curses.Color.RED); - /* initialize widgets */ - banner = new Logo(); + /* initialize widgets */ + banner = new Logo(configdir); //statuswin = new StatusPanel(); messages = new MessageBox(); clkwin = new ClockWindow(); @@ -68,18 +70,18 @@ public class CursesUI { //} public void log(MessageType type, string message) { - switch (type) { + switch (type) { case MessageType.WARNING: messages.add(message, MessageBox.WARN_COLOR); break; - case MessageType.ERROR: + case MessageType.ERROR: messages.add(message, MessageBox.ERROR_COLOR); break; default: messages.add(message, MessageBox.INFO_COLOR); break; } - + } public void log_overlay(string title, string message, int closeAfter) { @@ -87,7 +89,7 @@ public class CursesUI { Timeout.add_seconds(closeAfter, closeMbOverlay); } - public void dialog_open(string title, string message, int closeAfter=0) { + public void dialog_open(string title, string message, int closeAfter=0) { dialog = new Dialog(message, title, closeAfter); if (closeAfter > 0) { Timeout.add_seconds(closeAfter, close); @@ -102,7 +104,7 @@ public class CursesUI { return false; } - bool close() { + bool close() { dialog_close(); // just call me once return false; diff --git a/src/curses-ui/dialog.vala b/src/curses-ui/dialog.vala index 7d2902b..e7ae0b8 100644 --- a/src/curses-ui/dialog.vala +++ b/src/curses-ui/dialog.vala @@ -51,7 +51,7 @@ public class Dialog { if (countdownValue > 0) { Timeout.add_seconds(1, decrementTitleCountdown); - } + } } private void setTitle() { @@ -61,7 +61,7 @@ public class Dialog { } int title_x = (dialogWidth-title.length)/2; win.mvaddstr(0, title_x, title); - win.mvaddch(0, title_x-2, Acs.RTEE); + win.mvaddch(0, title_x-2, Acs.RTEE); win.mvaddch(0, title_x-1, ' '); win.mvaddch(0, title_x+title.length, ' '); win.mvaddch(0, title_x+title.length+1, Acs.LTEE); @@ -70,7 +70,7 @@ public class Dialog { win.mvaddch(0, title_x+title.length+3, Acs.HLINE); } - private bool decrementTitleCountdown() { + private bool decrementTitleCountdown() { countdownValue--; setTitle(); win.refresh(); diff --git a/src/curses-ui/logo.vala b/src/curses-ui/logo.vala index dbc716d..1bb609b 100644 --- a/src/curses-ui/logo.vala +++ b/src/curses-ui/logo.vala @@ -1,4 +1,5 @@ /* Copyright 2013, Sebastian Reichel <sre@ring0.de> + * Copyright 2017-2018, Johannes Rudolph <johannes.rudolph@gmx.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -18,17 +19,30 @@ using Curses; public class Logo { Window win; - public Logo() { + public Logo(string configdir) { win = new Window(8, COLS - 2, 0, 1); win.bkgdset(COLOR_PAIR(1) | Attribute.BOLD); win.addstr("\n"); - win.addstr(" _ ___ _____ ____ _ \n"); - win.addstr(" | |/ / ||_ _| / ___|| |__ ___ _ __ \n"); - win.addstr(" | ' /| __|| | \\___ \\| '_ \\ / _ \\| '_ \\ \n"); - win.addstr(" | . \\| |_ | | ___) | | | | (_) | |_) )\n"); - win.addstr(" |_|\\_\\\\__||_| |____/|_| |_|\\___/| .__/ \n"); - win.addstr(" |_| \n"); + + var logofilename = Path.build_filename(configdir, "logo.txt"); + var file = File.new_for_path(logofilename); + if (!file.query_exists()) { + stderr.printf (_("File '%s' doesn't exist.\n"), file.get_path ()); + } + + try { + // Open file for reading and wrap returned FileInputStream into a + // DataInputStream, so we can read line by line + var dis = new DataInputStream(file.read()); + string line; + // Read lines until end of file (null) is reached + while ((line = dis.read_line(null)) != null) { + win.addstr(line+"\n"); + } + } catch(Error e) { + error (_("Error: %s"), e.message); + } win.clrtobot(); diff --git a/src/curses-ui/main.vala b/src/curses-ui/main.vala index da822a9..adeb67f 100644 --- a/src/curses-ui/main.vala +++ b/src/curses-ui/main.vala @@ -1,4 +1,6 @@ /* Copyright 2013, Sebastian Reichel <sre@ring0.de> + * Copyright 2017-2018, Johannes Rudolph <johannes.rudolph@gmx.com> + * Copyright 2018, Malte Modler <malte@malte-modler.de> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -22,7 +24,9 @@ private static void play(string file) { try { audio.play_system(file); } catch(IOError e) { - ui.log(MessageType.WARNING, "could not play audio: %s".printf(e.message)); + ui.log(MessageType.WARNING, _("could not play audio: %s").printf(e.message)); + } catch(DBusError e) { + ui.log(MessageType.WARNING, _("could not play audio: %s").printf(e.message)); } } @@ -42,31 +46,39 @@ public void log_handler(string? log_domain, LogLevelFlags flags, string message) public static int main(string[] args) { loop = new MainLoop(); + Intl.setlocale(LocaleCategory.ALL, ""); + Intl.textdomain("shopsystem"); + /* handle unix signals */ - Unix.signal_add(Posix.SIGTERM, handle_signals); - Unix.signal_add(Posix.SIGINT, handle_signals); + Unix.signal_add(Posix.Signal.TERM, handle_signals); + Unix.signal_add(Posix.Signal.INT, handle_signals); try { audio = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.AudioPlayer", "/io/mainframe/shopsystem/audio"); scanner = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.ScannerSession", "/io/mainframe/shopsystem/scanner_session"); + + var configdir = "/etc/shopsystem"; + ui = new CursesUI(configdir); } catch(IOError e) { - error("IOError: %s\n", e.message); + error(_("IO Error: %s\n"), e.message); + } catch(DBusError e) { + error(_("DBus Error: %s\n"), e.message); + } catch(KeyFileError e) { + error(_("KeyFile Error: %s\n"), e.message); } - ui = new CursesUI(); - Log.set_default_handler(log_handler); scanner.msg.connect(msg_handler); scanner.msg_overlay.connect(msg_overlay_handler); - ui.log(MessageType.INFO, "KtT Shop System has been started"); + ui.log(MessageType.INFO, _("Shop System has been started")); play("startup.ogg"); /* run mainloop */ loop.run(); - ui.log(MessageType.INFO, "Stopping Shop System"); + ui.log(MessageType.INFO, _("Stopping Shop System")); play("shutdown.ogg"); /* leave curses mode */ diff --git a/src/curses-ui/message_box.vala b/src/curses-ui/message_box.vala index d6823fe..70c4d10 100644 --- a/src/curses-ui/message_box.vala +++ b/src/curses-ui/message_box.vala @@ -41,7 +41,7 @@ public class MessageBox { init_pair (INFO_COLOR, Color.WHITE, Color.BLACK); init_pair (WARN_COLOR, Color.YELLOW, Color.BLACK); - init_pair (ERROR_COLOR, Color.RED, Color.BLACK); + init_pair (ERROR_COLOR, Color.RED, Color.BLACK); } public void add(string msg, short color_pair = MessageBox.INFO_COLOR) { @@ -49,7 +49,7 @@ public class MessageBox { if(now.get_day_of_year() != last.get_day_of_year() || now.get_year() != last.get_year()) { string curtime = now.format("%Y-%m-%d"); - subwin.addstr("\nDate Changed: " + curtime); + subwin.addstr(_("\nDate Changed: ") + curtime); } last = now; diff --git a/src/curses-ui/message_box_overlay.vala b/src/curses-ui/message_box_overlay.vala index 721ab06..7b5a09b 100644 --- a/src/curses-ui/message_box_overlay.vala +++ b/src/curses-ui/message_box_overlay.vala @@ -37,13 +37,13 @@ public class MessageBoxOverlay { Timeout.add_seconds(1, decrementTitleCountdown); } - private void setTitle() { + private void setTitle() { var title = " === %s (%d) === ".printf(dialogTitle, countdownValue); int title_x = (COLS - title.length)/2; - win.mvaddstr(0, title_x, title); + win.mvaddstr(0, title_x, title); } - private bool decrementTitleCountdown() { + private bool decrementTitleCountdown() { countdownValue--; setTitle(); win.refresh(); diff --git a/src/curses-ui/numbers.vala b/src/curses-ui/numbers.vala index 200cf63..5cbcd23 100644 --- a/src/curses-ui/numbers.vala +++ b/src/curses-ui/numbers.vala @@ -1,4 +1,6 @@ /* Copyright 2013, Sebastian Reichel <sre@ring0.de> + * Copyright 2017-2018, Johannes Rudolph <johannes.rudolph@gmx.com> + * Copyright 2018, Malte Modler <malte@malte-modler.de> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/src/database/.gitignore b/src/database/.gitignore deleted file mode 100644 index aa0d57e..0000000 --- a/src/database/.gitignore +++ /dev/null @@ -1 +0,0 @@ -database diff --git a/src/database/Makefile b/src/database/Makefile deleted file mode 100644 index fac0f08..0000000 --- a/src/database/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -all: database - @echo > /dev/null - -database: main.vala database.vala db-interface.vala ../config/config-interface.vala ../price.vapi - valac -X -w -o $@ --pkg sqlite3 --pkg gee-0.8 --pkg gio-2.0 $^ - -clean: - rm -rf db - -.PHONY: all clean diff --git a/src/database/database.vala b/src/database/database.vala index 0d3d1ff..52c5dd2 100644 --- a/src/database/database.vala +++ b/src/database/database.vala @@ -1,4 +1,5 @@ /* Copyright 2012-2013, Sebastian Reichel <sre@ring0.de> + * Copyright 2017-2018, Johannes Rudolph <johannes.rudolph@gmx.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -24,7 +25,7 @@ public class DataBase : Object { int rc = db.prepare_v2(query, -1, out stmt); if(rc != Sqlite.OK) { - error("could not prepare statement: %s", query); + error(_("Error: could not prepare statement: %s"), query); } } @@ -77,7 +78,7 @@ public class DataBase : Object { rc = Sqlite.Database.open(file, out db); if(rc != Sqlite.OK) { - error("could not open database!"); + error(_("Error: could not open database!")); } /* setup queries */ @@ -127,6 +128,7 @@ public class DataBase : Object { queries["user_disable"] = "UPDATE users SET disabled = ? WHERE id = ?"; queries["last_timestamp"] = "SELECT timestamp FROM sales ORDER BY timestamp DESC LIMIT 1"; queries["category_list"] = "SELECT id, name FROM categories"; + queries["category_add"] = "INSERT INTO categories('name') VALUES (?)"; queries["supplier_list"] = "SELECT id, name, postal_code, city, street, phone, website FROM supplier"; queries["supplier_get"] = "SELECT id, name, postal_code, city, street, phone, website FROM supplier WHERE id = ?"; queries["supplier_add"] = "INSERT INTO supplier('name', 'postal_code', 'city', 'street', 'phone', 'website') VALUES (?, ?, ?, ?, ?, ?)"; @@ -139,6 +141,10 @@ public class DataBase : Object { queries["alias_ean_add"] = "INSERT OR IGNORE INTO ean_aliases (id, real_ean) VALUES (?, ?)"; queries["alias_ean_get"] = "SELECT real_ean FROM ean_aliases WHERE id = ?"; queries["alias_ean_list"] = "SELECT id, real_ean FROM ean_aliases ORDER BY id ASC"; + queries["userid_rfid"] = "SELECT user FROM rfid_users WHERE rfid = ?"; + queries["rfid_userid"] = "SELECT rfid FROM rfid_users WHERE user = ?"; + queries["rfid_insert"] = "INSERT OR REPLACE INTO rfid_users ('user','rfid') VALUES (?,?)"; + queries["rfid_delete_user"] = "DELETE FROM rfid_users WHERE user = ?"; /* compile queries into statements */ foreach(var entry in queries.entries) { @@ -151,7 +157,7 @@ public class DataBase : Object { #endif } - public GLib.HashTable<string,string> get_products() { + public GLib.HashTable<string,string> get_products() throws DBusError, IOError, DatabaseError { var result = new GLib.HashTable<string,string>(null, null); statements["products"].reset(); @@ -274,13 +280,13 @@ public class DataBase : Object { } #endif - public StockEntry[] get_stock() { - StockEntry[] result = {}; + public DetailedProduct[] get_stock() throws DBusError, IOError, DatabaseError { + DetailedProduct[] result = {}; statements["stock_status"].reset(); while(statements["stock_status"].step() == Sqlite.ROW) { - StockEntry entry = { - statements["stock_status"].column_text(0), + DetailedProduct entry = { + uint64.parse(statements["stock_status"].column_text(0)), statements["stock_status"].column_text(1), statements["stock_status"].column_text(2), statements["stock_status"].column_int(3), @@ -294,7 +300,23 @@ public class DataBase : Object { return result; } - public PriceEntry[] get_prices(uint64 product) { + public DetailedProduct get_product_for_ean(uint64 ean) throws DBusError, IOError, DatabaseError { + DetailedProduct p = {}; + + try { + p.ean = ean_alias_get(ean); + p.name = get_product_name(p.ean); + p.category = get_product_category(p.ean); + p.amount = get_product_amount(p.ean); + p.memberprice = get_product_price(1, p.ean); + p.guestprice = get_product_price(0, p.ean); + return p; + } catch(DatabaseError e){ + throw e; + } + } + + public PriceEntry[] get_prices(uint64 product) throws DBusError, IOError, DatabaseError { PriceEntry[] result = {}; statements["prices"].reset(); @@ -312,7 +334,7 @@ public class DataBase : Object { return result; } - public RestockEntry[] get_restocks(uint64 product, bool descending) { + public RestockEntry[] get_restocks(uint64 product, bool descending) throws DBusError, IOError, DatabaseError { RestockEntry[] result = {}; var statement = statements[descending ? "restocks_desc" : "restocks_asc"]; @@ -336,7 +358,7 @@ public class DataBase : Object { return result; } - public bool buy(int32 user, uint64 article) throws DatabaseError { + public bool buy(int32 user, uint64 article) throws DBusError, IOError, DatabaseError { int rc = 0; int64 timestamp = (new DateTime.now_utc()).to_unix(); @@ -347,12 +369,12 @@ public class DataBase : Object { rc = statements["purchase"].step(); if(rc != Sqlite.DONE) - throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc); + throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc); return true; } - public string get_product_name(uint64 article) throws DatabaseError { + public string get_product_name(uint64 article) throws DBusError, IOError, DatabaseError { statements["product_name"].reset(); statements["product_name"].bind_text(1, "%llu".printf(article)); @@ -362,13 +384,13 @@ public class DataBase : Object { case Sqlite.ROW: return statements["product_name"].column_text(0); case Sqlite.DONE: - throw new DatabaseError.PRODUCT_NOT_FOUND("unknown product: %llu", article); + throw new DatabaseError.PRODUCT_NOT_FOUND(_("unknown product: %llu"), article); default: - throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc); + throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc); } } - public string get_product_category(uint64 article) throws DatabaseError { + public string get_product_category(uint64 article) throws DBusError, IOError, DatabaseError { statements["product_category"].reset(); statements["product_category"].bind_text(1, "%llu".printf(article)); @@ -378,13 +400,13 @@ public class DataBase : Object { case Sqlite.ROW: return statements["product_category"].column_text(0); case Sqlite.DONE: - throw new DatabaseError.PRODUCT_NOT_FOUND("unknown product: %llu", article); + throw new DatabaseError.PRODUCT_NOT_FOUND(_("unknown product: %llu"), article); default: - throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc); + throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc); } } - public int get_product_amount(uint64 article) throws DatabaseError { + public int get_product_amount(uint64 article) throws DBusError, IOError, DatabaseError { statements["product_amount"].reset(); statements["product_amount"].bind_text(1, "%llu".printf(article)); @@ -394,13 +416,13 @@ public class DataBase : Object { case Sqlite.ROW: return statements["product_amount"].column_int(0); case Sqlite.DONE: - throw new DatabaseError.PRODUCT_NOT_FOUND("unknown product: %llu", article); + throw new DatabaseError.PRODUCT_NOT_FOUND(_("unknown product: %llu"), article); default: - throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc); + throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc); } } - public bool get_product_deprecated(uint64 article) throws DatabaseError { + public bool get_product_deprecated(uint64 article) throws DBusError, IOError, DatabaseError { statements["product_deprecated"].reset(); statements["product_deprecated"].bind_text(1, "%llu".printf(article)); @@ -410,13 +432,13 @@ public class DataBase : Object { case Sqlite.ROW: return statements["product_deprecated"].column_int(0) == 1; case Sqlite.DONE: - throw new DatabaseError.PRODUCT_NOT_FOUND("unknown product: %llu", article); + throw new DatabaseError.PRODUCT_NOT_FOUND(_("unknown product: %llu"), article); default: - throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc); + throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc); } } - public void product_deprecate(uint64 article, bool value) throws DatabaseError { + public void product_deprecate(uint64 article, bool value) throws DBusError, IOError, DatabaseError { int rc; statements["product_set_deprecated"].reset(); @@ -425,10 +447,10 @@ public class DataBase : Object { rc = statements["product_set_deprecated"].step(); if(rc != Sqlite.DONE) - throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc); + throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc); } - public Price get_product_price(int user, uint64 article) throws DatabaseError { + public Price get_product_price(int user, uint64 article) throws DBusError, IOError, DatabaseError { int64 timestamp = (new DateTime.now_utc()).to_unix(); bool member = user != 0; @@ -445,13 +467,13 @@ public class DataBase : Object { else return statements["price"].column_int(1); case Sqlite.DONE: - throw new DatabaseError.PRODUCT_NOT_FOUND("unknown product: %llu", article); + throw new DatabaseError.PRODUCT_NOT_FOUND(_("unknown product: %llu"), article); default: - throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc); + throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc); } } - public string undo(int32 user) throws DatabaseError { + public string undo(int32 user) throws DBusError, IOError, DatabaseError { uint64 pid = 0; int rc = 0; string pname; @@ -464,12 +486,12 @@ public class DataBase : Object { case Sqlite.ROW: pid = uint64.parse(statements["last_purchase"].column_text(0)); pname = get_product_name(pid); - write_to_log("Remove purchase of %s", pname); + stderr.printf(_("Remove purchase of %s"), pname); break; case Sqlite.DONE: - throw new DatabaseError.PRODUCT_NOT_FOUND("undo not possible without purchases"); + throw new DatabaseError.PRODUCT_NOT_FOUND(_("undo not possible without purchases")); default: - throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc); + throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc); } statements["undo"].reset(); @@ -477,12 +499,12 @@ public class DataBase : Object { rc = statements["undo"].step(); if(rc != Sqlite.DONE) - throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc); + throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc); return pname; } - public void restock(int user, uint64 product, uint amount, uint price, int supplier, int64 best_before_date) throws DatabaseError { + public void restock(int user, uint64 product, uint amount, uint price, int supplier, int64 best_before_date) throws DBusError, IOError, DatabaseError { int rc = 0; int64 timestamp = (new DateTime.now_utc()).to_unix(); @@ -504,10 +526,10 @@ public class DataBase : Object { rc = statements["stock"].step(); if(rc != Sqlite.DONE) - throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc); + throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc); } - public void new_product(uint64 id, string name, int category, int memberprice, int guestprice) throws DatabaseError { + public void new_product(uint64 id, string name, int category, int memberprice, int guestprice) throws DBusError, IOError, DatabaseError { statements["product_create"].reset(); statements["product_create"].bind_text(1, @"$id"); statements["product_create"].bind_text(2, name); @@ -518,13 +540,13 @@ public class DataBase : Object { if(rc == Sqlite.CONSTRAINT) { throw new DatabaseError.CONSTRAINT_FAILED(db.errmsg()); } else if(rc != Sqlite.DONE) { - throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc); + throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc); } new_price(id, 0, memberprice, guestprice); } - public void new_price(uint64 product, int64 timestamp, int memberprice, int guestprice) throws DatabaseError { + public void new_price(uint64 product, int64 timestamp, int memberprice, int guestprice) throws DBusError, IOError, DatabaseError { statements["price_create"].reset(); statements["price_create"].bind_text(1, @"$product"); statements["price_create"].bind_int64(2, timestamp); @@ -533,11 +555,11 @@ public class DataBase : Object { int rc = statements["price_create"].step(); if(rc != Sqlite.DONE) { - throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc); + throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc); } } - public bool check_user_password(int32 user, string password) { + public bool check_user_password(int32 user, string password) throws DBusError, IOError, DatabaseError { statements["password_get"].reset(); statements["password_get"].bind_int(1, user); @@ -551,7 +573,7 @@ public class DataBase : Object { } } - public void set_user_password(int32 user, string password) throws DatabaseError { + public void set_user_password(int32 user, string password) throws DBusError, IOError, DatabaseError { var pwhash = Checksum.compute_for_string(ChecksumType.SHA256, password); int rc; @@ -560,7 +582,7 @@ public class DataBase : Object { statements["user_auth_create"].bind_int(1, user); rc = statements["user_auth_create"].step(); if(rc != Sqlite.DONE) - throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc); + throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc); /* set password */ statements["password_set"].reset(); @@ -568,31 +590,31 @@ public class DataBase : Object { statements["password_set"].bind_int(2, user); rc = statements["password_set"].step(); if(rc != Sqlite.DONE) - throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc); + throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc); } - public void set_sessionid(int user, string sessionid) throws DatabaseError { + public void set_sessionid(int user, string sessionid) throws DBusError, IOError, DatabaseError { statements["session_set"].reset(); statements["session_set"].bind_text(1, sessionid); statements["session_set"].bind_int(2, user); int rc = statements["session_set"].step(); if(rc != Sqlite.DONE) - throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc); + throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc); } - public int get_user_by_sessionid(string sessionid) throws DatabaseError { + public int get_user_by_sessionid(string sessionid) throws DBusError, IOError, DatabaseError { statements["session_get"].reset(); statements["session_get"].bind_text(1, sessionid); if(statements["session_get"].step() == Sqlite.ROW) { return statements["session_get"].column_int(0); } else { - throw new DatabaseError.SESSION_NOT_FOUND("No such session available in database!"); + throw new DatabaseError.SESSION_NOT_FOUND(_("No such session available in database!")); } } - public UserInfo get_user_info(int user) throws DatabaseError { + public UserInfo get_user_info(int user) throws DBusError, IOError, DatabaseError { var result = UserInfo(); statements["userinfo"].reset(); statements["userinfo"].bind_int(1, user); @@ -613,15 +635,26 @@ public class DataBase : Object { result.soundTheme = statements["userinfo"].column_text(10); result.joined_at = statements["userinfo"].column_int64(11); } else if(rc == Sqlite.DONE) { - throw new DatabaseError.USER_NOT_FOUND("user not found"); + throw new DatabaseError.USER_NOT_FOUND(_("user not found")); } else { - throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc); + throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc); + } + + statements["rfid_userid"].reset(); + statements["rfid_userid"].bind_int(1, user); + rc = statements["rfid_userid"].step(); + + string[] rfid = {}; + while(rc == Sqlite.ROW) { + rfid += statements["rfid_userid"].column_text(0); + rc = statements["rfid_userid"].step(); } + result.rfid = rfid; return result; } - public UserAuth get_user_auth(int user) throws DatabaseError { + public UserAuth get_user_auth(int user) throws DBusError, IOError, DatabaseError { var result = UserAuth(); result.id = user; result.superuser = false; @@ -641,13 +674,13 @@ public class DataBase : Object { } else if(rc == Sqlite.DONE) { /* entry not found, we return defaults */ } else { - throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc); + throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc); } return result; } - public void set_user_auth(UserAuth auth) throws DatabaseError { + public void set_user_auth(UserAuth auth) throws DBusError, IOError, DatabaseError { int rc; /* create user auth line if not existing */ @@ -655,7 +688,7 @@ public class DataBase : Object { statements["user_auth_create"].bind_int(1, auth.id); rc = statements["user_auth_create"].step(); if(rc != Sqlite.DONE) - throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc); + throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc); /* set authentication */ statements["userauth_set"].reset(); @@ -666,21 +699,21 @@ public class DataBase : Object { rc = statements["userauth_set"].step(); if(rc != Sqlite.DONE) - throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc); + throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc); } - public string get_username(int user) throws DatabaseError { + public string get_username(int user) throws DBusError, IOError, DatabaseError { statements["username"].reset(); statements["username"].bind_int(1, user); if(statements["username"].step() == Sqlite.ROW) { return statements["username"].column_text(0)+" "+statements["username"].column_text(1); } else { - throw new DatabaseError.USER_NOT_FOUND("No such user available in database!"); + throw new DatabaseError.USER_NOT_FOUND(_("No such user available in database!")); } } - public string get_user_theme(int user, string fallback) throws DatabaseError { + public string get_user_theme(int user, string fallback) throws DBusError, IOError, DatabaseError { statements["user_theme_get"].reset(); statements["user_theme_get"].bind_text(1, fallback); statements["user_theme_get"].bind_int(2, user); @@ -688,11 +721,11 @@ public class DataBase : Object { if(statements["user_theme_get"].step() == Sqlite.ROW) { return statements["user_theme_get"].column_text(0); } else { - throw new DatabaseError.USER_NOT_FOUND("No such user available in database!"); + throw new DatabaseError.USER_NOT_FOUND(_("No such user available in database!")); } } - public void set_userTheme(int user, string userTheme) throws DatabaseError { + public void set_userTheme(int user, string userTheme) throws DBusError, IOError, DatabaseError { statements["user_theme_set"].reset(); if (userTheme == "") { statements["user_theme_set"].bind_null(1); @@ -703,10 +736,10 @@ public class DataBase : Object { int rc = statements["user_theme_set"].step(); if(rc != Sqlite.DONE) - throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc); + throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc); } - public InvoiceEntry[] get_invoice(int user, int64 from=0, int64 to=-1) throws DatabaseError { + public InvoiceEntry[] get_invoice(int user, int64 from=0, int64 to=-1) throws DBusError, IOError, DatabaseError { InvoiceEntry[] result = {}; if(to == -1) { @@ -731,13 +764,13 @@ public class DataBase : Object { } if(rc != Sqlite.DONE) { - throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc); + throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc); } return result; } - public int64 get_first_purchase(int user) { + public int64 get_first_purchase(int user) throws DBusError, IOError, DatabaseError { statements["purchase_first"].reset(); statements["purchase_first"].bind_int(1, user); @@ -747,7 +780,7 @@ public class DataBase : Object { return 0; } - public int64 get_last_purchase(int user) { + public int64 get_last_purchase(int user) throws DBusError, IOError, DatabaseError { statements["purchase_last"].reset(); statements["purchase_last"].bind_int(1, user); @@ -757,7 +790,7 @@ public class DataBase : Object { return 0; } - public StatsInfo get_stats_info() { + public StatsInfo get_stats_info() throws DBusError, IOError, DatabaseError { var result = StatsInfo(); DateTime now = new DateTime.now_local(); @@ -832,7 +865,7 @@ public class DataBase : Object { return result; } - public int[] get_member_ids() { + public int[] get_member_ids() throws DBusError, IOError, DatabaseError { int[] result = {}; statements["user_get_ids"].reset(); @@ -842,7 +875,7 @@ public class DataBase : Object { return result; } - public int[] get_system_member_ids() { + public int[] get_system_member_ids() throws DBusError, IOError, DatabaseError { int[] result = {}; statements["system_user_get_ids"].reset(); @@ -852,7 +885,7 @@ public class DataBase : Object { return result; } - public void user_disable(int user, bool value) throws DatabaseError { + public void user_disable(int user, bool value) throws DBusError, IOError, DatabaseError { int rc; /* create user auth line if not existing */ @@ -860,7 +893,7 @@ public class DataBase : Object { statements["user_auth_create"].bind_int(1, user); rc = statements["user_auth_create"].step(); if(rc != Sqlite.DONE) - throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc); + throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc); /* set disabled flag */ statements["user_disable"].reset(); @@ -868,10 +901,10 @@ public class DataBase : Object { statements["user_disable"].bind_int(2, user); rc = statements["user_disable"].step(); if(rc != Sqlite.DONE) - throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc); + throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc); } - public void user_replace(UserInfo u) throws DatabaseError { + public void user_replace(UserInfo u) throws DBusError, IOError, DatabaseError { statements["user_replace"].reset(); statements["user_replace"].bind_int(1, u.id); statements["user_replace"].bind_text(2, u.email); @@ -889,32 +922,47 @@ public class DataBase : Object { int rc = statements["user_replace"].step(); if(rc != Sqlite.DONE) - throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc); + throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc); + + statements["rfid_delete_user"].reset(); + statements["rfid_delete_user"].bind_int(1, u.id); + rc = statements["rfid_delete_user"].step(); + if(rc != Sqlite.DONE) + throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc); + + foreach (string rfid in u.rfid) { + statements["rfid_insert"].reset(); + statements["rfid_insert"].bind_int(1, u.id); + statements["rfid_insert"].bind_text(2, rfid); + rc = statements["rfid_insert"].step(); + if(rc != Sqlite.DONE) + throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc); + } } - public bool user_is_disabled(int user) throws DatabaseError { + public bool user_is_disabled(int user) throws DBusError, IOError, DatabaseError { return get_user_info(user).disabled; } - public bool user_exists(int user) throws DatabaseError { + public bool user_exists(int user) throws DBusError, IOError, DatabaseError { if(user in get_member_ids()) return true; return false; } - public bool user_equals(UserInfo u) throws DatabaseError { + public bool user_equals(UserInfo u) throws DBusError, IOError, DatabaseError { var dbu = get_user_info(u.id); return u.equals(dbu); } - public int64 get_timestamp_of_last_purchase() { + public int64 get_timestamp_of_last_purchase() throws DBusError, IOError, DatabaseError { statements["last_timestamp"].reset(); if(statements["last_timestamp"].step() != Sqlite.ROW) return 0; return statements["last_timestamp"].column_int64(0); } - public Category[] get_category_list() { + public Category[] get_category_list() throws DBusError, IOError, DatabaseError { Category[] result = {}; statements["category_list"].reset(); @@ -930,7 +978,24 @@ public class DataBase : Object { return result; } - public Supplier[] get_supplier_list() { + public void add_category(string name) throws DBusError, IOError, DatabaseError { + /* check if category already exists */ + foreach(var c in get_category_list()) { + if(name == c.name) { + return; + } + } + + statements["category_add"].reset(); + statements["category_add"].bind_text(1, name); + int rc = statements["category_add"].step(); + + if(rc != Sqlite.DONE) { + throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc); + } + } + + public Supplier[] get_supplier_list() throws DBusError, IOError, DatabaseError { Supplier[] result = {}; statements["supplier_list"].reset(); @@ -951,7 +1016,7 @@ public class DataBase : Object { return result; } - public Supplier get_supplier(int id) { + public Supplier get_supplier(int id) throws DBusError, IOError, DatabaseError { Supplier result = Supplier(); statements["supplier_get"].reset(); @@ -978,7 +1043,7 @@ public class DataBase : Object { return result; } - public void add_supplier(string name, string postal_code, string city, string street, string phone, string website) throws DatabaseError { + public void add_supplier(string name, string postal_code, string city, string street, string phone, string website) throws DBusError, IOError, DatabaseError { statements["supplier_add"].reset(); statements["supplier_add"].bind_text(1, name); statements["supplier_add"].bind_text(2, postal_code); @@ -989,11 +1054,11 @@ public class DataBase : Object { int rc = statements["supplier_add"].step(); if(rc != Sqlite.DONE) { - throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc); + throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc); } } - public int[] get_users_with_sales(int64 timestamp_from, int64 timestamp_to) { + public int[] get_users_with_sales(int64 timestamp_from, int64 timestamp_to) throws DBusError, IOError, DatabaseError { var result = new int[0]; statements["users_with_sales"].reset(); statements["users_with_sales"].bind_int64(1, timestamp_from); @@ -1006,7 +1071,7 @@ public class DataBase : Object { return result; } - public Price get_user_invoice_sum(int user, int64 timestamp_from, int64 timestamp_to) { + public Price get_user_invoice_sum(int user, int64 timestamp_from, int64 timestamp_to) throws DBusError, IOError, DatabaseError { Price result = 0; statements["user_invoice_sum"].reset(); @@ -1020,7 +1085,7 @@ public class DataBase : Object { return result; } - public Price cashbox_status() { + public Price cashbox_status() throws DBusError, IOError, DatabaseError { Price result = 0; statements["cashbox_status"].reset(); @@ -1031,7 +1096,7 @@ public class DataBase : Object { return result; } - public void cashbox_add(int user, Price amount, int64 timestamp) throws DatabaseError { + public void cashbox_add(int user, Price amount, int64 timestamp) throws DBusError, IOError, DatabaseError { statements["cashbox_add"].reset(); statements["cashbox_add"].bind_int(1, user); statements["cashbox_add"].bind_int(2, amount); @@ -1040,11 +1105,11 @@ public class DataBase : Object { int rc = statements["cashbox_add"].step(); if(rc != Sqlite.DONE) { - throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc); + throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc); } } - public CashboxDiff[] cashbox_history() { + public CashboxDiff[] cashbox_history() throws DBusError, IOError, DatabaseError { CashboxDiff[] result = {}; statements["cashbox_history"].reset(); @@ -1062,7 +1127,7 @@ public class DataBase : Object { return result; } - public CashboxDiff[] cashbox_changes(int64 start, int64 stop) { + public CashboxDiff[] cashbox_changes(int64 start, int64 stop) throws DBusError, IOError, DatabaseError { CashboxDiff[] result = {}; statements["cashbox_changes"].reset(); @@ -1082,7 +1147,7 @@ public class DataBase : Object { return result; } - public void ean_alias_add(uint64 ean, uint64 real_ean) throws DatabaseError { + public void ean_alias_add(uint64 ean, uint64 real_ean) throws DBusError, IOError, DatabaseError { statements["alias_ean_add"].reset(); statements["alias_ean_add"].bind_text(1, "%llu".printf(ean)); statements["alias_ean_add"].bind_text(2, "%llu".printf(real_ean)); @@ -1090,11 +1155,11 @@ public class DataBase : Object { int rc = statements["alias_ean_add"].step(); if(rc != Sqlite.DONE) { - throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc); + throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc); } } - public uint64 ean_alias_get(uint64 ean) { + public uint64 ean_alias_get(uint64 ean) throws DBusError, IOError, DatabaseError { uint64 result = ean; statements["alias_ean_get"].reset(); @@ -1106,7 +1171,7 @@ public class DataBase : Object { return result; } - public EanAlias[] ean_alias_list() { + public EanAlias[] ean_alias_list() throws DBusError, IOError, DatabaseError { EanAlias[] result = {}; statements["alias_ean_list"].reset(); @@ -1131,12 +1196,12 @@ public class DataBase : Object { return 1; } - public BestBeforeEntry?[] bestbeforelist() { + public BestBeforeEntry?[] bestbeforelist() throws DBusError, IOError, DatabaseError { var bbdlist = new GLib.GenericArray<BestBeforeEntry?>(); foreach(var product in get_stock()) { var amount = product.amount; - var pid = uint64.parse(product.id); + var pid = product.ean; if(amount <= 0) continue; @@ -1160,4 +1225,20 @@ public class DataBase : Object { return bbdlist.data; } + + public int get_userid_for_rfid(string rfid) throws DBusError, IOError, DatabaseError { + statements["userid_rfid"].reset(); + statements["userid_rfid"].bind_text(1, rfid); + + int rc = statements["userid_rfid"].step(); + + switch(rc) { + case Sqlite.ROW: + return statements["userid_rfid"].column_int(0); + case Sqlite.DONE: + throw new DatabaseError.RFID_NOT_FOUND(_("unknown rfid: %s"), rfid); + default: + throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc); + } + } } diff --git a/src/database/db-interface.vala b/src/database/db-interface.vala index ac8c215..f2d3e87 100644 --- a/src/database/db-interface.vala +++ b/src/database/db-interface.vala @@ -1,4 +1,5 @@ /* Copyright 2013, Sebastian Reichel <sre@ring0.de> + * Copyright 2017-2018, Johannes Rudolph <johannes.rudolph@gmx.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -15,56 +16,61 @@ [DBus (name = "io.mainframe.shopsystem.Database")] public interface Database : Object { - public abstract StockEntry[] get_stock() throws IOError; - public abstract PriceEntry[] get_prices(uint64 product) throws IOError; - public abstract RestockEntry[] get_restocks(uint64 product, bool descending) throws IOError; - public abstract bool buy(int32 user, uint64 article) throws IOError, DatabaseError; - public abstract string get_product_name(uint64 article) throws IOError, DatabaseError; - public abstract string get_product_category(uint64 article) throws IOError, DatabaseError; - public abstract int get_product_amount(uint64 article) throws IOError, DatabaseError; - public abstract bool get_product_deprecated(uint64 article) throws IOError, DatabaseError; - public abstract void product_deprecate(uint64 article, bool value) throws IOError, DatabaseError; - public abstract Price get_product_price(int user, uint64 article) throws IOError, DatabaseError; - public abstract string undo(int32 user) throws IOError, DatabaseError; - public abstract void restock(int user, uint64 product, uint amount, uint price, int supplier, int64 best_before_date) throws IOError, DatabaseError; - public abstract void new_product(uint64 id, string name, int category, int memberprice, int guestprice) throws IOError, DatabaseError; - public abstract void new_price(uint64 product, int64 timestamp, int memberprice, int guestprice) throws IOError, DatabaseError; - public abstract bool check_user_password(int32 user, string password) throws IOError; - public abstract void set_user_password(int32 user, string password) throws IOError, DatabaseError; - public abstract void set_sessionid(int user, string sessionid) throws IOError, DatabaseError; - public abstract void set_userTheme(int user, string userTheme) throws IOError, DatabaseError; - public abstract int get_user_by_sessionid(string sessionid) throws IOError, DatabaseError; - public abstract UserInfo get_user_info(int user) throws IOError, DatabaseError; - public abstract UserAuth get_user_auth(int user) throws IOError, DatabaseError; - public abstract void set_user_auth(UserAuth auth) throws IOError, DatabaseError; - public abstract string get_username(int user) throws IOError, DatabaseError; - public abstract string get_user_theme(int user, string fallback) throws IOError, DatabaseError; - public abstract InvoiceEntry[] get_invoice(int user, int64 from=0, int64 to=-1) throws IOError, DatabaseError; - public abstract int64 get_first_purchase(int user) throws IOError; - public abstract int64 get_last_purchase(int user) throws IOError; - public abstract StatsInfo get_stats_info() throws IOError; - public abstract int[] get_member_ids() throws IOError; - public abstract int[] get_system_member_ids() throws IOError; - public abstract void user_disable(int user, bool value) throws IOError, DatabaseError; - public abstract void user_replace(UserInfo u) throws IOError, DatabaseError; - public abstract bool user_is_disabled(int user) throws IOError, DatabaseError; - public abstract bool user_exists(int user) throws IOError, DatabaseError; - public abstract bool user_equals(UserInfo u) throws IOError, DatabaseError; - public abstract int64 get_timestamp_of_last_purchase() throws IOError; - public abstract Category[] get_category_list() throws IOError; - public abstract Supplier[] get_supplier_list() throws IOError; - public abstract Supplier get_supplier(int id) throws IOError; - public abstract void add_supplier(string name, string postal_code, string city, string street, string phone, string website) throws IOError, DatabaseError; - public abstract int[] get_users_with_sales(int64 timestamp_from, int64 timestamp_to) throws IOError; - public abstract Price get_user_invoice_sum(int user, int64 timestamp_from, int64 timestamp_to) throws IOError; - public abstract Price cashbox_status() throws IOError; - public abstract void cashbox_add(int user, Price amount, int64 timestamp) throws IOError, DatabaseError; - public abstract CashboxDiff[] cashbox_history() throws IOError; - public abstract CashboxDiff[] cashbox_changes(int64 start, int64 stop) throws IOError; - public abstract void ean_alias_add(uint64 ean, uint64 real_ean) throws IOError, DatabaseError; - public abstract uint64 ean_alias_get(uint64 ean) throws IOError; - public abstract EanAlias[] ean_alias_list() throws IOError; - public abstract BestBeforeEntry[] bestbeforelist() throws IOError; + public abstract DetailedProduct[] get_stock() throws DBusError, IOError; + public abstract DetailedProduct get_product_for_ean(uint64 ean) throws DBusError, IOError, DatabaseError; + public abstract PriceEntry[] get_prices(uint64 product) throws DBusError, IOError; + public abstract RestockEntry[] get_restocks(uint64 product, bool descending) throws DBusError, IOError; + public abstract bool buy(int32 user, uint64 article) throws DBusError, IOError, DatabaseError; + public abstract string get_product_name(uint64 article) throws DBusError, IOError, DatabaseError; + public abstract string get_product_category(uint64 article) throws DBusError, IOError, DatabaseError; + public abstract int get_product_amount(uint64 article) throws DBusError, IOError, DatabaseError; + public abstract bool get_product_deprecated(uint64 article) throws DBusError, IOError, DatabaseError; + public abstract void product_deprecate(uint64 article, bool value) throws DBusError, IOError, DatabaseError; + public abstract Price get_product_price(int user, uint64 article) throws DBusError, IOError, DatabaseError; + public abstract string undo(int32 user) throws DBusError, IOError, DatabaseError; + public abstract void restock(int user, uint64 product, uint amount, uint price, int supplier, int64 best_before_date) throws DBusError, IOError, DatabaseError; + public abstract void new_product(uint64 id, string name, int category, int memberprice, int guestprice) throws DBusError, IOError, DatabaseError; + public abstract void new_price(uint64 product, int64 timestamp, int memberprice, int guestprice) throws DBusError, IOError, DatabaseError; + public abstract bool check_user_password(int32 user, string password) throws DBusError, IOError; + public abstract void set_user_password(int32 user, string password) throws DBusError, IOError, DatabaseError; + public abstract void set_sessionid(int user, string sessionid) throws DBusError, IOError, DatabaseError; + public abstract void set_userTheme(int user, string userTheme) throws DBusError, IOError, DatabaseError; + public abstract int get_user_by_sessionid(string sessionid) throws DBusError, IOError, DatabaseError; + public abstract UserInfo get_user_info(int user) throws DBusError, IOError, DatabaseError; + public abstract UserAuth get_user_auth(int user) throws DBusError, IOError, DatabaseError; + public abstract void set_user_auth(UserAuth auth) throws DBusError, IOError, DatabaseError; + public abstract string get_username(int user) throws DBusError, IOError, DatabaseError; + public abstract string get_user_theme(int user, string fallback) throws DBusError, IOError, DatabaseError; + public abstract InvoiceEntry[] get_invoice(int user, int64 from=0, int64 to=-1) throws DBusError, IOError, DatabaseError; + public abstract int64 get_first_purchase(int user) throws DBusError, IOError; + public abstract int64 get_last_purchase(int user) throws DBusError, IOError; + public abstract StatsInfo get_stats_info() throws DBusError, IOError; + public abstract int[] get_member_ids() throws DBusError, IOError, DatabaseError; + public abstract int[] get_system_member_ids() throws DBusError, IOError, DatabaseError; + public abstract void user_disable(int user, bool value) throws DBusError, IOError, DatabaseError; + public abstract void user_replace(UserInfo u) throws DBusError, IOError, DatabaseError; + public abstract bool user_is_disabled(int user) throws DBusError, IOError, DatabaseError; + public abstract bool user_exists(int user) throws DBusError, IOError, DatabaseError; + public abstract bool user_equals(UserInfo u) throws DBusError, IOError, DatabaseError; + public abstract int64 get_timestamp_of_last_purchase() throws DBusError, IOError; + public abstract Category[] get_category_list() throws DBusError, IOError, DatabaseError; + public abstract int add_category(string name) throws DBusError, IOError, DatabaseError; + public abstract Supplier[] get_supplier_list() throws DBusError, IOError; + public abstract Supplier get_supplier(int id) throws DBusError, IOError; + public abstract void add_supplier(string name, string postal_code, string city, string street, string phone, string website) throws DBusError, IOError, DatabaseError; + public abstract int[] get_users_with_sales(int64 timestamp_from, int64 timestamp_to) throws DBusError, IOError; + public abstract Price get_user_invoice_sum(int user, int64 timestamp_from, int64 timestamp_to) throws DBusError, IOError; + public abstract Price cashbox_status() throws DBusError, IOError; + public abstract void cashbox_add(int user, Price amount, int64 timestamp) throws DBusError, IOError, DatabaseError; + public abstract CashboxDiff[] cashbox_history() throws DBusError, IOError; + public abstract CashboxDiff[] cashbox_changes(int64 start, int64 stop) throws DBusError, IOError; + public abstract void ean_alias_add(uint64 ean, uint64 real_ean) throws DBusError, IOError, DatabaseError; + public abstract uint64 ean_alias_get(uint64 ean) throws DBusError, IOError; + public abstract EanAlias[] ean_alias_list() throws DBusError, IOError; + public abstract BestBeforeEntry[] bestbeforelist() throws DBusError, IOError; + public abstract int get_userid_for_rfid(string rfid) throws DBusError, IOError, DatabaseError; + public abstract void addrfid(string rfid, int user) throws DBusError, IOError, DatabaseError; + public abstract void delete_rfid_for_user(int user) throws DBusError, IOError, DatabaseError; } public struct Category { @@ -72,8 +78,8 @@ public struct Category { public string name; } -public struct StockEntry { - public string id; +public struct DetailedProduct { + public uint64 ean; public string name; public string category; public int amount; @@ -126,6 +132,7 @@ public struct UserInfo { public bool disabled; public bool hidden; public string soundTheme; + public string[] rfid; public bool equals(UserInfo x) { if(id != x.id) return false; @@ -140,6 +147,7 @@ public struct UserInfo { if(joined_at != x.joined_at) return false; if(disabled != x.disabled) return false; if(hidden != x.hidden) return false; + if(rfid != x.rfid) return false; return true; } @@ -197,4 +205,5 @@ public errordomain DatabaseError { SESSION_NOT_FOUND, USER_NOT_FOUND, CONSTRAINT_FAILED, + RFID_NOT_FOUND, } diff --git a/src/database/main.vala b/src/database/main.vala index 72e3813..bd4c7dc 100644 --- a/src/database/main.vala +++ b/src/database/main.vala @@ -13,40 +13,41 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -public static void write_to_log(string message, ...) { - /* TODO: send message via DBus? Replace some write_to_log by throwing an error? */ -} - DataBase db; public static int main(string[] args) { + Intl.setlocale(LocaleCategory.ALL, ""); + Intl.textdomain("shopsystem"); + try { Config cfg = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Config", "/io/mainframe/shopsystem/config"); var dbfile = cfg.get_string("DATABASE", "file"); db = new DataBase(dbfile); } catch(IOError e) { - error("IOError: %s\n", e.message); + error(_("IO Error: %s\n"), e.message); } catch(KeyFileError e) { - error("Config Error: %s\n", e.message); + error(_("Config Error: %s\n"), e.message); + } catch(DBusError e) { + error(_("DBus Error: %s\n"), e.message); } Bus.own_name( BusType.SYSTEM, "io.mainframe.shopsystem.Database", BusNameOwnerFlags.NONE, - on_bus_aquired, + on_bus_acquired, () => {}, - () => stderr.printf("Could not aquire name\n")); + () => stderr.printf(_("Could not acquire name\n"))); new MainLoop().run(); return 0; } -void on_bus_aquired(DBusConnection con) { +void on_bus_acquired(DBusConnection con) { try { con.register_object("/io/mainframe/shopsystem/database", db); } catch(IOError e) { - stderr.printf("Could not register service\n"); + stderr.printf(_("Could not register service\n")); } } diff --git a/src/display-on-off/display-on-off.sh b/src/display-on-off/display-on-off.sh new file mode 100755 index 0000000..23ab10a --- /dev/null +++ b/src/display-on-off/display-on-off.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +function getMqttConfig +{ + echo `busctl --system call io.mainframe.shopsystem.Config /io/mainframe/shopsystem/config io.mainframe.shopsystem.Config GetString ss MQTT $1 | sed -s "s;s ;;"` +} + +BROKER=$(getMqttConfig broker) +TOPIC=$(getMqttConfig topic) +ON=$(getMqttConfig displayOn) +OFF=$(getMqttConfig displayOff) + +mosquitto_sub -h $BROKER -t $TOPIC | while read RAW_DATA +do + case $RAW_DATA in + $ON) + vbetool dpms on + ;; + $OFF) + vbetool dpms off + ;; + *) + #vbetool dpms on + ;; + esac +done diff --git a/src/input-device/.gitignore b/src/input-device/.gitignore deleted file mode 100644 index 3ba6df4..0000000 --- a/src/input-device/.gitignore +++ /dev/null @@ -1 +0,0 @@ -input-device diff --git a/src/input-device/Makefile b/src/input-device/Makefile deleted file mode 100644 index aba6c73..0000000 --- a/src/input-device/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -all: input-device - @echo > /dev/null - -input-device: main.vala input-device.vala input-device-interface.vala ../config/config-interface.vala - valac -X -w -o $@ --pkg linux --pkg posix --pkg gio-2.0 $^ - -clean: - rm -rf input-device - -.PHONY: all clean diff --git a/src/input-device/input-device-interface.vala b/src/input-device/input-device-interface.vala index 067b827..9b16da4 100644 --- a/src/input-device/input-device-interface.vala +++ b/src/input-device/input-device-interface.vala @@ -16,5 +16,5 @@ [DBus (name = "io.mainframe.shopsystem.InputDevice")] public interface InputDevice : Object { public abstract signal void received_barcode(string barcode); - public abstract void blink(uint duration) throws IOError; + public abstract void blink(uint duration) throws IOError, DBusError; } diff --git a/src/input-device/input-device.vala b/src/input-device/input-device.vala index 6988c6d..1dba213 100644 --- a/src/input-device/input-device.vala +++ b/src/input-device/input-device.vala @@ -1,4 +1,5 @@ /* Copyright 2015, Sebastian Reichel <sre@ring0.de> + * Copyright 2017-2018, Johannes Rudolph <johannes.rudolph@gmx.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -23,7 +24,7 @@ public class Device { public Device(string device) { if (device == "ignore") { - stdout.printf("Ignoring InputDevice!\n"); + stdout.printf(_("Ignoring InputDevice!\n")); return; } try { @@ -36,10 +37,10 @@ public class Device { Posix.fcntl(fd, Posix.F_SETFL, flags | Posix.O_NONBLOCK); if(!(io_read.add_watch(IOCondition.IN | IOCondition.HUP, device_read) != 0)) { - error("Could not bind IOChannel"); + error(_("Could not bind IOChannel")); } } catch(FileError e) { - error("FileError: %s", e.message); + error(_("File Error: %s"), e.message); } } @@ -211,7 +212,7 @@ public class Device { char key = '\0'; if((cond & IOCondition.HUP) == IOCondition.HUP) - error("Lost device"); + error(_("Lost device")); do { int fd = source.unix_get_fd(); @@ -220,7 +221,7 @@ public class Device { /* short read */ if (s != sizeof(Linux.Input.Event)) { if(s > 0) - stdout.printf("short read!\n"); + stdout.printf(_("short read!\n")); return true; } @@ -245,7 +246,7 @@ public class Device { buffer += "%c".printf(key); } while(key != '\n'); - stdout.printf("barcode: %s\n", buffer); + stdout.printf(_("barcode: %s\n"), buffer); if(buffer.has_prefix("USER ") || buffer.has_prefix("STOCK") || buffer.has_prefix("AMOUNT ")) { if(!check_code39_checksum(buffer)) @@ -315,7 +316,7 @@ public class Device { /** * @param duration duration of the blink in 0.1 seconds */ - public void blink(uint duration) { + public void blink(uint duration) throws IOError, DBusError { /* not supported */ } } diff --git a/src/input-device/main.vala b/src/input-device/main.vala index 8578033..487c028 100644 --- a/src/input-device/main.vala +++ b/src/input-device/main.vala @@ -1,4 +1,5 @@ /* Copyright 2015, Sebastian Reichel <sre@ring0.de> + * Copyright 2017-2018, Johannes Rudolph <johannes.rudolph@gmx.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -13,35 +14,47 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -Device dev; +Device devScanner; +Device devRfid; public static int main(string[] args) { + Intl.setlocale(LocaleCategory.ALL, ""); + Intl.textdomain("shopsystem"); + try { Config cfg = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Config", "/io/mainframe/shopsystem/config"); - dev = new Device(cfg.get_string("INPUT", "device")); + devScanner = new Device(cfg.get_string("INPUT", "barcodescanner")); + devRfid = new Device(cfg.get_string("INPUT", "rfidreader")); } catch(IOError e) { - error("IOError: %s\n", e.message); + error(_("IO Error: %s\n"), e.message); } catch(KeyFileError e) { - error("Config Error: %s\n", e.message); + error(_("Config Error: %s\n"), e.message); + } catch(DBusError e) { + error(_("DBus Error: %s\n"), e.message); } Bus.own_name( BusType.SYSTEM, "io.mainframe.shopsystem.InputDevice", BusNameOwnerFlags.NONE, - on_bus_aquired, + on_bus_acquired, () => {}, - () => stderr.printf("Could not aquire name\n")); + () => stderr.printf(_("Could not acquire name\n"))); new MainLoop().run(); return 0; } -void on_bus_aquired(DBusConnection con) { +void on_bus_acquired(DBusConnection con) { + try { + con.register_object("/io/mainframe/shopsystem/device/scanner", devScanner); + } catch(IOError e) { + stderr.printf(_("Could not register service\n")); + } try { - con.register_object("/io/mainframe/shopsystem/device", dev); + con.register_object("/io/mainframe/shopsystem/device/rfid", devRfid); } catch(IOError e) { - stderr.printf("Could not register service\n"); + stderr.printf(_("Could not register service\n")); } } diff --git a/src/invoice/.gitignore b/src/invoice/.gitignore deleted file mode 100644 index 0d4d46e..0000000 --- a/src/invoice/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -invoice -single-invoice diff --git a/src/invoice/Makefile b/src/invoice/Makefile deleted file mode 100644 index 73eaf48..0000000 --- a/src/invoice/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -all: invoice single-invoice - @echo > /dev/null - -invoice: main.vala invoice.vala ../mail/mailer-interface.vala ../pdf-invoice/pdf-invoice-interface.vala ../database/db-interface.vala ../config/config-interface.vala ../price.vapi - valac -X -w -o $@ --pkg gio-2.0 $^ - -single-invoice: single.vala invoice.vala ../mail/mailer-interface.vala ../pdf-invoice/pdf-invoice-interface.vala ../database/db-interface.vala ../config/config-interface.vala ../price.vapi - valac -X -w -o $@ --pkg gio-2.0 $^ - -clean: - rm -f invoice single-invoice - -.PHONY: all clean diff --git a/src/invoice/invoice.vala b/src/invoice/invoice.vala index 1e6dd58..e83f4bf 100644 --- a/src/invoice/invoice.vala +++ b/src/invoice/invoice.vala @@ -32,16 +32,29 @@ public class InvoiceImplementation { Database db; PDFInvoice pdf; string datadir; - - public InvoiceImplementation() throws IOError, KeyFileError { + string mailfromaddress; + string treasurermailaddress; + string shortname; + string spacename; + string vat; + string jverein_membership_number; + + public InvoiceImplementation() throws DBusError, IOError, KeyFileError { mailer = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Mail", "/io/mainframe/shopsystem/mailer"); db = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Database", "/io/mainframe/shopsystem/database"); pdf = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.InvoicePDF", "/io/mainframe/shopsystem/invoicepdf"); Config cfg = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Config", "/io/mainframe/shopsystem/config"); - datadir = cfg.get_string("INVOICE", "datadir"); + var datapath = cfg.get_string("GENERAL", "datapath"); + datadir = Path.build_filename(datapath, "invoice"); + mailfromaddress = cfg.get_string("MAIL", "mailfromaddress"); + treasurermailaddress = cfg.get_string("MAIL", "treasurermailaddress"); + shortname = cfg.get_string("GENERAL", "shortname"); + spacename = cfg.get_string("GENERAL", "spacename"); + vat = cfg.get_string("INVOICE", "vat"); + jverein_membership_number = cfg.get_string("JVEREIN", "membership_number"); } - public void send_invoice(bool temporary, int64 timestamp, int user) throws IOError, InvoicePDFError, DatabaseError { + public void send_invoice(bool temporary, int64 timestamp, int user) throws DBusError, IOError, InvoicePDFError, DatabaseError { int64 prevtimestamp = timestamp - day_in_seconds; if(!temporary) @@ -67,9 +80,9 @@ public class InvoiceImplementation { string treasurer_path = mailer.create_mail(); Mail treasurer_mail = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Mail", treasurer_path); - treasurer_mail.from = {"KtT Shopsystem", "shop@kreativitaet-trifft-technik.de"}; + treasurer_mail.from = {shortname + " Shopsystem", mailfromaddress}; treasurer_mail.subject = mailtitle; - treasurer_mail.add_recipient({"Schatzmeister", "shop-einzug@kreativitaet-trifft-technik.de"}, RecipientType.TO); + treasurer_mail.add_recipient({"Schatzmeister", treasurermailaddress}, RecipientType.TO); var csvinvoicedata = ""; foreach(var userid in users) { @@ -82,7 +95,7 @@ public class InvoiceImplementation { var invoicedata = generate_invoice(temporary, timestamp, userid, invoiceid); string mail_path = mailer.create_mail(); Mail mail = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Mail", mail_path); - mail.from = {"KtT Shopsystem", "shop@kreativitaet-trifft-technik.de"}; + mail.from = {shortname + " Shopsystem", mailfromaddress}; mail.subject = mailtitle; mail.add_recipient({@"$(userdata.firstname) $(userdata.lastname)", userdata.email}, RecipientType.TO); @@ -109,11 +122,15 @@ public class InvoiceImplementation { } } - public void send_invoices(bool temporary, int64 timestamp) throws IOError, InvoicePDFError, DatabaseError { + public void send_invoices(bool temporary, int64 timestamp) throws DBusError, IOError, InvoicePDFError, DatabaseError { int64 prevtimestamp = timestamp - day_in_seconds; + string due_date_string = ""; - if(!temporary) + if(!temporary) { prevtimestamp = new DateTime.from_unix_local(timestamp).add_months(-1).to_unix(); + var due_date = new DateTime.from_unix_local(timestamp).add_days(10); + due_date_string = due_date.format("%d.%m.%Y"); + } Timespan ts = get_timespan(temporary, prevtimestamp); Timespan tst = get_timespan(false, prevtimestamp); @@ -135,10 +152,17 @@ public class InvoiceImplementation { string treasurer_path = mailer.create_mail(); Mail treasurer_mail = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Mail", treasurer_path); - treasurer_mail.from = {"KtT Shopsystem", "shop@kreativitaet-trifft-technik.de"}; + treasurer_mail.from = {shortname + " Shopsystem", mailfromaddress}; treasurer_mail.subject = mailtitle; - treasurer_mail.add_recipient({"Schatzmeister", "shop-einzug@kreativitaet-trifft-technik.de"}, RecipientType.TO); + treasurer_mail.add_recipient({"Schatzmeister", treasurermailaddress}, RecipientType.TO); var csvinvoicedata = ""; + var csvjvereininvoicedata = ""; + if(jverein_membership_number == "extern") { + csvjvereininvoicedata = "Ext_Mitglieds_Nr;Betrag;Buchungstext;Fälligkeit;Intervall;Endedatum"; + } + else { + csvjvereininvoicedata = "Mitglieds_Nr;Betrag;Buchungstext;Fälligkeit;Intervall;Endedatum"; + } foreach(var userid in users) { number++; @@ -149,7 +173,7 @@ public class InvoiceImplementation { string mail_path = mailer.create_mail(); Mail mail = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Mail", mail_path); - mail.from = {"KtT Shopsystem", "shop@kreativitaet-trifft-technik.de"}; + mail.from = {shortname + " Shopsystem", mailfromaddress}; mail.subject = mailtitle; mail.add_recipient({@"$(userdata.firstname) $(userdata.lastname)", userdata.email}, RecipientType.TO); @@ -165,17 +189,19 @@ public class InvoiceImplementation { if(!temporary) { treasurer_mail.add_attachment(invoicedata.pdffilename, "application/pdf", invoicedata.pdfdata); csvinvoicedata += @"$(userdata.id),$(userdata.lastname),$(userdata.firstname),$invoiceid,$total_sum\n"; + csvjvereininvoicedata += @"$(userdata.id);$total_sum;Shopsystem Rechnung Nummer $invoiceid;$due_date_string;0;$due_date_string\n"; } } if(!temporary) { treasurer_mail.set_main_part(get_treasurer_text(), MessageType.PLAIN); treasurer_mail.add_attachment("invoice.csv", "text/csv; charset=utf-8", csvinvoicedata.data); + treasurer_mail.add_attachment("jvereininvoice.csv", "text/csv; charset=utf-8", csvjvereininvoicedata.data); mailer.send_mail(treasurer_path); } } - public InvoiceData generate_invoice(bool temporary, int64 timestamp, int userid, string invoiceid) throws IOError, InvoicePDFError, DatabaseError { + public InvoiceData generate_invoice(bool temporary, int64 timestamp, int userid, string invoiceid) throws DBusError, IOError, InvoicePDFError, DatabaseError { int64 prevtimestamp = timestamp - day_in_seconds; if(!temporary) prevtimestamp = new DateTime.from_unix_local(timestamp).add_months(-1).to_unix(); @@ -222,11 +248,13 @@ public class InvoiceImplementation { string text; try { - FileUtils.get_contents(datadir + "/treasurer.mail.txt", out text); + FileUtils.get_contents(Path.build_filename(datadir, "/treasurer.mail.txt"), out text); } catch(GLib.FileError e) { - throw new IOError.FAILED("Could not open invoice template: %s", e.message); + throw new IOError.FAILED(_("Could not open invoice template: %s"), e.message); } + text = text.replace("{{{SHORTNAME}}}", shortname); + return text; } @@ -284,19 +312,36 @@ public class InvoiceImplementation { table = generate_invoice_table_html(entries); if(filename == "") - throw new IOError.FAILED("Unknown MessageType"); + throw new IOError.FAILED(_("Unknown MessageType")); try { - FileUtils.get_contents(datadir + "/" + filename, out text); + FileUtils.get_contents(Path.build_filename(datadir, filename), out text); } catch(GLib.FileError e) { - throw new IOError.FAILED("Could not open invoice template: %s", e.message); + throw new IOError.FAILED(_("Could not open invoice template: %s"), e.message); } text = text.replace("{{{ADDRESS}}}", address); text = text.replace("{{{LASTNAME}}}", name); + text = text.replace("{{{SPACENAME}}}", spacename); text = text.replace("{{{INVOICE_TABLE}}}", table); text = text.replace("{{{SUM_MONTH}}}", "%d,%02d".printf(total_sum / 100, total_sum % 100)); + if(vat == "yes") { + text = text.replace("{{{VAT}}}", ""); + } else { + string vattext; + string vattextfilename; + vattextfilename = (type == MessageType.HTML) ? "vat.html" : "vat.txt"; + + try { + FileUtils.get_contents(Path.build_filename(datadir, vattextfilename), out vattext); + } catch(GLib.FileError e) { + throw new IOError.FAILED(_("Could not open VAT template: %s"), e.message); + } + + text = text.replace("{{{VAT}}}", vattext); + } + return text; } diff --git a/src/invoice/main.vala b/src/invoice/main.vala index b341adb..f5ec69b 100644 --- a/src/invoice/main.vala +++ b/src/invoice/main.vala @@ -16,11 +16,14 @@ InvoiceImplementation invoice; public static void help(string name) { - stderr.printf("Usage: %s <temporary> [timestamp]\n", name); - stderr.printf("Possible values for <temporary>: temporary, final\n"); + stderr.printf(_("Usage: %s <temporary> [timestamp]\n"), name); + stderr.printf(_("Possible values for <temporary>: temporary, final\n")); } public static int main(string[] args) { + Intl.setlocale(LocaleCategory.ALL, ""); + Intl.textdomain("shopsystem"); + bool temporary = false; int64 timestamp = new DateTime.now_local().to_unix(); @@ -45,14 +48,14 @@ public static int main(string[] args) { try { invoice = new InvoiceImplementation(); } catch(Error e) { - stderr.printf("Error: %s\n", e.message); + stderr.printf(_("Error: %s\n"), e.message); return 1; } try { invoice.send_invoices(temporary, timestamp); } catch(Error e) { - stderr.printf("Error: %s\n", e.message); + stderr.printf(_("Error: %s\n"), e.message); return 1; } diff --git a/src/invoice/single.vala b/src/invoice/single.vala index 54cba26..6f54afe 100644 --- a/src/invoice/single.vala +++ b/src/invoice/single.vala @@ -48,14 +48,14 @@ public static int main(string[] args) { try { invoice = new InvoiceImplementation(); } catch(Error e) { - stderr.printf("Error: %s\n", e.message); + stderr.printf(_("Error: %s\n"), e.message); return 1; } try { invoice.send_invoice(temporary, timestamp, user); } catch(Error e) { - stderr.printf("Error: %s\n", e.message); + stderr.printf(_("Error: %s\n"), e.message); return 1; } diff --git a/src/libcairobarcode/code39.vala b/src/libcairobarcode/code39.vala new file mode 100644 index 0000000..ca5451c --- /dev/null +++ b/src/libcairobarcode/code39.vala @@ -0,0 +1,128 @@ +/* Copyright 2014, Sebastian Reichel <sre@ring0.de> + * + * 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 Code39 { + Cairo.Context ctx; + double width; + double height; + + /* 0 = wide black, 1 = narrow black, 2 = wide white, 3 = narrow white */ + const uint32[] lookup_table = { + 0x1d8cd, 0xd9dc, 0x1c9dc, 0xc9dd, 0x1d8dc, 0xd8dd, 0x1c8dd, 0x1d9cc, + 0xd9cd, 0x1c9cd, 0xdd9c, 0x1cd9c, 0xcd9d, 0x1dc9c, 0xdc9d, 0x1cc9d, + 0x1dd8c, 0xdd8d, 0x1cd8d, 0x1dc8d, 0xddd8, 0x1cdd8, 0xcdd9, 0x1dcd8, + 0xdcd9, 0x1ccd9, 0x1ddc8, 0xddc9, 0x1cdc9, 0x1dcc9, 0x9ddc, 0x18ddc, + 0x8ddd, 0x19cdc, 0x9cdd, 0x18cdd, 0x19dcc, 0x9dcd, 0x18dcd, 0x1999d, + 0x199d9, 0x19d99, 0x1d999, 0x19ccd + }; + + public Code39(Cairo.Context ctx, double width, double height) { + this.ctx = ctx; + this.width = width; + this.height = height; + } + + private int lookup_index(char c) throws BarcodeError { + if(c >= '0' && c <= '9') + return c - '0'; + if(c >= 'A' && c <= 'Z') + return c - 'A' + 10; + switch (c) { + case '-': + return 36; + case '.': + return 37; + case ' ': + return 38; + case '$': + return 39; + case '/': + return 40; + case '+': + return 41; + case '%': + return 42; + case '*': + return 43; + default: + throw new BarcodeError.UNEXPECTED_CHARACTER("Character '%c' is not allowed in Code 39".printf(c)); + } + } + + private uint32 lookup(char c) throws BarcodeError { + return lookup_table[lookup_index(c)]; + } + + private void draw_line(bool black, double linewidth) { + double x,y; + + if(black) + ctx.set_source_rgb(0, 0, 0); + else + ctx.set_source_rgb(1, 1, 1); + + ctx.rel_line_to(0,height); + ctx.rel_move_to(linewidth,-height); + + ctx.get_current_point(out x, out y); + ctx.stroke(); + ctx.move_to(x,y); + } + + public void draw(string code) throws BarcodeError { + string mycode = code; + + if(!mycode.has_prefix("*")) + mycode = "*" + mycode; + if(!mycode.has_suffix("*")) + mycode = mycode + "*"; + + double linewidth = width / (mycode.length * 13.0); + + ctx.save(); + ctx.set_line_width(linewidth); + ctx.move_to(0,0); + ctx.rel_move_to(0.5*linewidth,0); + + for(int i=0; i<mycode.length; i++) { + var format = lookup(mycode[i]); + + for(int j=8; j>=0; j--) { + var line = (format >> (2*j)) & 0x3; + + switch(line) { + case 0: + draw_line(true, linewidth); + draw_line(true, linewidth); + break; + case 1: + draw_line(true, linewidth); + break; + case 2: + draw_line(false, linewidth); + draw_line(false, linewidth); + break; + default: + draw_line(false, linewidth); + break; + } + } + + draw_line(false, linewidth); + } + + ctx.restore(); + } +} diff --git a/src/libcairobarcode/ean.vala b/src/libcairobarcode/ean.vala new file mode 100644 index 0000000..4a4f621 --- /dev/null +++ b/src/libcairobarcode/ean.vala @@ -0,0 +1,142 @@ +/* Copyright 2014, Sebastian Reichel <sre@ring0.de> + * + * 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 EAN { + Cairo.Context ctx; + double width; + double height; + const double marker_length_diff = 10.0; + double basex; + double basey; + + const uint8[] C = { 0x00, 0x0b, 0x0d, 0x0e, 0x13, 0x19, 0x1c, 0x15, 0x16, 0x1a }; + + static uint8[,] lookup = { + { 0x0d, 0x19, 0x13, 0x3d, 0x23, 0x31, 0x2f, 0x3b, 0x37, 0x0b }, /* L */ + { 0x27, 0x33, 0x1b, 0x21, 0x1d, 0x39, 0x05, 0x11, 0x09, 0x17 }, /* G */ + { 0x72, 0x66, 0x6c, 0x42, 0x5c, 0x4e, 0x50, 0x44, 0x48, 0x74 } /* R */ + }; + + public EAN(Cairo.Context ctx, double width, double height) { + this.ctx = ctx; + this.width = width / 95.0; + this.height = height; + } + + private void draw_line(bool black) { + double x,y; + + if(black) + ctx.set_source_rgb(0, 0, 0); + else + ctx.set_source_rgb(1, 1, 1); + + ctx.rel_line_to(0,height); + ctx.rel_move_to(width,-height); + + ctx.get_current_point(out x, out y); + ctx.stroke(); + ctx.move_to(x,y); + } + + private void draw_startstop() { + draw_line(true); + draw_line(false); + draw_line(true); + } + + private void draw_middle() { + draw_line(false); + draw_line(true); + draw_line(false); + draw_line(true); + draw_line(false); + } + + private void draw_number_text(uint8 number) { + double x, y; + ctx.get_current_point(out x, out y); + ctx.set_source_rgb(0, 0, 0); + + ctx.stroke(); + ctx.move_to(x,y); + } + + private void draw_number(uint8 number, uint8 type) { + draw_number_text(number); + + for(int i=6; i>=0; i--) { + var bit = (lookup[type,number] >> i) & 1; + draw_line(bit == 1); + } + } + + private uint8 get_number(string ean, int pos) { + return (uint8) int.parse("%c".printf(ean[pos])); + } + + private void draw13(string ean) { + /* need some extra space for the checksum */ + width = (width * 95.0) / 102.0; + + uint8 LG = C[get_number(ean, 0)]; + + draw_startstop(); + for(int i=1, x = 5; i<7; i++, x--) { + uint8 type = (uint8) (LG >> x) & 1; + draw_number(get_number(ean,i), type); + } + draw_middle(); + for(int i=7; i<13; i++) + draw_number(get_number(ean,i), 2); + draw_startstop(); + + /* remove extra space for the checksum */ + width = (width * 102.0) / 95.0; + + } + + private void draw8(string ean) { + draw_startstop(); + for(int i=0; i<4; i++) + draw_number(get_number(ean,i), 0); + draw_middle(); + for(int i=4; i<8; i++) + draw_number(get_number(ean,i), 2); + draw_startstop(); + } + + public void draw(string ean) throws BarcodeError { + ctx.save(); + ctx.set_line_width(width); + ctx.get_current_point(out basex, out basey); + ctx.rel_move_to(0.5*width,0); + + for(int i=0; i<ean.length; i++) { + if(ean[i] < '0' || ean[i] > '9') { + throw new BarcodeError.UNEXPECTED_CHARACTER("Character '%c' is not allowed in EAN".printf(ean[i])); + } + } + + if(ean.length == 13) + draw13(ean); + else if(ean.length == 8) + draw8(ean); + else + throw new BarcodeError.UNEXPECTED_LENGTH("length of EAN is incorrect (must be 8 or 13)"); + + ctx.restore(); + } +} diff --git a/src/libcairobarcode/error.vala b/src/libcairobarcode/error.vala new file mode 100644 index 0000000..f81df91 --- /dev/null +++ b/src/libcairobarcode/error.vala @@ -0,0 +1,19 @@ +/* Copyright 2014, Sebastian Reichel <sre@ring0.de> + * + * 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 errordomain BarcodeError { + UNEXPECTED_CHARACTER, + UNEXPECTED_LENGTH +} diff --git a/src/mail/.gitignore b/src/mail/.gitignore deleted file mode 100644 index f2ae723..0000000 --- a/src/mail/.gitignore +++ /dev/null @@ -1 +0,0 @@ -mailer diff --git a/src/mail/Makefile b/src/mail/Makefile deleted file mode 100644 index 6ec153c..0000000 --- a/src/mail/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -all: mailer - @echo > /dev/null - -mailer: main.vala mailer.vala mail.vala mailer-interface.vala ../config/config-interface.vala - valac -X -w -o $@ --vapidir=../../vapi --pkg posix --pkg libesmtp --pkg gio-2.0 --pkg gmime-2.6 -X -D_GNU_SOURCE -X -lesmtp -X -lssl -X -lcrypto -X -ldl -X -pthread $^ - -clean: - rm -f mailer - -.PHONY: all clean diff --git a/src/mail/mail.vala b/src/mail/mail.vala index 41153f8..f6cedc0 100644 --- a/src/mail/mail.vala +++ b/src/mail/mail.vala @@ -19,16 +19,18 @@ public class MailImplementation { private GMime.Part? main_text = null; private GMime.Part? main_html = null; private GMime.Part[] attachments; + private DateTime gdate; - private GMime.FilterCRLF filter; + private GMime.FilterUnix2Dos filter_unix2dos; + private GMime.FilterSmtpData filter_smtp; private string[] recipients; private string? reversepath; public MailContact from { set { - string sender = value.name + " " + "<" + value.email + ">"; reversepath = value.email; - m.set_sender(sender); + m.add_mailbox(GMime.AddressType.SENDER, value.name, value.email); + m.add_mailbox(GMime.AddressType.FROM, value.name, value.email); }} public string subject { @@ -37,7 +39,7 @@ public class MailImplementation { return (result == null) ? "" : result; } set { - m.set_subject(value); + m.set_subject(value, "utf-8"); } } @@ -53,30 +55,36 @@ public class MailImplementation { public string reply_to { owned get { - var result = m.get_reply_to(); + var result = m.get_reply_to().to_string(new GMime.FormatOptions(), true); return (result == null) ? "" : result; } set { - m.set_reply_to(value); + m.add_mailbox(GMime.AddressType.REPLY_TO, "", value); } } public MailDate date { owned get { MailDate result = {}; - m.get_date(out result.date, out result.tz_offset); + result.timezone = this.gdate.get_timezone_abbreviation(); + result.date = this.gdate.to_unix(); return result; } set { - m.set_date((ulong) value.date, value.tz_offset); + var timezone = new TimeZone(value.timezone); + this.gdate = new DateTime.from_unix_utc((int64) value.date).to_timezone(timezone); + m.set_date(this.gdate); } } public MailImplementation() { m = new GMime.Message(true); - m.set_header("X-Mailer", "KtT Shopsystem"); + m.set_header("X-Mailer", "KtT Shopsystem", "utf-8"); + this.gdate = new DateTime.now_local(); + m.set_date(this.gdate); attachments = new GMime.Part[0]; - filter = new GMime.FilterCRLF(true, true); + filter_smtp = new GMime.FilterSmtpData(); + filter_unix2dos = new GMime.FilterUnix2Dos(true); recipients = new string[0]; } @@ -89,41 +97,37 @@ public class MailImplementation { public void set_subject(string subject) { m.set_subject(subject); } - - public void set_date(uint64 date, int tz_offset) { - m.set_date((ulong) date, tz_offset); - } #endif - public void add_recipient(MailContact contact, GMime.RecipientType type) { - m.add_recipient(type, contact.name, contact.email); + public void add_recipient(MailContact contact, GMime.AddressType type) throws DBusError, IOError { + m.add_mailbox(type, contact.name, contact.email); recipients += contact.email; } - public void set_main_part(string text, MessageType type) { + public void set_main_part(string text, MessageType type) throws DBusError, IOError { GMime.DataWrapper content = new GMime.DataWrapper.with_stream( new GMime.StreamMem.with_buffer(text.data), GMime.ContentEncoding.DEFAULT); GMime.Part? part = new GMime.Part(); - part.set_content_object(content); + part.set_content(content); switch(type) { case MessageType.HTML: - part.set_content_type(new GMime.ContentType.from_string("text/html; charset=utf-8")); + part.set_content_type(GMime.ContentType.parse(new GMime.ParserOptions(), "text/html; charset=utf-8")); part.set_content_encoding(part.get_best_content_encoding(GMime.EncodingConstraint.7BIT)); main_html = part; break; case MessageType.PLAIN: default: - part.set_content_type(new GMime.ContentType.from_string("text/plain; charset=utf-8; format=flowed")); + part.set_content_type(GMime.ContentType.parse(new GMime.ParserOptions(), "text/plain; charset=utf-8; format=flowed")); part.set_content_encoding(part.get_best_content_encoding(GMime.EncodingConstraint.7BIT)); main_text = part; break; } } - public void add_attachment(string filename, string content_type, uint8[] data) { + public void add_attachment(string filename, string content_type, uint8[] data) throws DBusError, IOError { GMime.Part part = new GMime.Part(); GMime.DataWrapper content = new GMime.DataWrapper.with_stream( @@ -133,8 +137,8 @@ public class MailImplementation { /* configure part */ part.set_disposition("attachment"); part.set_filename(filename); - part.set_content_type(new GMime.ContentType.from_string(content_type)); - part.set_content_object(content); + part.set_content_type(GMime.ContentType.parse(new GMime.ParserOptions(), content_type)); + part.set_content(content); part.set_content_encoding(part.get_best_content_encoding(GMime.EncodingConstraint.7BIT)); attachments += part; @@ -190,11 +194,13 @@ public class MailImplementation { [DBus (visible = false)] public string generate() { update_mime_part(); - string result = m.to_string(); + string result = m.to_string(new GMime.FormatOptions()); uint8[] crlfdata; + uint8[] smtpdata; size_t prespace; - filter.filter(result.data, 0, out crlfdata, out prespace); - return (string) crlfdata; + filter_unix2dos.filter(result.data, 0, out crlfdata, out prespace); + filter_smtp.filter(crlfdata, 0, out smtpdata, out prespace); + return (string) smtpdata; } [DBus (visible = false)] diff --git a/src/mail/mailer-interface.vala b/src/mail/mailer-interface.vala index 54b4865..019585b 100644 --- a/src/mail/mailer-interface.vala +++ b/src/mail/mailer-interface.vala @@ -15,9 +15,9 @@ [DBus (name = "io.mainframe.shopsystem.Mailer")] public interface Mailer : Object { - public abstract string create_mail() throws IOError; - public abstract void delete_mail(string path) throws IOError; - public abstract void send_mail(string path) throws IOError; + public abstract string create_mail() throws IOError, DBusError; + public abstract void delete_mail(string path) throws IOError, DBusError; + public abstract void send_mail(string path) throws IOError, DBusError; } [DBus (name = "io.mainframe.shopsystem.Mail")] @@ -28,9 +28,9 @@ public interface Mail : Object { public abstract string reply_to { owned get; set; } public abstract MailDate date { owned get; set; } - public abstract void add_recipient(MailContact contact, RecipientType type = RecipientType.TO) throws IOError; - public abstract void set_main_part(string text, MessageType type = MessageType.PLAIN) throws IOError; - public abstract void add_attachment(string filename, string content_type, uint8[] data) throws IOError; + public abstract void add_recipient(MailContact contact, RecipientType type = RecipientType.TO) throws IOError, DBusError; + public abstract void set_main_part(string text, MessageType type = MessageType.PLAIN) throws IOError, DBusError; + public abstract void add_attachment(string filename, string content_type, uint8[] data) throws IOError, DBusError; } public struct MailAttachment { @@ -51,7 +51,7 @@ public struct MailContact { public struct MailDate { uint64 date; - int tz_offset; + string timezone; } public enum MessageType { diff --git a/src/mail/mailer.vala b/src/mail/mailer.vala index 3ec9381..642ceaa 100644 --- a/src/mail/mailer.vala +++ b/src/mail/mailer.vala @@ -56,10 +56,10 @@ public class MailerImplementation { return 1; } - public MailerImplementation() throws IOError { + public MailerImplementation() throws DBusError, IOError { int result; - GMime.init(0); + GMime.init(); Smtp.auth_client_init(); session = Smtp.Session(); @@ -67,7 +67,7 @@ public class MailerImplementation { send_queue = new Queue<MailImplementation>(); /* ignore SIGPIPE, as suggested by libESMTP */ - Posix.signal(Posix.SIGPIPE, Posix.SIG_IGN); + Posix.signal(Posix.Signal.PIPE, Posix.SIG_IGN); /* get configuration */ Config config = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Config", "/io/mainframe/shopsystem/config"); @@ -76,7 +76,7 @@ public class MailerImplementation { var cfgport = config.get_integer("MAIL", "port"); server = @"$cfgserv:$cfgport"; } catch(KeyFileError e) { - throw new IOError.FAILED("server or port configuration is missing"); + throw new IOError.FAILED(_("server or port configuration is missing")); } try { @@ -96,7 +96,7 @@ public class MailerImplementation { /* setup server */ result = session.set_server(server); if(result == 0) - throw new IOError.FAILED("could not setup server"); + throw new IOError.FAILED(_("could not setup server")); /* Use TLS if possible */ if (starttls) @@ -104,7 +104,7 @@ public class MailerImplementation { else result = session.starttls_enable(Smtp.StartTlsOption.DISABLED); if(result == 0) - throw new IOError.FAILED("could not configure STARTTLS"); + throw new IOError.FAILED(_("could not configure STARTTLS")); /* setup authentication */ if(username != "") { @@ -120,7 +120,7 @@ public class MailerImplementation { GMime.shutdown(); } - public string create_mail() throws IOError { + public string create_mail() throws DBusError, IOError { string path = @"/io/mainframe/shopsystem/mail/$mailcounter"; var mail = new MailImplementation(); @@ -136,17 +136,17 @@ public class MailerImplementation { return path; } - public void delete_mail(string path) throws IOError { + public void delete_mail(string path) throws DBusError, IOError { if(!(path in mails)) - throw new IOError.NOT_FOUND("No such mail"); + throw new IOError.NOT_FOUND(_("No such mail")); mail_bus.unregister_object(mails[path].registration_id); mails.remove(path); } - public void send_mail(string path) throws IOError { + public void send_mail(string path) throws DBusError, IOError { if(!(path in mails)) - throw new IOError.NOT_FOUND("No such mail"); + throw new IOError.NOT_FOUND(_("No such mail")); send_queue.push_tail(mails[path].mail); delete_mail(path); @@ -172,12 +172,16 @@ public class MailerImplementation { message.set_reverse_path(current_mail.get_reverse_path()); int result = session.start_session(); - if(result == 0) - throw new IOError.FAILED("eSMTP: Start Session failed!"); + if(result == 0) { + stderr.printf(_("eSMTP: Start Session failed!")); + return false; + } unowned Smtp.Status status = message.transfer_status(); - if(status.code < 200 || status.code >= 300) - throw new IOError.FAILED("Reply from SMTP-Server: %s", status.text); + if(status.code < 200 || status.code >= 300) { + stderr.printf(_("Reply from SMTP-Server: %s")); + return false; + } current_mail = null; diff --git a/src/mail/main.vala b/src/mail/main.vala index 0c36f6b..b3a7088 100644 --- a/src/mail/main.vala +++ b/src/mail/main.vala @@ -17,30 +17,33 @@ MailerImplementation m; DBusConnection mail_bus; public static int main(string[] args) { + Intl.setlocale(LocaleCategory.ALL, ""); + Intl.textdomain("shopsystem"); + try { m = new MailerImplementation(); - } catch(IOError e) { - stderr.printf("Error: %s\n", e.message); + } catch(Error e) { + stderr.printf(_("Error: %s\n"), e.message); } Bus.own_name( BusType.SYSTEM, "io.mainframe.shopsystem.Mail", BusNameOwnerFlags.NONE, - on_mail_bus_aquired, + on_mail_bus_acquired, () => {}, - () => stderr.printf("Error: Could not aquire name\n")); + () => stderr.printf(_("Error: Could not acquire name\n"))); new MainLoop().run(); return 0; } -void on_mail_bus_aquired(DBusConnection con) { +void on_mail_bus_acquired(DBusConnection con) { try { mail_bus = con; con.register_object("/io/mainframe/shopsystem/mailer", m); } catch(IOError e) { - stderr.printf("Error: Could not register service\n"); + stderr.printf(_("Error: Could not register service\n")); } } diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 0000000..8f7ed3d --- /dev/null +++ b/src/meson.build @@ -0,0 +1,52 @@ +add_project_arguments(['--vapidir', join_paths(meson.current_source_dir(), '..', 'vapi')], language: 'vala') +add_project_arguments('-DGETTEXT_PACKAGE="shopsystem"', language:'c') + +glib_dep = dependency('glib-2.0') +gobject_dep = dependency('gobject-2.0') +gio_dep = dependency('gio-2.0') +gstreamer_dep = dependency('gstreamer-1.0') +gee_dep = dependency('gee-0.8') +sqlite_dep = dependency('sqlite3') +gmime_dep = dependency('gmime-3.0') +rsvg_dep = dependency('librsvg-2.0') +pangocairo_dep = dependency('pangocairo') +cairo_dep = dependency('cairo') +archive_dep = dependency('libarchive') +soup_dep = dependency('libsoup-2.4') + +gpgme_lib = meson.get_compiler('c').find_library('gpgme') +gpgme_vapi = meson.get_compiler('vala').find_library('gpgme', dirs: join_paths(meson.current_source_dir(), '..', 'vapi')) +gpgerror_vapi = meson.get_compiler('vala').find_library('gpg-error', dirs: join_paths(meson.current_source_dir(), '..', 'vapi')) +gpgme_dep = declare_dependency(dependencies: [gpgme_lib, gpgme_vapi, gpgerror_vapi]) + +esmtp_lib = meson.get_compiler('c').find_library('libesmtp') +esmtp_vapi = meson.get_compiler('vala').find_library('libesmtp') +esmtp_dep = declare_dependency(dependencies: [esmtp_lib, esmtp_vapi]) + +curses_lib = meson.get_compiler('c').find_library('ncursesw') +curses_vapi = meson.get_compiler('vala').find_library('curses') +curses_dep = declare_dependency(dependencies: [curses_lib, curses_vapi]) + +gdk_dep = meson.get_compiler('vala').find_library('gdk-2.0') # gdk is only needed for librsvg vapi, not really used, so we only check for the vapi file +posix_dep = meson.get_compiler('vala').find_library('posix') +linux_dep = meson.get_compiler('vala').find_library('linux') + +libcairobarcode = library('cairobarcode', ['libcairobarcode/ean.vala', 'libcairobarcode/code39.vala', 'libcairobarcode/error.vala'], soversion: '0', version: meson.project_version(), dependencies: [gio_dep, cairo_dep], install: true, install_dir: [true, true, true]) +executable('shop-audio', ['audio/main.vala', 'audio/audio.vala', 'audio/audio-interface.vala', 'config/config-interface.vala'], dependencies :[gio_dep, gstreamer_dep], install: true, install_dir: join_paths(get_option('libexecdir'), 'shopsystem')) +executable('shop-backup', ['backup/main.vala', 'mail/mailer-interface.vala', 'config/config-interface.vala'], dependencies: [gio_dep], install: true, install_dir: join_paths(get_option('libexecdir'), 'shopsystem')) +executable('shop-cli', ['cli/main.vala', 'cli/cli.vala', 'cli/cli-interface.vala', 'config/config-interface.vala'], dependencies: [gio_dep], install: true, install_dir: join_paths(get_option('libexecdir'), 'shopsystem')) +executable('shop-config', ['config/main.vala', 'config/config.vala', 'config/config-interface.vala'], dependencies: [gio_dep], install: true, install_dir: join_paths(get_option('libexecdir'), 'shopsystem')) +executable('shop-curses-ui', ['curses-ui/clock.vala', 'curses-ui/curses-ui.vala', 'curses-ui/dialog.vala', 'curses-ui/logo.vala', 'curses-ui/main.vala', 'curses-ui/message_box.vala', 'curses-ui/message_box_overlay.vala', 'curses-ui/numbers.vala', 'curses-ui/status.vala', 'audio/audio-interface.vala', 'scanner-session/scannersession-interface.vala', 'config/config-interface.vala'], dependencies: [gio_dep, posix_dep, curses_dep], install: true, install_dir: join_paths(get_option('libexecdir'), 'shopsystem')) +executable('shop-database', ['database/main.vala', 'database/database.vala', 'database/db-interface.vala', 'config/config-interface.vala', 'price.vapi'], dependencies: [gio_dep, gee_dep, sqlite_dep], install: true, install_dir: join_paths(get_option('libexecdir'), 'shopsystem')) +executable('shop-input-device', ['input-device/main.vala', 'input-device/input-device.vala', 'input-device/input-device-interface.vala', 'config/config-interface.vala'], dependencies: [gio_dep, linux_dep], install: true, install_dir: join_paths(get_option('libexecdir'), 'shopsystem')) +executable('shop-invoice', ['invoice/main.vala', 'invoice/invoice.vala', 'mail/mailer-interface.vala', 'pdf-invoice/pdf-invoice-interface.vala', 'database/db-interface.vala', 'config/config-interface.vala', 'price.vapi'], dependencies: [gio_dep], install: true, install_dir: join_paths(get_option('libexecdir'), 'shopsystem')) +executable('shop-single-invoice', ['invoice/single.vala', 'invoice/invoice.vala', 'mail/mailer-interface.vala', 'pdf-invoice/pdf-invoice-interface.vala', 'database/db-interface.vala', 'config/config-interface.vala', 'price.vapi'], dependencies: [gio_dep], install: true, install_dir: join_paths(get_option('libexecdir'), 'shopsystem')) +executable('shop-mailer', ['mail/main.vala', 'mail/mailer.vala', 'mail/mail.vala', 'mail/mailer-interface.vala', 'config/config-interface.vala'], dependencies: [gio_dep, posix_dep, esmtp_dep, gmime_dep], install: true, install_dir: join_paths(get_option('libexecdir'), 'shopsystem')) +executable('shop-pdf-invoice', ['pdf-invoice/main.vala', 'pdf-invoice/pdf-invoice.vala', 'pdf-invoice/pdf-invoice-interface.vala', 'config/config-interface.vala', 'database/db-interface.vala', 'price.vapi'], dependencies: [gio_dep, pangocairo_dep, rsvg_dep, gdk_dep, posix_dep], install: true, install_dir: join_paths(get_option('libexecdir'), 'shopsystem')) +executable('shop-test-pdf-invoice', ['pdf-invoice/test.vala', 'pdf-invoice/pdf-invoice-interface.vala', 'database/db-interface.vala', 'price.vapi'], dependencies: [gio_dep, pangocairo_dep, rsvg_dep, gdk_dep, posix_dep], install: true, install_dir: join_paths(get_option('libexecdir'), 'shopsystem')) +executable('shop-pdf-stock', ['pdf-stock/main.vala', 'pdf-stock/pdf-stock.vala', 'database/db-interface.vala', 'price.vapi'], dependencies: [gio_dep, pangocairo_dep, cairo_dep, posix_dep], link_with: libcairobarcode, install: true, install_dir: join_paths(get_option('libexecdir'), 'shopsystem')) +executable('shop-test-pdf-stock', ['pdf-stock/test.vala', 'pdf-stock/pdf-stock-interface.vala'], dependencies: [gio_dep], install: true, install_dir: join_paths(get_option('libexecdir'), 'shopsystem')) +executable('shop-pgp', ['pgp/main.vala', 'pgp/pgp.vala', 'pgp/pgp-interface.vala', 'config/config-interface.vala'], dependencies: [gio_dep, gpgme_dep, archive_dep], c_args: ['-D_FILE_OFFSET_BITS=64'], install: true, install_dir: join_paths(get_option('libexecdir'), 'shopsystem')) +executable('shop-scanner-session', ['scanner-session/main.vala', 'scanner-session/scannersession.vala', 'scanner-session/scannersession-interface.vala', 'database/db-interface.vala', 'input-device/input-device-interface.vala', 'cli/cli-interface.vala', 'audio/audio-interface.vala', 'price.vapi'], dependencies: [gio_dep], install: true, install_dir: join_paths(get_option('libexecdir'), 'shopsystem')) +executable('shop-serial-device', ['serial-device/main.vala', 'serial-device/serial-device.vala', 'config/config-interface.vala'], dependencies: [gio_dep, linux_dep], install: true, install_dir: join_paths(get_option('libexecdir'), 'shopsystem')) +executable('shop-web', ['web/main.vala', 'web/web.vala', 'web/websession.vala', 'web/csv.vala', 'web/template.vala', 'database/db-interface.vala', 'pgp/pgp-interface.vala', 'price.vapi', 'config/config-interface.vala', 'audio/audio-interface.vala', 'pdf-stock/pdf-stock-interface.vala'], dependencies: [gio_dep, gee_dep, soup_dep, posix_dep], install: true, install_dir: join_paths(get_option('libexecdir'), 'shopsystem')) diff --git a/src/pdf-invoice/.gitignore b/src/pdf-invoice/.gitignore deleted file mode 100644 index 0b84a09..0000000 --- a/src/pdf-invoice/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -pdf-invoice -test -test.pdf diff --git a/src/pdf-invoice/Makefile b/src/pdf-invoice/Makefile deleted file mode 100644 index 9805f6e..0000000 --- a/src/pdf-invoice/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -all: pdf-invoice - @echo > /dev/null - -pdf-invoice: main.vala pdf-invoice.vala pdf-invoice-interface.vala ../config/config-interface.vala ../database/db-interface.vala ../price.vapi - valac -X -w -g -o $@ --pkg pangocairo --pkg librsvg-2.0 --pkg posix --pkg gdk-2.0 --pkg gio-2.0 $^ - -test: pdf-invoice-interface.vala ../database/db-interface.vala test.vala ../price.vapi - valac -X -w -o $@ --pkg gio-2.0 $^ - -clean: - rm -rf pdf-invoice test - -.PHONY: all clean diff --git a/src/pdf-invoice/main.vala b/src/pdf-invoice/main.vala index 3d8e298..4217eb8 100644 --- a/src/pdf-invoice/main.vala +++ b/src/pdf-invoice/main.vala @@ -16,32 +16,38 @@ private string datadir; public static int main(string[] args) { + Intl.setlocale(LocaleCategory.ALL, ""); + Intl.textdomain("shopsystem"); + try { Config cfg = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Config", "/io/mainframe/shopsystem/config"); - datadir = cfg.get_string("INVOICE", "datadir"); + var datapath = cfg.get_string("GENERAL", "datapath"); + datadir = Path.build_filename(datapath, "invoice"); + } catch(DBusError e) { + error(_("DBus Error: %s\n"), e.message); } catch(IOError e) { - error("IOError: %s\n", e.message); + error(_("IO Error: %s\n"), e.message); } catch(KeyFileError e) { - error("Config Error: %s\n", e.message); + error(_("Config Error: %s\n"), e.message); } Bus.own_name( BusType.SYSTEM, "io.mainframe.shopsystem.InvoicePDF", BusNameOwnerFlags.NONE, - on_bus_aquired, + on_bus_acquired, () => {}, - () => stderr.printf("Could not aquire name\n")); + () => stderr.printf(_("Could not acquire name\n"))); new MainLoop().run(); return 0; } -void on_bus_aquired(DBusConnection conn) { +void on_bus_acquired(DBusConnection conn) { try { conn.register_object("/io/mainframe/shopsystem/invoicepdf", new InvoicePDF(datadir)); - } catch(IOError e) { - stderr.printf("Could not register service\n"); + } catch(Error e) { + stderr.printf(_("Could not register service: %s\n"), e.message); } } diff --git a/src/pdf-invoice/pdf-invoice-interface.vala b/src/pdf-invoice/pdf-invoice-interface.vala index 8fccbf0..41abc6d 100644 --- a/src/pdf-invoice/pdf-invoice-interface.vala +++ b/src/pdf-invoice/pdf-invoice-interface.vala @@ -43,6 +43,6 @@ public interface PDFInvoice : Object { public abstract InvoiceRecipient invoice_recipient { set; owned get; } public abstract InvoiceEntry[] invoice_entries { set; owned get; } - public abstract uint8[] generate() throws IOError, InvoicePDFError; - public abstract void clear() throws IOError; + public abstract uint8[] generate() throws DBusError, IOError, InvoicePDFError; + public abstract void clear() throws DBusError, IOError; } diff --git a/src/pdf-invoice/pdf-invoice.vala b/src/pdf-invoice/pdf-invoice.vala index c50fe6c..d3177c9 100644 --- a/src/pdf-invoice/pdf-invoice.vala +++ b/src/pdf-invoice/pdf-invoice.vala @@ -1,4 +1,5 @@ /* Copyright 2013, Sebastian Reichel <sre@ring0.de> + * Copyright 2017-2018, Johannes Rudolph <johannes.rudolph@gmx.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -15,6 +16,8 @@ [DBus (name = "io.mainframe.shopsystem.InvoicePDF")] public class InvoicePDF { + Config cfg; + /* A4 sizes (in points, 72 DPI) */ private const double width = 595.27559; /* 210mm */ private const double height = 841.88976; /* 297mm */ @@ -60,8 +63,14 @@ public class InvoicePDF { "Dezember" }; - public InvoicePDF(string datadir) { + string longname; + string vat; + + public InvoicePDF(string datadir) throws DBusError, IOError, KeyFileError { this.datadir = datadir; + cfg = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Config", "/io/mainframe/shopsystem/config"); + longname = cfg.get_string("GENERAL", "longname"); + vat = cfg.get_string("INVOICE", "vat"); } private void render_svg(Cairo.Context ctx, string file) { @@ -69,7 +78,7 @@ public class InvoicePDF { var svg = new Rsvg.Handle.from_file(file); svg.render_cairo(ctx); } catch(Error e) { - error("Could not load SVG: %s\n", e.message); + error(_("Could not load SVG: %s\n"), e.message); } } @@ -77,18 +86,18 @@ public class InvoicePDF { ctx.save(); ctx.translate(-20, 818); ctx.scale(1.42, 1.42); - render_svg(ctx, datadir + "/footer-line.svg"); + render_svg(ctx, Path.build_filename(datadir, "footer-line.svg")); ctx.restore(); } private void draw_logo(Cairo.Context ctx) { ctx.save(); ctx.translate(366,25); - render_svg(ctx, datadir + "/logo.svg"); + render_svg(ctx, Path.build_filename(datadir, "logo.svg")); ctx.restore(); } - private void draw_address(Cairo.Context ctx) { + private void draw_address(Cairo.Context ctx) throws DBusError, IOError, KeyFileError { ctx.save(); ctx.set_source_rgb(0, 0, 0); ctx.set_line_width(1.0); @@ -104,8 +113,7 @@ public class InvoicePDF { ctx.set_font_size(8.45); ctx.move_to(56.5, 142); - /* TODO: get string from config file */ - ctx.show_text("Kreativität trifft Technik e.V., Bahnhofsplatz 10, 26122 Oldenburg"); + ctx.show_text(cfg.get_string("INVOICE", "addressrow")); /* actually LMRoman12 */ ctx.select_font_face("LMSans10", Cairo.FontSlant.NORMAL, Cairo.FontWeight.NORMAL); @@ -194,13 +202,12 @@ public class InvoicePDF { ctx.move_to(56.5, 323); - /* TODO: get text from config file */ ctx.show_text(@"Rechnung Nr. $invoice_id"); ctx.restore(); } - private void draw_footer_text_left(Cairo.Context ctx) { + private void draw_footer_text_left(Cairo.Context ctx) throws DBusError, IOError, KeyFileError { ctx.save(); ctx.move_to(64.0, 742.0); ctx.set_source_rgb(0, 0, 0); @@ -224,8 +231,7 @@ public class InvoicePDF { /* set page width */ layout.set_width((int) 140 * Pango.SCALE); - /* TODO: get text from config file */ - var text = "<b>Kreativität trifft Technik e.V.</b>\nAmtsgericht Oldenburg VR 201044\n\nHackspace „Mainframe“\nFabLab „Fab-O-Lab“\nSchnittstelle „Schnittstelle“\n\nBahnhofsplatz 10 • 26122 Oldenburg"; + var text = cfg.get_string("INVOICE", "footer1"); /* write invoice date */ layout.set_markup(text, text.length); @@ -237,7 +243,7 @@ public class InvoicePDF { ctx.restore(); } - private void draw_footer_text_middle(Cairo.Context ctx) { + private void draw_footer_text_middle(Cairo.Context ctx) throws DBusError, IOError, KeyFileError { ctx.save(); ctx.move_to(216.5, 742.0); ctx.set_source_rgb(0, 0, 0); @@ -261,8 +267,7 @@ public class InvoicePDF { /* set page width */ layout.set_width((int) 190 * Pango.SCALE); - /* TODO: get text from config file */ - var text = "<b>Mail:</b> vorstand@kreativitaet-trifft-technik.de\n<b>Web:</b> www.kreativitaet-trifft-technik.de\n\n\n\n<b>BGB-Vorstand:</b>\nPatrick Günther, Michael Pensler, Jan Janssen"; + var text = cfg.get_string("INVOICE", "footer2"); /* write invoice date */ layout.set_markup(text, text.length); @@ -274,7 +279,7 @@ public class InvoicePDF { ctx.restore(); } - private void draw_footer_text_right(Cairo.Context ctx) { + private void draw_footer_text_right(Cairo.Context ctx) throws DBusError, IOError, KeyFileError { ctx.save(); ctx.move_to(410.0, 742.0); ctx.set_source_rgb(0, 0, 0); @@ -298,8 +303,7 @@ public class InvoicePDF { /* set page width */ layout.set_width((int) 150 * Pango.SCALE); - /* TODO: get text from config file */ - var text = "<b>Raiffeisenbank Oldenburg</b>\nIBAN: DE34 2806 0228 0037 0185 00\nBIC: GENODEF1OL2\n\n\n<b>Finanzamt Oldenburg</b>\nAls gemeinnützig anerkannt.\nSteuer Nr.: 64/220/18413"; + var text = cfg.get_string("INVOICE", "footer3"); /* write invoice date */ layout.set_markup(text, text.length); @@ -328,7 +332,7 @@ public class InvoicePDF { return "Moin"; } - private void draw_first_page_text(Cairo.Context ctx) { + private void draw_first_page_text(Cairo.Context ctx) throws IOError { ctx.save(); ctx.move_to(56.5, 352.5); ctx.set_source_rgb(0, 0, 0); @@ -358,13 +362,29 @@ public class InvoicePDF { /* load text template */ try { var text = ""; - FileUtils.get_contents(datadir + "/pdf-template.txt", out text); + FileUtils.get_contents(Path.build_filename(datadir, "pdf-template.txt"), out text); text = text.replace("{{{ADDRESS}}}", address); text = text.replace("{{{LASTNAME}}}", invoice_recipient.lastname); text = text.replace("{{{SUM}}}", @"$sum"); + text = text.replace("{{{ORGANIZATION}}}", longname); + + if(vat == "yes") { + text = text.replace("{{{VAT}}}", ""); + } else { + string vattext; + + try { + FileUtils.get_contents(Path.build_filename(datadir, "vat.txt"), out vattext); + } catch(GLib.FileError e) { + throw new IOError.FAILED(_("Could not open VAT template: %s"), e.message); + } + + text = text.replace("{{{VAT}}}", vattext); + } + layout.set_markup(text, text.length); } catch(GLib.FileError e) { - error("File Error: %s\n", e.message); + error(_("File Error: %s\n"), e.message); } /* render text */ @@ -448,11 +468,11 @@ public class InvoicePDF { var price = @"$(e.price)€".replace(".", ","); if(e.price > 999999) { - throw new InvoicePDFError.PRICE_TOO_HIGH("Prices > 9999.99€ are not supported!"); + throw new InvoicePDFError.PRICE_TOO_HIGH(_("Prices > 9999.99€ are not supported!")); } if(tm.get_year() > 9999) { - throw new InvoicePDFError.TOO_FAR_IN_THE_FUTURE("Years after 9999 are not supported!"); + throw new InvoicePDFError.TOO_FAR_IN_THE_FUTURE(_("Years after 9999 are not supported!")); } /* if date remains the same do not add it again */ @@ -568,7 +588,7 @@ public class InvoicePDF { /* retry adding the entry */ if(!draw_invoice_table_entry(ctx, y, entry, out y)) { - throw new InvoicePDFError.ARTICLE_NAME_TOO_LONG("Article name \"%s\" does not fit on a single page!", entry.product.name); + throw new InvoicePDFError.ARTICLE_NAME_TOO_LONG(_("Article name \"%s\" does not fit on a single page!"), entry.product.name); } } } @@ -597,7 +617,7 @@ public class InvoicePDF { return Cairo.Status.SUCCESS; } - public uint8[] generate() throws InvoicePDFError { + public uint8[] generate() throws DBusError, IOError, InvoicePDFError, KeyFileError { data = null; var document = new Cairo.PdfSurface.for_stream(pdf_write, width, height); @@ -605,16 +625,16 @@ public class InvoicePDF { var ctx = new Cairo.Context(document); if(invoice_id == "") - throw new InvoicePDFError.NO_INVOICE_ID("No invoice ID given!"); + throw new InvoicePDFError.NO_INVOICE_ID(_("No invoice ID given!")); if(invoice_entries == null) - throw new InvoicePDFError.NO_INVOICE_DATA("No invoice data given!"); + throw new InvoicePDFError.NO_INVOICE_DATA(_("No invoice data given!")); if(invoice_date == 0) - throw new InvoicePDFError.NO_INVOICE_DATE("No invoice date given!"); + throw new InvoicePDFError.NO_INVOICE_DATE(_("No invoice date given!")); if(invoice_recipient.firstname == "" && invoice_recipient.lastname == "") - throw new InvoicePDFError.NO_INVOICE_RECIPIENT("No invoice recipient given!"); + throw new InvoicePDFError.NO_INVOICE_RECIPIENT(_("No invoice recipient given!")); /* first page */ draw_logo(ctx); @@ -638,7 +658,7 @@ public class InvoicePDF { return data; } - public void clear() { + public void clear() throws DBusError, IOError { invoice_date = 0; invoice_id = ""; invoice_recipient.firstname = ""; diff --git a/src/pdf-invoice/test.vala b/src/pdf-invoice/test.vala index e55f3a3..5eb7f52 100644 --- a/src/pdf-invoice/test.vala +++ b/src/pdf-invoice/test.vala @@ -14,7 +14,13 @@ */ public static int main(string args[]) { - PDFInvoice invoice = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.InvoicePDF", "/io/mainframe/shopsystem/invoicepdf"); + PDFInvoice invoice; + + try { + invoice = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.InvoicePDF", "/io/mainframe/shopsystem/invoicepdf"); + } catch(IOError e) { + error(_("IO Error: %s\n"), e.message); + } InvoiceRecipient r = { "Max", @@ -43,10 +49,20 @@ public static int main(string args[]) { invoice.invoice_entries = {e1}; /* generate pdf */ - var pdfdata = invoice.generate(); + try { + var pdfdata = invoice.generate(); - /* write pdf into file */ - FileUtils.set_contents("test.pdf", (string) pdfdata, pdfdata.length); + /* write pdf into file */ + FileUtils.set_contents("test.pdf", (string) pdfdata, pdfdata.length); + } catch(DBusError e) { + error(_("DBus Error: %s\n"), e.message); + } catch(IOError e) { + error(_("IO Error: %s\n"), e.message); + } catch(InvoicePDFError e) { + error(_("Invoice PDF Error: %s\n"), e.message); + } catch(FileError e) { + error(_("File Error: %s\n"), e.message); + } return 0; } diff --git a/src/pdf-stock/.gitignore b/src/pdf-stock/.gitignore deleted file mode 100644 index 58a1e54..0000000 --- a/src/pdf-stock/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -pdf-stock -test -test.pdf diff --git a/src/pdf-stock/Makefile b/src/pdf-stock/Makefile deleted file mode 100644 index a9ad1a6..0000000 --- a/src/pdf-stock/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -include ../../config.mk - -LIBCAIROBARCODE=-X -I../../libcairobarcode -X -L../../libcairobarcode -X -lcairobarcode - -all: pdf-stock - @echo > /dev/null - -pdf-stock: main.vala pdf-stock.vala ../database/db-interface.vala ../price.vapi ../../libcairobarcode/libcairobarcode.vapi - valac -X -w ${LIBCAIROBARCODE} -o $@ --pkg cairo --pkg pangocairo --pkg gio-2.0 --pkg posix $^ - -test: test.vala pdf-stock-interface.vala - valac -X -w -o $@ --pkg gio-2.0 $^ - -run: pdf-stock - LD_LIBRARY_PATH=../../libcairobarcode ./pdf-stock - -clean: - rm -rf pdf-stock test - -.PHONY: all clean run diff --git a/src/pdf-stock/main.vala b/src/pdf-stock/main.vala index 49613f4..10aef60 100644 --- a/src/pdf-stock/main.vala +++ b/src/pdf-stock/main.vala @@ -14,23 +14,26 @@ */ public static int main(string[] args) { + Intl.setlocale(LocaleCategory.ALL, ""); + Intl.textdomain("shopsystem"); + Bus.own_name( BusType.SYSTEM, "io.mainframe.shopsystem.StockPDF", BusNameOwnerFlags.NONE, - on_bus_aquired, + on_bus_acquired, () => {}, - () => stderr.printf("Could not aquire name\n")); + () => stderr.printf(_("Could not acquire name\n"))); new MainLoop().run(); return 0; } -void on_bus_aquired(DBusConnection conn) { +void on_bus_acquired(DBusConnection conn) { try { conn.register_object("/io/mainframe/shopsystem/stockpdf", new StockPDF()); } catch(IOError e) { - stderr.printf("Could not register service\n"); + stderr.printf(_("Could not register service\n")); } } diff --git a/src/pdf-stock/pdf-stock-interface.vala b/src/pdf-stock/pdf-stock-interface.vala index be49a20..87d9f61 100644 --- a/src/pdf-stock/pdf-stock-interface.vala +++ b/src/pdf-stock/pdf-stock-interface.vala @@ -15,6 +15,5 @@ [DBus (name = "io.mainframe.shopsystem.StockPDF")] public interface PDFStock : Object { - // if false, only products with an amount > 0 are selected - public abstract uint8[] generate(bool allProducts) throws IOError; + public abstract uint8[] generate(bool allProducts) throws DBusError, IOError; } diff --git a/src/pdf-stock/pdf-stock.vala b/src/pdf-stock/pdf-stock.vala index ac51c23..6270c43 100644 --- a/src/pdf-stock/pdf-stock.vala +++ b/src/pdf-stock/pdf-stock.vala @@ -40,7 +40,7 @@ public class StockPDF { EAN ean; Cairo.Context ctx; Pango.Layout layout; - StockEntry[] stock; + DetailedProduct[] stock; /* pdf data */ private uint8[] data; @@ -99,7 +99,7 @@ public class StockPDF { ctx.restore(); } - private void render_table_row(StockEntry product) throws BarcodeError { + private void render_table_row(DetailedProduct product) throws BarcodeError { ctx.set_line_width(0.8); /* borders */ @@ -117,7 +117,7 @@ public class StockPDF { /* EAN */ ctx.move_to(col1 + padding, y + padding); - ean.draw(product.id); + ean.draw(@"$(product.ean)"); /* Product Name */ ctx.move_to(col2 + padding, y); @@ -125,7 +125,7 @@ public class StockPDF { layout.set_wrap(Pango.WrapMode.WORD_CHAR); layout.set_spacing((int) (-padding * Pango.SCALE)); layout.set_width((int) (col3-col2) * Pango.SCALE); - var text = @"$(product.id)\n$(product.name)"; + var text = @"$(product.ean)\n$(product.name)"; layout.set_text(text, text.length); Pango.cairo_update_layout(ctx, layout); Pango.cairo_show_layout(ctx, layout); @@ -154,7 +154,7 @@ public class StockPDF { return Cairo.Status.SUCCESS; } - public uint8[] generate(bool allProducts) { + public uint8[] generate(bool allProducts) throws DBusError, IOError { data = null; var surface = new Cairo.PdfSurface.for_stream(pdf_write, a4w, a4h); diff --git a/src/pdf-stock/test.vala b/src/pdf-stock/test.vala index cc3da4f..92b6232 100644 --- a/src/pdf-stock/test.vala +++ b/src/pdf-stock/test.vala @@ -16,12 +16,14 @@ public static int main(string args[]) { try { PDFStock stock = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.StockPDF", "/io/mainframe/shopsystem/stockpdf"); - var pdfdata = stock.generate(); + var pdfdata = stock.generate(true); FileUtils.set_contents("test.pdf", (string) pdfdata, pdfdata.length); } catch(IOError e) { - error("IOError: %s", e.message); + error(_("IO Error: %s"), e.message); } catch(FileError e) { - error("FileError: %s", e.message); + error(_("File Error: %s"), e.message); + } catch(DBusError e) { + error(_("DBus Error: %s"), e.message); } return 0; diff --git a/src/pgp/.gitignore b/src/pgp/.gitignore deleted file mode 100644 index c8886a7..0000000 --- a/src/pgp/.gitignore +++ /dev/null @@ -1 +0,0 @@ -pgp diff --git a/src/pgp/Makefile b/src/pgp/Makefile deleted file mode 100644 index 6292111..0000000 --- a/src/pgp/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -all: pgp - @echo > /dev/null - -pgp: main.vala pgp.vala pgp-interface.vala ../config/config-interface.vala - valac --Xcc="-D_FILE_OFFSET_BITS=64" -X -w -o $@ --vapidir ../../vapi -X -lgpgme --pkg gpgme --pkg gio-2.0 --pkg libarchive $^ - -clean: - rm -rf pgp - -.PHONY: all clean diff --git a/src/pgp/main.vala b/src/pgp/main.vala index c866c7b..74bcf0e 100644 --- a/src/pgp/main.vala +++ b/src/pgp/main.vala @@ -21,33 +21,37 @@ PGPKeyArchive pgp; Config cfg; public static int main(string[] args) { + Intl.setlocale(LocaleCategory.ALL, ""); + Intl.textdomain("shopsystem"); + try { cfg = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Config", "/io/mainframe/shopsystem/config"); pgp = new PGPKeyArchive(cfg.get_string("PGP", "keyring")); + } catch(DBusError e) { + error(_("DBus Error: %s\n"), e.message); } catch(IOError e) { - error("IOError: %s\n", e.message); + error(_("IO Error: %s\n"), e.message); } catch(KeyFileError e) { - error("Config Error: %s\n", e.message); + error(_("Config Error: %s\n"), e.message); } - Bus.own_name( BusType.SYSTEM, "io.mainframe.shopsystem.PGP", BusNameOwnerFlags.NONE, - on_bus_aquired, + on_bus_acquired, () => {}, - () => stderr.printf("Could not aquire name\n")); + () => stderr.printf(_("Could not acquire name\n"))); new MainLoop().run(); return 0; } -void on_bus_aquired(DBusConnection con) { +void on_bus_acquired(DBusConnection con) { try { con.register_object("/io/mainframe/shopsystem/pgp", pgp); } catch(IOError e) { - stderr.printf("Could not register service\n"); + stderr.printf(_("Could not register service\n")); } } diff --git a/src/pgp/pgp-interface.vala b/src/pgp/pgp-interface.vala index 62bfe67..2621fae 100644 --- a/src/pgp/pgp-interface.vala +++ b/src/pgp/pgp-interface.vala @@ -15,7 +15,7 @@ [DBus (name = "io.mainframe.shopsystem.PGP")] public interface PGP : Object { - public abstract string[] import_archive(uint8[] data) throws IOError; - public abstract string[] list_keys() throws IOError; - public abstract string get_key(string fingerprint) throws IOError; + public abstract string[] import_archive(uint8[] data) throws IOError, DBusError; + public abstract string[] list_keys() throws IOError, DBusError; + public abstract string get_key(string fingerprint) throws IOError, DBusError; } diff --git a/src/pgp/pgp.vala b/src/pgp/pgp.vala index bb48c61..6d9bed0 100644 --- a/src/pgp/pgp.vala +++ b/src/pgp/pgp.vala @@ -42,13 +42,13 @@ public class PGPKeyArchive { gpg.set_armor(true); } - public string[] import_archive(uint8[] data) { + public string[] import_archive(uint8[] data) throws DBusError, IOError { string[] result = {}; unowned Archive.Entry entry; var archive = new Archive.Read(); /* support all formats & compression types */ - archive.support_compression_all(); + archive.support_filter_all(); archive.support_format_all(); /* load test archive for now */ @@ -93,7 +93,7 @@ public class PGPKeyArchive { return result; } - public string[] list_keys() { + public string[] list_keys() throws DBusError, IOError { string[] result = {}; GPG.Key key; @@ -108,7 +108,7 @@ public class PGPKeyArchive { return result; } - public string get_key(string fingerprint) { + public string get_key(string fingerprint) throws DBusError, IOError { GPG.Data keydata; GPG.Data.create(out keydata); @@ -120,7 +120,7 @@ public class PGPKeyArchive { keydata.read(data); return (string) data; } else { - stdout.printf("error!\n"); + stdout.printf(_("Error!\n")); return ""; } } diff --git a/src/scanner-session/.gitignore b/src/scanner-session/.gitignore deleted file mode 100644 index b16547e..0000000 --- a/src/scanner-session/.gitignore +++ /dev/null @@ -1 +0,0 @@ -scanner-session diff --git a/src/scanner-session/Makefile b/src/scanner-session/Makefile deleted file mode 100644 index 5320025..0000000 --- a/src/scanner-session/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -all: scanner-session - @echo > /dev/null - -scanner-session: main.vala scannersession.vala scannersession-interface.vala ../database/db-interface.vala ../input-device/input-device-interface.vala ../cli/cli-interface.vala ../audio/audio-interface.vala ../price.vapi - valac -X -w -o $@ --pkg gio-2.0 $^ - -clean: - rm -rf scanner-session - -.PHONY: all clean diff --git a/src/scanner-session/main.vala b/src/scanner-session/main.vala index 2ed94f9..3b876c5 100644 --- a/src/scanner-session/main.vala +++ b/src/scanner-session/main.vala @@ -16,13 +16,16 @@ ScannerSessionImplementation session; public static int main(string[] args) { + Intl.setlocale(LocaleCategory.ALL, ""); + Intl.textdomain("shopsystem"); + Bus.own_name( BusType.SYSTEM, "io.mainframe.shopsystem.ScannerSession", BusNameOwnerFlags.NONE, - on_bus_aquired, + on_bus_acquired, () => {}, - () => stderr.printf("Could not aquire name\n")); + () => stderr.printf(_("Could not acquire name\n"))); session = new ScannerSessionImplementation(); @@ -31,10 +34,10 @@ public static int main(string[] args) { return 0; } -void on_bus_aquired(DBusConnection con) { +void on_bus_acquired(DBusConnection con) { try { con.register_object("/io/mainframe/shopsystem/scanner_session", session); } catch(IOError e) { - stderr.printf("Could not register service\n"); + stderr.printf(_("Could not register service\n")); } } diff --git a/src/scanner-session/scannersession-interface.vala b/src/scanner-session/scannersession-interface.vala index 89498df..c19a434 100644 --- a/src/scanner-session/scannersession-interface.vala +++ b/src/scanner-session/scannersession-interface.vala @@ -24,3 +24,25 @@ public enum MessageType { WARNING, ERROR } + +public enum ScannerSessionCodeType { + USER, + GUEST, + UNDO, + LOGOUT, + EAN, + RFIDEM4100, + UNKNOWN +} + +public enum ScannerSessionState { + READY, + USER +} + +public struct ScannerResult { + public MessageType type; + public string message; + public AudioType audioType; + public string nextScannerdata; +} diff --git a/src/scanner-session/scannersession.vala b/src/scanner-session/scannersession.vala index 9638ef5..796cf93 100644 --- a/src/scanner-session/scannersession.vala +++ b/src/scanner-session/scannersession.vala @@ -1,4 +1,5 @@ /* Copyright 2012-2013, Sebastian Reichel <sre@ring0.de> + * Copyright 2017-2018, Johannes Rudolph <johannes.rudolph@gmx.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -16,16 +17,19 @@ [DBus (name = "io.mainframe.shopsystem.ScannerSession")] public class ScannerSessionImplementation { private int user = 0; - private string name = "Guest"; + private string name = _("Guest"); private bool logged_in = false; private bool disabled = false; private string theme = "beep"; private Database db; private AudioPlayer audio; - private InputDevice dev; + private InputDevice devScanner; + private InputDevice devRfid; private Cli cli; + private ScannerSessionState state = ScannerSessionState.READY; + private DetailedProduct[] shoppingCard = {}; public signal void msg(MessageType type, string message); public signal void msg_overlay(string title, string message); @@ -33,14 +37,16 @@ public class ScannerSessionImplementation { public ScannerSessionImplementation() { try { db = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Database", "/io/mainframe/shopsystem/database"); - dev = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.InputDevice", "/io/mainframe/shopsystem/device"); + devScanner = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.InputDevice", "/io/mainframe/shopsystem/device/scanner"); + devRfid = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.InputDevice", "/io/mainframe/shopsystem/device/rfid"); cli = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Cli", "/io/mainframe/shopsystem/cli"); audio = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.AudioPlayer", "/io/mainframe/shopsystem/audio"); - dev.received_barcode.connect(handle_barcode); + devScanner.received_barcode.connect(handle_barcode); + devRfid.received_barcode.connect(handle_barcode); cli.received_barcode.connect(handle_barcode); } catch(IOError e) { - error("IOError: %s\n", e.message); + error(_("IO Error: %s\n"), e.message); } } @@ -51,19 +57,21 @@ public class ScannerSessionImplementation { msg(type, message); } - private void logout() { - logged_in = false; - } - - private bool login(int user) throws IOError { + private bool login(int user) throws DBusError, IOError { this.user = user; - try { - this.name = db.get_username(user); - this.disabled = db.user_is_disabled(user); - } catch(DatabaseError e) { - send_message(MessageType.ERROR, "Error (user=%d): %s", user, e.message); - return false; + if (user != 0) { + try { + this.name = db.get_username(user); + this.disabled = db.user_is_disabled(user); + } catch(DatabaseError e) { + send_message(MessageType.ERROR, _("Error (user=%d): %s"), user, e.message); + return false; + } + } else { + this.name = _("Guest"); + this.disabled = false; } + this.logged_in = true; try { @@ -78,141 +86,255 @@ public class ScannerSessionImplementation { return true; } - private void handle_barcode(string scannerdata) { - try { - stdout.printf("scannerdata: %s\n", scannerdata); - if(interpret(scannerdata)) - dev.blink(1000); - } catch(IOError e) { - send_message(MessageType.ERROR, "IOError: %s", e.message); - } catch(DatabaseError e) { - send_message(MessageType.ERROR, "DatabaseError: %s", e.message); - } - } - - private bool interpret(string scannerdata) throws DatabaseError, IOError { - if(scannerdata.has_prefix("USER ")) { - string str_id = scannerdata.substring(5); - int32 id = int.parse(str_id); + private ScannerSessionCodeType getCodeType(string scannerdata) { + if(scannerdata.has_prefix("USER ")){ + return ScannerSessionCodeType.USER; + } else if(scannerdata == "GUEST") { + return ScannerSessionCodeType.GUEST; + } else if(scannerdata == "UNDO") { + return ScannerSessionCodeType.UNDO; + } else if(scannerdata == "LOGOUT") { + return ScannerSessionCodeType.LOGOUT; + } else if(scannerdata.length == 10) { + return ScannerSessionCodeType.RFIDEM4100; + } else { + //Handle EAN Code + uint64 id = 0; + scannerdata.scanf("%llu", out id); /* check if scannerdata has valid format */ - if(scannerdata != "USER %d".printf(id)) { - audio.play_system("error.ogg"); - send_message(MessageType.ERROR, "Invalid User ID: %s", scannerdata); - return false; - } - - if(logged_in) { - send_message(MessageType.WARNING, "Last user forgot to logout"); - logout(); + if(scannerdata != "%llu".printf(id) && scannerdata != "%08llu".printf(id) && scannerdata != "%013llu".printf(id)) { + return ScannerSessionCodeType.UNKNOWN; } + return ScannerSessionCodeType.EAN; + } + } - if(login(id)) { - audio.play_user(theme, "login"); - send_message(MessageType.INFO, "Login: %s (%d)", name, user); - return true; - } else { + private void play_audio(AudioType audioType) throws DBusError, IOError { + switch (audioType) { + case AudioType.ERROR: audio.play_system("error.ogg"); - send_message(MessageType.ERROR, "Login failed (User ID = %d)", id); - return false; - } - } else if(scannerdata == "GUEST") { - if(logged_in) { - send_message(MessageType.WARNING, "Last user forgot to logout"); - logout(); - } - - if(login(0)) { + break; + case AudioType.LOGIN: audio.play_user(theme, "login"); - send_message(MessageType.INFO, "Login: %s (%d)", name, user); - return true; - } else { - audio.play_system("error.ogg"); - send_message(MessageType.ERROR, "Login failed (User ID = 0)"); - return false; - } - } else if(scannerdata == "UNDO") { - if(!logged_in) { - audio.play_system("error.ogg"); - send_message(MessageType.ERROR, "Can't undo if not logged in!"); - return false; - } else { - string product = db.undo(user); + break; + case AudioType.LOGOUT: + audio.play_user(theme, "logout"); + break; + case AudioType.PURCHASE: + audio.play_user(theme, "purchase"); + break; + case AudioType.INFO: + audio.play_user(theme, "login"); + break; + } + } - if(product != "") { - audio.play_user(theme, "purchase"); - send_message(MessageType.INFO, "Removed purchase of %s", product); - return true; + private ScannerResult handleReadyState(string scannerdata) throws DatabaseError, DBusError, IOError { + ScannerSessionCodeType codeType = getCodeType(scannerdata); + ScannerResult scannerResult = ScannerResult(); + switch (codeType) { + case ScannerSessionCodeType.USER: + int32 userid = int.parse(scannerdata.substring(5)); + if(login(userid)) { + scannerResult.type = MessageType.INFO; + scannerResult.message = _("Login: %s (%d)").printf(name, user); + scannerResult.audioType = AudioType.LOGIN; + shoppingCard = {}; + state = ScannerSessionState.USER; } else { - audio.play_user(theme, "error"); - send_message(MessageType.ERROR, "Couldn't undo last purchase!"); - return false; + scannerResult.type = MessageType.ERROR; + scannerResult.message = _("Login failed (User ID = %d)").printf(userid); + scannerResult.audioType = AudioType.ERROR; + state = ScannerSessionState.READY; + } + return scannerResult; + case ScannerSessionCodeType.GUEST: + if(login(0)) { + scannerResult.type = MessageType.INFO; + scannerResult.message = _("Login as Guest"); + scannerResult.audioType = AudioType.LOGIN; + shoppingCard = {}; + state = ScannerSessionState.USER; + } else { + scannerResult.type = MessageType.ERROR; + scannerResult.message = _("Login failed (Guest)"); + scannerResult.audioType = AudioType.ERROR; + state = ScannerSessionState.READY; + } + return scannerResult; + case ScannerSessionCodeType.EAN: + uint64 ean = 0; + scannerdata.scanf("%llu", out ean); + var p = DetailedProduct(); + try { + p = db.get_product_for_ean(ean); + } catch(IOError e) { + scannerResult.type = MessageType.ERROR; + scannerResult.message = _("Internal Error!"); + scannerResult.audioType = AudioType.ERROR; + return scannerResult; + } catch(DatabaseError e) { + if(e is DatabaseError.PRODUCT_NOT_FOUND) { + scannerResult.type = MessageType.ERROR; + scannerResult.message = _("Error: unknown product: %llu").printf(ean); + scannerResult.audioType = AudioType.ERROR; + } else { + scannerResult.type = MessageType.ERROR; + scannerResult.message = _("Error: %s").printf(e.message); + scannerResult.audioType = AudioType.ERROR; + } + return scannerResult; } - } - } else if(scannerdata == "LOGOUT") { - if(logged_in) { - audio.play_user(theme, "logout"); - send_message(MessageType.INFO, "Logout!"); - logout(); - return true; - } - return false; - } else { - uint64 id = 0; - scannerdata.scanf("%llu", out id); + var mprice = p.memberprice; + var gprice = p.guestprice; + var pname = p.name; - /* check if scannerdata has valid format */ - if(scannerdata != "%llu".printf(id) && scannerdata != "%08llu".printf(id) && scannerdata != "%013llu".printf(id)) { - audio.play_user(theme, "error"); - send_message(MessageType.ERROR, "invalid product: %s", scannerdata); - return false; - } + scannerResult.type = MessageType.INFO; + scannerResult.message = _("Article info: %s (Member: %s €, Guest: %s €").printf(@"$pname", @"$mprice", @"$gprice"); + scannerResult.audioType = AudioType.ERROR; + state = ScannerSessionState.READY; + return scannerResult; + case ScannerSessionCodeType.RFIDEM4100: + int user = db.get_userid_for_rfid(scannerdata); + scannerResult.nextScannerdata = @"USER $user"; + return scannerResult; + default: + state = ScannerSessionState.READY; + return scannerResult; + } + } - string name = "unknown product"; + private ScannerResult handleUserState(string scannerdata) throws DatabaseError, DBusError, IOError { + ScannerSessionCodeType codeType = getCodeType(scannerdata); + ScannerResult scannerResult = ScannerResult(); + switch (codeType) { + case ScannerSessionCodeType.EAN: + uint64 ean = 0; + scannerdata.scanf("%llu", out ean); + var p = DetailedProduct(); + try { + p = db.get_product_for_ean(ean); + } catch(IOError e) { + scannerResult.type = MessageType.ERROR; + scannerResult.message = _("Internal Error!"); + scannerResult.audioType = AudioType.ERROR; + return scannerResult; + } catch(DatabaseError e) { + if(e is DatabaseError.PRODUCT_NOT_FOUND) { + scannerResult.type = MessageType.ERROR; + scannerResult.message = _("Error: unknown product: %llu").printf(ean); + scannerResult.audioType = AudioType.ERROR; + } else { + scannerResult.type = MessageType.ERROR; + scannerResult.message = _("Error: %s").printf(e.message); + scannerResult.audioType = AudioType.ERROR; + } + return scannerResult; + } - try { - id = db.ean_alias_get(id); - name = db.get_product_name(id); - } catch(IOError e) { - audio.play_user(theme, "error"); - send_message(MessageType.ERROR, "Internal Error!"); - return false; - } catch(DatabaseError e) { - if(e is DatabaseError.PRODUCT_NOT_FOUND) { - audio.play_user(theme, "error"); - var msg = "Error: unknown product: %llu".printf(id); - send_message(MessageType.ERROR, msg); - msg_overlay("Attention", msg); + shoppingCard += p; + + Price price = p.memberprice; + + if(user == 0){ + price = p.guestprice; + } + + scannerResult.type = MessageType.INFO; + scannerResult.message = _("Article added to shopping card: %s (%s €)").printf(@"$(p.name)", @"$price"); + scannerResult.audioType = AudioType.PURCHASE; + state = ScannerSessionState.USER; + break; + case ScannerSessionCodeType.UNDO: + if(shoppingCard.length > 0){ + var removedProduct = shoppingCard[shoppingCard.length-1]; + shoppingCard = shoppingCard[0:shoppingCard.length-1]; + scannerResult.type = MessageType.INFO; + scannerResult.message = _("Removed last Item from Shopping Cart: %s").printf(@"$(removedProduct.name)"); + scannerResult.audioType = AudioType.INFO; } else { - audio.play_user(theme, "error"); - send_message(MessageType.ERROR, "Error: %s", e.message); + scannerResult.type = MessageType.INFO; + scannerResult.message = _("No more Items on your Shopping Cart"); + scannerResult.audioType = AudioType.ERROR; } - return false; - } + break; + case ScannerSessionCodeType.LOGOUT: + scannerResult = logout(); + break; + case ScannerSessionCodeType.USER: + case ScannerSessionCodeType.GUEST: + case ScannerSessionCodeType.RFIDEM4100: + /* Logout old user session (and buy articles) */ + scannerResult = logout(); + scannerResult.nextScannerdata = scannerdata; + break; + } - if(!logged_in) { - var mprice = db.get_product_price(1, id); - var gprice = db.get_product_price(0, id); - var msg = @"article info: $name (Member: $mprice €, Guest: $gprice €)"; - audio.play_system("error.ogg"); - send_message(MessageType.INFO, msg); - send_message(MessageType.ERROR, "Login required for purchase!"); - msg_overlay("Attention", "%s\nLogin required for purchase!".printf(msg)); + return scannerResult; + } - return false; + private ScannerResult buyShoppingCard() throws DatabaseError, DBusError, IOError { + ScannerResult scannerResult = ScannerResult(); + uint8 amountOfItems = 0; + Price totalPrice = 0; + uint8 i = 0; + DetailedProduct p = DetailedProduct(); + for(i = 0; i < shoppingCard.length; i++) { + p = shoppingCard[i]; + db.buy(user, p.ean); + amountOfItems++; + Price price = p.memberprice; + if(user == 0) { + price = p.guestprice; } + totalPrice += price; + } + scannerResult.type = MessageType.INFO; + scannerResult.message = @_("%s bought %d items for %s €").printf(@"$name", amountOfItems, @"$totalPrice"); + scannerResult.audioType = AudioType.INFO; + return scannerResult; + } - if(db.buy(user, id)) { - var price = db.get_product_price(user, id); - audio.play_user(theme, "purchase"); - send_message(MessageType.INFO, @"article bought: $name ($price €)"); - return true; - } else { - audio.play_user(theme, "error"); - send_message(MessageType.ERROR, "purchase failed!"); - return false; - } + private void handle_barcode(string scannerdata) { + try { + stdout.printf("scannerdata: %s\n", scannerdata); + if(interpret(scannerdata)) + devScanner.blink(1000); + } catch(DBusError e) { + send_message(MessageType.ERROR, _("DBus Error: %s"), e.message); + } catch(IOError e) { + send_message(MessageType.ERROR, _("IO Error: %s"), e.message); + } catch(DatabaseError e) { + send_message(MessageType.ERROR, _("Database Error: %s"), e.message); + } + } + + private bool interpret(string scannerdata) throws DatabaseError, DBusError, IOError { + ScannerResult scannerResult = ScannerResult(); + switch (state) { + case ScannerSessionState.READY: + scannerResult = handleReadyState(scannerdata); + break; + case ScannerSessionState.USER: + scannerResult = handleUserState(scannerdata); + break; } + + play_audio(scannerResult.audioType); + send_message(scannerResult.type, scannerResult.message); + if(scannerResult.nextScannerdata != null){ + interpret(scannerResult.nextScannerdata); + } + return true; + } + + private ScannerResult logout() throws DatabaseError, DBusError, IOError { + ScannerResult scannerResult = buyShoppingCard(); + scannerResult.audioType = AudioType.LOGOUT; + logged_in = false; + state = ScannerSessionState.READY; + return scannerResult; } } diff --git a/src/serial-device/.gitignore b/src/serial-device/.gitignore deleted file mode 100644 index e455d42..0000000 --- a/src/serial-device/.gitignore +++ /dev/null @@ -1 +0,0 @@ -serial-device diff --git a/src/serial-device/Makefile b/src/serial-device/Makefile deleted file mode 100644 index d80279d..0000000 --- a/src/serial-device/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -all: serial-device - @echo > /dev/null - -serial-device: main.vala serial-device.vala ../input-device/input-device-interface.vala ../config/config-interface.vala - valac -X -w -o $@ --pkg linux --pkg posix --pkg gio-2.0 $^ - -clean: - rm -rf serial-device - -.PHONY: all clean diff --git a/src/serial-device/main.vala b/src/serial-device/main.vala index 0dea907..95926b5 100644 --- a/src/serial-device/main.vala +++ b/src/serial-device/main.vala @@ -1,4 +1,5 @@ /* Copyright 2013, Sebastian Reichel <sre@ring0.de> + * Copyright 2017-2018, Johannes Rudolph <johannes.rudolph@gmx.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -13,35 +14,40 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -Device dev; +Device scanner; public static int main(string[] args) { + Intl.setlocale(LocaleCategory.ALL, ""); + Intl.textdomain("shopsystem"); + try { Config cfg = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Config", "/io/mainframe/shopsystem/config"); - dev = new Device(cfg.get_string("INPUT", "device"), 9600, 8, 1); + scanner = new Device(cfg.get_string("INPUT", "barcodescanner"), 9600, 8, 1); } catch(IOError e) { - error("IOError: %s\n", e.message); + error(_("IO Error: %s\n"), e.message); } catch(KeyFileError e) { - error("Config Error: %s\n", e.message); + error(_("Config Error: %s\n"), e.message); + } catch(DBusError e) { + error(_("DBus Error: %s\n"), e.message); } Bus.own_name( BusType.SYSTEM, "io.mainframe.shopsystem.InputDevice", BusNameOwnerFlags.NONE, - on_bus_aquired, + on_bus_acquired, () => {}, - () => stderr.printf("Could not aquire name\n")); + () => stderr.printf(_("Could not acquire name\n"))); new MainLoop().run(); return 0; } -void on_bus_aquired(DBusConnection con) { +void on_bus_acquired(DBusConnection con) { try { - con.register_object("/io/mainframe/shopsystem/device", dev); + con.register_object("/io/mainframe/shopsystem/devicescanner", scanner); } catch(IOError e) { - stderr.printf("Could not register service\n"); + stderr.printf(_("Could not register service\n")); } } diff --git a/src/serial-device/serial-device.vala b/src/serial-device/serial-device.vala index 549cd74..3900128 100644 --- a/src/serial-device/serial-device.vala +++ b/src/serial-device/serial-device.vala @@ -36,11 +36,11 @@ public class Device { if(lockfile.load_contents(null, out data, null)) { pid = int.parse((string) data); } else { - error("Can't read lock file!\n"); + error(_("Can't read lock file!\n")); } if(Posix.kill(pid, 0) == 0) { - error("serial device is locked!\n"); + error(_("serial device is locked!\n")); } } @@ -52,11 +52,11 @@ public class Device { if(fd < 0) { fd = -1; lockfile.delete(); - error("Could not open device!\n"); + error(_("Could not open device!\n")); } } catch(Error e) { - error("Could not create lock file: %s!\n", e.message); + error(_("Could not create lock file: %s!\n"), e.message); } @@ -160,12 +160,12 @@ public class Device { io_read = new IOChannel.unix_new(fd); io_read.set_line_term("\r\n", 2); if(io_read.set_encoding(null) != IOStatus.NORMAL) - error("Failed to set encoding"); + error(_("Failed to set encoding")); if(!(io_read.add_watch(IOCondition.IN | IOCondition.HUP, device_read) != 0)) { - error("Could not bind IOChannel"); + error(_("Could not bind IOChannel")); } } catch(IOChannelError e) { - error("IOChannel: %s", e.message); + error(_("IOChannel: %s"), e.message); } } @@ -186,7 +186,7 @@ public class Device { size_t len, term_char; if((cond & IOCondition.HUP) == IOCondition.HUP) - error("Lost device"); + error(_("Lost device")); try { ret = gio.read_line(out msg, out len, out term_char); @@ -204,11 +204,11 @@ public class Device { received_barcode(msg); } catch(IOChannelError e) { - stderr.printf("IOChannel Error: %s", e.message); + stderr.printf(_("IOChannel Error: %s"), e.message); return false; } catch(ConvertError e) { - stderr.printf("Convert Error: %s", e.message); + stderr.printf(_("Convert Error: %s"), e.message); return false; } return true; @@ -272,7 +272,7 @@ public class Device { /** * @param duration duration of the blink in 0.1 seconds */ - public void blink(uint duration) { + public void blink(uint duration) throws IOError, DBusError { uint size = (byterate/1000) * duration; var msg = new uint8[size]; Posix.memset(msg, 0xFF, msg.length); diff --git a/src/web/.gitignore b/src/web/.gitignore deleted file mode 100644 index c077218..0000000 --- a/src/web/.gitignore +++ /dev/null @@ -1 +0,0 @@ -web diff --git a/src/web/Makefile b/src/web/Makefile deleted file mode 100644 index 94aab97..0000000 --- a/src/web/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -all: web - @echo > /dev/null - -web: main.vala web.vala websession.vala csv.vala template.vala ../database/db-interface.vala ../pgp/pgp-interface.vala ../price.vapi ../config/config-interface.vala ../audio/audio-interface.vala ../pdf-stock/pdf-stock-interface.vala - valac -X -w -o $@ --vapidir=../../vapi --enable-experimental --pkg gee-0.8 --pkg gio-2.0 --pkg libsoup-2.4 --pkg posix $^ - -clean: - rm -rf web - -.PHONY: all clean diff --git a/src/web/csv.vala b/src/web/csv.vala index 299af8d..6356f77 100644 --- a/src/web/csv.vala +++ b/src/web/csv.vala @@ -1,4 +1,5 @@ /* Copyright 2012, Sebastian Reichel <sre@ring0.de> + * Copyright 2017-2018, Johannes Rudolph <johannes.rudolph@gmx.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -16,7 +17,7 @@ public class CSVMemberFile { private UserInfo[] members; - public Gee.List<int> missing_unblocked_members() throws DatabaseError, IOError { + public Gee.List<int> missing_unblocked_members() throws DatabaseError, IOError, DBusError { var result = new Gee.ArrayList<int>(); var dbusers = db.get_member_ids(); @@ -66,6 +67,15 @@ public class CSVMemberFile { m.pgp = csv_value(linedata[9]); m.hidden = int.parse(csv_value(linedata[10])) != 0; m.disabled = int.parse(csv_value(linedata[11])) != 0; + string[] rfid = {}; + if(csv_value(linedata[12]) != "") + rfid += csv_value(linedata[12]); + if(csv_value(linedata[13]) != "") + rfid += csv_value(linedata[13]); + if(csv_value(linedata[14]) != "") + rfid += csv_value(linedata[14]); + m.rfid = rfid; + m.soundTheme = ""; if(csv_value(linedata[0]) != "EXTERNEMITGLIEDSNUMMER") members += m; diff --git a/src/web/main.vala b/src/web/main.vala index aefe7fd..4c89e40 100644 --- a/src/web/main.vala +++ b/src/web/main.vala @@ -20,38 +20,59 @@ public Config cfg; public AudioPlayer audio; public PDFStock pdfStock; string templatedir; +string? shortname; public static int main(string[] args) { + Intl.setlocale(LocaleCategory.ALL, ""); + Intl.textdomain("shopsystem"); + TlsCertificate? cert = null; string certificate = ""; string privatekey = ""; uint port = 8080; try { - db = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Database", "/io/mainframe/shopsystem/database"); - pgp = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.PGP", "/io/mainframe/shopsystem/pgp"); - cfg = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Config", "/io/mainframe/shopsystem/config"); - audio = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.AudioPlayer", "/io/mainframe/shopsystem/audio"); + db = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Database", "/io/mainframe/shopsystem/database"); + pgp = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.PGP", "/io/mainframe/shopsystem/pgp"); + cfg = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Config", "/io/mainframe/shopsystem/config"); + audio = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.AudioPlayer", "/io/mainframe/shopsystem/audio"); pdfStock = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.StockPDF", "/io/mainframe/shopsystem/stockpdf"); - - templatedir = cfg.get_string("WEB", "filepath"); + var datapath = cfg.get_string("GENERAL", "datapath"); + templatedir = Path.build_filename(datapath, "templates"); port = cfg.get_integer("WEB", "port"); + } catch(IOError e) { + error(_("IO Error: %s\n"), e.message); + } catch(KeyFileError e) { + error(_("KeyFile Error: %s\n"), e.message); + } catch(DBusError e) { + error(_("DBus Error: %s\n"), e.message); + } - try { - certificate = cfg.get_string("WEB", "cert"); - privatekey = cfg.get_string("WEB", "key"); - } catch(KeyFileError e) { - warning("KeyFileError: %s\n", e.message); - } + try { + certificate = cfg.get_string("WEB", "cert"); + privatekey = cfg.get_string("WEB", "key"); + } catch(KeyFileError e) { + warning(_("KeyFile Error: %s\n"), e.message); } catch(IOError e) { - error("IOError: %s\n", e.message); + error(_("IO Error: %s\n"), e.message); + } catch(DBusError e) { + error(_("DBus Error: %s\n"), e.message); + } + + try { + shortname = cfg.get_string("GENERAL", "shortname"); } catch(KeyFileError e) { - error("KeyFileError: %s\n", e.message); + shortname = ""; + warning(_("KeyFile Error: %s\n"), e.message); + } catch(IOError e) { + error(_("IO Error: %s\n"), e.message); + } catch(DBusError e) { + error(_("DBus Error: %s\n"), e.message); } - stdout.printf("Web Server Port: %u\n", port); - stdout.printf("TLS certificate: %s\n", certificate); - stdout.printf("TLS private key: %s\n", privatekey); + stdout.printf(_("Web Server Port: %u\n"), port); + stdout.printf(_("TLS certificate: %s\n"), certificate); + stdout.printf(_("TLS private key: %s\n"), privatekey); /* attach WebServer to MainLoop */ try { @@ -59,7 +80,7 @@ public static int main(string[] args) { cert = new TlsCertificate.from_files(certificate, privatekey); new WebServer(port, cert); } catch(Error e) { - error("Could not start Webserver: %s\n", e.message); + error(_("Could not start Webserver: %s\n"), e.message); } /* start MainLoop */ diff --git a/src/web/template.vala b/src/web/template.vala index b5265d8..009ebcf 100644 --- a/src/web/template.vala +++ b/src/web/template.vala @@ -24,44 +24,48 @@ public class WebTemplate { public uint8[] data { get { return template.data; } } public WebTemplate(string file, WebSession login) throws TemplateError { - var b = File.new_for_path(templatedir+"base.html"); - var m = File.new_for_path(templatedir+"menu.html"); - var f = File.new_for_path(templatedir+file); + var bf = Path.build_filename(templatedir, "base.html"); + var b = File.new_for_path(bf); + var mf = Path.build_filename(templatedir, "menu.html"); + var m = File.new_for_path(mf); + var ff = Path.build_filename(templatedir, file); + var f = File.new_for_path(ff); File fauth; if(login.logged_in) - fauth = File.new_for_path(templatedir+"menu_logout.html"); + fauth = File.new_for_path(Path.build_filename(templatedir, "menu_logout.html")); else - fauth = File.new_for_path(templatedir+"menu_login.html"); + fauth = File.new_for_path(Path.build_filename(templatedir, "menu_login.html")); uint8[] basis, menu, template, auth; if(!b.query_exists()) - throw new TemplateError.NOT_FOUND(templatedir+"base.html not found!"); + throw new TemplateError.NOT_FOUND(_("%s not found!").printf(bf)); if(!m.query_exists()) - throw new TemplateError.NOT_FOUND(templatedir+"menu.html not found!"); + throw new TemplateError.NOT_FOUND(_("%s not found!").printf(mf)); if(!fauth.query_exists()) - throw new TemplateError.NOT_FOUND(fauth.get_path()+" not found!"); + throw new TemplateError.NOT_FOUND(_("%s not found!").printf(fauth.get_path())); if(!f.query_exists()) - throw new TemplateError.NOT_FOUND(templatedir+file+" not found!"); + throw new TemplateError.NOT_FOUND(_("%s not found!").printf(ff)); try { if(!b.load_contents(null, out basis, null)) - throw new TemplateError.NOT_LOADABLE(templatedir+"base.html could not be loaded!"); + throw new TemplateError.NOT_LOADABLE(_("%s could not be loaded!").printf(bf)); if(!m.load_contents(null, out menu, null)) - throw new TemplateError.NOT_LOADABLE(templatedir+"menu.html could not be loaded!"); + throw new TemplateError.NOT_LOADABLE(_("%s could not be loaded!").printf(mf)); if(!fauth.load_contents(null, out auth, null)) - throw new TemplateError.NOT_LOADABLE(fauth.get_path()+" could not be loaded!"); + throw new TemplateError.NOT_LOADABLE(_("%s could not be loaded!").printf(fauth.get_path())); if(!f.load_contents(null, out template, null)) - throw new TemplateError.NOT_LOADABLE(templatedir+file+" could not be loaded!"); + throw new TemplateError.NOT_LOADABLE(_("%s could not be loaded!").printf(ff)); } catch(Error e) { - throw new TemplateError.NOT_LOADABLE("could not load templates!"); + throw new TemplateError.NOT_LOADABLE(_("could not load templates!")); } this.template = ((string) basis).replace("{{{NAVBAR}}}", ((string) menu)); + this.template = this.template.replace("{{{SHORTNAME}}}", shortname); this.template = this.template.replace("{{{AUTH}}}", ((string) auth)); this.template = this.template.replace("{{{CONTENT}}}", ((string) template)); this.template = this.template.replace("{{{USERNAME}}}", login.name); @@ -71,17 +75,18 @@ public class WebTemplate { } public WebTemplate.DATA(string file) throws TemplateError { - var f = File.new_for_path(templatedir+file); + var ff = Path.build_filename(templatedir, file); + var f = File.new_for_path(ff); uint8[] template; if(!f.query_exists()) - throw new TemplateError.NOT_FOUND(templatedir+file+" not found!"); + throw new TemplateError.NOT_FOUND(_("%s not found!").printf(ff)); try { if(!f.load_contents(null, out template, null)) - throw new TemplateError.NOT_LOADABLE(templatedir+file+" could not be loaded!"); + throw new TemplateError.NOT_LOADABLE(_("%s could not be loaded!").printf(ff)); } catch(Error e) { - throw new TemplateError.NOT_LOADABLE("could not load templates!"); + throw new TemplateError.NOT_LOADABLE(_("could not load templates!")); } this.template = (string) template; diff --git a/src/web/web.vala b/src/web/web.vala index 5a44409..3098308 100644 --- a/src/web/web.vala +++ b/src/web/web.vala @@ -1,4 +1,5 @@ /* Copyright 2012, Sebastian Reichel <sre@ring0.de> + * Copyright 2017-2018, Johannes Rudolph <johannes.rudolph@gmx.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -20,7 +21,8 @@ public class WebServer { try { var l = new WebSession(server, msg, path, query, client); var t = new WebTemplate("index.html", l); - t.replace("TITLE", "KtT Shop System"); + t.replace("TITLE", shortname + " Shop System"); + t.replace("SHORTNAME", shortname); t.menu_set_active("home"); msg.set_response("text/html", Soup.MemoryUse.COPY, t.data); msg.set_status(200); @@ -31,6 +33,8 @@ public class WebServer { handler_400(server, msg, path, query, client, e.message); } catch(IOError e) { handler_400(server, msg, path, query, client, e.message); + } catch(DBusError e) { + handler_400(server, msg, path, query, client, e.message); } } @@ -39,7 +43,7 @@ public class WebServer { 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.replace("TITLE", shortname + " Shop System"); t.menu_set_active("home"); msg.set_response("text/html", Soup.MemoryUse.COPY, t.data); msg.set_status(200); @@ -50,6 +54,8 @@ public class WebServer { handler_400(server, msg, path, query, client, e.message); } catch(IOError e) { handler_400(server, msg, path, query, client, e.message); + } catch(DBusError e) { + handler_400(server, msg, path, query, client, e.message); } } @@ -96,7 +102,7 @@ public class WebServer { } var t = new WebTemplate("users/index.html", session); - t.replace("TITLE", "KtT Shop System: User"); + t.replace("TITLE", shortname + " Shop System: User"); t.menu_set_active("users"); var data = ""; foreach(var m in db.get_member_ids()) { @@ -118,6 +124,8 @@ public class WebServer { handler_400(server, msg, path, query, client, e.message); } catch(IOError e) { handler_400(server, msg, path, query, client, e.message); + } catch(DBusError e) { + handler_400(server, msg, path, query, client, e.message); } } @@ -130,7 +138,7 @@ public class WebServer { } var t = new WebTemplate("users/import-pgp.html", session); - t.replace("TITLE", "KtT Shop System: PGP Key Import"); + t.replace("TITLE", shortname + " Shop System: PGP Key Import"); t.menu_set_active("users"); Soup.Buffer filedata; @@ -171,6 +179,8 @@ public class WebServer { handler_400(server, msg, path, query, client, e.message); } catch(IOError e) { handler_400(server, msg, path, query, client, e.message); + } catch(DBusError e) { + handler_400(server, msg, path, query, client, e.message); } } @@ -182,7 +192,7 @@ public class WebServer { return; } var t = new WebTemplate("users/import.html", session); - t.replace("TITLE", "KtT Shop System: User Import"); + t.replace("TITLE", shortname + " Shop System: User Import"); t.menu_set_active("users"); Soup.Buffer filedata; @@ -275,6 +285,8 @@ public class WebServer { handler_400(server, msg, path, query, client, e.message); } catch(IOError e) { handler_400(server, msg, path, query, client, e.message); + } catch(DBusError e) { + handler_400(server, msg, path, query, client, e.message); } } @@ -315,6 +327,8 @@ public class WebServer { handler_400(server, msg, path, query, client, e.message); } catch(IOError e) { handler_400(server, msg, path, query, client, e.message); + } catch(DBusError e) { + handler_400(server, msg, path, query, client, e.message); } } @@ -322,12 +336,12 @@ public class WebServer { try { var session = new WebSession(server, msg, path, query, client); - if(id != session.user && !(session.superuser || session.auth_users)) { + if(id == 0 || id != session.user && !(session.superuser || session.auth_users)) { handler_403(server, msg, path, query, client); return; } var t = new WebTemplate("users/entry.html", session); - t.replace("TITLE", "KtT Shop System: User Info %llu".printf(id)); + t.replace("TITLE", shortname + " Shop System: User Info %llu".printf(id)); t.menu_set_active("users"); var userinfo = db.get_user_info(id); @@ -343,6 +357,7 @@ public class WebServer { t.replace("PGPKEYID", userinfo.pgp); t.replace("DISABLED", userinfo.disabled ? "true" : "false"); t.replace("HIDDEN", userinfo.hidden ? "true" : "false"); + t.replace("RFID", string.joinv("<br>",userinfo.rfid)); var userauth = db.get_user_auth(id); t.replace("ISSUPERUSER", userauth.superuser ? "true" : "false"); @@ -400,6 +415,8 @@ public class WebServer { handler_400(server, msg, path, query, client, e.message); } catch(IOError e) { handler_400(server, msg, path, query, client, e.message); + } catch(DBusError e) { + handler_400(server, msg, path, query, client, e.message); } } @@ -431,7 +448,7 @@ public class WebServer { return; } var t = new WebTemplate("users/invoice.html", l); - t.replace("TITLE", "KtT Shop System: User Invoice %llu".printf(id)); + t.replace("TITLE", shortname + " Shop System: User Invoice %llu".printf(id)); t.menu_set_active("users"); /* years, in which something has been purchased by the user */ @@ -517,6 +534,8 @@ public class WebServer { handler_400(server, msg, path, query, client, e.message); } catch(IOError e) { handler_400(server, msg, path, query, client, e.message); + } catch(DBusError e) { + handler_400(server, msg, path, query, client, e.message); } } @@ -553,12 +572,12 @@ public class WebServer { 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.replace("TITLE", shortname + " Shop System: Product List"); t.menu_set_active("products"); string table = ""; foreach(var e in db.get_stock()) { - table += @"<tr><td><a href=\"/products/$(e.id)\">$(e.id)</a></td><td><a href=\"/products/$(e.id)\">$(e.name)</a></td><td>$(e.category)</td><td>$(e.amount)</td><td>$(e.memberprice)€</td><td>$(e.guestprice)€</td></tr>"; + table += @"<tr><td><a href=\"/products/$(e.ean)\">$(e.ean)</a></td><td><a href=\"/products/$(e.ean)\">$(e.name)</a></td><td>$(e.category)</td><td>$(e.amount)</td><td>$(e.memberprice)€</td><td>$(e.guestprice)€</td></tr>"; } t.replace("DATA", table); @@ -583,6 +602,8 @@ public class WebServer { handler_400(server, msg, path, query, client, e.message); } catch(IOError e) { handler_400(server, msg, path, query, client, e.message); + } catch(DBusError e) { + handler_400(server, msg, path, query, client, e.message); } } @@ -590,7 +611,7 @@ public class WebServer { try { var l = new WebSession(server, msg, path, query, client); var t = new WebTemplate("products/bestbefore.html", l); - t.replace("TITLE", "KtT Shop System: Best Before List"); + t.replace("TITLE", shortname + " Shop System: Best Before List"); t.menu_set_active("products"); string table = ""; @@ -615,6 +636,8 @@ public class WebServer { handler_400(server, msg, path, query, client, e.message); } catch(IOError e) { handler_400(server, msg, path, query, client, e.message); + } catch(DBusError e) { + handler_400(server, msg, path, query, client, e.message); } } @@ -638,6 +661,8 @@ public class WebServer { handler_400(server, msg, path, query, client, e.message); } catch(IOError e) { handler_400(server, msg, path, query, client, e.message); + } catch(DBusError e) { + handler_400(server, msg, path, query, client, e.message); } } @@ -645,7 +670,7 @@ public class WebServer { 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.replace("TITLE", shortname + " Shop System: Product %llu".printf(id)); t.menu_set_active("products"); /* ean */ @@ -718,6 +743,8 @@ public class WebServer { handler_400(server, msg, path, query, client, e.message); } catch(IOError e) { handler_400(server, msg, path, query, client, e.message); + } catch(DBusError e) { + handler_400(server, msg, path, query, client, e.message); } } @@ -734,9 +761,7 @@ public class WebServer { var pdfdata = pdfStock.generate(allProducts); msg.set_status(200); msg.set_response("application/pdf", Soup.MemoryUse.COPY, pdfdata); - } catch(DatabaseError e) { - handler_400(server, msg, path, query, client, e.message); - } catch(IOError e) { + } catch(Error e) { handler_400(server, msg, path, query, client, e.message); } } @@ -765,18 +790,18 @@ public class WebServer { if (!postdata.contains("apply_inventory")) { // PUT / show changes and request an apply foreach(var e in db.get_stock()) { - var realAmountStr = postdata.get(e.id); + var realAmountStr = postdata.get(e.ean.to_string()); if (realAmountStr != null && realAmountStr.length > 0) { var realAmount = int.parse(realAmountStr); var amountStyleClass = "success"; if (realAmount < e.amount) { - amountStyleClass = "error"; + amountStyleClass = "danger"; } else if (realAmount > e.amount) { amountStyleClass = "info"; } var diff = realAmount - e.amount; - table += @"<tr class='$(amountStyleClass)'><td>$(e.id)</td><td>$(e.name)</td><td>$(e.category)</td><td>$(e.amount)</td><td>" - + @"$(realAmount) <strong>[ $(diff) ]</strong><input type=\"hidden\" name=\"$(e.id)\" value=\"$(realAmount)\"></td></tr>"; + table += @"<tr class='$(amountStyleClass)'><td>$(e.ean)</td><td>$(e.name)</td><td>$(e.category)</td><td>$(e.amount)</td><td>" + + @"$(realAmount) <strong>[ $(diff) ]</strong><input type=\"hidden\" name=\"$(e.ean)\" value=\"$(realAmount)\"></td></tr>"; } } actionTemplate = """<input type="hidden" name="apply_inventory" value="true"><button type="submit" class="btn btn-primary">Apply Changes</button>"""; @@ -800,9 +825,9 @@ public class WebServer { var supplierId = int.parse(postdata.get("supplierId")); var userId = int.parse(postdata.get("userId")); foreach(var e in db.get_stock()) { - var realAmountStr = postdata.get(e.id); + var realAmountStr = postdata.get(e.ean.to_string()); if (realAmountStr != null && realAmountStr.length > 0) { - var pId = uint64.parse(e.id); + var pId = uint64.parse(e.ean.to_string()); var realAmount = int.parse(realAmountStr); if (realAmount < e.amount) { // Loss transaction @@ -835,7 +860,7 @@ public class WebServer { // default GET / list products with a form var tabindexCounter = 1; foreach(var e in db.get_stock()) { - table += @"<tr><td><a href=\"/products/$(e.id)\">$(e.id)</a></td><td><a href=\"/products/$(e.id)\">$(e.name)</a></td><td>$(e.category)</td><td>$(e.amount)</td><td><input type=\"number\" name=\"$(e.id)\" tabindex=\"$(tabindexCounter)\"></td></tr>"; + table += @"<tr><td><a href=\"/products/$(e.ean)\">$(e.ean)</a></td><td><a href=\"/products/$(e.ean)\">$(e.name)</a></td><td>$(e.category)</td><td>$(e.amount)</td><td><input type=\"number\" name=\"$(e.ean)\" tabindex=\"$(tabindexCounter)\"></td></tr>"; tabindexCounter++; } actionTemplate = """<button type="submit" class="btn btn-primary">Preview</button>"""; @@ -857,9 +882,7 @@ public class WebServer { } catch(TemplateError e) { stderr.printf(e.message+"\n"); handler_404(server, msg, path, query, client); - } catch(DatabaseError e) { - handler_400(server, msg, path, query, client, e.message); - } catch(IOError e) { + } catch(Error e) { handler_400(server, msg, path, query, client, e.message); } } @@ -868,7 +891,7 @@ public class WebServer { 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.replace("TITLE", shortname + " Shop System: New Product"); template.menu_set_active("products"); if(!session.superuser && !session.auth_products) { @@ -911,6 +934,8 @@ public class WebServer { handler_400(server, msg, path, query, client, e.message); } catch(IOError e) { handler_400(server, msg, path, query, client, e.message); + } catch(DBusError e) { + handler_400(server, msg, path, query, client, e.message); } } @@ -924,7 +949,7 @@ public class WebServer { } var template = new WebTemplate("products/restock.html", session); - template.replace("TITLE", "KtT Shop System: Restock Product %llu".printf(id)); + template.replace("TITLE", shortname + " Shop System: Restock Product %llu".printf(id)); template.replace("NAME", db.get_product_name(id)); template.menu_set_active("products"); @@ -968,6 +993,8 @@ public class WebServer { handler_400(server, msg, path, query, client, e.message); } catch(IOError e) { handler_400(server, msg, path, query, client, e.message); + } catch(DBusError e) { + handler_400(server, msg, path, query, client, e.message); } } @@ -982,7 +1009,7 @@ public class WebServer { } var template = new WebTemplate("products/newprice.html", session); - template.replace("TITLE", "KtT Shop System: New Price for Product %llu".printf(id)); + template.replace("TITLE", shortname + " Shop System: New Price for Product %llu".printf(id)); template.replace("NAME", db.get_product_name(id)); template.menu_set_active("products"); @@ -1014,6 +1041,8 @@ public class WebServer { handler_400(server, msg, path, query, client, e.message); } catch(IOError e) { handler_400(server, msg, path, query, client, e.message); + } catch(DBusError e) { + handler_400(server, msg, path, query, client, e.message); } } @@ -1021,7 +1050,7 @@ public class WebServer { try { var l = new WebSession(server, msg, path, query, client); var t = new WebTemplate("aliases/index.html", l); - t.replace("TITLE", "KtT Shop System: Alias List"); + t.replace("TITLE", shortname + " Shop System: Alias List"); t.menu_set_active("aliases"); string table = ""; @@ -1046,6 +1075,8 @@ public class WebServer { handler_400(server, msg, path, query, client, e.message); } catch(IOError e) { handler_400(server, msg, path, query, client, e.message); + } catch(DBusError e) { + handler_400(server, msg, path, query, client, e.message); } } @@ -1053,7 +1084,7 @@ public class WebServer { try { var session = new WebSession(server, msg, path, query, client); var template = new WebTemplate("aliases/new.html", session); - template.replace("TITLE", "KtT Shop System: New Alias"); + template.replace("TITLE", shortname + " Shop System: New Alias"); template.menu_set_active("aliases"); if(!session.superuser && !session.auth_products) { @@ -1093,6 +1124,8 @@ public class WebServer { handler_400(server, msg, path, query, client, e.message); } catch(IOError e) { handler_400(server, msg, path, query, client, e.message); + } catch(DBusError e) { + handler_400(server, msg, path, query, client, e.message); } } @@ -1101,7 +1134,7 @@ public class WebServer { 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.replace("TITLE", shortname + " Shop System: Statistics"); t.menu_set_active("stats"); var stats = db.get_stats_info(); @@ -1134,7 +1167,7 @@ public class WebServer { 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.replace("TITLE", shortname + " Shop System: Statistics: Stock"); t.menu_set_active("stats"); msg.set_response("text/html", Soup.MemoryUse.COPY, t.data); msg.set_status(200); @@ -1150,7 +1183,7 @@ public class WebServer { 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.replace("TITLE", shortname + " Shop System: Statistics: Profit"); t.menu_set_active("stats"); msg.set_response("text/html", Soup.MemoryUse.COPY, t.data); msg.set_status(200); @@ -1166,7 +1199,7 @@ public class WebServer { 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.replace("TITLE", shortname + " Shop System: Statistics: Profit/Weekday"); t.menu_set_active("stats"); msg.set_response("text/html", Soup.MemoryUse.COPY, t.data); msg.set_status(200); @@ -1182,7 +1215,7 @@ public class WebServer { 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.replace("TITLE", shortname + " Shop System: Statistics: Profit/Product"); t.menu_set_active("stats"); msg.set_response("text/html", Soup.MemoryUse.COPY, t.data); msg.set_status(200); @@ -1217,7 +1250,7 @@ public class WebServer { void handler_img(Soup.Server server, Soup.Message msg, string path, GLib.HashTable? query, Soup.ClientContext client) { try { - var f = File.new_for_path(templatedir+path); + var f = File.new_for_path(Path.build_filename(templatedir, path)); uint8[] data = null; if(f.query_exists() && f.load_contents(null, out data, null)) { @@ -1226,7 +1259,25 @@ public class WebServer { return; } } catch(Error e) { - error("there has been some error: %s!\n", e.message); + error(_("Error: %s\n"), e.message); + } + + handler_404(server, msg, path, query, client); + return; + } + + void handler_font(Soup.Server server, Soup.Message msg, string path, GLib.HashTable? query, Soup.ClientContext client) { + try { + var f = File.new_for_path(Path.build_filename(templatedir, path)); + uint8[] data = null; + + if(f.query_exists() && f.load_contents(null, out data, null)) { + msg.set_response("application/octet-stream; charset=binary", Soup.MemoryUse.COPY, data); + msg.set_status(200); + return; + } + } catch(Error e) { + error(_("Error: %s\n"), e.message); } handler_404(server, msg, path, query, client); @@ -1260,6 +1311,8 @@ public class WebServer { } catch(IOError e) { stderr.printf(e.message+"\n"); handler_400_fallback(server, msg, path, query, client); + } catch(DBusError e) { + handler_400(server, msg, path, query, client, e.message); } } @@ -1284,6 +1337,8 @@ public class WebServer { handler_400(server, msg, path, query, client, e.message); } catch(IOError e) { handler_400(server, msg, path, query, client, e.message); + } catch(DBusError e) { + handler_400(server, msg, path, query, client, e.message); } } @@ -1291,7 +1346,7 @@ public class WebServer { 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.replace("TITLE", shortname + " Shop System: ToDo"); template.menu_set_active(""); msg.set_response("text/html", Soup.MemoryUse.COPY, template.data); msg.set_status(200); @@ -1302,6 +1357,8 @@ public class WebServer { handler_400(server, msg, path, query, client, e.message); } catch(IOError e) { handler_400(server, msg, path, query, client, e.message); + } catch(DBusError e) { + handler_400(server, msg, path, query, client, e.message); } } @@ -1336,7 +1393,7 @@ public class WebServer { hist += "</tr>\n"; } - template.replace("TITLE", "KtT Shop System: Cashbox"); + template.replace("TITLE", shortname + " Shop System: Cashbox"); template.replace("CASHBOX_STATUS", status); template.replace("CASHBOX_HISTORY", hist); template.menu_set_active("cashbox"); @@ -1349,6 +1406,8 @@ public class WebServer { handler_400(server, msg, path, query, client, e.message); } catch(IOError e) { handler_400(server, msg, path, query, client, e.message); + } catch(DBusError e) { + handler_400(server, msg, path, query, client, e.message); } } @@ -1362,7 +1421,7 @@ public class WebServer { } var template = new WebTemplate("cashbox/add.html", session); - template.replace("TITLE", "KtT Shop System: Cashbox Balance"); + template.replace("TITLE", shortname + " Shop System: Cashbox Balance"); template.menu_set_active("cashbox"); bool error = false; @@ -1420,6 +1479,8 @@ public class WebServer { handler_400(server, msg, path, query, client, e.message); } catch(IOError e) { handler_400(server, msg, path, query, client, e.message); + } catch(DBusError e) { + handler_400(server, msg, path, query, client, e.message); } } @@ -1434,7 +1495,7 @@ public class WebServer { try { var session = new WebSession(server, msg, path, query, client); var template = new WebTemplate("cashbox/selection.html", session); - template.replace("TITLE", "KtT Shop System: Cashbox Detail"); + template.replace("TITLE", shortname + " Shop System: Cashbox Detail"); template.menu_set_active("cashbox"); msg.set_response("text/html", Soup.MemoryUse.COPY, template.data); msg.set_status(200); @@ -1445,6 +1506,8 @@ public class WebServer { handler_400(server, msg, path, query, client, e.message); } catch(IOError e) { handler_400(server, msg, path, query, client, e.message); + } catch(DBusError e) { + handler_400(server, msg, path, query, client, e.message); } } } @@ -1504,9 +1567,8 @@ public class WebServer { } var template = new WebTemplate("cashbox/detail.html", session); - template.replace("TITLE", "KtT Shop System: Cashbox Detail"); + template.replace("TITLE", shortname + " Shop System: Cashbox Detail"); template.menu_set_active("cashbox"); - template.replace("DATE", start.format("%B %Y")); template.replace("DEBIT", debit.to_string()); template.replace("LOSS", loss.to_string()); @@ -1526,6 +1588,8 @@ public class WebServer { handler_400(server, msg, path, query, client, e.message); } catch(IOError e) { handler_400(server, msg, path, query, client, e.message); + } catch(DBusError e) { + handler_400(server, msg, path, query, client, e.message); } } @@ -1537,7 +1601,7 @@ public class WebServer { options |= Soup.ServerListenOptions.HTTPS; if(!srv.listen_all(port, options)) { - throw new GLib.IOError.FAILED("Could not setup webserver!"); + throw new GLib.IOError.FAILED(_("Could not setup webserver!")); } /* index */ @@ -1550,6 +1614,7 @@ public class WebServer { srv.add_handler("/js", handler_js); srv.add_handler("/css", handler_css); srv.add_handler("/img", handler_img); + srv.add_handler("/fonts", handler_font); /* cashbox */ srv.add_handler("/cashbox", handler_cashbox); diff --git a/src/web/websession.vala b/src/web/websession.vala index ae3cafc..85fd516 100644 --- a/src/web/websession.vala +++ b/src/web/websession.vala @@ -82,7 +82,7 @@ public class WebSession { return result; } - private void setup_auth(int user) throws DatabaseError, IOError { + private void setup_auth(int user) throws DatabaseError, IOError, DBusError { var auth = db.get_user_auth(user); this.disabled = db.user_is_disabled(user); this.superuser = auth.superuser; @@ -92,7 +92,7 @@ public class WebSession { this.logged_in = true; } - public void logout() throws DatabaseError, IOError { + public void logout() throws DatabaseError, IOError, DBusError { if(logged_in) { db.set_sessionid(user, ""); superuser = false; @@ -103,7 +103,7 @@ public class WebSession { } } - public WebSession(Soup.Server server, Soup.Message msg, string path, GLib.HashTable<string,string>? query, Soup.ClientContext client) throws DatabaseError, IOError { + public WebSession(Soup.Server server, Soup.Message msg, string path, GLib.HashTable<string,string>? query, Soup.ClientContext client) throws DatabaseError, IOError, DBusError { var cookies = Soup.cookies_from_request(msg); /* Check for existing session */ @@ -128,9 +128,9 @@ public class WebSession { return; } var form_data = Soup.Form.decode((string) msg.request_body.data); - if (form_data == null || !form_data.contains("user") || !form_data.contains("password")) { - return; - } + if (form_data == null || !form_data.contains("user") || !form_data.contains("password")) { + return; + } /* get credentials */ @@ -162,7 +162,7 @@ public class WebSession { setup_auth(user); } else { - stderr.printf("Login for user id %d failed\n", userid); + stderr.printf(_("Login for user id %d failed\n"), userid); /* login failed */ failed=true; } |