diff --git a/timesheet/inc/class.botimesheet.inc.php b/timesheet/inc/class.botimesheet.inc.php new file mode 100644 index 0000000000..f40575d111 --- /dev/null +++ b/timesheet/inc/class.botimesheet.inc.php @@ -0,0 +1,447 @@ + * +* ------------------------------------------------------- * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU General Public License as published by the * +* Free Software Foundation; either version 2 of the License, or (at your * +* option) any later version. * +\**************************************************************************/ + +/* $Id$ */ + +require_once(EGW_INCLUDE_ROOT.'/etemplate/inc/class.so_sql.inc.php'); + +if (!defined('TIMESHEET_APP')) +{ + define('TIMESHEET_APP','timesheet'); +} + +/** + * Business object of the TimeSheet + * + * Uses eTemplate's so_sql as storage object (Table: egw_timesheet). + * + * @package timesheet + * @author RalfBecker-AT-outdoor-training.de + * @copyright (c) 2005 by RalfBecker-AT-outdoor-training.de + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + */ +class botimesheet extends so_sql +{ + /** + * @var array $config timesheets config data + */ + var $config = array(); + /** + * @var array $timestamps timestaps that need to be adjusted to user-time on reading or saving + */ + var $timestamps = array( + 'ts_start','ts_modified' + ); + /** + * @var int $tz_offset_s offset in secconds between user and server-time, + * it need to be add to a server-time to get the user-time or substracted from a user-time to get the server-time + */ + var $tz_offset_s; + /** + * @var int $now actual user-time as timestamp + */ + var $now; + /** + * @var int $today start of today in user-time + */ + var $today; + /** + * @var array $date_filters filter for search limiting the date-range + */ + var $date_filters = array( // Start: year,month,day,week, End: year,month,day,week + 'Today' => array(0,0,0,0, 0,0,1,0), + 'Yesterday' => array(0,0,-1,0, 0,0,0,0), + 'This week' => array(0,0,0,0, 0,0,0,1), + 'Last week' => array(0,0,0,-1, 0,0,0,0), + 'This month' => array(0,0,0,0, 0,1,0,0), + 'Last month' => array(0,-1,0,0, 0,0,0,0), + '2 month ago' => array(0,-2,0,0, 0,-1,0,0), + 'This year' => array(0,0,0,0, 1,0,0,0), + 'Last year' => array(-1,0,0,0, 0,0,0,0), + '2 years ago' => array(-2,0,0,0, -1,0,0,0), + '3 years ago' => array(-3,0,0,0, -2,0,0,0), + ); + + function botimesheet() + { + $this->so_sql(TIMESHEET_APP,'egw_timesheet'); + + $config =& CreateObject('phpgwapi.config',TIMESHEET_APP); + $config->read_repository(); + $this->config =& $config->config_data; + unset($config); + + if (!is_object($GLOBALS['egw']->datetime)) + { + $GLOBALS['egw']->datetime =& CreateObject('phpgwapi.datetime'); + } + $this->tz_offset_s = $GLOBALS['egw']->datetime->tz_offset; + $this->now = time() + $this->tz_offset_s; // time() is server-time and we need a user-time + $this->today = mktime(0,0,0,date('m',$this->now),date('d',$this->now),date('Y',$this->now)); + + // save us in $GLOBALS['botimesheet'] for ExecMethod used in hooks + if (!is_object($GLOBALS['botimesheet'])) + { + $GLOBALS['botimesheet'] =& $this; + } + // instanciation of link-class has to be after making us globaly availible, as it calls us to get the search_link + if (!is_object($GLOBALS['egw']->link)) + { + $GLOBALS['egw']->link =& CreateObject('phpgwapi.bolink'); + } + $this->link =& $GLOBALS['egw']->link; + + $this->grants = $GLOBALS['egw']->acl->get_grants(TIMESHEET_APP); + } + + /** + * get list of specified grants as uid => Username pairs + * + * @param int $required=EGW_ACL_READ + * @return array with uid => Username pairs + */ + function grant_list($required=EGW_ACL_READ) + { + $result = array(); + foreach($this->grants as $uid => $grant) + { + if ($grant & $required) + { + $result[$uid] = $GLOBALS['egw']->common->grab_owner_name($uid); + } + } + natcasesort($result); + + return $result; + } + + /** + * checks if the user has enough rights for a certain operation + * + * Rights are given via owner grants or role based acl + * + * @param int $required EGW_ACL_READ, EGW_ACL_WRITE, EGW_ACL_ADD, EGW_ACL_DELETE, EGW_ACL_BUDGET, EGW_ACL_EDIT_BUDGET + * @param array/int $data=null project or project-id to use, default the project in $this->data + * @return boolean true if the rights are ok, false if not + */ + function check_acl($required,$data=null) + { + if (!$data) + { + $data =& $this->data; + } + if (!is_array($data)) + { + $save_data = $this->data; + $data = $this->read($data,true); + $this->data = $save_data; + } + $rights = $this->grants[$data['ts_owner']]; + + return $data && !!($rights & $required); + } + + function date_filter($name) + { + if (!isset($this->date_filters[$name])) + { + return false; + } + $year = (int) date('Y',$this->today); + $month = (int) date('m',$this->today); + $day = (int) date('d',$this->today); + + list($syear,$smonth,$sday,$sweek,$eyear,$emonth,$eday,$eweek) = $this->date_filters[$name]; + + if ($syear || $eyear) + { + $start = mktime(0,0,0,1,1,$syear+$year); + $end = mktime(0,0,0,1,1,$eyear+$year); + } + elseif ($smonth || $emonth) + { + $start = mktime(0,0,0,$smonth+$month,1,$year); + $end = mktime(0,0,0,$emonth+$month,1,$year); + } + elseif ($sday || $eday) + { + $start = mktime(0,0,0,$month,$sday+$day,$year); + $end = mktime(0,0,0,$month,$eday+$day,$year); + } + elseif ($sweek || $eweek) + { + $wday = (int) date('w',$this->today); // 0=sun, ..., 6=sat + switch($GLOBALS['egw_info']['user']['preferences']['calendar']['weekdaystarts']) + { + case 'Sunday': + $weekstart = $this->today - $wday * 24*60*60; + break; + case 'Saturday': + $weekstart = $this->today - (6-$wday) * 24*60*60; + break; + case 'Moday': + default: + $weekstart = $this->today - ($wday ? $wday-1 : 6) * 24*60*60; + break; + } + $start = $weekstart + $sweek*7*24*60*60; + $end = $weekstart + $eweek*7*24*60*60; + // todo + } + //echo "

date_filter($name) today=".date('l, Y-m-d H:i',$this->today)." ==> ".date('l, Y-m-d H:i:s',$start)." <= date < ".date('l, Y-m-d H:i:s',$end)."

\n"; + // convert start + end from user to servertime + $start -= $this->tz_offset_s; + $end -= $this->tz_offset_s; + + return "($start <= ts_start AND ts_start < $end)"; + } + + /** + * search the timesheet + * + * reimplemented to limit result to users we have grants from + * + * @param array/string $criteria array of key and data cols, OR a SQL query (content for WHERE), fully quoted (!) + * @param boolean/string $only_keys=true True returns only keys, False returns all cols. comma seperated list of keys to return + * @param string $order_by='' fieldnames + {ASC|DESC} separated by colons ',', can also contain a GROUP BY (if it contains ORDER BY) + * @param string/array $extra_cols='' string or array of strings to be added to the SELECT, eg. "count(*) as num" + * @param string $wildcard='' appended befor and after each criteria + * @param boolean $empty=false False=empty criteria are ignored in query, True=empty have to be empty in row + * @param string $op='AND' defaults to 'AND', can be set to 'OR' too, then criteria's are OR'ed together + * @param mixed $start=false if != false, return only maxmatch rows begining with start, or array($start,$num) + * @param array $filter=null if set (!=null) col-data pairs, to be and-ed (!) into the query without wildcards + * @param string $join='' sql to do a join, added as is after the table-name, eg. ", table2 WHERE x=y" or + * "LEFT JOIN table2 ON (x=y)", Note: there's no quoting done on $join! + * @param boolean $need_full_no_count=false If true an unlimited query is run to determine the total number of rows, default false + * @return array of matching rows (the row is an array of the cols) or False + */ + function &search($criteria,$only_keys=True,$order_by='',$extra_cols='',$wildcard='',$empty=False,$op='AND',$start=false,$filter=null,$join='',$need_full_no_count=false) + { + if (!$extra_cols) $extra_cols = 'ts_quantity*ts_unitprice AS ts_total'; + + if (!isset($filter['ts_owner']) || !count($filter['ts_owner'])) + { + $filter['ts_owner'] = array_keys($this->grants); + } + else + { + if (!is_array($filter['ts_owner'])) $filter['ts_owner'] = array($filter['ts_owner']); + + foreach($filter['ts_owner'] as $key => $owner) + { + if (!isset($this->grants[$owner])) + { + unset($filter['ts_owner'][$key]); + } + } + } + if (!count($filter['ts_owner'])) + { + $this->total = 0; + return array(); + } + $this->summary = parent::search($criteria,'SUM(ts_duration) AS duration,SUM(ts_quantity*ts_unitprice) AS price', + '','',$wildcard,$empty,$op,false,$filter,$join); + $this->summary = $this->summary[0]; + + return parent::search($criteria,$only_keys,$order_by,$extra_cols,$wildcard,$empty,$op,$start,$filter,$join,$need_full_no_count); + } + + /** + * read a timesheet entry + * + * @param int $ts_id + * @param boolean $ignore_acl=false should the acl be checked + * @return array/boolean array with timesheet entry or false if no rights + */ + function read($ts_id,$ignore_acl=false) + { + if (!(int)$ts_id || !$ignore_acl && !$this->check_acl(EGW_ACL_READ,$ts_id) || + $this->data['ts_id'] != (int)$ts_id && !parent::read((int)$ts_id)) + { + return false; // no read rights, or entry not found + } + return $this->data; + } + + /** + * saves a timesheet entry + * + * reimplemented to notify the link-class + * + * @param array $keys if given $keys are copied to data before saveing => allows a save as + * @param boolean $touch_modified=true should modification date+user be set, default yes + * @param boolean $ignore_acl=false should the acl be checked, returns true if no edit-rigts + * @return int 0 on success and errno != 0 else + */ + function save($keys=null,$touch_modified=true,$ignore_acl=false) + { + if ($keys) $this->data_merge($keys); + + if (!$ignore_acl && $this->data['ts_id'] && !$this->check_acl(EGW_ACL_EDIT)) + { + return true; + } + if ($touch_modified) + { + $this->data['ts_modifier'] = $GLOBALS['egw_info']['user']['account_id']; + $this->data['ts_modified'] = $this->now; + } + if (!($err = parent::save(null))) + { + // notify the link-class about the update, as other apps may be subscribt to it + $this->link->notify_update(TIMESHEET_APP,$this->data['ts_id'],$this->data); + } + return $err; + } + + /** + * deletes a timesheet entry identified by $keys or the loaded one, reimplemented to notify the link class (unlink) + * + * @param array $keys if given array with col => value pairs to characterise the rows to delete + * @param boolean $ignore_acl=false should the acl be checked, returns false if no delete-rigts + * @return int affected rows, should be 1 if ok, 0 if an error + */ + function delete($keys=null,$ignore_acl=false) + { + $ts_id = is_null($keys) ? $this->data['ts_id'] : (is_array($keys) ? $keys['ts_id'] : $keys); + + if (!$this->check_acl(EGW_ACL_DELETE,$ts_id)) + { + return false; + } + if (($ret = parent::delete($keys)) && $ts_id) + { + // delete all links to timesheet entry $ts_id + $this->link->unlink(0,TIMESHEET_APP,$ts_id); + } + return $ret; + } + + /** + * changes the data from the db-format to your work-format + * + * reimplemented to adjust the timezone of the timestamps (adding $this->tz_offset_s to get user-time) + * Please note, we do NOT call the method of the parent so_sql !!! + * + * @param array $data if given works on that array and returns result, else works on internal data-array + * @return array with changed data + */ + function db2data($data=null) + { + if (!is_array($data)) + { + $data = &$this->data; + } + foreach($this->timestamps as $name) + { + if (isset($data[$name]) && $data[$name]) $data[$name] += $this->tz_offset_s; + } + return $data; + } + + /** + * changes the data from your work-format to the db-format + * + * reimplemented to adjust the timezone of the timestamps (subtraction $this->tz_offset_s to get server-time) + * Please note, we do NOT call the method of the parent so_sql !!! + * + * @param array $data if given works on that array and returns result, else works on internal data-array + * @return array with changed data + */ + function data2db($data=null) + { + if ($intern = !is_array($data)) + { + $data = &$this->data; + } + foreach($this->timestamps as $name) + { + if (isset($data[$name]) && $data[$name]) $data[$name] -= $this->tz_offset_s; + } + return $data; + } + + /** + * get title for an timesheet entry identified by $entry + * + * Is called as hook to participate in the linking + * + * @param int/array $entry int ts_id or array with timesheet entry + * @param string the title + */ + function link_title( $entry ) + { + if (!is_array($entry)) + { + $entry = $this->read( $entry ); + } + if (!$entry) + { + return False; + } + $format = $GLOBALS['egw_info']['user']['preferences']['common']['dateformat']; + if (date('H:i',$entry['ts_start']) != '00:00') // dont show 00:00 time, as it means date only + { + $format .= ' '.($GLOBALS['egw_info']['user']['preferences']['common']['timeformat'] == 12 ? 'h:i a' : 'H:i'); + } + return date($format,$entry['ts_start']).': '.$entry['ts_title']; + } + + /** + * query timesheet for entries matching $pattern + * + * Is called as hook to participate in the linking + * + * @param string $pattern pattern to search + * @return array with ts_id - title pairs of the matching entries + */ + function link_query( $pattern ) + { + $criteria = array(); + foreach(array('ts_project','ts_title','ts_description') as $col) + { + $criteria[$col] = $pattern; + } + $result = array(); + foreach((array) $this->search($criteria,false,'','','%',false,'OR') as $ts ) + { + $result[$ts['ts_id']] = $this->link_title($ts); + } + return $result; + } + + /** + * Hook called by link-class to include timesheet in the appregistry of the linkage + * + * @param array/string $location location and other parameters (not used) + * @return array with method-names + */ + function search_link($location) + { + return array( + 'query' => TIMESHEET_APP.'.botimesheet.link_query', + 'title' => TIMESHEET_APP.'.botimesheet.link_title', + 'view' => array( + 'menuaction' => TIMESHEET_APP.'.uitimesheet.view', + ), + 'view_id' => 'ts_id', + 'view_popup' => '600x400', + 'add' => array( + 'menuaction' => TIMESHEET_APP.'.uitimesheet.edit', + ), + 'add_app' => 'link_app', + 'add_id' => 'link_id', + 'add_popup' => '600x400', + ); + } +} \ No newline at end of file diff --git a/timesheet/inc/class.datasource_timesheet.inc.php b/timesheet/inc/class.datasource_timesheet.inc.php new file mode 100644 index 0000000000..82fc945a11 --- /dev/null +++ b/timesheet/inc/class.datasource_timesheet.inc.php @@ -0,0 +1,82 @@ + * +* -------------------------------------------- * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU General Public License as published by the * +* Free Software Foundation; either version 2 of the License, or (at your * +* option) any later version. * +\**************************************************************************/ + +/* $Id$ */ + +include_once(EGW_INCLUDE_ROOT.'/projectmanager/inc/class.datasource.inc.php'); +include_once('class.botimesheet.inc.php'); + +/** + * DataSource for the TimeSheet + * + * @package timesheet + * @author RalfBecker-AT-outdoor-training.de + * @copyright (c) 2005 by RalfBecker-AT-outdoor-training.de + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + */ +class datasource_timesheet extends datasource +{ + /** + * Constructor + */ + function datasource_timesheet() + { + $this->datasource(TIMESHEET_APP); + + $this->valid = PM_REAL_START|PM_REAL_END|PM_USED_TIME|PM_USED_BUDGET|PM_RESOURCES|PM_DETAILS|PM_COMPLETION; + } + + /** + * get an entry from the underlaying app (if not given) and convert it into a datasource array + * + * @param mixed $data_id id as used in the link-class for that app, or complete entry as array + * @return array/boolean array with the data supported by that source or false on error (eg. not found, not availible) + */ + function get($data_id) + { + // we use $GLOBALS['bocal'] as an already running instance is availible there + if (!is_object($GLOBALS['botimesheet'])) + { + $GLOBALS['bocal'] =& new botimesheet(); + } + if (!is_array($data_id)) + { + if (!(int) $data_id || !($data = $GLOBALS['botimesheet']->read((int) $data_id))) + { + return false; + } + } + else + { + $data =& $data_id; + } + $ds = array( + 'pe_title' => $GLOBALS['botimesheet']->link_title($data), + 'pe_real_start' => $data['ts_start'], + 'pe_resources' => array($data['ts_owner']), + 'pe_details' => $data['ts_description'] ? nl2br($data['ts_description']) : '', + 'pe_used_budget' => $data['ts_quantity'] * $data['ts_unitprice'], + 'pe_completion' => 100, + ); + if ($data['ts_duration']) + { + $ds['pe_real_end'] = $data['ts_start'] + 60*$data['ts_duration']; + $ds['pe_used_time'] = $data['ts_duration']; + } + if ($this->debug) + { + echo "datasource_timesheet($data_id) data="; _debug_array($data); + echo "datasource="; _debug_array($ds); + } + return $ds; + } +} diff --git a/timesheet/inc/class.ts_admin_prefs_sidebox_hooks.inc.php b/timesheet/inc/class.ts_admin_prefs_sidebox_hooks.inc.php new file mode 100644 index 0000000000..c47960c8c3 --- /dev/null +++ b/timesheet/inc/class.ts_admin_prefs_sidebox_hooks.inc.php @@ -0,0 +1,131 @@ + * +* ------------------------------------------------------- * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU General Public License as published by the * +* Free Software Foundation; either version 2 of the License, or (at your * +* option) any later version. * +\**************************************************************************/ + +/* $Id$ */ + +if (!defined('TIMESHEET_APP')) +{ + define('TIMESHEET_APP','timesheet'); +} + +class ts_admin_prefs_sidebox_hooks +{ + var $public_functions = array( +// 'check_set_default_prefs' => true, + ); + var $config = array(); + + function pm_admin_prefs_sidebox_hooks() + { + $config =& CreateObject('phpgwapi.config',TIMESHEET_APP); + $config->read_repository(); + $this->config =& $config->config_data; + unset($config); + } + + /** + * hooks to build projectmanager's sidebox-menu plus the admin and preferences sections + * + * @param string/array $args hook args + */ + function all_hooks($args) + { + $appname = TIMESHEET_APP; + $location = is_array($args) ? $args['location'] : $args; + //echo "

ts_admin_prefs_sidebox_hooks::all_hooks(".print_r($args,True).") appname='$appname', location='$location'

\n"; + + if ($location == 'sidebox_menu') + { + $file = array( + ); + display_sidebox($appname,$GLOBALS['egw_info']['apps'][$appname]['title'].' '.lang('Menu'),$file); + } + + if ($GLOBALS['egw_info']['user']['apps']['preferences'] && $location != 'admin') + { + $file = array( +// 'Preferences' => $GLOBALS['egw']->link('/index.php','menuaction=preferences.uisettings.index&appname='.$appname), + 'Grant Access' => $GLOBALS['egw']->link('/index.php','menuaction=preferences.uiaclprefs.index&acl_app='.$appname), + 'Edit Categories' => $GLOBALS['egw']->link('/index.php','menuaction=preferences.uicategories.index&cats_app=' . $appname . '&cats_level=True&global_cats=True') + ); + if ($location == 'preferences') + { + display_section($appname,$file); + } + else + { + display_sidebox($appname,lang('Preferences'),$file); + } + } + + if ($GLOBALS['egw_info']['user']['apps']['admin'] && $location != 'preferences') + { + $file = Array( +// 'Site configuration' => $GLOBALS['egw']->link('/index.php','menuaction=projectmanager.admin.config'), +// 'Custom fields' => $GLOBALS['egw']->link('/index.php','menuaction=admin.customfields.edit&appname='.$appname), + 'Global Categories' => $GLOBALS['egw']->link('/index.php',array( + 'menuaction' => 'admin.uicategories.index', + 'appname' => $appname, + 'global_cats'=> True)), + ); + if ($location == 'admin') + { + display_section($appname,$file); + } + else + { + display_sidebox($appname,lang('Admin'),$file); + } + } + } + + /** + * populates $GLOBALS['settings'] for the preferences + */ + function settings() + { + $this->check_set_default_prefs(); + + return true; // otherwise prefs say it cant find the file ;-) + } + + /** + * Check if reasonable default preferences are set and set them if not + * + * It sets a flag in the app-session-data to be called only once per session + */ + function check_set_default_prefs() + { + if ($GLOBALS['egw']->session->appsession('default_prefs_set',TIMESHEET_APP)) + { + return; + } + $GLOBALS['egw']->session->appsession('default_prefs_set',TIMESHEET_APP,'set'); + + $default_prefs =& $GLOBALS['egw']->preferences->default[TIMESHEET_APP]; + + $defaults = array( + ); + foreach($defaults as $var => $default) + { + if (!isset($default_prefs[$var]) || $default_prefs[$var] === '') + { + $GLOBALS['egw']->preferences->add(TIMESHEET_APP,$var,$default,'default'); + $need_save = True; + } + } + if ($need_save) + { + $GLOBALS['egw']->preferences->save_repository(False,'default'); + } + } +} \ No newline at end of file diff --git a/timesheet/inc/class.uitimesheet.inc.php b/timesheet/inc/class.uitimesheet.inc.php new file mode 100644 index 0000000000..d5f4fb1616 --- /dev/null +++ b/timesheet/inc/class.uitimesheet.inc.php @@ -0,0 +1,316 @@ + * +* ------------------------------------------------------- * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU General Public License as published by the * +* Free Software Foundation; either version 2 of the License, or (at your * +* option) any later version. * +\**************************************************************************/ + +/* $Id$ */ + +require_once(EGW_INCLUDE_ROOT.'/etemplate/inc/class.uietemplate.inc.php'); +require_once('class.botimesheet.inc.php'); + +/** + * User interface object of the TimeSheet + * + * @package timesheet + * @author RalfBecker-AT-outdoor-training.de + * @copyright (c) 2005 by RalfBecker-AT-outdoor-training.de + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + */ +class uitimesheet extends botimesheet +{ + var $public_functions = array( + 'view' => true, + 'edit' => true, + 'index' => true, + ); + + function uitimesheet() + { + $this->botimesheet(); + } + + function view() + { + $this->edit(null,true); + } + + function edit($content = null,$view = false) + { + $tabs = 'general|notes|links'; + + if (!is_array($content)) + { + if ($view || (int)$_GET['ts_id']) + { + if (!$this->read((int)$_GET['ts_id'])) + { + $GLOBALS['egw']->common->egw_header(); + echo "\n"; + $GLOBALS['egw']->common->egw_exit(); + } + if (!$view && !$this->check_acl(EGW_ACL_EDIT)) + { + $view = true; + } + } + else // new entry + { + $this->data = array( + 'ts_start' => $this->today, + 'ts_owner' => $GLOBALS['egw_info']['user']['account_id'], + 'cat_id' => (int) $_REQUEST['cat_id'], + ); + } + $referer = preg_match('/menuaction=([^&]+)/',$_SERVER['HTTP_REFERER'],$matches) ? $matches[1] : TIMESHEET_APP.'.uitimesheet.index'; + } + else + { + list($button) = each($content['button']); + $view = $content['view']; + $referer = $content['referer']; + $this->data = $content; + foreach(array('button','view','referer',$tabs) as $key) + { + unset($this->data[$key]); + } + switch($button) + { + case 'edit': + if ($this->check_acl(EGW_ACL_EDIT)) $view = false; + break; + + case 'save': + case 'save_new': + case 'apply': + if (!$this->data['ts_quantity']) // set the quantity (in h) from the duration (in min) + { + $this->data['ts_quantity'] = $this->data['ts_duration'] / 60.0; + } + if (!$this->data['ts_project']) $this->data['ts_project'] = $this->data['ts_project_blur']; + + if ($this->save() != 0) + { + $msg = lang('Error saving the entry!!!'); + $button = ''; + } + else + { + $msg = lang('Entry saved'); + if (is_array($content['link_to']['to_id']) && count($content['link_to']['to_id'])) + { + $this->link->link(TIMESHEET_APP,$this->data['ts_id'],$content['link_to']['to_id']); + } + } + $js = "opener.location.href='".$GLOBALS['egw']->link('/index.php',array( + 'menuaction' => $referer, + 'msg' => $msg, + ))."';"; + if ($button == 'apply') break; + if ($button == 'save_new') + { + // create a new entry + $this->data['ts_start'] += 60 * $this->data['ts_duration']; + foreach(array('ts_id','ts_title','ts_description','ts_duration','ts_quantity','ts_modified','ts_modifier') as $name) + { + unset($this->data[$name]); + } + break; + } + // fall-through for save + case 'delete': + if ($button == 'delete') + { + $this->delete(); + $msg = lang('Entry deleted'); + $js = "opener.location.href=opener.location.href+'&msg=$msg'"; + } + case 'cancel': + $js .= 'window.close();'; + echo "\n\n\n\n\n"; + $GLOBALS['egw']->common->egw_exit(); + break; + } + } + $preserv = $this->data + array( + 'view' => $view, + 'referer' => $referer, + ); + $content = $this->data + array( + 'msg' => $msg, + 'view' => $view, + $tabs => $content[$tabs], + 'link_to' => array( + 'to_id' => $content['link_to']['to_id'] ? $content['link_to']['to_id'] : $this->data['ts_id'], + 'to_app' => TIMESHEET_APP, + ), + 'js' => "\n", + 'ts_quantity_blur' => $this->data['ts_duration'] ? $this->data['ts_duration'] / 60.0 : '', + ); + if (!$this->data['ts_id'] && isset($_GET['link_app']) && isset($_GET['link_id']) && + preg_match('/^[a-z_0-9-]+:[:a-z_0-9-]+$/i',$_GET['link_app'].':'.$_GET['link_id']) && // gard against XSS + !is_array($content['link_to']['to_id'])) + { + $this->link->link(TIMESHEET_APP,$content['link_to']['to_id'],$_GET['link_app'],$_GET['link_id']); +// $content['ts_project'] = $this->link->title($_GET['link_app'],$_GET['link_id']); + if ($_GET['link_app'] == 'projectmanager') + { + $links = array($_GET['link_id']); + } + } + elseif ($this->data['ts_id']) + { + $links = $this->link->get_links(TIMESHEET_APP,$this->data['ts_id'],'projectmanager'); + } + if ($links) + { + $preserv['ts_project_blur'] = $content['ts_project_blur'] = $this->link->title('projectmanager',array_shift($links)); + } + $readonlys = array( + 'button[delete]' => !$this->data['ts_id'] || !$this->check_acl(EGW_ACL_DELETE), + 'button[edit]' => !$view || !$this->check_acl(EGW_ACL_EDIT), + 'button[save]' => $view, + 'button[save_new]' => $view, + 'button[apply]' => $view, + ); + if ($view) + { + foreach($this->data as $key => $val) + { + $readonlys[$key] = true; + } + } + $edit_grants = $this->grant_list(EGW_ACL_EDIT); + if (count($edit_grants) == 1) + { + $readonlys['ts_owner'] = true; + } + $GLOBALS['egw_info']['flags']['app_header'] = lang('timesheet').' - '. + ($view ? lang('View') : ($this->data['ts_id'] ? lang('Edit') : lang('Add'))); + + $etpl =& new etemplate('timesheet.edit'); + + return $etpl->exec(TIMESHEET_APP.'.uitimesheet.edit',$content,array( + 'ts_owner' => $edit_grants, + ),$readonlys,$preserv,2); + } + + /** + * query projects for nextmatch in the projects-list + * + * reimplemented from so_sql to disable action-buttons based on the acl and make some modification on the data + * + * @param array $query + * @param array &$rows returned rows/cups + * @param array &$readonlys eg. to disable buttons based on acl + */ + function get_rows($query,&$rows,&$readonlys) + { + $GLOBALS['egw']->session->appsession('index',TIMESHEET_APP,$query); + + unset($query['col_filter']['cat_id']); + if ($query['cat_id']) + { + if (!is_object($GLOBALS['egw']->categories)) + { + $GLOBALS['egw']->categories =& CreateObject('phpgwapi.categories'); + } + $cats = $GLOBALS['egw']->categories->return_all_children((int)$query['cat_id']); + $query['col_filter']['cat_id'] = count($cats) > 1 ? $cats : $query['cat_id']; + } + if ($query['filter']) + { + $query['col_filter'][0] = $this->date_filter($query['filter']); + } + if (!$query['col_filter']['ts_owner']) unset($query['col_filter']['ts_owner']); + + $total = parent::get_rows($query,$rows,$readonlys); + + unset($query['col_filter'][0]); + + $readonlys = array(); + foreach($rows as $n => $val) + { + $row =& $rows[$n]; + if (!$this->check_acl(EGW_ACL_EDIT,$row)) + { + $readonlys["edit[$row[ts_id]]"] = true; + } + if (!$this->check_acl(EGW_ACL_DELETE,$row)) + { + $readonlys["delete[$row[ts_id]]"] = true; + } + if ($query['col_filter']['ts_project'] || !$query['filter2']) + { + unset($row['ts_project']); // dont need or want to show it + } + else + { + if (($links = $this->link->get_links(TIMESHEET_APP,$row['ts_id'],'projectmanager'))) + { + $row['ts_link'] = array( + 'app' => 'projectmanager', + 'id' => array_shift($links), + ); + } + $row['ts_link']['title'] = $row['ts_project']; + } + if (!$query['filter2']) + { + unset($row['ts_description']); + } + } + $rows['no_owner_col'] = $query['no_owner_col']; + if ($query['filter']) + { + $rows['duration'] = $this->summary['duration']; + $rows['price'] = $this->summary['price']; + } + return $total; + } + + /** + * List timesheet entries + * + * @param array $content=null + */ + function index($content = null,$msg='') + { + $etpl =& new etemplate('timesheet.index'); + + if ($_GET['msg']) $msg = $_GET['msg']; + + $content = array( + 'nm' => $GLOBALS['egw']->session->appsession('index',TIMESHEET_APP), + 'msg' => $msg, + ); + if (!is_array($content['nm'])) + { + $date_filters = array('All'); + foreach($this->date_filters as $name => $date) + { + $date_filters[$name] = $name; + } + $content['nm'] = array( + 'get_rows' => TIMESHEET_APP.'.uitimesheet.get_rows', + 'options-filter' => $date_filters, + 'options-filter2' => array('No details','Details'), + 'order' => 'ts_start',// IO name of the column to sort after (optional for the sortheaders) + 'sort' => 'DESC',// IO direction of the sort: 'ASC' or 'DESC' + ); + } + $read_grants = $this->grant_list(EGW_ACL_READ); + $content['nm']['no_owner_col'] = count($read_grants) == 1; + + return $etpl->exec(TIMESHEET_APP.'.uitimesheet.index',$content,array( + 'ts_project' => $this->query_list('ts_project'), + 'ts_owner' => $read_grants, + ),$readonlys,$preserv); + } +} \ No newline at end of file diff --git a/timesheet/index.php b/timesheet/index.php new file mode 100644 index 0000000000..2d129f73f1 --- /dev/null +++ b/timesheet/index.php @@ -0,0 +1,39 @@ + * +* -------------------------------------------- * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU General Public License as published by the * +* Free Software Foundation; either version 2 of the License, or (at your * +* option) any later version. * +\**************************************************************************/ + +/* $Id$ */ + +include_once('setup/setup.inc.php'); +$ts_version = $setup_info[TIMESHEET_APP]['version']; +unset($setup_info); + +$GLOBALS['egw_info'] = array( + 'flags' => array( + 'currentapp' => TIMESHEET_APP, + 'noheader' => True, + 'nonavbar' => True +)); +include('../header.inc.php'); + +if ($ts_version != $GLOBALS['egw_info']['apps'][TIMESHEET_APP]['version']) +{ + $GLOBALS['egw']->common->egw_header(); + parse_navbar(); + echo '

'.lang('Your database is NOT up to date (%1 vs. %2), please run %3setup%4 to update your database.', + $ts_version,$GLOBALS['egw_info']['apps'][TIMESHEET_APP]['version'], + '','')."

\n"; + $GLOBALS['egw']->common->egw_exit(); +} + +//ExecMethod(TIMESHEET_APP.'.pm_admin_prefs_sidebox_hooks.check_set_default_prefs'); + +$GLOBALS['egw']->redirect_link('/index.php',array('menuaction'=>TIMESHEET_APP.'.uitimesheet.index')); diff --git a/timesheet/setup/etemplates.inc.php b/timesheet/setup/etemplates.inc.php new file mode 100644 index 0000000000..5fe073a50a --- /dev/null +++ b/timesheet/setup/etemplates.inc.php @@ -0,0 +1,19 @@ + 'timesheet.edit','template' => '','lang' => '','group' => '0','version' => '0.1.001','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:8:{i:0;a:8:{s:2:"c2";s:2:"th";s:2:"c3";s:3:"row";s:1:"A";s:3:"100";s:2:"c6";s:3:"row";s:2:"h6";s:14:",!@ts_modified";s:2:"c4";s:3:"row";s:2:"h2";s:2:"28";s:2:"h1";s:6:",!@msg";}i:1;a:2:{s:1:"A";a:5:{s:4:"type";s:5:"label";s:4:"span";s:13:"all,redItalic";s:4:"name";s:3:"msg";s:7:"no_lang";s:1:"1";s:5:"align";s:6:"center";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:2;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:11:",,,ts_owner";s:5:"label";s:4:"User";}s:1:"B";a:4:{s:4:"type";s:6:"select";s:4:"name";s:8:"ts_owner";s:4:"span";s:3:"all";s:7:"no_lang";s:1:"1";}}i:3;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:13:",,,ts_project";s:5:"label";s:7:"Project";}s:1:"B";a:5:{s:4:"type";s:4:"text";s:4:"size";s:5:"65,80";s:4:"name";s:10:"ts_project";s:4:"span";s:3:"all";s:4:"blur";s:16:"@ts_project_blur";}}i:4;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:14:",,ts_unitprice";s:5:"label";s:9:"Unitprice";}s:1:"B";a:3:{s:4:"type";s:5:"float";s:4:"name";s:12:"ts_unitprice";s:4:"span";s:3:"all";}}i:5;a:2:{s:1:"A";a:4:{s:4:"type";s:3:"tab";s:5:"label";s:19:"General|Notes|Links";s:4:"name";s:19:"general|notes|links";s:4:"span";s:3:"all";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:6;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:13:"Last modified";}s:1:"B";a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";i:1;a:3:{s:4:"type";s:9:"date-time";s:4:"name";s:11:"ts_modified";s:8:"readonly";s:1:"1";}i:2;a:4:{s:4:"type";s:14:"select-account";s:4:"name";s:11:"ts_modifier";s:5:"label";s:2:"by";s:8:"readonly";s:1:"1";}}}i:7;a:2:{s:1:"A";a:5:{s:4:"type";s:4:"hbox";s:4:"size";s:5:"2,0,0";s:4:"span";s:1:"2";i:1;a:8:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"6";i:1;a:4:{s:4:"type";s:6:"button";s:5:"label";s:4:"Edit";s:4:"name";s:12:"button[edit]";s:4:"help";s:15:"Edit this entry";}i:2;a:4:{s:4:"type";s:6:"button";s:4:"name";s:16:"button[save_new]";s:5:"label";s:10:"Save & New";s:4:"help";s:34:"Saves this entry and add a new one";}i:3;a:4:{s:4:"type";s:6:"button";s:4:"name";s:12:"button[save]";s:5:"label";s:4:"Save";s:4:"help";s:22:"Saves the changes made";}i:4;a:4:{s:4:"type";s:6:"button";s:4:"name";s:13:"button[apply]";s:5:"label";s:5:"Apply";s:4:"help";s:24:"Applies the changes made";}i:5;a:5:{s:4:"type";s:6:"button";s:4:"name";s:14:"button[cancel]";s:5:"label";s:6:"Cancel";s:4:"help";s:44:"closes the window without saving the changes";s:7:"onclick";s:15:"window.close();";}i:6;a:2:{s:4:"type";s:4:"html";s:4:"name";s:2:"js";}}i:2;a:6:{s:4:"type";s:6:"button";s:5:"label";s:6:"Delete";s:5:"align";s:5:"right";s:4:"name";s:14:"button[delete]";s:4:"help";s:17:"Delete this entry";s:7:"onclick";s:36:"return confirm(\'Delete this entry\');";}}s:1:"B";a:1:{s:4:"type";s:5:"label";}}}s:4:"rows";i:7;s:4:"cols";i:2;s:4:"size";s:4:"100%";s:7:"options";a:1:{i:0;s:4:"100%";}}}','size' => '100%','style' => '','modified' => '1134772260',); + +$templ_data[] = array('name' => 'timesheet.edit.general','template' => '','lang' => '','group' => '0','version' => '0.1.001','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:6:{i:0;a:5:{s:2:"c1";s:3:"row";s:1:"A";s:2:"95";s:2:"c3";s:3:"row";s:2:"c4";s:3:"row";s:2:"c2";s:3:"row";}i:1;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:11:",,,ts_title";s:5:"label";s:5:"Title";}s:1:"B";a:4:{s:4:"type";s:4:"text";s:4:"size";s:5:"65,80";s:4:"name";s:8:"ts_title";s:6:"needed";s:1:"1";}}i:2;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:9:",,,cat_id";s:5:"label";s:8:"Category";}s:1:"B";a:3:{s:4:"type";s:10:"select-cat";s:4:"name";s:6:"cat_id";s:4:"size";s:4:"None";}}i:3;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:11:",,,ts_start";s:5:"label";s:4:"Date";}s:1:"B";a:4:{s:4:"type";s:9:"date-time";s:4:"name";s:8:"ts_start";s:6:"needed";s:1:"1";s:4:"size";s:2:",8";}}i:4;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:14:",,,ts_duration";s:5:"label";s:8:"Duration";}s:1:"B";a:3:{s:4:"type";s:13:"date-duration";s:4:"name";s:11:"ts_duration";s:4:"size";s:2:",h";}}i:5;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:14:",,,ts_quantity";s:5:"label";s:8:"Quantity";}s:1:"B";a:4:{s:4:"type";s:5:"float";s:4:"name";s:11:"ts_quantity";s:4:"help";s:30:"empty if identical to duration";s:4:"blur";s:17:"@ts_quantity_blur";}}}s:4:"rows";i:5;s:4:"cols";i:2;s:4:"size";s:8:"100%,150";s:7:"options";a:2:{i:0;s:4:"100%";i:1;s:3:"150";}}}','size' => '100%,150','style' => '','modified' => '1134773043',); + +$templ_data[] = array('name' => 'timesheet.edit.links','template' => '','lang' => '','group' => '0','version' => '0.1.001','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:5:{i:0;a:7:{s:1:"A";s:3:"100";s:2:"h1";s:13:",@status_only";s:2:"h2";s:13:",@status_only";s:2:"c1";s:2:"th";s:2:"c2";s:3:"row";s:2:"c3";s:2:"th";s:2:"c4";s:11:"row_off,top";}i:1;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"span";s:3:"all";s:5:"label";s:16:"Create new links";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:2;a:2:{s:1:"A";a:3:{s:4:"type";s:7:"link-to";s:4:"span";s:3:"all";s:4:"name";s:7:"link_to";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:3;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"span";s:3:"all";s:5:"label";s:14:"Existing links";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:4;a:2:{s:1:"A";a:3:{s:4:"type";s:9:"link-list";s:4:"span";s:3:"all";s:4:"name";s:7:"link_to";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}}s:4:"rows";i:4;s:4:"cols";i:2;s:4:"size";s:17:"100%,150,,,,,auto";s:7:"options";a:3:{i:0;s:4:"100%";i:1;s:3:"150";i:6;s:4:"auto";}}}','size' => '100%,150,,,,,auto','style' => '','modified' => '1134775301',); + +$templ_data[] = array('name' => 'timesheet.edit.notes','template' => '','lang' => '','group' => '0','version' => '0.1.001','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:2:{i:0;a:1:{s:2:"c1";s:7:"row,top";}i:1;a:1:{s:1:"A";a:3:{s:4:"type";s:8:"textarea";s:4:"size";s:4:"8,70";s:4:"name";s:14:"ts_description";}}}s:4:"rows";i:1;s:4:"cols";i:1;s:4:"size";s:8:"100%,150";s:7:"options";a:2:{i:0;s:4:"100%";i:1;s:3:"150";}}}','size' => '100%,150','style' => '','modified' => '1134773787',); + +$templ_data[] = array('name' => 'timesheet.index','template' => '','lang' => '','group' => '0','version' => '0.1.001','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:4:{i:0;a:1:{s:2:"h1";s:6:",!@msg";}i:1;a:1:{s:1:"A";a:5:{s:4:"type";s:5:"label";s:4:"span";s:13:"all,redItalic";s:7:"no_lang";s:1:"1";s:4:"name";s:3:"msg";s:5:"align";s:6:"center";}}i:2;a:1:{s:1:"A";a:3:{s:4:"type";s:9:"nextmatch";s:4:"name";s:2:"nm";s:4:"size";s:20:"timesheet.index.rows";}}i:3;a:1:{s:1:"A";a:4:{s:4:"type";s:6:"button";s:5:"label";s:3:"Add";s:4:"name";s:3:"add";s:7:"onclick";s:163:"window.open(egw::link(\'/index.php\',\'menuaction=timesheet.uitimesheet.edit\'),\'_blank\',\'dependent=yes,width=600,height=400,scrollbars=yes,status=yes\'); return false;";}}}s:4:"rows";i:3;s:4:"cols";i:1;s:4:"size";s:4:"100%";s:7:"options";a:1:{i:0;s:4:"100%";}}}','size' => '100%','style' => '','modified' => '1134799202',); + +$templ_data[] = array('name' => 'timesheet.index.rows','template' => '','lang' => '','group' => '0','version' => '0.1.001','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:3:{i:0;a:5:{s:2:"c1";s:2:"th";s:2:"c2";s:3:"row";s:1:"A";s:3:"15%";s:1:"G";s:14:",@no_owner_col";s:1:"B";s:3:"50%";}i:1;a:8:{s:1:"A";a:3:{s:4:"type";s:20:"nextmatch-sortheader";s:5:"label";s:4:"Date";s:4:"name";s:8:"ts_start";}s:1:"B";a:5:{s:4:"type";s:4:"vbox";s:4:"size";s:1:"2";s:7:"no_lang";s:1:"1";i:1;a:4:{s:4:"type";s:22:"nextmatch-filterheader";s:4:"size";s:7:"Project";s:4:"name";s:10:"ts_project";s:7:"no_lang";s:1:"1";}i:2;a:3:{s:4:"type";s:20:"nextmatch-sortheader";s:5:"label";s:5:"Title";s:4:"name";s:8:"ts_title";}}s:1:"C";a:4:{s:4:"type";s:4:"vbox";s:4:"size";s:1:"2";i:1;a:3:{s:4:"type";s:20:"nextmatch-sortheader";s:5:"label";s:8:"Duration";s:4:"name";s:11:"ts_duration";}i:2;a:4:{s:4:"type";s:13:"date-duration";s:4:"name";s:8:"duration";s:4:"size";s:6:",h,,,1";s:8:"readonly";s:1:"1";}}s:1:"D";a:3:{s:4:"type";s:20:"nextmatch-sortheader";s:5:"label";s:8:"Quantity";s:4:"name";s:11:"ts_quantity";}s:1:"E";a:3:{s:4:"type";s:20:"nextmatch-sortheader";s:5:"label";s:5:"Price";s:4:"name";s:12:"ts_unitprice";}s:1:"F";a:4:{s:4:"type";s:4:"vbox";s:4:"size";s:1:"2";i:1;a:3:{s:4:"type";s:20:"nextmatch-sortheader";s:5:"label";s:5:"Total";s:4:"name";s:8:"ts_total";}i:2;a:3:{s:4:"type";s:5:"float";s:4:"name";s:5:"price";s:8:"readonly";s:1:"1";}}s:1:"G";a:4:{s:4:"type";s:22:"nextmatch-filterheader";s:4:"name";s:8:"ts_owner";s:4:"size";s:4:"User";s:7:"no_lang";s:1:"1";}s:1:"H";a:2:{s:4:"type";s:5:"label";s:5:"label";s:7:"Actions";}}i:2;a:8:{s:1:"A";a:4:{s:4:"type";s:9:"date-time";s:4:"name";s:16:"${row}[ts_start]";s:8:"readonly";s:1:"1";s:4:"size";s:2:",8";}s:1:"B";a:5:{s:4:"type";s:4:"vbox";s:4:"size";s:1:"3";i:1;a:3:{s:4:"type";s:4:"link";s:4:"name";s:15:"${row}[ts_link]";s:7:"no_lang";s:1:"1";}i:2;a:4:{s:4:"type";s:5:"label";s:4:"name";s:16:"${row}[ts_title]";s:7:"no_lang";s:1:"1";s:4:"size";s:1:"b";}i:3;a:3:{s:4:"type";s:5:"label";s:4:"name";s:22:"${row}[ts_description]";s:7:"no_lang";s:1:"1";}}s:1:"C";a:4:{s:4:"type";s:13:"date-duration";s:4:"name";s:19:"${row}[ts_duration]";s:8:"readonly";s:1:"1";s:4:"size";s:5:",,,,1";}s:1:"D";a:3:{s:4:"type";s:5:"label";s:4:"name";s:19:"${row}[ts_quantity]";s:7:"no_lang";s:1:"1";}s:1:"E";a:3:{s:4:"type";s:5:"label";s:7:"no_lang";s:1:"1";s:4:"name";s:20:"${row}[ts_unitprice]";}s:1:"F";a:3:{s:4:"type";s:5:"label";s:7:"no_lang";s:1:"1";s:4:"name";s:16:"${row}[ts_total]";}s:1:"G";a:3:{s:4:"type";s:14:"select-account";s:4:"name";s:16:"${row}[ts_owner]";s:8:"readonly";s:1:"1";}s:1:"H";a:5:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"3";i:1;a:6:{s:4:"type";s:6:"button";s:4:"size";s:4:"view";s:5:"label";s:4:"View";s:4:"name";s:22:"view[$row_cont[ts_id]]";s:7:"onclick";s:186:"window.open(egw::link(\'/index.php\',\'menuaction=timesheet.uitimesheet.view&ts_id=$row_cont[ts_id]\'),\'_blank\',\'dependent=yes,width=600,height=400,scrollbars=yes,status=yes\'); return false;";s:4:"help";s:15:"View this entry";}i:2;a:6:{s:4:"type";s:6:"button";s:4:"size";s:4:"edit";s:5:"label";s:4:"Edit";s:4:"name";s:22:"edit[$row_cont[ts_id]]";s:4:"help";s:15:"Edit this entry";s:7:"onclick";s:186:"window.open(egw::link(\'/index.php\',\'menuaction=timesheet.uitimesheet.edit&ts_id=$row_cont[ts_id]\'),\'_blank\',\'dependent=yes,width=600,height=400,scrollbars=yes,status=yes\'); return false;";}i:3;a:6:{s:4:"type";s:6:"button";s:4:"size";s:6:"delete";s:5:"label";s:6:"Delete";s:4:"name";s:24:"delete[$row_cont[ts_id]]";s:4:"help";s:17:"Delete this entry";s:7:"onclick";s:36:"return confirm(\'Delete this entry\');";}}}}s:4:"rows";i:2;s:4:"cols";i:8;s:4:"size";s:4:"100%";s:7:"options";a:1:{i:0;s:4:"100%";}}}','size' => '100%','style' => '','modified' => '1134799629',); + diff --git a/timesheet/setup/phpgw_de.lang b/timesheet/setup/phpgw_de.lang new file mode 100644 index 0000000000..82225f302d --- /dev/null +++ b/timesheet/setup/phpgw_de.lang @@ -0,0 +1,34 @@ +2 month ago timesheet de Vor 2 Monaten +2 years ago timesheet de Vor 2 Jahren +3 years ago timesheet de Vor 3 Jahren +actions timesheet de Aktionen +by timesheet de von +create new links timesheet de Neue Verknüpfung erstellen +delete this entry timesheet de Diesen Eintrag löschen +edit this entry timesheet de diesen Eintrag bearbeiten +empty if identical to duration timesheet de leer lassen wenn gleich Dauer +entry deleted timesheet de Eintrag gelöscht +entry saved timesheet de Eintrag gespeichert +error saving the entry!!! timesheet de Fehler beim Speichern des Eintrags!!! +existing links timesheet de Bestehende Verknüpfungen +general timesheet de Allgemein +last modified timesheet de Zuletzt geändert +last month timesheet de Letzten Monat +last week timesheet de Letzte Woche +last year timesheet de Letztes Jahr +links timesheet de Verknüpfungen +no details timesheet de Keine Details +permission denied!!! timesheet de Zugriff verweigert!!! +price timesheet de Preis +quantity timesheet de Menge +save & new timesheet de Speichern & Neu +saves the changes made timesheet de Speichert die Änderungen +saves this entry and add a new one timesheet de Speichert diesen Eintrag und fügt einen neune hinzu +this month timesheet de Diesen Monat +this week timesheet de Diese Woche +this year timesheet de Diese Jahr +timesheet common de Stundenzettel +unitprice timesheet de Preis pro Einheit +view this entry timesheet de Diesen Eintrag anzeigen +yesterday timesheet de Gestern +your database is not up to date (%1 vs. %2), please run %3setup%4 to update your database. timesheet de Ihre Datenbank ist NICHT aktuell (%1 statt %2), bitte rufen sie %3setup%4 auf um die Datenbank zu aktualisieren. diff --git a/timesheet/setup/phpgw_en.lang b/timesheet/setup/phpgw_en.lang new file mode 100644 index 0000000000..85feb8e828 --- /dev/null +++ b/timesheet/setup/phpgw_en.lang @@ -0,0 +1,34 @@ +2 month ago timesheet en 2 month ago +2 years ago timesheet en 2 years ago +3 years ago timesheet en 3 years ago +actions timesheet en Actions +by timesheet en by +create new links timesheet en Create new links +delete this entry timesheet en Delete this entry +edit this entry timesheet en Edit this entry +empty if identical to duration timesheet en empty if identical to duration +entry deleted timesheet en Entry deleted +entry saved timesheet en Entry saved +error saving the entry!!! timesheet en Error saving the entry!!! +existing links timesheet en Existing links +general timesheet en General +last modified timesheet en Last modified +last month timesheet en Last month +last week timesheet en Last week +last year timesheet en Last year +links timesheet en Links +no details timesheet en no details +permission denied!!! timesheet en Permission denied!!! +price timesheet en Price +quantity timesheet en Quantity +save & new timesheet en Save & New +saves the changes made timesheet en Saves the changes made +saves this entry and add a new one timesheet en Saves this entry and add a new one +this month timesheet en This month +this week timesheet en This week +this year timesheet en This year +timesheet common en TimeSheet +unitprice timesheet en Unitprice +view this entry timesheet en View this entry +yesterday timesheet en Yesterday +your database is not up to date (%1 vs. %2), please run %3setup%4 to update your database. timesheet en Your database is NOT up to date (%1 vs. %2), please run %3setup%4 to update your database. diff --git a/timesheet/setup/setup.inc.php b/timesheet/setup/setup.inc.php new file mode 100644 index 0000000000..a903276c1b --- /dev/null +++ b/timesheet/setup/setup.inc.php @@ -0,0 +1,55 @@ + * +* -------------------------------------------- * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU General Public License as published by the * +* Free Software Foundation; either version 2 of the License, or (at your * +* option) any later version. * +\**************************************************************************/ + +/* $Id$ */ + +if (!defined('TIMESHEET_APP')) +{ + define('TIMESHEET_APP','timesheet'); +} + +$setup_info[TIMESHEET_APP]['name'] = TIMESHEET_APP; +$setup_info[TIMESHEET_APP]['version'] = '0.1.001'; +$setup_info[TIMESHEET_APP]['app_order'] = 5; +$setup_info[TIMESHEET_APP]['tables'] = array('egw_timesheet'); +$setup_info[TIMESHEET_APP]['enable'] = 1; + +$setup_info[TIMESHEET_APP]['author'] = +$setup_info[TIMESHEET_APP]['maintainer'] = array( + 'name' => 'Ralf Becker', + 'email' => 'RalfBecker@outdoor-training.de' +); +$setup_info[TIMESHEET_APP]['license'] = 'GPL'; +$setup_info[TIMESHEET_APP]['description'] = +'Tracking times and other activities for the Projectmanager.'; +$setup_info[TIMESHEET_APP]['note'] = +'The TimeSheet application is sponsored by:'; + +/* The hooks this app includes, needed for hooks registration */ +$setup_info[TIMESHEET_APP]['hooks']['preferences'] = TIMESHEET_APP.'.ts_admin_prefs_sidebox_hooks.all_hooks'; +$setup_info[TIMESHEET_APP]['hooks']['settings'] = TIMESHEET_APP.'.ts_admin_prefs_sidebox_hooks.settings'; +$setup_info[TIMESHEET_APP]['hooks']['admin'] = TIMESHEET_APP.'.ts_admin_prefs_sidebox_hooks.all_hooks'; +$setup_info[TIMESHEET_APP]['hooks']['sidebox_menu'] = TIMESHEET_APP.'.ts_admin_prefs_sidebox_hooks.all_hooks'; +$setup_info[TIMESHEET_APP]['hooks']['search_link'] = TIMESHEET_APP.'.botimesheet.search_link'; + +/* Dependencies for this app to work */ +$setup_info[TIMESHEET_APP]['depends'][] = array( + 'appname' => 'phpgwapi', + 'versions' => Array('1.2','1.3') +); +$setup_info[TIMESHEET_APP]['depends'][] = array( + 'appname' => 'etemplate', + 'versions' => Array('1.2','1.3') +); diff --git a/timesheet/setup/tables_current.inc.php b/timesheet/setup/tables_current.inc.php new file mode 100644 index 0000000000..0b336419cc --- /dev/null +++ b/timesheet/setup/tables_current.inc.php @@ -0,0 +1,37 @@ + array( + 'fd' => array( + 'ts_id' => array('type' => 'auto','nullable' => False), + 'ts_project' => array('type' => 'varchar','precision' => '80'), + 'ts_title' => array('type' => 'varchar','precision' => '80','nullable' => False), + 'ts_description' => array('type' => 'text'), + 'ts_start' => array('type' => 'int','precision' => '8','nullable' => False), + 'ts_duration' => array('type' => 'int','precision' => '8','nullable' => False,'default' => '0'), + 'ts_quantity' => array('type' => 'float','precision' => '8','nullable' => False), + 'ts_unitprice' => array('type' => 'float','precision' => '4'), + 'cat_id' => array('type' => 'int','precision' => '4','default' => '0'), + 'ts_owner' => array('type' => 'int','precision' => '4','nullable' => False), + 'ts_modified' => array('type' => 'int','precision' => '8','nullable' => False), + 'ts_modifier' => array('type' => 'int','precision' => '4','nullable' => False) + ), + 'pk' => array('ts_id'), + 'fk' => array(), + 'ix' => array('ts_project','ts_owner'), + 'uc' => array() + ) + ); diff --git a/timesheet/templates/default/app.css b/timesheet/templates/default/app.css new file mode 100644 index 0000000000..8e37c8cbbe --- /dev/null +++ b/timesheet/templates/default/app.css @@ -0,0 +1,2 @@ +/* $Id$ */ +.redItalic { color: red; font-style: italics; } \ No newline at end of file diff --git a/timesheet/templates/default/edit.xet b/timesheet/templates/default/edit.xet new file mode 100644 index 0000000000..d2fbbccec1 --- /dev/null +++ b/timesheet/templates/default/edit.xet @@ -0,0 +1,134 @@ + + + + + + +