diff options
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;  		} |