Merge branch 'sprit'
authorJoey Schulze <joey@infodrom.org>
Thu, 21 Jul 2016 17:22:13 +0000 (19:22 +0200)
committerJoey Schulze <joey@infodrom.org>
Thu, 21 Jul 2016 17:22:13 +0000 (19:22 +0200)
15 files changed:
.gitignore
Styles/service.style
class/.gitignore [deleted file]
class/ajaxbackend.class.php
class/ajaxbackendbase.class.php [new file with mode: 0644]
class/spritlog.class.php [new file with mode: 0644]
src/InfoCon/functions.inc
src/InfoCon/sprit/index.wml [new file with mode: 0644]
src/InfoCon/sprit/list.wml [new file with mode: 0644]
src/InfoCon/sprit/moduleajaxbackend.class.php [new file with mode: 0644]
src/InfoCon/sprit/submenu.inc [new file with mode: 0644]
src/ajax.php
src/future.php
src/infodrom.css
src/infodrom.js [new file with mode: 0644]

index 7862b07..a458483 100644 (file)
@@ -1,5 +1,6 @@
 *.depend
 *.html
 *.php
+!*.class.php
 Makefile
 imgdot-1x1-transp-ffffff.gif
index 9b49130..d043889 100644 (file)
@@ -35,6 +35,7 @@
 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
 <link href="<root_prefix>infodrom.css" rel="stylesheet" type="text/css">
 <script type="text/javascript" src="<root_prefix>/jquery-2.1.0.min.js"></script>
+<script type="text/javascript" src="<root_prefix>/infodrom.js"></script>
 </head>
 <body>
 
@@ -101,6 +102,18 @@ img.calendar {
 <img class="calendar" src="<root_prefix>pix/calendar.gif" onclick="event.cancelBubble=true;popcalendar('%0');">
 </define-tag>
 
+<define-tag popups>
+<script type="text/javascript" src="<root_prefix>jquery.udraggable.js"></script>
+<script type="text/javascript" src="<root_prefix>jquery.event.ue.js"></script>
+<script type="text/javascript">
+$(function(){
+    $('div.popup').udraggable({
+        'handle': 'div.popup_title'
+    })
+});
+</script>
+</define-tag>
+
 <define-tag future>
 <?php
 $dir = getcwd();
diff --git a/class/.gitignore b/class/.gitignore
deleted file mode 100644 (file)
index 4ce92e4..0000000
+++ /dev/null
@@ -1 +0,0 @@
-!*.class.php
index 53c9d92..234974b 100644 (file)
@@ -1,14 +1,6 @@
 <?php
 
-class AJAXBackend {
-  private $db;
-
-  public function __construct__()
-  {
-    global $db;
-    $this->db = $db;
-  }
-
+class AJAXBackend extends AJAXBackendBase {
   public function salesText()
   {
     $sales = new Sales($_POST['id']);
@@ -16,4 +8,3 @@ class AJAXBackend {
   }
 }
 
-?>
\ No newline at end of file
diff --git a/class/ajaxbackendbase.class.php b/class/ajaxbackendbase.class.php
new file mode 100644 (file)
index 0000000..cd12b17
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+
+class AJAXBackendBase {
+  protected $db;
+  protected $relativeRootPath;
+
+  public function __construct()
+  {
+    global $db;
+    $this->db = $db;
+
+    $path = substr($_SERVER['HTTP_REFERER'], strpos($_SERVER['HTTP_REFERER'], $_SERVER['SERVER_NAME'])+strlen($_SERVER['SERVER_NAME'])+1);
+
+    $pos = 0;
+    $this->relativeRootPath = '';
+    while (($pos = strpos($path, '/', $pos)) !== false) {
+      $this->relativeRootPath .= '../';
+      $pos++;
+    }
+  }
+
+  public function relativeRootPath()
+  {
+    return $this->relativeRootPath;
+  }
+
+}
diff --git a/class/spritlog.class.php b/class/spritlog.class.php
new file mode 100644 (file)
index 0000000..2e501e4
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+
+class SpritLog extends DatabaseTable {
+
+  public function __construct($id=false)
+  {
+    parent::__construct('sprit_log', $id);
+  }
+
+  public function distinctYears()
+  {
+    $sql = "SELECT DISTINCT EXTRACT(YEAR from date) AS year FROM sprit_log ORDER BY year DESC";
+    return $this->db->fetchObjectList($sql);
+  }
+
+  public function formatYear($year)
+  {
+    $out = '';
+    $sql = sprintf("SELECT * FROM sprit_log WHERE EXTRACT(YEAR from date) = %d ORDER BY date ASC", $year);;
+
+    $total_km = 0;
+    $total_liter = 0;
+    $total_price = 0;
+    foreach ($this->db->fetchObjectList($sql) as $row) {
+      $out .= sprintf('<tr id="%d"><td>%s</td><td>%s</td><td>%s</td>' .
+                     '<td class="right">%.2f</td><td class="right">%.2f</td><td class="right">%.2f</td>' .
+                     '<td class="right">%d</td><td class="right">%d</td></tr>',
+                     $row->id, assert_german_date($row->date),
+                     $row->city, $row->tankstelle,
+                     $row->price_liter, $row->liter,$row->price,
+                     $row->km, $row->km_total);
+      $total_km += $row->km;
+      $total_liter += $row->liter;
+      $total_price += $row->price;
+    }
+
+    if (strlen($out)) {
+      $out = '<table class="smallfont" width="100%">' .
+       '<thead><tr>' .
+       '<th width="70">Datum</th><th width="130" class="left">Ort</th><th class="left">Tankstelle</th>' .
+       '<th width="40">EUR/l</th><th width="40">l</th><th width="40">EUR</th><th width="40">km</th><th width="40">gesamt</th>' .
+       '</tr></thead>' .
+       $out .
+       '<tfoot><tr>' .
+       '<th colspan="4" class="left">Summe</th>' .
+       sprintf('<th class="right">%.2f</th><th class="right">%.2f</th><th class="right">%d</th><th>&nbsp;</th>',
+               $total_liter, $total_price, $total_km).
+       '</tr></tfoot>' .
+       '</table>';
+    }
+    return $out;
+  }
+
+}
+
index d7f40c8..740dab2 100644 (file)
@@ -4,4 +4,5 @@
 &nbsp;<a href="$(prefix)account/">Kontoführung</a><br>
 &nbsp;<a href="$(prefix)buch/">Buchhaltung</a><br>
 &nbsp;<a href="$(prefix)stempel/">Stempeluhr</a><br>
+&nbsp;<a href="$(prefix)sprit/">Tankbuch</a><br>
 &nbsp;<a href="$(prefix)hwdb/">Hardware</a><br>
diff --git a/src/InfoCon/sprit/index.wml b/src/InfoCon/sprit/index.wml
new file mode 100644 (file)
index 0000000..8c00f34
--- /dev/null
@@ -0,0 +1,67 @@
+#include <infocon.style>
+
+<future>
+<page func=InfoCon title="Tankbuch">
+<style type="text/css">
+.jsaction {
+    color: #999;
+}
+label {
+    display: block;
+}
+</style>
+
+<ul id="machines">
+</ul>
+<a class="jsaction" href="#" onclick="return machine_new()">Neues Fahrzeug</a>
+
+<div id="details" style="margin-top: 5px;display:none;">
+<input type="hidden" id="id" name="id" value="">
+<label for="name">Name</label>
+<input type="text" id="name" name="name" size="30">
+<br>
+<input type="submit" onclick="return machine_new_save()" value="Speichern">
+&nbsp;&nbsp;&nbsp;
+<input type="submit" onclick="$('#details').hide();return false" value="Abbrechen">
+</div>
+
+</page>
+<protect>
+<script type="text/javascript">
+$(function(){
+    load_machines();
+});
+
+function load_machines()
+{
+    ajax_request('machines', null,
+                function(data){
+                    $('#machines').html(data.list);
+                });
+}
+
+function machine_new()
+{
+    $('#details input').not('input[type="submit"]').val('');
+    $('#details').show();
+    return false;
+}
+
+function machine_new_save()
+{
+    ajax_request('savemachine', $('#details input').serialize(),
+          function(data){
+              load_machines();
+              $('#details').hide();
+          });
+    return false;
+}
+
+function machine_list(obj)
+{
+    $(obj).parents('form:first').get(0).submit();
+}
+
+</script>
+</protect>
+
diff --git a/src/InfoCon/sprit/list.wml b/src/InfoCon/sprit/list.wml
new file mode 100644 (file)
index 0000000..c66cbed
--- /dev/null
@@ -0,0 +1,103 @@
+#include <infocon.style>
+
+<future>
+<page func=InfoCon title="Tankbuch">
+<calendar_init -5>
+<popups>
+<style type="text/css">
+div#details {
+    width: 270px;
+}
+</style>
+
+<div id="details" class="popup" style="margin-top: 5px;display:none;">
+<div class="popup_title">Bearbeiten</div>
+<div class="popup_body">
+<input type="hidden" id="id" name="id" value="">
+<input type="hidden" id="machine" name="machine">
+<label for="date">Datum</label>
+<input type="text" id="date" name="date" size="8">
+<img class="calendar" src="<root_prefix>pix/calendar.gif" onclick="event.cancelBubble=true;popcalendar('date');">
+
+<label for="city">Ort</label>
+<input type="text" id="city" name="city" size="30">
+<label for="tankstelle">Tankstelle</label>
+<input type="text" id="tankstelle" name="tankstelle" size="30">
+
+<label for="price_liter">Preis pro Liter</label>
+<input type="text" id="price_liter" name="price_liter" size="30">
+
+<label for="liter">Liter / Preis</label>
+<input type="text" id="liter" name="liter" size="13">
+<input type="text" id="price" name="price" size="13">
+
+<label for="km">Tageskilometer / Gesamt</label>
+<input type="text" id="km" name="km" size="13">
+<input type="text" id="km_total" name="km_total" size="13">
+
+<div style="margin-top: 8px; text-align: center;">
+<input type="submit" onclick="return log_save()" value="Speichern">
+&nbsp;&nbsp;&nbsp;
+<input type="submit" onclick="$('#details').hide();return false" value="Abbrechen">
+</div>
+</div>
+</div>
+
+<?php
+  $log = new SpritLog();
+  $list = $log->distinctYears();
+  foreach ($list as $row) {
+    printf('<h3 year="%d" class="bar year">%d</h3>', $row->year, $row->year);
+    printf('<div id="list_%d"></div>', $row->year);
+  }
+  if (count($list)) {
+    $out = <<<EOT
+<script type="text/javascript">
+$(function(){
+    load_year({$list[0]->year});
+    $('h3.year').click(function(e){
+       var year = \$(this).attr('year');
+       load_year(year);
+    });
+});
+</script>
+EOT;
+echo $out;
+  }
+?>
+
+</page>
+<protect><script text="text/javascript">
+var sprit_machine = <?=intval($_POST['machine'])?>;
+function log_new()
+{
+    $('#details input').not('input[type="submit"]').val('');
+    $('input#machine').val(sprit_machine);
+    if ($('#details').css('left') == '0px')
+      $('#details').css('left', ($(window).width()-280)+'px').css('top', '30px');
+    $('#details').show();
+    return false;
+}
+
+function log_save()
+{
+    ajax_request('savelog', $('#details input').serialize(),
+          function(data){
+              load_year(data.year);
+              $('#details').hide();
+          });
+    return false;
+}
+
+function load_year(year)
+{
+    ajax_request('loadyear', {year: year},
+                function(data){
+                    var div = $('div#list_'+data.year);
+                    if (div.length)
+                        div.html(data.list);
+                    else
+                        window.location.reload();
+                });
+}
+</script></protect>
diff --git a/src/InfoCon/sprit/moduleajaxbackend.class.php b/src/InfoCon/sprit/moduleajaxbackend.class.php
new file mode 100644 (file)
index 0000000..8684bba
--- /dev/null
@@ -0,0 +1,73 @@
+<?php
+
+class ModuleAJAXBackend extends AJAXBackendBase {
+
+  public function machines()
+  {
+    $out = '';
+    $sql = "SELECT id,name FROM sprit_machine WHERE active = 1 ORDER BY name";
+    foreach ($this->db->fetchObjectList($sql) as $row) {
+      $out .= sprintf('<li>%s ', $row->name);
+      $out .= '<form action="list.php" method="POST" style="display:inline;">';
+      $out .= sprintf('<input type="hidden" name="machine" value="%d">', $row->id);
+      $out .= sprintf('<img src="%spix/arrowrightmonth.gif" onclick="machine_list(this);" style="margin-bottom:-4px;" title="Liste zeigen">',
+                     $this->relativeRootPath());;
+      $out .= '</form>';
+      $out .= '</li>';
+    }
+    return array('status' => true, 'list' => $out);;
+  }
+
+  public function savemachine()
+  {
+    if (empty($_POST['id'])) {
+      $sql = sprintf("INSERT INTO sprit_machine (name,active,sys_user,sys_edit) " .
+                    "VALUES (%s,1,%s,now())",
+                    $this->db->quote(utf8_decode($_POST['name'])),
+                    $this->db->quote($_SERVER['REMOTE_USER']));
+    } else {
+      $sql = sprintf("UPDATE sprit_machine SET name=%s,sys_edit=now(),sys_user=%s WHERE id = %d",
+                    $this->db->quote(utf8_decode($_POST['name'])),
+                    $this->db->quote($_SERVER['REMOTE_USER']),
+                    $_POST['id']);
+    }
+    return array('status' => $this->db->execute($sql));
+  }
+
+  public function savelog()
+  {
+    $data = array('machine' => intval($_POST['machine']),
+                 'date' => assert_iso_date($_POST['date']),
+                 'city' => $_POST['city'],
+                 'tankstelle' => $_POST['tankstelle'],
+                 'price_liter' => str_replace(',','.',$_POST['price_liter']),
+                 'liter' => str_replace(',','.',$_POST['liter']),
+                 'price' => str_replace(',','.',$_POST['price']),
+                 'km' => intval($_POST['km']),
+                 'km_total' => intval($_POST['km_total']),
+                 'sys_edit' => 'now()',
+                 'sys_user' => $_SERVER['REMOTE_USER']);
+
+    foreach ($data as $k => $v)
+      if (empty($v))
+       json_error(sprintf('Field %s must not be empty', $k));
+
+    if (empty($_POST['id'])) {
+      $ok = $this->db->insertInto('sprit_log', $data);
+    } else {
+      $ok = $this->db->update('sprit_log', $data, 'id = ' . intval($_POST['id']));
+    }
+
+    $d = explode('-', $data['date']);
+    return array('status' => $ok, 'year' => $d[0]);
+  }
+
+  public function loadyear()
+  {
+    $log = new SpritLog();
+    return array('status' => true,
+                'list' => $log->formatYear($_POST['year']),
+                'year' => $_POST['year']);
+  }
+
+}
diff --git a/src/InfoCon/sprit/submenu.inc b/src/InfoCon/sprit/submenu.inc
new file mode 100644 (file)
index 0000000..af47d24
--- /dev/null
@@ -0,0 +1,17 @@
+<strong>Menu</strong><br>
+
+&nbsp;<menu-item base=index text=Fahrzeuge href=index.php><br>
+
+<when <string-eq "<get-var WML_SRC_BASENAME>" "list" />>
+&nbsp;<a class="jsaction" href="#" onclick="return log_new()">Neuer Eintrag</a>
+</when>
+
+<menu-rule>
+
+#include "../functions.inc" prefix="../"
+
+# Local variables:
+# mode: indented-text
+# mode: auto-fill
+# mode: iso-accents
+# end:
index c8926f4..da92334 100644 (file)
@@ -2,19 +2,38 @@
 require_once('config.php');
 require_once('future.php');
 
-$backend = new AJAXBackend();
-
 $data = array();
 
 if (strlen($_POST['func'])) {
+  $backend = new AJAXBackend();
+
   if (method_exists($backend, $_POST['func'])) {
     $func = $_POST['func'];
-    $data = $backend->$func();
-  } else {
-    $data = array('error' => 'Unbekannte Funktion '.$_POST['func'].'.');
+    json_return($backend->$func());
+  }
+
+  $path = substr($_SERVER['HTTP_REFERER'], strpos($_SERVER['HTTP_REFERER'], $_SERVER['SERVER_NAME'])+strlen($_SERVER['SERVER_NAME'])+1);
+  $slash = strpos($path, '/', strpos($path, '/')+1);
+
+  if ($slash !== false) {
+    $path = getcwd() . '/' . substr($path, 0, $slash) . '/' . 'moduleajaxbackend.class.php';
+
+    if (file_exists($path)) {
+      require_once($path);
+      $backend = new ModuleAJAXBackend();
+
+      if (method_exists($backend, $_POST['func'])) {
+       $func = $_POST['func'];
+       json_return($backend->$func());
+      }
+    }
   }
+
+  error_log('Unknown function '.$_POST['func']);
+  json_return(array('status' => false,
+                   'error' => 'Unknown function '.urlencode($_POST['func']).'.'));
 }
 
-json_return($data);
+error_log('Unknown usage');
+json_return(array('status' => false, 'error' => 'Unknown usage'));
 
-?>
index 1cf7bcb..779f700 100644 (file)
@@ -17,16 +17,35 @@ function json_return($data)
   $encoded = json_encode($data);
 
   if ($encoded === false) {
-    error_log('Wrong encoding: ' . var_export($encoded,true));
+    error_log('Return ' . var_export($result,true));
     return json_encode(array('status' => false,
-                            'error' => 'Cannot encode return values, see server log.'));
-  } else {
+                            'error' => 'Rückgabedaten können nicht kodiert werden.'));
+  } else
     echo $encoded;
-  }
 
   exit();
 }
 
+function json_error($text)
+{
+  $data = array('status' => false, 'error' => $text);
+  json_return($data);
+}
+
+function assert_iso_date($date)
+{
+  if (strpos($date, '.') === false) return $date;
+  $d = explode('.', $date);
+  return $d[2] . '-' . $d[1] . '-' . $d[0];
+}
+
+function assert_german_date($date)
+{
+  if (is_null($date)) return '';
+  if (strpos($date, '-') === false) return $date;
+  $d = explode('-', $date);
+  return $d[2] . '.' . $d[1] . '.' . $d[0];
+}
 
 global $db;
 
index 20642a6..450cf9d 100644 (file)
@@ -43,9 +43,12 @@ a:visited {
 h3.bar {
   padding-left: 5px;
   padding-bottom: 2px;
-  font-size: 15px;
+  font-size: 14px;
   background: #98c5e5;
 }
+.jsaction {
+    color: #666;
+}
 img.border {
   border: 2px solid #e5e5e5;
 }
@@ -85,6 +88,12 @@ td.in {
 td.out {
   color: #ff0000;
 }
+.left {
+  text-align: left;
+}
+.right {
+  text-align: right;
+}
 
 pre {
   border: 1pt solid #cfcfcf;
@@ -140,6 +149,40 @@ input.button:hover {
   background-color: #bfbfbf;
 }
 
+/*
+ * Popups
+ */
+div.popup {
+  background: white;
+  border: 1px solid #CCC;
+  height: auto;
+  position: absolute;
+}
+div.popup_title {
+  width: 100%;
+  cursor: move;
+  background: #EEE;
+  margin-bottom: 5px;
+  font-weight: bold;
+  padding-bottom: 1px;
+}
+div.popup_body {
+  padding-left: 2px;
+  padding-right: 2px;
+  padding-bottom: 4px;
+}
+div.popup label {
+  margin-top: 2px;
+  display: block;
+}
+
+/*
+ * Calendar
+ */
+table#fc {
+  z-index: 100;
+}
+
 /*
  * Footer
  */
diff --git a/src/infodrom.js b/src/infodrom.js
new file mode 100644 (file)
index 0000000..c27a86b
--- /dev/null
@@ -0,0 +1,27 @@
+function ajax_request(funcname, params, callback, error_callback)
+{
+    if (params === null)
+       params = 'func='+funcname;
+    else if (typeof params == 'object')
+       params['func'] = funcname
+    else if (typeof params == 'string')
+       params = 'func='+funcname+'&'+params
+    else
+       return false;
+
+    $.post('/ajax.php', params,
+          function(data){
+              if (!data.status) {
+                  if (typeof data.error == 'string')
+                      var error = data.error;
+                  else
+                      var error = 'An error occurred'
+                  alert(error);
+                  if (typeof error_callback == 'function')
+                      error_callback(data);
+                  return;
+              }
+              if (typeof callback == 'function')
+                  callback(data);
+          });
+}