[svn-inject] Installing original source of quickappoint upstream upstream/0.1+svn20051119
authorgregor herrmann <gregoa@debian.org>
Sat, 25 Feb 2006 17:48:32 +0000 (17:48 -0000)
committergregor herrmann <gregoa@debian.org>
Sat, 25 Feb 2006 17:48:32 +0000 (17:48 -0000)
20 files changed:
appointment.php [new file with mode: 0644]
cgi-bin/auth.php [new file with mode: 0644]
cgi-bin/editdb.php [new file with mode: 0644]
css/default.css [new file with mode: 0644]
doc/dump.sql [new file with mode: 0644]
doc/quickappoint.txt [new file with mode: 0644]
doc/quickappoint.xls [new file with mode: 0644]
etc/options.php [new file with mode: 0644]
index.php [new file with mode: 0644]
lib/db.php [new file with mode: 0644]
lib/dbedit.php [new file with mode: 0644]
lib/dblogic.php [new file with mode: 0644]
lib/html.php [new file with mode: 0644]
lib/prepend.php [new file with mode: 0644]
locale/de/LC_MESSAGES/makefile [new file with mode: 0644]
locale/de/LC_MESSAGES/messages.mo [new file with mode: 0644]
locale/de/LC_MESSAGES/messages.po [new file with mode: 0644]
login.php [new file with mode: 0644]
overview.php [new file with mode: 0644]
person.php [new file with mode: 0644]

diff --git a/appointment.php b/appointment.php
new file mode 100644 (file)
index 0000000..e97ff0b
--- /dev/null
@@ -0,0 +1,216 @@
+<?php
+/*
+Description:
+
+Required variables:
+       id
+Optional variable:
+       comment
+               values: 0 (or not specified): show no comments
+                               1: show short comments
+                               2: show all comments
+*/
+
+require_once(dirname(__FILE__) . '/lib/prepend.php');
+require_once(dirname(__FILE__) . '/lib/dblogic.php');
+require_once(dirname(__FILE__) . '/lib/html.php');
+
+$appointmentid = isset($_GET['id']) ? $_GET['id'] : '';
+if (empty($appointmentid)) die('No id specified');
+if (!is_numeric($appointmentid)) die('Invalid id');
+$appointmentid = (int) $appointmentid;
+if (isset($_GET['comments'])) {
+       $showcomments = (int) $_GET['comments'];
+       if ($showcomments < 0 || $showcomments > 2) $showcomments = 0;
+} else $showcomments = 0;
+
+$appointment = getAppointment($appointmentid);
+extract(html_escape_array($appointment));
+$proposals = getProposals($appointmentid);
+$participants = getParticipants($appointmentid);
+$appointmentrights = getAppointmentRights($appointmentid, $_SESSION['person']['id']);
+$expired = strtotime($expire_date)+84600 > time();
+
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+<head>
+       <title><?php echo htmlspecialchars($title); ?></title>
+       <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+       <meta name="Author" content="Toastfreeware">
+       <link rel="stylesheet" type="text/css" href="<?php echo $cssname; ?>">
+       <!-- <link rel="icon" href="icons/icon.png" type="image/png"> -->
+</head>
+<body>
+
+<?php
+
+if (isset($_SESSION['editdbmsg'])) echo html_element('p', array('class'=>'hint'), $_SESSION['editdbmsg']);
+unset($_SESSION['editdbmsg']);
+
+if ($appointmentrights) {
+
+echo html_element('h1', $title);
+if ($description) echo html_element('p', array('class' => 'description'), $description);
+if ($location) echo html_element('p', array('class' => 'location'), $location);
+if ($expired) echo html_element('p', array('id' => 'expirewarning'), _('This appointment has expired.'));
+?>
+
+
+<h2><?php echo htmlspecialchars(_("Available proposals")); ?></h2>
+
+<?php if (count($proposals) == 0)
+echo html_element('p', array('class' => 'noproposals'), _('There are no proposals.'));
+else {
+       echo "<table>\n";
+
+       // title
+       echo "\t<tr><th>" . htmlspecialchars(_('proposal')) . '</th>';
+       foreach ($participants as $person) {
+               extract(html_escape_array($person));
+               echo "<th>";
+               if ($email) echo html_element('a', array('href' => "mailto:$email"), $fullname, TRUE);
+               else echo(htmlspecialchars($fullname));
+               echo "</th>";
+       }
+       echo "</tr>\n";
+       
+       // responses
+       foreach ($proposals as $proposal) {
+               extract(html_escape_array($proposal));
+               echo "\t<tr";
+               switch ($accepted) {
+                       case 't': echo ' class="accepted"'; break;
+                       case 'f': echo ' class="declined"'; break;
+               }
+               // new idea: 
+               // $timestamp = strtotime("$date $time");
+               // echo "><td>$date $time (" . strftime('%a', $timestamp) . ')</td>';
+               echo "><td>$date $time ($day)</td>";
+               $respones = getResponses($id);
+               foreach ($respones as $response) {
+                       extract(html_escape_array($response));
+                       if ($showcomments == '1') $comment = htmlspecialchars(text_cut($response['comment'], 15));
+                       echo "<td>";
+                       if ($value) echo "<strong>$value</strong>";
+                       if ($showcomments) echo '&nbsp;', $comment;
+                       echo "</td>";
+               }
+               echo "</tr>\n";
+       }
+       echo "</table>\n";
+       echo '<p>';
+       if ($showcomments != 0) echo '<a href="', $_SERVER['PHP_SELF'], "?id=$appointmentid&amp;comments=0\">", htmlspecialchars(_('No comments')), '</a> ';
+       if ($showcomments != 1) echo '<a href="', $_SERVER['PHP_SELF'], "?id=$appointmentid&amp;comments=1\">", htmlspecialchars(_('Short comments')), '</a> ';
+       if ($showcomments != 2) echo '<a href="', $_SERVER['PHP_SELF'], "?id=$appointmentid&amp;comments=2\">", htmlspecialchars(_('Full comments')), '</a>';
+       echo "</p>\n";
+
+
+// don't show if expired except the user is manager
+if ($expired || $appointmentrights['manageappointment']) {
+?>
+
+<h2><?php echo htmlspecialchars(_('Own responses')); ?></h2>
+
+<p><?php echo htmlspecialchars(sprintf(_('A short comment on values: The range for values is %1$d to %2$d.'), $appointment['minvalue'], $appointment['maxvalue']));
+echo ' ';
+if ($appointment['approvaldesc']) $text = _('The lower the better.');
+else $text = _('The higher the better.');
+echo htmlspecialchars($text); ?></p>
+
+<?php
+       $sql = 'select proposalid, value, comment, personid from response, proposal where personid=' . $_SESSION['person']['id'] . " and appointmentid=$appointmentid and response.proposalid=proposal.id and date >= date(now()) order by date";
+       $data = pg_u_query_assoc($db_conn, $sql);
+       $sql = "select id, date || ', ' || to_char(time, 'HH24:MI') || to_char(date, ' (Dy)') as datetime from proposal where appointmentid=$appointmentid and date >= date(now()) order by date";
+       $lookup_hash = array_to_hash(pg_u_query_num($db_conn, $sql));
+       $sql = "select id, date || ', ' || to_char(time, 'HH24:MI') || to_char(date, ' (Dy)') as datetime from proposal where appointmentid=$appointmentid and date >= date(now()) and id not in (select proposalid from response where personid=" . $_SESSION['person']['id'] . ") order by date";
+       $insert_hash = array_to_hash(pg_u_query_num($db_conn, $sql));
+
+       $columns = array(
+               new DiOnlyInputSelect('proposalid:', _('proposal'), NULL, $lookup_hash, $insert_hash),
+               new DiTextEdit('value', _('value'), NULL, 3),
+               new DiTextEdit('comment', _('comment'), NULL, 50),
+               new DiHidden('personid:', (string) $_SESSION['person']['id'])
+       );
+
+       echo html_createEditForm(
+       'cgi-bin/editdb.php',
+       basename($_SERVER['REQUEST_URI']),
+       'response',
+       $columns,
+       $data, // Array of rows, that are again arrays of columns
+       count($insert_hash) > 0, TRUE, TRUE);
+} // if not expired
+} // if (count($proposals) > 0)
+
+
+if (($expired || $appointmentrights['manageappointment']) && ($appointmentrights['insertproposal'] || $appointmentrights['updateproposal'] || $appointmentrights['deleteproposal'])) {
+?>
+
+<h2><?php echo htmlspecialchars(_('Manage proposals')); ?></h2>
+
+<?php
+       $columns = array(
+               new DiHidden('id:'),
+               new DiReadOnly(_('day')),
+               new DiTextEdit('date', _('date'), NULL, 15, 'yyyy-mm-dd'),
+               new DiTextEdit('time', _('time'), NULL, 15, 'hh:mm'),
+               ($appointmentrights['manageappointment'] ? new DiBoolEdit('accepted', _('accepted')) : new DiBoolReadOnly(_('accepted'))),
+               new DiHidden('appointmentid', (string) $appointmentid)
+       );
+       
+       echo html_createEditForm(
+       'cgi-bin/editdb.php',
+       basename($_SERVER['REQUEST_URI']),
+       'proposal',
+       $columns,
+       $proposals, // Array of rows, that are again arrays of columns
+       $appointmentrights['insertproposal'], $appointmentrights['updateproposal'], $appointmentrights['deleteproposal']);
+}
+
+
+if ($appointmentrights['manageperson']) {      
+?>
+
+
+<h2><?php echo htmlspecialchars(_('Invited persons')); ?></h2>
+
+<?php 
+       $sql = "select personid as id, appointmentid, personid, insertproposal, updateproposal, deleteproposal, manageappointment, permission.manageperson from permission, person where appointmentid=$appointmentid and personid=person.id order by fullname";
+       $permission_data = pg_u_query_assoc($db_conn, $sql);
+       
+       $sql = "select id, fullname from person order by fullname";
+       $person_data = array_to_hash(pg_u_query_num($db_conn, $sql));
+       
+       $columns = array(
+               new DiHidden('personid:'),
+               new DiHidden('appointmentid:', (string) $appointmentid),
+               new DiOnlyInputSelect('personid', _('person'), NULL, $person_data, $person_data),
+               new DiBoolEdit('insertproposal', _('insert proposal'), 'f'),
+               new DiBoolEdit('updateproposal', _('update proposal'), 'f'),
+               new DiBoolEdit('deleteproposal', _('delete proposal'), 'f'),
+               new DiBoolEdit('manageappointment', _('manage appointment'), 'f'),
+               new DiBoolEdit('manageperson', _('manage person'), 'f')
+       );
+
+       echo html_createEditForm(
+       'cgi-bin/editdb.php',
+       basename($_SERVER['REQUEST_URI']),
+       'permission',
+       $columns,
+       $permission_data, // Array of rows, that are again arrays of columns
+       TRUE, TRUE, TRUE);      
+}
+
+} else { ?>
+
+<p><em>Person and appointment don't go well together ...</em></p>
+
+<?php } ?>     
+       
+<p><a href="overview.php"><?php echo htmlspecialchars(_('Back')); ?></a></p>
+
+
+</body>
+</html>
diff --git a/cgi-bin/auth.php b/cgi-bin/auth.php
new file mode 100644 (file)
index 0000000..93a0c15
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+require_once(dirname(__FILE__) . '/../lib/prepend.php');
+if (!isset($_POST['username']) || !isset($_POST['password'])) die('invalid post parameter');
+
+
+require_once(dirname(__FILE__) . '/../lib/dblogic.php');
+$persondata = getPersonData($_POST['username'], $_POST['password']);
+
+if ($persondata) {
+       // success
+       $_SESSION['person'] = $persondata;
+       header('Location:' . $project_options['projecturl'] . 'overview.php');
+} else {
+       // no success
+       unset($_SESSION['person']);
+       $errormsg = _('username/password combination not valid!');
+       $_SESSION['errormsg'] = $errormsg;
+       header('Location:' . $project_options['projecturl'] . 'login.php');
+}
+
+?>
diff --git a/cgi-bin/editdb.php b/cgi-bin/editdb.php
new file mode 100644 (file)
index 0000000..f78d67b
--- /dev/null
@@ -0,0 +1,206 @@
+<?php
+require_once(dirname(__FILE__) . '/../lib/prepend.php');
+require_once(dirname(__FILE__) . '/../lib/db.php');
+require_once(dirname(__FILE__) . '/../lib/dbedit.php');
+
+// File for handling simple database requests:
+// - insert one row of one table,
+// - update one row of one table,
+// - delete one row of one table
+
+// The array $_POST is taken to get the $key => $value pairs.
+//
+// All keys that begin with : are treated as special words:
+//   :tablename
+//   :operation_insert
+//   :operation_update
+//   :operation_delete
+//   :nextpage
+//
+// All keys that end with : are taken as $key => $value pairs
+//   for identifying a row when updating or deleting.
+//   They are taken as normal fields when inserting (but this behavior may change).
+//
+// All keys that have the form <functionname>@<keyname> are taken as
+// <keyname> => <functionname>(<value>).
+// This is mainly intended for applying the md5 function to values:
+// md5@password
+//
+// All other pairs are taken as fieldname => value and they are not
+// expected to be encoded.
+//
+// :nextpage is the filename to call via location (http://... is added automatically)
+//   after completion of this script.
+
+$allowedfunctions = array('md5');
+
+
+// returns true if permitted, the error message as string otherwise
+// personid specifies the user who wants to do the operation.
+function checkPermission($db_conn, $table, $operation, $key_field_value_map, $field_value_map, $personid) {
+       // Is personid valid?
+       if (!is_numeric($personid)) return('Invalid person id');
+       $personid = (int) $personid;
+       
+       // manageperson, addappointment
+       $sql = "select manageperson, addappointment from person where id=$personid";
+       $result = pg_u_query_assoc($db_conn, $sql);
+       if (count($result) != 1) return "Person with id $personid does not exist";
+       extract($result[0]);
+       
+       // table person
+       if ($table == 'person') {
+               if ($manageperson == 't') return TRUE;
+               if ($key_field_value_map['id'] == $personid
+               && $operation == 'update'
+               && !in_array('manageperson', array_keys($field_value_map))
+               && !in_array('addappointment', array_keys($field_value_map))) return TRUE;
+               return 'No right to manage persons!';
+       }
+       
+       // table appointment
+       if ($table == 'appointment' && $operation == 'insert') {
+               if ($addappointment != 't') return 'No right to add an appointment!';
+               return TRUE;
+       }
+       
+       
+       // get selected permission (found in table permission) for user $personid and
+       // for appointment $appointmentid (these two fields are the primary key fields).
+       // $appointmentid is checked to be numeric.
+       // The permission is returned as bool. If an error occurs, 
+       // it is returned as string.
+       function getPermissions($db_conn, $wantedpermission, $personid, $appointmentid) {
+               // Is $appointmentid numeric?
+               if (!is_numeric($appointmentid)) return('Invalid appointment id: ' . $appointmentid);
+               $appointmentid = (int) $appointmentid;
+               
+               // Query right
+               $sql = "select $wantedpermission from permission where personid=$personid and appointmentid=$appointmentid";
+               $result = pg_u_query_num($db_conn, $sql);
+               if (count($result) != 1) return "No right to change things for appointment with id $appointmentid or this appointment does not exist.";
+               return $result[0][0] == 't';
+       }
+       
+       
+       // table appointment
+       if ($table == 'appointment') {
+               $manageappointment = getPermissions($db_conn, 'manageappointment', $personid, $key_field_value_map['id']);
+               if (is_string($manageappointment)) return $manageappointment;
+               if (!$manageappointment) return "No right to $operation appointment";
+               return TRUE;
+       }
+       
+       // table proposal
+       if ($table == 'proposal') {
+               $manageproposal = getPermissions($db_conn, 'manageproposal', $personid, $field_value_map['appointmentid']);
+               if (is_string($manageproposal)) return $manageproposal;
+               if (!$manageproposal) return "No right to $operation proposals.";
+               return TRUE;
+       }
+       
+       // table permission
+       if ($table == 'permission') {
+               $manageappointmentperson = getPermissions($db_conn, 'manageperson', $personid, $key_field_value_map['appointmentid']);
+               if (is_string($manageappointmentperson)) return $manageappointmentperson;
+               if (!$manageappointmentperson) return "No right to $operation persons for this appointment";
+               return TRUE;
+       }
+       
+       // table response
+       if ($table == 'response') {
+               if ($personid != $_SESSION['person']['id']) return "No right to $operation other people's data.";
+               $proposalid=(int) $key_field_value_map['proposalid'];
+               $sql = "select personid, appointmentid from permission where personid=$personid and appointmentid=(select appointmentid from proposal where id=$proposalid)";
+               $result = pg_u_query_num($db_conn, $sql);
+               if (count($result) != 1) return "No right to $operation for this response.";
+               return TRUE;
+       }
+       
+       return "Table $table unknown or operation on it not permitted";
+}
+
+
+// put the functionality in a function:
+// returns errormessage as string if an error occurs
+// returns TRUE if success
+function doEditDb($db_conn, $request) {
+       global $allowedfunctions;
+
+       // Decode data
+       $metaData = decodeRequestMetaData($request, TRUE);
+       
+       if (!isset($metaData['tablename'])) return 'There is no table name submitted.';
+       $tablename = $metaData['tablename'];
+       
+       $operation = NULL;
+       if (isset($metaData['operation_insert'])) $operation = 'insert';
+       if (isset($metaData['operation_update'])) $operation = 'update';
+       if (isset($metaData['operation_delete'])) $operation = 'delete';
+       if (is_null($operation)) return 'No operation (insert, update, delete)';
+       
+       decodeRequest($request, $key_field_value_map, $field_value_map,
+       $key_function_map);
+       
+       // Security check
+       $result = checkPermission($db_conn, $tablename, $operation, $key_field_value_map, $field_value_map, $_SESSION['person']['id']);
+       if (!($result === TRUE)) return $result;
+       
+       // Apply function
+       foreach ($key_function_map as $key => $function) {
+               // Special functions
+               if ($function == 'pwd') {
+                       if ($operation == 'insert') $function = 'md5';
+                       elseif ($operation == 'update') {
+                               // ... to do: better coding ...
+                               if ($field_value_map[$key] == 'XXXXXX') {
+                                       unset($field_value_map[$key]); 
+                                       continue;
+                               } else $function = 'md5';
+                       } else continue;
+               }
+               // Other functions
+               if (!in_array($function, $allowedfunctions)) return "Function $function not allowed.";
+               if (isset($key_field_value_map[$key])) $key_field_value_map[$key] = $function($key_field_value_map[$key]);
+               if (isset($field_value_map[$key])) $field_value_map[$key] = $function($field_value_map[$key]);
+       }
+
+       // Value check
+       $result = pg_checkcolumnvalues($db_conn, $tablename, array_merge($key_field_value_map, $field_value_map));
+       if ($result === FALSE) return 'The data types could not be checked.';
+       else if (is_string($result)) return $result;
+       
+       // Operate
+       if ($operation == 'insert') {
+               $msg = pg_e_insert($db_conn, $tablename, array_merge($key_field_value_map, $field_value_map));
+               if (!($msg === TRUE)) return $msg;
+       }
+       if ($operation == 'update') {
+               $msg = pg_e_update($db_conn, $tablename, $field_value_map, $key_field_value_map);
+               if (!($msg === TRUE)) return $msg;
+       }
+       if ($operation == 'delete') {
+               $msg = pg_e_delete($db_conn, $tablename, $key_field_value_map);
+               if (!($msg === TRUE)) return $msg;
+       }
+       
+       return TRUE;
+}
+
+
+$result = doEditDb($db_conn, $_POST);
+
+// Error handling
+if (!($result === TRUE)) {
+       $_SESSION['editdbmsg'] = $result;
+       $_SESSION['editdbsuccess'] = FALSE;
+} else {
+       $_SESSION['editdbmsg'] = _('Operation successful.');
+       $_SESSION['editdbsuccess'] = TRUE;
+}
+
+// To 'result' page
+$nextpage = $_POST[':nextpage'];
+header('location:' . $project_options['projecturl'] . $nextpage);
+
+?>
diff --git a/css/default.css b/css/default.css
new file mode 100644 (file)
index 0000000..5469bcb
--- /dev/null
@@ -0,0 +1,69 @@
+body {
+       color:black;
+       background-color:#eee;
+}
+h1 {
+       color:#eee;
+       text-align: center;
+       background-color:#191970; /* midnightblue */
+       padding:5px;
+}
+h2 {
+       border-bottom-color:#191970; /* midnightblue */
+       border-bottom-style:solid;
+       border-bottom-width:medium;
+}
+a:visited {
+       color: #014e9a;
+} 
+a {
+       color:#014e9a;
+       text-decoration:underline;
+}
+hr {
+       border-style: none;
+       background-color: #CCCCDD;
+}
+.hint {
+       background-color: #bbb;
+       border-style:solid;
+       border-width:thin;
+       border-color:#aaaaaa;
+       font-size:small;
+       padding:5px;
+       margin-bottom:2ex;
+}
+table, .table {
+       display: table;
+       border-collapse: collapse;
+       empty-cells: show;
+       text-align: left;
+       background-color:#ddd
+}
+.tr_head, .tr_insert, .tr_even, .tr_odd, .tr, tr, .evenrow, .oddrow {
+       display: table-row;
+       border-color: #999;
+       border-style: solid;
+       border-width: thin;
+}
+.tr_insert .td {
+       background-color:#bbb;
+}
+td, th, .td, .th {
+       display: table-cell; 
+       padding:3px;
+       vertical-align:top;
+}
+th, .th {
+       background-color:#999;
+       font-weight:bold;
+}
+.accepted {
+       background-color: darkgreen;
+}
+.declined {
+       background-color: darkred;
+}
+#expirewarning {
+       background-color:#881133;
+}
diff --git a/doc/dump.sql b/doc/dump.sql
new file mode 100644 (file)
index 0000000..f146f19
--- /dev/null
@@ -0,0 +1,264 @@
+--
+-- PostgreSQL database dump
+--
+
+SET client_encoding = 'LATIN9';
+SET check_function_bodies = false;
+
+SET search_path = public, pg_catalog;
+
+--
+-- TOC entry 20 (OID 52099)
+-- Name: plpgsql_call_handler(); Type: FUNC PROCEDURAL LANGUAGE; Schema: public; Owner: philipp
+--
+
+CREATE FUNCTION plpgsql_call_handler() RETURNS language_handler
+    AS '$libdir/plpgsql', 'plpgsql_call_handler'
+    LANGUAGE c;
+
+
+--
+-- TOC entry 18 (OID 52100)
+-- Name: plpgsql; Type: PROCEDURAL LANGUAGE; Schema: public; Owner: 
+--
+
+CREATE TRUSTED PROCEDURAL LANGUAGE plpgsql HANDLER plpgsql_call_handler;
+
+
+--
+-- TOC entry 5 (OID 21697)
+-- Name: person; Type: TABLE; Schema: public; Owner: gregoa
+--
+
+CREATE TABLE person (
+    id serial NOT NULL,
+    username character varying(16) NOT NULL,
+    "password" character varying(32) DEFAULT ''::character varying NOT NULL,
+    fullname character varying(64),
+    email character varying(64),
+    manageperson boolean DEFAULT false NOT NULL,
+    addappointment boolean DEFAULT false NOT NULL,
+    locale character varying(32)
+) WITHOUT OIDS;
+
+
+--
+-- TOC entry 6 (OID 21704)
+-- Name: proposal; Type: TABLE; Schema: public; Owner: gregoa
+--
+
+CREATE TABLE proposal (
+    id serial NOT NULL,
+    appointmentid bigint NOT NULL,
+    date date NOT NULL,
+    "time" time without time zone,
+    status smallint DEFAULT 0 NOT NULL,
+    accepted boolean
+) WITHOUT OIDS;
+
+
+--
+-- TOC entry 7 (OID 21710)
+-- Name: appointment; Type: TABLE; Schema: public; Owner: gregoa
+--
+
+CREATE TABLE appointment (
+    id serial NOT NULL,
+    title character varying(32) NOT NULL,
+    description text,
+    "location" character varying(128),
+    "minvalue" smallint DEFAULT 0 NOT NULL,
+    "maxvalue" smallint DEFAULT 10 NOT NULL,
+    approvaldesc boolean DEFAULT false NOT NULL,
+    cssname character varying(256),
+    creator_personid integer,
+    creation_date date DEFAULT now(),
+    expire_date date,
+    CONSTRAINT min_max_check CHECK (("minvalue" < "maxvalue"))
+) WITHOUT OIDS;
+
+
+--
+-- TOC entry 8 (OID 21720)
+-- Name: response; Type: TABLE; Schema: public; Owner: gregoa
+--
+
+CREATE TABLE response (
+    personid bigint NOT NULL,
+    proposalid bigint NOT NULL,
+    value smallint NOT NULL,
+    "comment" text
+) WITHOUT OIDS;
+
+
+--
+-- TOC entry 9 (OID 21725)
+-- Name: permission; Type: TABLE; Schema: public; Owner: gregoa
+--
+
+CREATE TABLE permission (
+    personid bigint NOT NULL,
+    appointmentid bigint NOT NULL,
+    manageproposal boolean DEFAULT false NOT NULL,
+    manageappointment boolean DEFAULT false NOT NULL,
+    manageperson boolean DEFAULT false NOT NULL,
+    insertproposal boolean DEFAULT false NOT NULL,
+    updateproposal boolean DEFAULT false NOT NULL,
+    deleteproposal boolean DEFAULT false NOT NULL
+) WITHOUT OIDS;
+
+
+--
+-- TOC entry 19 (OID 52775)
+-- Name: insert_creator_permission(); Type: FUNCTION; Schema: public; Owner: philipp
+--
+
+CREATE FUNCTION insert_creator_permission() RETURNS "trigger"
+    AS 'begin insert into permission (personid, appointmentid, manageproposal, manageappointment, manageperson) values (NEW.creator_personid, NEW.id, ''t'', ''t'', ''t''); return null; end;'
+    LANGUAGE plpgsql;
+
+
+--
+-- TOC entry 11 (OID 21730)
+-- Name: person_pkey; Type: CONSTRAINT; Schema: public; Owner: gregoa
+--
+
+ALTER TABLE ONLY person
+    ADD CONSTRAINT person_pkey PRIMARY KEY (id);
+
+
+--
+-- TOC entry 12 (OID 21732)
+-- Name: person_username_key; Type: CONSTRAINT; Schema: public; Owner: gregoa
+--
+
+ALTER TABLE ONLY person
+    ADD CONSTRAINT person_username_key UNIQUE (username);
+
+
+--
+-- TOC entry 15 (OID 21734)
+-- Name: appointment_pkey; Type: CONSTRAINT; Schema: public; Owner: gregoa
+--
+
+ALTER TABLE ONLY appointment
+    ADD CONSTRAINT appointment_pkey PRIMARY KEY (id);
+
+
+--
+-- TOC entry 14 (OID 21736)
+-- Name: proposal_pkey; Type: CONSTRAINT; Schema: public; Owner: gregoa
+--
+
+ALTER TABLE ONLY proposal
+    ADD CONSTRAINT proposal_pkey PRIMARY KEY (id);
+
+
+--
+-- TOC entry 13 (OID 21738)
+-- Name: proposal_appointmentid_key; Type: CONSTRAINT; Schema: public; Owner: gregoa
+--
+
+ALTER TABLE ONLY proposal
+    ADD CONSTRAINT proposal_appointmentid_key UNIQUE (appointmentid, date, "time");
+
+
+--
+-- TOC entry 17 (OID 21740)
+-- Name: permission_pkey; Type: CONSTRAINT; Schema: public; Owner: gregoa
+--
+
+ALTER TABLE ONLY permission
+    ADD CONSTRAINT permission_pkey PRIMARY KEY (personid, appointmentid);
+
+
+--
+-- TOC entry 16 (OID 38159)
+-- Name: response_proposalid_key; Type: CONSTRAINT; Schema: public; Owner: gregoa
+--
+
+ALTER TABLE ONLY response
+    ADD CONSTRAINT response_proposalid_key UNIQUE (proposalid, personid);
+
+
+--
+-- TOC entry 21 (OID 21742)
+-- Name: proposal_appointmentid_fkey; Type: FK CONSTRAINT; Schema: public; Owner: gregoa
+--
+
+ALTER TABLE ONLY proposal
+    ADD CONSTRAINT proposal_appointmentid_fkey FOREIGN KEY (appointmentid) REFERENCES appointment(id) ON UPDATE CASCADE ON DELETE CASCADE;
+
+
+--
+-- TOC entry 23 (OID 21746)
+-- Name: response_personid_fkey; Type: FK CONSTRAINT; Schema: public; Owner: gregoa
+--
+
+ALTER TABLE ONLY response
+    ADD CONSTRAINT response_personid_fkey FOREIGN KEY (personid) REFERENCES person(id) ON UPDATE CASCADE ON DELETE CASCADE;
+
+
+--
+-- TOC entry 24 (OID 21750)
+-- Name: response_proposalid_fkey; Type: FK CONSTRAINT; Schema: public; Owner: gregoa
+--
+
+ALTER TABLE ONLY response
+    ADD CONSTRAINT response_proposalid_fkey FOREIGN KEY (proposalid) REFERENCES proposal(id) ON UPDATE CASCADE ON DELETE CASCADE;
+
+
+--
+-- TOC entry 25 (OID 21754)
+-- Name: permission_personid_fkey; Type: FK CONSTRAINT; Schema: public; Owner: gregoa
+--
+
+ALTER TABLE ONLY permission
+    ADD CONSTRAINT permission_personid_fkey FOREIGN KEY (personid) REFERENCES person(id) ON UPDATE CASCADE ON DELETE CASCADE;
+
+
+--
+-- TOC entry 26 (OID 21758)
+-- Name: permission_appointmentid_fkey; Type: FK CONSTRAINT; Schema: public; Owner: gregoa
+--
+
+ALTER TABLE ONLY permission
+    ADD CONSTRAINT permission_appointmentid_fkey FOREIGN KEY (appointmentid) REFERENCES appointment(id) ON UPDATE CASCADE ON DELETE CASCADE;
+
+
+--
+-- TOC entry 22 (OID 52771)
+-- Name: appointment_creator_personid_fkey; Type: FK CONSTRAINT; Schema: public; Owner: gregoa
+--
+
+ALTER TABLE ONLY appointment
+    ADD CONSTRAINT appointment_creator_personid_fkey FOREIGN KEY (creator_personid) REFERENCES person(id) ON UPDATE CASCADE ON DELETE SET NULL;
+
+
+--
+-- TOC entry 27 (OID 52776)
+-- Name: set_permissions_for_new_appointment; Type: TRIGGER; Schema: public; Owner: gregoa
+--
+
+CREATE TRIGGER set_permissions_for_new_appointment
+    AFTER INSERT ON appointment
+    FOR EACH ROW
+    EXECUTE PROCEDURE insert_creator_permission();
+
+
+--
+-- TOC entry 3 (OID 2200)
+-- Name: SCHEMA public; Type: COMMENT; Schema: -; Owner: postgres
+--
+
+COMMENT ON SCHEMA public IS 'Standard public namespace';
+
+
+--
+-- TOC entry 10 (OID 21725)
+-- Name: COLUMN permission.manageperson; Type: COMMENT; Schema: public; Owner: gregoa
+--
+
+COMMENT ON COLUMN permission.manageperson IS 'if true, the person may add or remove persons for this appointment.';
+
+
diff --git a/doc/quickappoint.txt b/doc/quickappoint.txt
new file mode 100644 (file)
index 0000000..b704d47
--- /dev/null
@@ -0,0 +1,122 @@
+quickappoint=# \d appointment
+                                       Table "public.appointment"
+      Column      |          Type          |                          Modifiers
+------------------+------------------------+-------------------------------------------------------------
+ id               | integer                | not null default nextval('public.appointment_id_seq'::text)
+ title            | character varying(32)  | not null
+ description      | text                   |
+ location         | character varying(128) |
+ minvalue         | smallint               | not null default 0
+ maxvalue         | smallint               | not null default 10
+ approvaldesc     | boolean                | not null default false
+ cssname          | character varying(256) |
+ creator_personid | integer                |
+ creation_date    | date                   | default now()
+ expire_date      | date                   |
+Indexes:
+    "appointment_pkey" primary key, btree (id)
+Check constraints:
+    "min_max_check" CHECK ("minvalue" < "maxvalue")
+Foreign-key constraints:
+    "appointment_creator_personid_fkey" FOREIGN KEY (creator_personid) REFERENCES person(id) ON UPDATE CASCADE ON DELETE SET NULL
+
+quickappoint=# \d permission
+              Table "public.permission"
+      Column       |  Type   |       Modifiers
+-------------------+---------+------------------------
+ personid          | bigint  | not null
+ appointmentid     | bigint  | not null
+ manageproposal    | boolean | not null default false   --------------- < delete!!!
+ manageappointment | boolean | not null default false
+ manageperson      | boolean | not null default false
+ insertproposal    | boolean | not null default false
+ updateproposal    | boolean | not null default false
+ deleteproposal    | boolean | not null default false
+Indexes:
+    "permission_pkey" primary key, btree (personid, appointmentid)
+Foreign-key constraints:
+    "permission_personid_fkey" FOREIGN KEY (personid) REFERENCES person(id) ON UPDATE CASCADE ON DELETE CASCADE
+    "permission_appointmentid_fkey" FOREIGN KEY (appointmentid) REFERENCES appointment(id) ON UPDATE CASCADE ON DELETE CASCADE
+
+quickappoint=# \d person
+                                      Table "public.person"
+     Column     |         Type          |                       Modifiers
+----------------+-----------------------+--------------------------------------------------------
+ id             | integer               | not null default nextval('public.person_id_seq'::text)
+ username       | character varying(16) | not null
+ password       | character varying(32) | not null default ''::character varying
+ fullname       | character varying(64) |
+ email          | character varying(64) |
+ manageperson   | boolean               | not null default false
+ addappointment | boolean               | not null default false
+Indexes:
+    "person_pkey" primary key, btree (id)
+    "person_username_key" unique, btree (username)
+
+quickappoint=# \d proposal
+                                      Table "public.proposal"
+    Column     |          Type          |                        Modifiers
+---------------+------------------------+----------------------------------------------------------
+ id            | integer                | not null default nextval('public.proposal_id_seq'::text)
+ appointmentid | bigint                 | not null
+ date          | date                   | not null
+ time          | time without time zone |
+ status        | smallint               | not null default 0 ------------------ << delete !!
+ accepted      | boolean                |
+Indexes:
+    "proposal_pkey" primary key, btree (id)
+    "proposal_appointmentid_key" unique, btree (appointmentid, date, "time")
+Foreign-key constraints:
+    "proposal_appointmentid_fkey" FOREIGN KEY (appointmentid) REFERENCES appointment(id) ON UPDATE CASCADE ON DELETE CASCADE
+
+quickappoint=# \d response
+      Table "public.response"
+   Column   |   Type   | Modifiers
+------------+----------+-----------
+ personid   | bigint   | not null
+ proposalid | bigint   | not null
+ value      | smallint | not null
+ comment    | text     |
+Indexes:
+    "response_proposalid_key" unique, btree (proposalid, personid)
+Foreign-key constraints:
+    "response_personid_fkey" FOREIGN KEY (personid) REFERENCES person(id) ON UPDATE CASCADE ON DELETE CASCADE
+    "response_proposalid_fkey" FOREIGN KEY (proposalid) REFERENCES proposal(id) ON UPDATE CASCADE ON DELETE CASCADE
+
+quickappoint=#
+
+
+todo:
+* email-benachrichtigungen
+* erklaerung zu approvaldesc beim anlegen
+* drop down liste für locales.
+* Nach Änderungen der locale-Einstellungen werden diese derzeit erst nach einer Neuanmeldung aktiv.
+* Auswertung (siehe unten)
+* Lange Personenliste...
+* Garbage Collection...
+* delete column proposal.status and permission.manageproposal
+* document the database changes in this file...
+* Wochentage sind derzeit nur englisch
+* Weitere Übersetzung.
+* vorschlag nach expire date:
+       - wenn es akzeptierte termine gibt -> übersichtliche anzeige derselben
+       - wenn nicht: defaultmäßig anzeige einer Auswertung und trotzdem noch möglichkeit, auf die appointment.php seite zu kommen und dort zu werkeln (vielleicht aber auch nür für den manager)
+
+=======
+
+kann quickapoint auch rechnen- man gibt deadline fur die
+terminprafernezen ein und dann mittelts und schickt den termin
+automatisch an die runde ? ware doch suss.
+
+=======
+
+debianpaket: wwwconfig-common anschauen --> deprecated ==> 
+
+dbconfig-common
+http://people.debian.org/%7Eseanius/policy/dbconfig-common.html
+
+Web Application Policy
+http://people.debian.org/%7Eseanius/policy/webapp-policy.html
+
+Database Application Policy
+http://people.debian.org/%7Eseanius/policy/dbapp-policy.html
diff --git a/doc/quickappoint.xls b/doc/quickappoint.xls
new file mode 100644 (file)
index 0000000..bf33338
Binary files /dev/null and b/doc/quickappoint.xls differ
diff --git a/etc/options.php b/etc/options.php
new file mode 100644 (file)
index 0000000..2a5a4b3
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+
+// Server settings
+$project_options = array(
+       'projecturl' => 'http://www.toastfreeware.priv.at/quickappoint/',
+       'locale' => '' // use empty string for system default or something like 'de_AT@euro'
+);
+
+
+// Database settings
+$db_options = array(
+       'host' => 'colleen.colgarra.priv.at',
+       'db' => 'quickappoint',
+       'user' => 'quickappoint',
+       'pass' => 'quickappoint'
+);
+
+
+// -------------
+// Local options for testing and developing on local machines below:
+
+$local = !ereg('toastfreeware', $_SERVER['HTTP_HOST']);
+
+if ($local) {
+       $project_options['projecturl'] = 'http://localhost/~philipp/quickappoint/';
+}
+
+
+// -------------
+// Further options (derived)
+// Pages where the user doesn't require a login
+$unrestricted_pages = array(
+       $project_options['projecturl'] . 'index.php',
+       $project_options['projecturl'] . 'login.php',
+       $project_options['projecturl'] . 'cgi-bin/auth.php'
+);
+
+
+?>
diff --git a/index.php b/index.php
new file mode 100644 (file)
index 0000000..13d4faf
--- /dev/null
+++ b/index.php
@@ -0,0 +1,9 @@
+<?php
+
+require_once(dirname(__FILE__) . '/lib/prepend.php');
+
+//die('Du bist auf der Index Seite!');
+
+header('Location:' . $project_options['projecturl'] . 'login.php');
+
+?>
diff --git a/lib/db.php b/lib/db.php
new file mode 100644 (file)
index 0000000..f74bff8
--- /dev/null
@@ -0,0 +1,275 @@
+<?php
+// This file should be called whenever a database connection
+// and/or database functions are needed.
+
+// database connection
+$db_conn = pg_connect(
+       'host=' . $db_options['host'] .
+       ' dbname=' . $db_options['db'] .
+       ' user=' . $db_options['user'] .
+       ' password=' . $db_options['pass']
+);
+if (!$db_conn) die("Could not open connection to database.");
+unset($db_options);
+
+
+
+// General Postgresql Database related functions
+// Naming convention
+// - All postgresql related functions begin with pg_
+// - Functions that die on an error begin with pg_u_
+// - Some Functions with a special error handling begin with pg_e_
+
+
+// Applies the function pg_escape_string on every value of an array.
+function pg_escape_array($array) {
+       return array_map('pg_escape_string', $array);
+}
+
+
+// Applies pg_escape_string on the value
+// - if the value is NULL or '', it is converted in null
+// - other values are placed within single quotes.
+function pg_encode($value) {
+       if (is_null($value) || $value == '') return 'null';
+       return "'" . pg_escape_string($value) . "'";
+}
+
+
+// converts 'f', '0', 0, '' and FALSE to FALSE, everything else to TRUE
+function pg_bool($value) {
+       if ($value == 'f') return FALSE; else return (bool) $value;
+}
+
+
+// The function creates a string like key1='value1', key2='value2'
+// The or key1='value1' and key2='value2'
+// out of a array like array('key1' => 'value1', 'key2' => 'value2')
+// On the keys, pg_escape_string is applied,
+// on the values, pg_enocde is applied.
+// $glue should be something like ', ' or ' and '
+function pg_field_value_string($field_value_map, $glue) {
+       $elements = array();
+       foreach($field_value_map as $key => $value)
+               $elements[] = pg_escape_string($key) . '=' . pg_encode($value);
+       return implode($glue, $elements);
+}
+
+
+// Connects to a database
+function pg_u_connect($host, $dbname, $username, $password) {
+        if ($password) $password = " password=$password";
+       if ($host != 'localhost') $host="host=$host "; else $host = '';
+       $connectstring = $host . "dbname=$dbname user=$username" . $password;
+        $db = pg_connect($connectstring);
+        if (!$db) die("Could not connect to database $dbname ($connectstring)");
+        pg_query($db, "set datestyle to 'German'");
+               return $db;
+}
+
+
+// same as pg_query but dies if an error occurs with a meaningful errormessage
+function pg_u_query($db, $sql) {
+       $result = pg_query($db, $sql);
+       if (!$result) die('Could not execute query: ' . $sql);
+       return $result;
+}
+
+
+// Returns the database - result of pg_query on success,
+// otherwise the errormessage as string
+function pg_e_query($db, $sql) {
+       // Execute SQL
+       $error_reporting = error_reporting(E_ERROR);
+       $result = pg_query($db, $sql);
+       error_reporting($error_reporting);
+
+       // Error handling
+       if (!$result) return "Could not execute query: '$sql', the message was '" .  pg_last_error() . "'"; // pg_result_error($result) does not work (always returns an empty string
+       return $result;
+}
+
+
+// helper function
+function pg_u_fetch($dbresult, $fetchfuncname = 'pg_fetch_assoc') {
+       if (!$dbresult) die('No valid result');
+       if (pg_num_rows($dbresult) == 0) return array();
+       while ($row = $fetchfuncname($dbresult)) {$result[] = $row;}
+       return $result;
+}
+
+
+// returns the result of the query as an array of associative row-arrays.
+function pg_u_fetch_assoc($dbresult) {
+       return pg_u_fetch($dbresult, 'pg_fetch_assoc');
+}
+
+
+// returns the result of the query as an array of numbered row-arrays.
+function pg_u_fetch_num($dbresult) {
+       return pg_u_fetch($dbresult, 'pg_fetch_row');
+}
+
+
+// same as pg_u_fetch_assoc, but needs an sql statement.
+function pg_u_query_assoc($db, $sql) {
+       return pg_u_fetch_assoc(pg_u_query($db, $sql));
+}
+
+
+// same as pg_u_fetch_num, but needs an sql statement.
+function pg_u_query_num($db, $sql) {
+       return pg_u_fetch_num(pg_u_query($db, $sql));
+}
+
+
+// returns the number of rows that a query of the specified table with the
+// field_value_map returns. If an error occurs, the function returns the
+// errormessage as string.
+function pg_e_rowsaffected($db, $tablename, $field_value_map) {
+       $sql = 'select count(*) from ' . pg_escape_string($tablename);
+       $sql .= ' where ' . pg_field_value_string($field_value_map, ' and ');
+       $result = pg_e_query($db, $sql);
+       if (is_string($result)) return $result; // error
+       return (int) pg_fetch_result($result, 0, 0);
+}
+
+
+// This function is similar to the pg_u_rowsaffected function but it
+// returns TRUE if the result is 1. In all other cases it returns a string
+// with the description of the "error".
+function pg_e_onerowaffected($db, $tablename, $field_value_map) {
+       $result = pg_e_rowsaffected($db, $tablename, $field_value_map);
+       if (is_string($result)) return $result; // error
+       if ($result == 0) return 'No row affected';
+       if ($result > 1) return 'More than one row affected';
+       return TRUE;
+}
+
+
+// Inserts a new row into the specified table.
+// On success, the function returns TRUE, otherwise a string with the error message.
+function pg_e_insert($db, $tablename, $field_value_map) {
+       // Build SQL
+       $tablename = pg_escape_string($tablename);
+       $fields = pg_escape_array(array_keys($field_value_map));
+       $values = array_map('pg_encode', array_values($field_value_map));
+       $fields = implode(', ', $fields);
+       $values = implode(", ", $values);
+       $sql = "insert into $tablename ($fields) values ($values)";
+
+       // Execute SQL
+       $result = pg_e_query($db, $sql);
+       if (is_string($result)) return $result;
+       $num = pg_affected_rows($result);
+       if ($num != 1) return "There were $num rows affected by query: '$sql'";
+       return TRUE;
+}
+
+
+// Updates _one_ row in the specified table.
+// On success, the function returns TRUE, otherwise a string with the error message.
+// - $db ... The database connection (has to have the suitable rights)
+// - $tablename ... Name of the table to update
+// - $field_value_map ... key/value array. The keys are the fieldnames.
+//       Not all fields have to be present
+// - $key_field_value_map ... key/value array of names of the key - fields
+//       that identify the row unique
+function pg_e_update($db, $tablename, $field_value_map, $key_field_value_map) {
+       // Only one row?
+       $one_result = pg_e_onerowaffected($db, $tablename, $key_field_value_map);
+       if (is_string($one_result)) return $one_result;
+
+       // Build update query
+       if (count($field_value_map) == 0) return TRUE;
+       $sql = 'update ' . pg_escape_string($tablename);
+       $sql .= ' set ' . pg_field_value_string($field_value_map, ', ');
+       $sql .= ' where ' . pg_field_value_string($key_field_value_map, ' and ');
+
+       // Execute SQL
+       $result = pg_e_query($db, $sql);
+       if (is_string($result)) return $result;
+       return TRUE;
+}
+
+
+
+// Deletes _one_ row in the specified table.
+// On success, the function returns TRUE, otherwise a string with the error message.
+// - $db ... The database connection (has to have the suitable rights)
+// - $tablename ... Name of the table where one row should be deleted
+// - $key_field_value_map ... array of names of keys with their values
+//   fields that identify the row to delete unique
+function pg_e_delete($db, $tablename, $key_field_value_map) {
+       // Only one row?
+       $one_result = pg_e_onerowaffected($db, $tablename, $key_field_value_map);
+       if (is_string($one_result)) return $one_result;
+
+       // Build update query
+       $sql = 'delete from ' . pg_escape_string($tablename);
+       $sql .= ' where ' . pg_field_value_string($key_field_value_map, ' and ');
+
+       // Execute SQL
+       $result = pg_e_query($db, $sql);
+       if (is_string($result)) return $result;
+       return TRUE;
+}
+
+
+// Returns the type of the column as string.
+// If there is an error (i.e. column does not exist), FALSE is returned.
+function pg_e_get_columntype($db, $tablename, $columnname) {
+       $tablename = pg_escape_string($tablename);
+       $columnname = pg_escape_string($columnname);
+       $sql = "select udt_name from information_schema.columns where table_name = '$tablename' and column_name = '$columnname'";
+               
+       // Execute SQL
+       $result = pg_e_query($db, $sql);
+       if (is_string($result)) return FALSE;
+       
+       $r = pg_u_fetch_num($result);
+       if (!$r) return FALSE; // The same: if (count($array) == 0) return FALSE
+       return $r[0][0];
+}
+
+
+// Return values:
+//     - TRUE, if the value would be accepted if used in an insert statement
+//     - FALSE if the column type can't be determined
+//     - the column type as string otherwise.
+function pg_check_columnvalue($db, $tablename, $columnname, $value) {
+       // 1st: determine the column type
+       $columntype = pg_e_get_columntype($db, $tablename, $columnname);
+       if ($columntype === FALSE) return FALSE;
+       
+       // "quick hack:"
+       if ($value == '') return TRUE;
+       
+       // a select is done to check if the value is convertable into $columntype.
+       $pg_columntype = pg_escape_string($columntype);
+       $value = pg_escape_string($value);
+       $sql = "select $pg_columntype '$value'"; 
+       $result = pg_e_query($db, $sql);
+       if (is_string($result)) return $columntype;
+       
+       return TRUE;
+}
+
+
+// Return values:
+//     - TRUE, if the values would be accepted if used in an insert statement
+//     - FALSE, if $db is not ok, the table does not exist or one of the columns doesn't exist.
+//     - a readable error message otherwise.
+function pg_checkcolumnvalues($db, $tablename, $field_value_map) {
+       $error_msg = array();
+       foreach($field_value_map as $column => $value) {
+               $result = pg_check_columnvalue($db, $tablename, $column, $value);
+               if ($result === FALSE) return FALSE;
+               if ($result === TRUE) continue;
+               $error_msg[] = "'$value' is not of type '$result'";
+       }
+       if (!$error_msg) return TRUE;
+       return "Error(s) in supplied data: " . implode(" / ", $error_msg);
+}
+
+?>
\ No newline at end of file
diff --git a/lib/dbedit.php b/lib/dbedit.php
new file mode 100644 (file)
index 0000000..854d36d
--- /dev/null
@@ -0,0 +1,85 @@
+<?php
+// require_once(dirname(__FILE__) . '/prepend.php');
+// require_once(dirname(__FILE__) . '/db.php');
+
+
+// Takes a hash "$request" and extracts all key/value pairs where the keys begin with :
+// The leading : is removed within the keys.
+// If $remove is true, the extracted entries are deleted from the request
+// Example:
+//   :tablename
+
+function decodeRequestMetaData(&$request, $remove = FALSE) {
+       $result = array();
+       foreach($request as $key => $value) {
+               if (substr($key, 0, 1) == ':') {
+                       $result[substr($key, 1)] = $value;
+                       if ($remove) unset($request[$key]);
+               }
+       }
+       return $result;
+}
+
+
+// Takes the hash "$request". The keys of its entries $key => $value are supposed
+// to have the following form
+// [functionname@]fieldname[:])
+// If the fieldname is followed by the (optional) : this field is handled as 
+// one of database key field names.
+// If  the fieldname as preceded by functionname@, this function should be applied
+// to the corresponding value (but this is not done within this function).
+//
+// Return hashes:
+// $key_field_value_map: 
+//   Contains key => value pairs for database keys fields
+//   The database key fields are freed from the : at the end and the optional
+//   functionname@ at the beginning.
+// $field_value_map: The rest of the fields.
+//   The fields are freed from the optional
+//   functionname@ at the beginning.
+// $key_function_map: A hash with all keys that have functionnames 
+//   The functionnames are freed from the @ at the end
+// 
+// Example:
+//   $request = array('id:' => 7, 'name' => 'hugo', 'md5@password' => 'abc');
+//   // result:
+//   $key_field_value_map == array('id' => 7)
+//   $field_value_map == array('name' => 'hugo', 'password' => 'abc')
+//   $key_function_map == array('password' => 'md5');
+
+function decodeRequest($request, &$key_field_value_map, &$field_value_map, &$key_function_map) {
+       // Begin with empty results
+       $key_field_value_map = array();
+       $field_value_map = array();
+       $key_function_map = array();
+       
+       foreach ($request as $key => $value) {
+               // Split $key in $functionname $fieldname $isdbkey
+               // - Init
+               $functionname = NULL;
+               $fieldname = '';
+               $isdbkey = FALSE;
+               // - functionname
+               $functionname_keyname = explode('@', $key);
+               if (count($functionname_keyname) == 2) {
+                       $key = $functionname_keyname[1];
+                       $functionname = $functionname_keyname[0];
+               }
+               // - isdbkey
+               if (substr($key, -1) == ':') {
+                       $key = substr($key, 0, -1);
+                       $isdbkey = TRUE;
+               }
+               // - fieldname
+               $fieldname = $key;
+               
+               // Insert in the corresponding hashes
+               if ($isdbkey) $key_field_value_map[$fieldname] = $value;
+               else $field_value_map[$fieldname] = $value;
+               if (!is_null($functionname)) $key_function_map[$fieldname] = $functionname;
+       }
+}
+
+
+
+?>
diff --git a/lib/dblogic.php b/lib/dblogic.php
new file mode 100644 (file)
index 0000000..2fdb588
--- /dev/null
@@ -0,0 +1,117 @@
+<?php
+
+require_once(dirname(__FILE__) . '/db.php');
+
+
+function getPersonData($username, $password) {
+       global $db_conn;
+       $sql = 'select id, username, fullname, email, manageperson, addappointment, locale from person where username=\'' . pg_escape_string($username) . '\' and password=\'' . md5($password) . '\'';
+       $r = pg_u_query($db_conn, $sql);
+       if (pg_num_rows($r) != 1) return 0;
+       return pg_fetch_assoc($r, 0);
+}
+
+
+// Returns a hash with the keys manageperson and addappointment with the values TRUE or FALSE.
+// If the person is not found, FALSE is returned.
+function getPersonRights($personid) {
+       global $db_conn;
+       $personid = (int) $personid;
+       $sql = "select manageperson, addappointment from person where id=$personid";
+       $r = pg_u_query($db_conn, $sql);
+       if (pg_num_rows($r) != 1) return FALSE;
+       return array_map('pg_bool', pg_fetch_assoc($r, 0));
+}
+
+
+
+function getAllPersons() {
+       global $db_conn;
+       $sql = 'select id, username, password, fullname, email, locale, manageperson, addappointment from person order by fullname';
+       return pg_u_query_assoc($db_conn, $sql);
+}
+
+
+
+function getPerson($personnr) {
+       global $db_conn;
+       $personnr = (int) $personnr;
+       $sql = 'select id, username, password, fullname, email, locale from person where id = ' . $personnr;
+       return pg_u_query_assoc($db_conn, $sql);
+}
+
+
+
+// if $personid==NULL, all appointments will be returned.
+// if $only_managed == TRUE, only the appointments will be returned, where the personid
+// has the right to edit or delete that appointments. In this case $personid _must_ be specified,
+// otherwise $only_managed will be ignored.
+function getAppointments($personid=NULL, $only_managed=FALSE) {
+       global $db_conn;
+       $sql = 'select id, title, description, location, minvalue, maxvalue, approvaldesc, cssname, expire_date, creator_personid from appointment ';
+       if ($personid) {
+               $personid = pg_escape_string($personid);
+               $sql .= "where id in (select appointmentid from permission where personid=$personid";
+               if ($only_managed) $sql .= " and manageappointment='t'";
+               $sql .= ') ';
+       }
+       $sql .= 'order by creation_date desc';  
+       return pg_u_query_assoc($db_conn, $sql);
+}
+
+
+function getAppointment($id) {
+       global $db_conn;
+       $id = (int) $id;
+       $sql = "select title, description, location, minvalue, maxvalue, approvaldesc, cssname, expire_date from appointment where id = $id";
+       $result = pg_u_query_assoc($db_conn, $sql);
+       if (!count($result)) die("Appointment with id $id not found");
+       return $result[0];
+}
+
+
+// If the person $personid participates in the appointment $appointmentid
+// the function returns a hash with the keys 
+//   manageperson
+//   manageappointment
+//   insertproposal
+//   updateproposal
+//   deleteproposal
+// with the boolean values TRUE or FALSE.
+// In all other cases, FALSE is returned.
+function getAppointmentRights($appointmentid, $personid) {
+       global $db_conn;
+       $appointmentid = (int) $appointmentid;
+       $personid = (int) $personid;
+       $sql = "select manageperson, manageappointment, insertproposal, updateproposal, deleteproposal from permission where appointmentid=$appointmentid and personid=$personid";
+       $result = pg_u_query_assoc($db_conn, $sql);
+       if (count($result) != 1) return FALSE;
+       return array_map('pg_bool', $result[0]);
+}
+
+
+function getProposals($id) {
+       global $db_conn;
+       $id = (int) $id;
+       $sql = "select id, to_char(date, 'Dy') as day, date, to_char(time, 'HH24:MI') as time, accepted, appointmentid from proposal where appointmentid = $id and date >= date(now()) order by date, time";
+       return pg_u_query_assoc($db_conn, $sql);
+}
+
+
+function getParticipants($appointmentid) {
+       global $db_conn;
+       $appointmentid = (int) $appointmentid;
+       $sql = "select id, fullname, email from person, permission where personid=id and appointmentid=$appointmentid order by fullname";
+       return pg_u_query_assoc($db_conn, $sql);
+}
+
+
+function getResponses($proposalid) {
+       global $db_conn;
+       $proposalid = (int) $proposalid;
+       $sql = "select personid, value, comment from (select participant.personid, value, comment from (select * from response where proposalid=$proposalid) as r right join (select personid from permission where appointmentid=(select appointmentid from proposal where id=$proposalid)) as participant on r.personid=participant.personid) as responses, person where id=personid order by fullname"; // select responses for _every_ person that belongs to the appointment (and assign null to value and comment if no response is there (using the right join)).
+       return pg_u_query_assoc($db_conn, $sql);
+}
+
+
+?>
diff --git a/lib/html.php b/lib/html.php
new file mode 100644 (file)
index 0000000..47acea0
--- /dev/null
@@ -0,0 +1,654 @@
+<?php
+// html related general functions
+
+// html formating functions
+// ========================
+
+// This function creates a tag <$tag $paramkey="$paramvalue">$content</$tag>
+// If is_null($content), a single tag will be createed (<$tag $paramkey="$paramvalue"/>).
+// If a value or key of the $parameter hash is NULL, the key="value" entry will not appear.
+// The content and the parameter are not encoded in any way.
+function html_tag($tagname, $parameter, $content) {
+       $result = "<$tagname";
+       foreach($parameter as $key => $value) {
+               if (!is_null($value) && !is_null($key))
+               $result .= " $key=\"$value\"";
+       }
+       if (is_null($content)) return $result . '/>';
+       return $result . ">$content</$tagname>";
+}
+
+
+// Applies the function htmlentities on every value of an array.
+function html_escape_array($array, $keystoo = FALSE) {
+       $newarray = array();
+       foreach($array as $key => $value) {
+               if (is_string($value)) $value = htmlspecialchars($value);
+               if ($keystoo && is_string($key)) $key = htmlspecialchars($key);
+               $newarray[$key] = $value;
+       }
+       return $newarray;
+}
+
+
+// indents a text
+function text_indent($text, $indentchar = "\t") {
+       if (strlen($text) == 0) return '';
+       $result = $indentchar . str_replace("\n", "\n$indentchar", $text);
+       if (substr($result, -2) == "\n$indentchar") $result = substr($result, 0, -strlen($indentchar));
+       return $result;
+}
+
+
+// Cuts a text to the specified max length and adds fill char. The cutting is done at word ends.
+// If the text is shorter than maxlength, it returns text.
+function text_cut($text, $maxlength, $fillchar = ' ...') {
+       if (strlen($text) <= $maxlength) return $text;
+       $text = wordwrap($text, $maxlength);
+       $lines = explode("\n", $text);
+       return $lines[0] . $fillchar;
+}
+
+
+
+// Shortcuts for creating simple things
+// ====================================
+
+// Shortcut for html_tag: You can use it in the following ways:
+// html_element($tagname);
+// html_element($tagname, $content [, $encode]);
+// html_element($tagname, $parameter [, $encode]);
+// html_element($tagname, $parameter, $content, [, $encode]);
+// The optional argument $encode is FALSE by default.
+// If $encode is TRUE, the $content _and_ the parameters _and_ the keys of the parameters are encoded.
+function html_element($tagname) {
+       list($tagname, $parameter, $content) = html_element_decode(func_get_args());
+       return html_tag($tagname, $parameter, $content);
+}
+
+
+// same as html_element but it does not result in
+// <tag>content</tag>
+// but
+// <tag>\n
+// \t content\n
+// </tag>
+function html_ielement($tagname) {
+       list($tagname, $parameter, $content) = html_element_decode(func_get_args());
+       if (!is_null($content) && !$content == '') {
+               $content = "\n" . text_indent($content);
+               if (substr($content, -1) != "\n") $content .= "\n";
+       }
+       return html_tag($tagname, $parameter, $content);
+}
+
+
+// "internal" function to use at html_element and html_ielement
+// Brings the arguments in the correct order and does the encoding
+function html_element_decode($args) {
+       // Parameter
+       $tagname = $args[0];
+       $encode = FALSE;
+       $parameter = array();
+       $content = NULL;
+       for ($i=1; $i != count($args); ++$i) {
+               $arg = $args[$i];
+               if (is_bool($arg)) $encode = $arg;
+               else if (is_array($arg)) $parameter = $arg;
+               else if (is_string($arg)) $content = $arg;
+               else die('unknown arguments in html_element');
+       }
+
+       // Encoding
+       if ($encode) {
+               $parameter = html_escape_array($parameter, TRUE);
+               if (!is_null($content)) $content = htmlspecialchars($content);
+       }
+
+       return array($tagname, $parameter, $content);
+}
+
+
+
+// Shortcuts for form fields
+// =========================
+
+// Helper function for the following form-field-generating functions
+function html_input_basic($name, $value, $type, $additionalParameter = array()) {
+       $parameter = array('type' => $type, 'name' => $name, 'value' => $value);
+       $parameter = array_merge($parameter, $additionalParameter);
+       return html_element('input', $parameter, TRUE);
+}
+
+
+// Creates an hidden field
+function html_input_hidden($name, $value) {
+       return html_input_basic($name, $value, 'hidden');
+}
+
+
+// Creates an input text field
+function html_input_text($name, $value=NULL, $length=NULL, $size=NULL) {
+       return html_input_basic($name, $value, 'text', array('maxlength' => $length, 'size' => $size));
+}
+
+
+// Creates an input password field
+function html_input_password($name, $value=NULL, $length=NULL, $size=NULL) {
+       return html_input_basic($name, $value, 'password', array('maxlength' => $length, 'size' => $size));
+}
+
+
+// Creates a submit field
+function html_input_submit($name, $value) {
+       return html_input_basic($name, $value, 'submit');
+}
+
+
+// Creates a reset field
+function html_input_reset($value) {
+       return html_input_basic(NULL, $value, 'reset');
+}
+
+
+// Creates a select box field
+function html_input_select($name, $value=NULL, $hash, $encode=FALSE, $format=TRUE, $size=1) {
+       $options = '';
+       foreach($hash as $k => $v) {
+               $parameter = array('value' => $k);
+               if ($value == $k) $parameter['selected'] = 'selected';
+               $options .= html_element('option', $parameter, $v, $encode) . "\n";
+       }
+       if ($format) $options = "\n" . text_indent($options);
+       if ($encode) $name = htmlspecialchars($name);
+       return html_element('select', array('name' => $name, 'size' => $size), $options);
+}
+
+
+// Creates a textarea field
+function html_textarea($name, $value, $cols, $rows, $encode=FALSE) {
+       $parameter = array('name' => $name, 'cols' => $cols, 'rows' => $rows);
+       if (is_null($value)) $value = ''; 
+       return html_element('textarea', $parameter, $value, $encode);
+}
+
+
+// Creates a string for an img element. You can use the function as follows:
+//   html_img($src) // alt=""
+//   html_img($src, [$alt,] [$encode,] [$keyvaluearray])
+//   html_img($src, [$alt,] [$keyvaluearray,] [$encode])
+// keyvaluearray: supply additional attributes. default: array()
+// encode: default: FALSE
+// alt: default: ''
+function html_img($src, $alt='') {
+       $encode = FALSE;
+       $attributes = array('src' => $src, 'alt' => $alt);
+       $args = func_get_args();
+       for($i = 2; $i != count($args); ++$i) {
+               $arg = $args[$i];
+               if (is_bool($arg)) $encode=$arg;
+               if (is_array($arg)) $attributes=array_merge($attributes, $arg);
+       }
+       return html_element('img', $attributes, $encode);
+}
+
+
+// Same as above with additional title. If title is NULL, $title = $alt.
+function html_img_title($src, $alt='', $title=NULL) {
+       $func_parameter = func_get_args();
+       if (is_null($title)) $title = $alt;
+       unset($func_parameter[2]);
+       $found = FALSE;
+       foreach($func_parameter as $k => $v) {
+               if (is_array($v)) {
+                       $func_parameter[$k]['title'] = $title;
+                       $found = TRUE;
+               }
+       }
+       if (!$found) $func_parameter[] = array('title' => $title);
+       return call_user_func_array('html_img', $func_parameter);
+}
+
+
+
+// DataInfo classes
+// ================
+
+// These classes are intended for use with EditForm.
+// For all class fields, the value NULL means that a default value should be taken.
+
+class DataInfo {
+       var $header;
+       var $name; // name for form.
+       var $default; // default value for insert
+
+       function DataInfo($name = NULL, $header = NULL, $default = NULL) {
+               $this->name = $name;
+               $this->header = $header;
+               $this->default = $default;
+       }
+
+       // virtual...
+       // creates the head cell of the table
+       function createHtmlTableHeader() {
+               if (!is_null($this->header))
+                       return html_element('span', array('class'=>'th'), $this->header, TRUE);
+       }
+
+       // virtual...
+       // creates the body cell of the table
+       function createHtmlTableBody($value) {
+               return html_element('span', array('class'=>'td'), $this->createHtml($value));
+       }
+
+       // virtual...
+       // creates the body cell of an insert row of the table
+       function createHtmlTableInsertBody() {
+               return html_element('span', array('class'=>'td'), $this->createInsertHtml());
+       }
+
+       // virtual...
+       function createHtml($value) {
+               return '';
+       }
+
+       // virtual...
+       function createInsertHtml() {
+               return $this->createHtml($this->default);
+       }
+};
+
+
+// This shows the text but is not "database sensitive"
+class DiReadOnly extends DataInfo {
+       function DiReadOnly($header = NULL, $default = NULL) {
+               $this->DataInfo(NULL, $header, $default);
+       }
+
+       function createHtml($value) {
+               return htmlspecialchars($value);
+       }
+};
+
+
+class DiHidden extends DataInfo {
+       function DiHidden($name, $default = NULL) {
+               $this->DataInfo($name, NULL, $default);
+       }
+
+       function createHtmlTableBody($value) {
+               return $this->createHtml($value);
+       }
+
+       function createHtmlTableInsertBody() {
+               return $this->createInsertHtml();
+       }
+
+       function createHtml($value) {
+               return html_input_hidden($this->name, $value);
+       }
+
+       function createInsertHtml() {
+               if (is_null($this->default)) return '';
+               return $this->createHtml($this->default);
+       }
+};
+
+/*
+class DiRawHtml extends DataInfo {
+       function createHtml($value) {
+               return $value;
+       }
+};
+*/
+
+
+class DiTextEdit extends DataInfo {
+       var $length = NULL;
+       var $size = NULL;
+
+       function DiTextEdit($name, $header, $length = NULL, $size = NULL, $default = NULL) {
+               $this->DataInfo($name, $header, $default);
+               $this->length = $length;
+               $this->size = $size;
+       }
+
+       function createHtml($value) {
+               return html_input_text($this->name, $value, $this->length, $this->size);
+       }
+       
+       function createInsertHtml() {
+               return $this->createHtml($this->default);
+       }       
+};
+
+
+class DiPasswordEdit extends DiTextEdit {
+       function createHtml($value) {
+               return html_input_password($this->name, 'XXXXXX', $this->length, $this->size);
+       }
+       
+       function createInsertHtml() {
+               return html_input_password($this->name, '', $this->length, $this->size);
+       }
+};
+
+
+// input: 1D array of arrays with 2 entries.
+// the first will be used as key, the second as value.
+// Useful to crate hash for DiSelect out of database query result.
+function array_to_hash($array) {
+       $result = array();
+       foreach ($array as $row) {
+               $result[$row[0]] = (string) $row[1];
+       }
+       return $result;
+}
+
+
+class DiSelect extends DataInfo {
+       var $hash;
+
+       function DiSelect($name, $heading, $hash) {
+               $this->DataInfo($name, $heading);
+               $this->hash = $hash;
+       }
+
+       function createHtml($value) {
+               $hash = html_escape_array($this->hash, TRUE);
+               return html_input_select($this->name, $value, $hash, TRUE);
+       }
+};
+
+
+class DiBoolEdit extends DataInfo {
+       function createHtml($value) {
+               $hash = array('' => '---', 't' => _('Yes'), 'f' => _('No'));
+               return html_input_select($this->name, $value, $hash);
+       }
+};
+
+
+class DiBoolReadOnly extends DataInfo {
+       function DiBoolReadOnly($header = NULL, $default = NULL) {
+               $this->DataInfo(NULL, $header, $default);
+       }
+
+       function createHtml($value) {
+               if ($value == 't') return htmlspecialchars(_('Yes'));
+               if ($value == 'f') return htmlspecialchars(_('No'));
+               return '';
+       }
+};
+
+
+// The data are not editable (but are shown) in normal rows.
+// Therefore a lookup hash is used.
+// In the "insert" row, a combobox is used.
+class DiOnlyInputSelect extends DataInfo {
+       var $lookupHash; // hash for "normal" lines to lookup.
+       var $insertHash; // hash for comboBox for "insert" line.
+       
+       function DiOnlyInputSelect($name, $header, $default, $lookupHash, $insertHash) {
+               $this->DataInfo($name, $header, $default);
+               $this->lookupHash = $lookupHash;
+               $this->insertHash = $insertHash;
+       }
+
+       function createHtml($value) {
+               $hash = html_escape_array($this->lookupHash, TRUE);
+               $hiddenField = html_input_hidden($this->name, $value);
+               if (array_key_exists($value, $hash)) return $hiddenField . $hash[$value];
+               return $hiddenField . '(' . htmlentities($value) . ')';
+       }
+
+       function createInsertHtml() {
+               $hash = html_escape_array($this->insertHash, TRUE);
+               return html_input_select($this->name, $this->default, $hash, TRUE);
+       }
+};
+
+
+class DiTextareaEdit extends DataInfo {
+       var $cols = NULL;
+       var $rows = NULL;
+
+       function DiTextareaEdit($name, $header, $cols = NULL, $rows = NULL) {
+               $this->DataInfo($name, $header);
+               $this->cols = $cols;
+               $this->rows = $rows;
+       }
+
+       function createHtml($value) {
+               return html_textarea($this->name, $value, $this->cols, $this->rows, TRUE);
+       }
+};
+
+
+
+// EditForm classes and functions
+// ==============================
+
+// creates an array of DataInfo classes suitable for the $dbresult.
+// if fieldnamekeys === TRUE, the field names will be taken as keys,
+//   else numbers will be taken.
+// $header: the names of the header
+// $size: the size of the field, if the type has a size.
+// $length: the length of the field, if the type has a length. If NULL,
+//   the $size will be taken.
+// $header, $size, $length can each be:
+// * assoziative array (hash): columnname => value
+// * normal array: values "from left to right"
+// * string functionname to apply to the default values i.e. ucwords.
+// The value NULL means that this property is not taken,
+// a value TRUE means that the default property is taken.
+function html_defaultColumns(
+       $dbresult,
+       $fieldnamekeys = FALSE,
+       $header = NULL,
+       $size = NULL,
+       $length = NULL
+) {
+       for ($col = 0; $col != pg_num_fields($dbresult); ++$col) {
+               // Name
+               $cname = pg_field_name($dbresult, $col);
+
+               // Type
+               $ctype = pg_field_type($dbresult, $col);
+
+               // Size
+               $dsize = pg_field_size($dbresult, $col);
+               $csize = FALSE;
+               if (is_string($size)) $csize = $size($dsize);
+               elseif (is_array($size) && array_key_exists($cname, $size)) $csize = $size[$cname];
+               elseif (is_array($size) && array_key_exists($col, $size)) $csize = $size[$col];
+               if ($csize === FALSE) $csize = $dsize;
+               if ($csize == -1) $csize = NULL;
+
+               // Length
+               $clength = FALSE;
+               if (is_string($length)) $clength = $length($csize);
+               elseif (is_array($length) && array_key_exists($cname, $length)) $clength = $length[$cname];
+               elseif (is_array($length) && array_key_exists($col, $length)) $clength = $length[$col];
+               if ($clength === FALSE) $clength = $csize;
+
+               // Header
+               $cheader = FALSE;
+               if (is_string($header)) $cheader = $header($cname);
+               elseif (is_array($header) && array_key_exists($cname, $header)) $cheader = $header[$cname];
+               elseif (is_array($header) && array_key_exists($col, $header)) $cheader = $header[$col];
+               if ($cheader === FALSE) $cheader = $cname;
+
+               // Create DataInfo class
+               $di = NULL;
+               if ($ctype == 'bool') $di = new DiBoolEdit($cname, $cheader);
+               if (is_null($di)) $di = new DiTextEdit($cname, $cheader, $csize, $clength);
+               if ($fieldnamekeys) $columns[$cname] = $di;
+               else $columns[] = $di;
+       }
+       return $columns;
+}
+
+
+function html_createEditForm(
+       $action,
+       $nextpage, // data to fill in in the field :nextpage (needed by editdb.php)
+       $tablename,
+       $columns, // 1D Array of EfDataInfo classes
+       $data, // Array of rows, that are again arrays of columns
+       $insert=TRUE,
+       $update=TRUE,
+       $delete=TRUE
+) {
+       if (count($data) == 0 && !$insert) return;
+       $action = htmlspecialchars($action);
+
+       // fill in nextpage
+       $columns[] = new DiHidden(':nextpage', $nextpage);
+       foreach ($data as $rownum => $datarow) {
+               $data[$rownum][] = $nextpage;
+       }
+       
+       // fill in tablename
+       $columns[] = new DiHidden(':tablename', $tablename);
+       foreach ($data as $rownum => $datarow) {
+               $data[$rownum][] = $tablename;
+       }
+       
+       // head
+       $head = '';
+       foreach($columns as $column) {
+               $h = $column->createHtmlTableHeader();
+               if (strlen($h) > 0) $h .= "\n";
+               $head .= $h;
+       }
+       $head .= html_element('span', array('class'=>'th'), '&nbsp;');
+       $result = html_ielement('div', array('class'=>'tr_head'), $head) . "\n";
+
+       // update
+       $rownr = 0;
+       foreach($data as $datarow) {
+               $row = '';
+               $columnnr = 0;
+               foreach ($datarow as $field) {
+                       $column = $columns[$columnnr];                  
+                       $html = $column->createHtmlTableBody($field);
+                       if (strlen($html) > 0) $html .= "\n";
+                       $row .= $html;
+                       ++$columnnr;
+               }
+               $buttons = '';
+               if ($update) $buttons .= html_input_submit(':operation_update', _('update'));
+               if ($delete) $buttons .= html_input_submit(':operation_delete', _('delete'));
+               $row .= html_element('span', array('class'=>'td'), $buttons);
+               $class = ($rownr % 2 == 0) ? 'tr_even' : 'tr_odd';
+
+               $result .= html_ielement('form', array('action' => $action, 'method' => 'post', 'class' => $class), $row) . "\n";
+               ++$rownr;
+       }
+
+       // insert
+       if ($insert) {
+               $row = '';
+               foreach($columns as $column) {
+                       $html = $column->createHtmlTableInsertBody();
+                       if (strlen($html) > 0) $html .= "\n";
+                       $row .= $html;
+               }
+               $button = html_input_submit(':operation_insert', _('insert')) . "\n";
+               $row .= html_element('span', array('class'=>'td'), $button);
+               $result .= html_ielement('form', array('action' => $action, 'method' => 'post', 'class' => 'tr_insert'), $row);
+               // $result .= html_ielement('div', array('class' => 'tr_insert'), $row);
+       }
+
+       return html_ielement('div', array('class'=>'table'), $result);
+}
+
+
+function html_createVEditForm(
+       $action,
+       $tablename,
+       $columns, // 1D Array of EfDataInfo classes
+       $data, // Array of rows, that are again arrays of columns
+       $insert=TRUE,
+       $update=TRUE,
+       $delete=TRUE
+) {
+       // fill in tablename
+       $columns[] = new DiHidden(':tablename', $tablename);
+       foreach ($data as $rownum => $datarow) {
+               $data[$rownum][] = $tablename;
+       }
+
+       // Rows
+       $rows = '';
+       $rownr = 0;
+       foreach($data as $row) {
+               $coltext = '';
+               reset($columns);
+               reset($row);
+               while($column = current($columns)) {
+                       $head = $column->createHtmlTableHeader();
+                       $field = $column->createHtmlTableBody(current($row));
+                       $text = "$head\n$field";
+                       if (!is_null($column->header)) $text = html_ielement('tr', $text);
+                       $coltext .= "$text\n";
+                       next($columns);
+                       next($row);
+               }
+               $buttons = '';
+               if ($update) $buttons .= html_input_submit(':operation_update', _('update'));
+               if ($delete) $buttons .= html_input_submit(':operation_delete', _('delete'));
+               $coltext .= '<tr><th>&nbsp;</th>' . html_element('td', $buttons) . "</tr>\n";
+               $coltext = html_ielement('form', array('action' => $action, 'method' => 'post'), $coltext) . "\n";
+               $class = ($rownr % 2 == 0) ? 'even' : 'odd';
+               $coltext = html_ielement('table', array('class' => $class), $coltext) . "\n";
+               $coltext = html_ielement('p', $coltext) . "\n";
+               $rows .= $coltext;
+               ++$rownr;
+       }
+
+       // Insert
+       if ($insert) {
+               $coltext = '';
+               reset($columns);
+               while($column = current($columns)) {
+                       $head = $column->createHtmlTableHeader();
+                       $field = $column->createHtmlTableInsertBody();
+                       $text = "$head\n$field";
+                       if (!is_null($column->header)) $text = html_ielement('tr', $text);
+                       $coltext .= "$text\n";
+                       next($columns);
+               }
+               $button = html_input_submit(':operation_insert', _('insert'));
+               $coltext .= '<tr><th>&nbsp;</th>' . html_element('td', $button) . "</tr>\n";
+               $coltext = html_ielement('form', array('action' => $action, 'method' => 'post'), $coltext) . "\n";
+               $coltext = html_ielement('table', array('class' => 'insert'), $coltext) . "\n";
+               $coltext = html_ielement('p', $coltext) . "\n";
+               $rows .= $coltext;
+       }
+
+       return $rows;
+}
+
+
+function html_createTable(
+       $head, // 1D Array of Head fields
+       $data // Array of rows, that are again arrays of columns
+) {
+       html_escape_array($head);
+       $thead = '<tr><th>' . implode('</th><th>', $head) . "</th></tr>\n";
+
+       $tbody = '';
+       $num = 0;
+       foreach($data as $row) {
+               html_escape_array($row);
+               $trow = '<td>' . implode('</td><td>', $row) . '</td>';
+               $tbody .= html_element('tr', array('class' => ($num % 2 == 0 ? 'even' : 'odd')), $trow);
+               ++$num;
+       }
+
+       return html_ielement('table', "$thead$tbody");
+}
+
+?>
diff --git a/lib/prepend.php b/lib/prepend.php
new file mode 100644 (file)
index 0000000..3da34cd
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+// This file should be called first from everywhere ;-)
+
+error_reporting(E_ALL);
+session_start();
+require_once(dirname(__FILE__) . '/../etc/options.php');
+
+
+// check server settings: magic quotes gpc
+if (get_magic_quotes_gpc()) die('The PHP variable magic_quotes_gpc is on. At the moment it is required to turn it off to use quickappoint.');
+
+// logout
+if (isset($_GET['logout'])) {
+       unset($_SESSION['person']);
+       session_destroy();
+}
+               
+
+// language settings
+if (isset($_SESSION['person']) && isset($_SESSION['person']['locale'])) setlocale(LC_ALL, $_SESSION['person']['locale']);
+else setlocale(LC_ALL, $project_options['locale']);
+bindtextdomain("messages", "./locale");
+textdomain("messages");
+
+// check authentification
+$request = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER["REQUEST_URI"];
+$found = FALSE;
+foreach ($unrestricted_pages as $page) {
+       if (strpos($request, $page) === 0) {$found = TRUE; break;}
+}
+if (!$found) {
+       if (!isset($_SESSION['person']) || empty($_SESSION['person'])) {
+               $_SESSION['errormsg'] = 'You have to login first!'; 
+               header('Location:' . $project_options['projecturl'] . 'login.php');
+       }
+}
+
+?>
diff --git a/locale/de/LC_MESSAGES/makefile b/locale/de/LC_MESSAGES/makefile
new file mode 100644 (file)
index 0000000..2961c35
--- /dev/null
@@ -0,0 +1,9 @@
+BASEDIR=../../../
+update:
+       xgettext $(BASEDIR)*.php $(BASEDIR)lib/*.php $(BASEDIR)cgi-bin/*.php -o messages.pot --keyword=_ --from-code=iso-8859-1 # Creates the .pot file
+       msgmerge -o messages.po.new messages.po messages.pot # Merge with existing
+       mv messages.po messages.po.backup
+       mv messages.po.new messages.po
+       # rm messages.pot
+compile:
+       msgfmt messages.po --output-file=messages.mo
diff --git a/locale/de/LC_MESSAGES/messages.mo b/locale/de/LC_MESSAGES/messages.mo
new file mode 100644 (file)
index 0000000..2e24544
Binary files /dev/null and b/locale/de/LC_MESSAGES/messages.mo differ
diff --git a/locale/de/LC_MESSAGES/messages.po b/locale/de/LC_MESSAGES/messages.po
new file mode 100644 (file)
index 0000000..4087334
--- /dev/null
@@ -0,0 +1,235 @@
+# This is the german translation file for quickappoint
+# Copyright (C) 2005 Toastfreeware (http://www.toastfreeware.priv.at)
+# This file is distributed under the same license as the quickappoint package.
+# "Philipp Spitzer" <philipp@toastfreeware.priv.at>, 2005.
+# "Gregor Herrmann" <gregoa@toastfreeware.priv.at>, 2005.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: quickappoint 1.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2005-03-05 19:55+0100\n"
+"PO-Revision-Date: 2005-03-05 11:10+0100\n"
+"Last-Translator: \"Philipp und Gregor\" <toast@toastfreeware.priv.at>\n"
+"Language-Team: german <toast@toastfreeware.priv.at>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=iso-8859-15\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../../../appointment.php:51
+msgid "Available proposals"
+msgstr "Verfügbare Terminvorschläge"
+
+#: ../../../appointment.php:54
+msgid "There are no proposals."
+msgstr "Keine Terminvorschläge vorhanden."
+
+#: ../../../appointment.php:59 ../../../appointment.php:102
+msgid "proposal"
+msgstr "Terminvorschlag"
+
+#: ../../../appointment.php:80
+msgid "Hide comments"
+msgstr "Kommentare ausblenden"
+
+#: ../../../appointment.php:80
+msgid "Show comments"
+msgstr "Kommentare anzeigen"
+
+#: ../../../appointment.php:85
+msgid "Own responses"
+msgstr "Eigene Antworten"
+
+#: ../../../appointment.php:87
+#, php-format
+msgid "A short comment on values: The range for values is %1$d to %2$d."
+msgstr ""
+"Kurze Erklärung zur Bewertung: Bitte mit Zahlen von %1$d bis %2$d bewerten."
+
+#: ../../../appointment.php:89
+msgid "The lower the better."
+msgstr "Niedrige Zahlen sind besser."
+
+#: ../../../appointment.php:90
+msgid "The higher the better."
+msgstr "Hohe Zahlen sind besser."
+
+#: ../../../appointment.php:103
+msgid "value"
+msgstr "Bewertung"
+
+#: ../../../appointment.php:104
+msgid "comment"
+msgstr "Kommentar"
+
+#: ../../../appointment.php:121
+msgid "Manage proposals"
+msgstr "Verfügbare Terminvorschläge"
+
+#: ../../../appointment.php:126
+msgid "day"
+msgstr "Tag"
+
+#: ../../../appointment.php:127
+msgid "date"
+msgstr "Datum"
+
+#: ../../../appointment.php:128
+msgid "time"
+msgstr "Zeit"
+
+#: ../../../appointment.php:129
+msgid "status"
+msgstr "Status"
+
+#: ../../../appointment.php:146
+msgid "Invited persons"
+msgstr "Eingeladene Personen"
+
+#: ../../../appointment.php:158
+msgid "person"
+msgstr "Person"
+
+#: ../../../appointment.php:159
+msgid "manage proposal"
+msgstr "Vorschläge bearbeiten"
+
+#: ../../../appointment.php:160
+msgid "manage appointment"
+msgstr "Termin bearbeiten"
+
+#: ../../../appointment.php:161 ../../../person.php:47
+msgid "manage person"
+msgstr "Personen einladen"
+
+#: ../../../appointment.php:179 ../../../person.php:70
+msgid "Back"
+msgstr "Zurück"
+
+#: ../../../login.php:24
+msgid "Welcome to QuickAppoint!"
+msgstr "Willkommen bei Quickappoint!"
+
+#: ../../../login.php:31
+msgid "Please log in:"
+msgstr "Bitte anmelden:"
+
+#: ../../../login.php:35 ../../../person.php:40
+msgid "username"
+msgstr "Benutzername"
+
+#: ../../../login.php:36 ../../../person.php:41
+msgid "password"
+msgstr "Passwort"
+
+#: ../../../login.php:38
+msgid "login"
+msgstr "Anmelden"
+
+#: ../../../overview.php:28
+msgid "Overview"
+msgstr "Übersicht"
+
+#: ../../../overview.php:35
+msgid "Available appointments"
+msgstr "Verfügbare Termine"
+
+#: ../../../overview.php:46
+msgid "There are no appointments at the moment!"
+msgstr "Derzeit gibt es keine Termine!"
+
+#: ../../../overview.php:56
+msgid "Manage appointments"
+msgstr "Termine bearbeiten"
+
+#: ../../../overview.php:61
+msgid "title"
+msgstr "Titel"
+
+#: ../../../overview.php:62
+msgid "description"
+msgstr "Beschreibung"
+
+#: ../../../overview.php:63
+msgid "location"
+msgstr "Ort"
+
+#: ../../../overview.php:64
+msgid "min value"
+msgstr "Min. Bewertung"
+
+#: ../../../overview.php:65
+msgid "max value"
+msgstr "Max. Bewertung"
+
+#: ../../../overview.php:66
+msgid "approval desc"
+msgstr "Min == Zustimmung"
+
+#: ../../../overview.php:67
+msgid "css name"
+msgstr "css Dateiname"
+
+#: ../../../overview.php:83
+msgid "Manage Persons"
+msgstr "Personen bearbeiten"
+
+#: ../../../overview.php:84 ../../../person.php:32
+msgid "Manage personal data"
+msgstr "Persönliche Daten bearbeiten"
+
+#: ../../../overview.php:87
+msgid "Logout"
+msgstr "Abmelden"
+
+#: ../../../person.php:30
+msgid "Manage persons"
+msgstr "Personen bearbeiten"
+
+#: ../../../person.php:42
+msgid "full name"
+msgstr "Vollständiger Name"
+
+#: ../../../person.php:43
+msgid "email"
+msgstr "E-Mail"
+
+#: ../../../person.php:44
+msgid "locale"
+msgstr "Locale"
+
+#: ../../../person.php:48
+msgid "add appointment"
+msgstr "Termin hinzufügen"
+
+#: ../../../person.php:60
+msgid "Valid locales for this system are:"
+msgstr "Folgende Einstellungen für \"Locale\" sind auf diesem System gültig:"
+
+#: ../../../lib/html.php:354
+msgid "Yes"
+msgstr "Ja"
+
+#: ../../../lib/html.php:354
+msgid "No"
+msgstr "Nein"
+
+#: ../../../lib/html.php:516 ../../../lib/html.php:575
+msgid "update"
+msgstr "Aktualisieren"
+
+#: ../../../lib/html.php:517 ../../../lib/html.php:576
+msgid "delete"
+msgstr "Löschen"
+
+#: ../../../lib/html.php:533 ../../../lib/html.php:598
+msgid "insert"
+msgstr "Einfügen"
+
+#: ../../../cgi-bin/auth.php:16
+msgid "username/password combination not valid!"
+msgstr "Der Benutzername und/oder das Passwort ist falsch!"
+
+#: ../../../cgi-bin/editdb.php:198
+msgid "Operation successful."
+msgstr "Aktion erfolgreich durchgeführt."
diff --git a/login.php b/login.php
new file mode 100644 (file)
index 0000000..7e0b8fa
--- /dev/null
+++ b/login.php
@@ -0,0 +1,42 @@
+<?php
+/*
+Description:
+       The login page
+Optional variable:
+       $errormsg ... if this is set it is shown above the login box.
+*/
+
+require_once(dirname(__FILE__) . '/lib/prepend.php');
+require_once(dirname(__FILE__) . '/lib/html.php'); // ????
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+<head>
+       <title>QuickAppoint</title>
+       <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+       <meta name="Author" content="Toastfreeware">
+       <link rel="stylesheet" type="text/css" href="css/default.css">
+       <!-- <link rel="icon" href="icons/icon.png" type="image/png"> -->
+</head>
+<body>
+
+<h1><?php echo htmlspecialchars(_("Welcome to QuickAppoint!")); ?></h1>
+
+<?php
+       if (isset($_SESSION['errormsg'])) echo html_element('p', array('class'=>'hint'), $_SESSION['errormsg']);
+       unset($_SESSION['errormsg']);
+?>
+
+<p><?php echo htmlspecialchars(_("Please log in:")); ?></p>
+
+<form method="post" action="cgi-bin/auth.php">
+<ul>
+       <li><?php echo htmlspecialchars(_("username")); ?>: <input type="text" name="username" size="16" maxlength="16"/></li>
+       <li><?php echo htmlspecialchars(_("password")); ?>: <input type="password" name="password" size="16" maxlength="32"/></li>
+</ul>
+<input type="submit" value="<?php echo htmlspecialchars(_("login")); ?>"/>
+</form>
+
+</body>
+</html>
diff --git a/overview.php b/overview.php
new file mode 100644 (file)
index 0000000..9b1d19a
--- /dev/null
@@ -0,0 +1,92 @@
+<?php
+require_once(dirname(__FILE__) . '/lib/prepend.php');
+require_once(dirname(__FILE__) . '/lib/dblogic.php');
+require_once(dirname(__FILE__) . '/lib/html.php');
+
+$personid = $_SESSION['person']['id'];
+
+$personright = getPersonRights($personid);
+
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+<head>
+       <title>QuickAppoint</title>
+       <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+       <meta name="Author" content="Toastfreeware">
+       <link rel="stylesheet" type="text/css" href="css/default.css">
+       <!-- <link rel="icon" href="icons/icon.png" type="image/png"> -->
+</head>
+<body>
+
+<?php
+if (isset($_SESSION['editdbmsg'])) echo html_element('p', array('class'=>'hint'), $_SESSION['editdbmsg']);
+unset($_SESSION['editdbmsg']);
+?>
+
+<h1><?php echo htmlspecialchars(_("Overview")) ?></h1>
+
+<?php 
+$appointments = getAppointments($personid);
+if (!empty($appointments)) { 
+?>
+
+<h2><?php echo htmlspecialchars(_("Available appointments")); ?></h2>
+
+<ul>
+
+<?php 
+foreach ($appointments as $appointment) { ?>
+<li><a href="appointment.php?id=<?php echo $appointment['id'];?>"><?php echo htmlspecialchars($appointment['title']), '</a>';
+if ($appointment['expire_date']) echo ' (', htmlspecialchars(_('expire date: ')), $appointment['expire_date'], ')';  ?></li>
+<?php } ?>
+</ul>
+
+<?php } else { ?>
+<em><?php echo htmlspecialchars(_('There are no appointments at the moment!')); ?></em>
+<?php } ?>
+
+
+
+<?php  
+       $appointments = getAppointments($personid, TRUE);
+       if (count($appointments) > 0 || $personright['addappointment']) 
+{ ?>
+
+<h2><?php echo _("Manage appointments"); ?></h2>
+
+<?php }
+       $columns = array(
+               new DiHidden('id:'),
+               new DiTextEdit('title', _('title'), 32, 20),
+               new DiTextareaEdit('description', _('description'), 20, 4),
+               new DiTextEdit('location', _('location'), 128, 20),
+               new DiTextEdit('minvalue', _('min value'), NULL, 5),
+               new DiTextEdit('maxvalue', _('max value'), NULL, 5),
+               new DiBoolEdit('approvaldesc', _('approval desc')),
+               new DiTextEdit('cssname', _('css name'), 256, 20),
+               new DiTextEdit('expire_date', _('expire date'), 10, 10),
+               new DiHidden('creator_personid', $_SESSION['person']['id'])
+       );
+
+       echo html_createEditForm(
+       'cgi-bin/editdb.php',
+       basename($_SERVER['REQUEST_URI']),
+       'appointment',
+       $columns,
+       $appointments, // Array of rows, that are again arrays of columns
+       $personright['addappointment'], TRUE, TRUE);    
+       
+?>
+
+<p><a href="person.php">
+<?php if ($personright['manageperson'])
+echo _("Manage Persons"); 
+else echo _("Manage personal data");
+?>
+</a></p>
+<p><a href="login.php?logout"><?php echo htmlspecialchars(_('Logout')); ?></a></p>
+
+</body>
+</html>
diff --git a/person.php b/person.php
new file mode 100644 (file)
index 0000000..b3e2fbd
--- /dev/null
@@ -0,0 +1,73 @@
+<?php
+require_once(dirname(__FILE__) . '/lib/prepend.php');
+require_once(dirname(__FILE__) . '/lib/dblogic.php');
+require_once(dirname(__FILE__) . '/lib/html.php');
+
+$personid = $_SESSION['person']['id'];
+$personright = getPersonRights($personid);
+
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+<head>
+       <title>QuickAppoint</title>
+       <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+       <meta name="Author" content="Toastfreeware">
+       <link rel="stylesheet" type="text/css" href="css/default.css">
+       <!-- <link rel="icon" href="icons/icon.png" type="image/png"> -->
+</head>
+<body>
+
+<?php
+if (isset($_SESSION['editdbmsg'])) echo html_element('p', array('class'=>'hint'), $_SESSION['editdbmsg']);
+unset($_SESSION['editdbmsg']);
+?>
+
+
+<?php
+if ($personright['manageperson']) { ?>
+<h1><?php echo htmlspecialchars(_('Manage persons')); ?></h1>
+<?php } else { ?>
+<h1><?php echo htmlspecialchars(_('Manage personal data')); ?></h1>
+<?php } ?>
+
+<?php 
+if ($personright['manageperson']) $persons = getAllPersons();
+else $persons = getPerson($_SESSION['person']['id']); 
+$columns = array(
+       new DiHidden('id:'),
+       new DiTextEdit('username', _('username'), 16, 10),
+       new DiPasswordEdit('pwd@password', _('password'), 32, 16),
+       new DiTextEdit('fullname', _('full name'), 64, 20),
+       new DiTextEdit('email', _('email'), 64, 20),
+       new DiTextEdit('locale', _('locale'), 32, 16)
+);
+if ($personright['manageperson']) {
+       $columns[] = new DiBoolEdit('manageperson', _('manage person'));
+       $columns[] = new DiBoolEdit('addappointment', _('add appointment'));
+}
+
+echo html_createEditForm(
+'cgi-bin/editdb.php',
+basename($_SERVER['REQUEST_URI']),
+'person',
+$columns,
+$persons, // Array of rows, that are again arrays of columns
+$personright['manageperson'], TRUE, $personright['manageperson']);     
+?>
+
+<p><?php echo htmlspecialchars(_('Valid locales for this system are:')); ?></p>
+<ul>
+       <li><?php 
+       $locales = array();
+       exec('locale -a', &$locales);
+       $locales = array_map('htmlspecialchars', $locales);
+       echo implode ("</li>\n\t\t<li>", $locales); ?></li>
+</ul>
+
+
+<p><a href="overview.php"><?php echo htmlspecialchars(_('Back')); ?></a></p>
+
+</body>
+</html>