* Update to 1.8.004: REQUIRES TO VISIT SETUP for schema updates

- backport of security features from Trunk: support for sha512_crypt password and session-list without access to session-directory
- backport of numerous CalDAV/CardDAV features and fixes from Trunk: multiple addressbooks and calendars, support of resources, request logging
This commit is contained in:
Ralf Becker 2012-03-31 14:12:25 +00:00
commit 41fd3575c9
126 changed files with 22971 additions and 3182 deletions

View File

@ -7,7 +7,7 @@
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @author Joerg Lehrke <jlehrke@noc.de>
* @package addressbook
* @copyright (c) 2005-8 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2005-12 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2005/6 by Cornelius Weiss <egw@von-und-zu-weiss.de>
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
@ -36,6 +36,9 @@ class addressbook_bo extends addressbook_so
'org_name: n_family, n_prefix',
'org_name: n_given n_family',
'org_name: n_fn',
'org_name, org_unit: n_family, n_given',
'org_name, adr_one_locality: n_family, n_given',
'org_name, org_unit, adr_one_locality: n_family, n_given',
'n_family, n_given: org_name',
'n_family, n_given (org_name)',
'n_family, n_prefix: org_name',
@ -63,6 +66,7 @@ class addressbook_bo extends addressbook_so
'adr_one_region',
'adr_one_postalcode',
'adr_one_countryname',
'adr_one_countrycode',
'label',
'tel_work',
'tel_fax',
@ -121,6 +125,12 @@ class addressbook_bo extends addressbook_so
* @var boolean
*/
var $default_private;
/**
* Use a separate private addressbook (former private flag), for contacts not shareable via regular read acl
*
* @var boolean
*/
var $private_addressbook = false;
/**
* Categories object
*
@ -128,9 +138,31 @@ class addressbook_bo extends addressbook_so
*/
var $categories;
function __construct($contact_app='addressbook')
/**
* Tracking changes
*
* @var object
*/
protected $tracking;
/**
* Keep deleted addresses, or really delete them
* Set in Admin -> Addressbook -> Site Configuration
* ''=really delete, 'history'=keep, only admins delete, 'userpurge'=keep, users delete
*
* @var string
*/
protected $delete_history = '';
/**
* Constructor
*
* @param string $contact_app='addressbook' used for acl->get_grants()
* @param egw_db $db=null
*/
function __construct($contact_app='addressbook',egw_db $db=null)
{
parent::__construct($contact_app);
parent::__construct($contact_app,$db);
if ($this->log)
{
$this->logfile = $GLOBALS['egw_info']['server']['temp_dir'].'/log-addressbook_bo';
@ -151,6 +183,7 @@ class addressbook_bo extends addressbook_so
{
$this->default_addressbook = $this->user; // admin set a default or forced pref for personal addressbook
}
$this->private_addressbook = $this->contact_repository == 'sql' && $this->prefs['private_addressbook'];
$this->contact_fields = array(
'id' => lang('Contact ID'),
@ -178,6 +211,7 @@ class addressbook_bo extends addressbook_so
'adr_one_region' => lang('state').' ('.lang('business').')',
'adr_one_postalcode' => lang('zip code').' ('.lang('business').')',
'adr_one_countryname' => lang('country').' ('.lang('business').')',
'adr_one_countrycode' => lang('country code').' ('.lang('business').')',
'label' => lang('label'),
'adr_two_street' => lang('street').' ('.lang('private').')',
'adr_two_street2' => lang('address line 2').' ('.lang('private').')',
@ -185,6 +219,7 @@ class addressbook_bo extends addressbook_so
'adr_two_region' => lang('state').' ('.lang('private').')',
'adr_two_postalcode' => lang('zip code').' ('.lang('private').')',
'adr_two_countryname' => lang('country').' ('.lang('private').')',
'adr_two_countrycode' => lang('country code').' ('.lang('private').')',
'tel_work' => lang('work phone'),
'tel_cell' => lang('mobile phone'),
'tel_fax' => lang('fax').' ('.lang('business').')',
@ -257,8 +292,24 @@ class addressbook_bo extends addressbook_so
if ($GLOBALS['egw_info']['server']['org_fileds_to_update'])
{
$this->org_fields = unserialize($GLOBALS['egw_info']['server']['org_fileds_to_update']);
// Set country code if country name is selected
$supported_fields = $this->get_fields('supported',null,0);
if(in_array('adr_one_countrycode', $supported_fields) && in_array('adr_one_countryname',$this->org_fields))
{
$this->org_fields[] = 'adr_one_countrycode';
}
if(in_array('adr_two_countrycode', $supported_fields) && in_array('adr_two_countryname',$this->org_fields))
{
$this->org_fields[] = 'adr_two_countrycode';
}
}
$this->categories = new categories($this->user,'addressbook');
$this->tracking = new addressbook_tracking($this);
$config = config::read('phpgwapi');
$this->delete_history = $config['history'];
}
/**
@ -323,15 +374,31 @@ class addressbook_bo extends addressbook_so
*
* @param array $contact
* @param string $type=null file_as type, default null to read it from the contact, unknown/not set type default to the first one
* @param boolean $update=false If true, reads the old record for any not set fields
* @return string
*/
function fileas($contact,$type=null)
function fileas($contact,$type=null, $isUpdate=false)
{
if (is_null($type)) $type = $contact['fileas_type'];
if (!$type) $type = $this->prefs['fileas_default'] ? $this->prefs['fileas_default'] : $this->fileas_types[0];
if (strpos($type,'n_fn') !== false) $contact['n_fn'] = $this->fullname($contact);
if($isUpdate)
{
$fileas_fields = array('n_prefix','n_given','n_middle','n_family','n_suffix','n_fn','org_name','org_unit','adr_one_locality');
$old = null;
foreach($fileas_fields as $field)
{
if(!isset($contact[$field]))
{
if(is_null($old)) $old = $this->read($contact['id']);
$contact[$field] = $old[$field];
}
}
unset($old);
}
$fileas = str_replace(array('n_prefix','n_given','n_middle','n_family','n_suffix','n_fn','org_name','org_unit','adr_one_locality'),
array($contact['n_prefix'],$contact['n_given'],$contact['n_middle'],$contact['n_family'],$contact['n_suffix'],
$contact['n_fn'],$contact['org_name'],$contact['org_unit'],$contact['adr_one_locality']),$type);
@ -480,8 +547,8 @@ class addressbook_bo extends addressbook_so
// fields that must not be touched
$fields_exclude = array(
'id' => true,
'tid' => true,
'id' => true,
'tid' => true,
'owner' => true,
'private' => true,
'created' => true,
@ -490,9 +557,9 @@ class addressbook_bo extends addressbook_so
'modifier' => true,
'account_id' => true,
'etag' => true,
'uid' => true,
'uid' => true,
'freebusy_uri' => true,
'calendar_uri' => true,
'calendar_uri' => true,
'photo' => true,
);
@ -589,6 +656,8 @@ class addressbook_bo extends addressbook_so
*/
function db2data($data, $date_format='ts')
{
static $fb_url = false;
// convert timestamps from server-time in the db to user-time
foreach ($this->timestamps as $name)
{
@ -602,10 +671,12 @@ class addressbook_bo extends addressbook_so
// set freebusy_uri for accounts
if (!$data['freebusy_uri'] && !$data['owner'] && $data['account_id'] && !is_object($GLOBALS['egw_setup']))
{
static $fb_url;
if (!$fb_url && @is_dir(EGW_SERVER_ROOT.'/calendar/inc')) $fb_url = calendar_bo::freebusy_url('');
if ($fb_url) $data['freebusy_uri'] = $fb_url.urlencode(
isset($data['account_lid']) ? $data['account_lid'] : $GLOBALS['egw']->accounts->id2name($data['account_id']));
if ($fb_url || @is_dir(EGW_SERVER_ROOT.'/calendar/inc'))
{
$fb_url = true;
$user = isset($data['account_lid']) ? $data['account_lid'] : $GLOBALS['egw']->accounts->id2name($data['account_id']);
$data['freebusy_uri'] = calendar_bo::freebusy_url($user);
}
}
return $data;
}
@ -672,17 +743,38 @@ class addressbook_bo extends addressbook_so
$id = is_array($c) ? $c['id'] : $c;
$ok = false;
if ($this->check_perms(EGW_ACL_DELETE,$c,$deny_account_delete) && ($ok = parent::delete($id,$check_etag)))
if ($this->check_perms(EGW_ACL_DELETE,$c,$deny_account_delete))
{
egw_link::unlink(0,'addressbook',$id);
$GLOBALS['egw']->contenthistory->updateTimeStamp('contacts', $id, 'delete', time());
if (!($old = $this->read($id))) return false;
// check if we only mark contacts as deleted, or really delete them
// already marked as deleted item and accounts are always really deleted
// we cant mark accounts as deleted, as no such thing exists for accounts!
if ($old['owner'] && $this->delete_history != '' && $old['tid'] != addressbook_so::DELETED_TYPE)
{
$delete = $old;
$delete['tid'] = addressbook_so::DELETED_TYPE;
$ok = $this->save($delete);
egw_link::unlink(0,'addressbook',$id,'','','',true);
}
elseif (($ok = parent::delete($id,$check_etag)))
{
egw_link::unlink(0,'addressbook',$id);
}
// Don't notify of final purge
if ($ok && $old['tid'] != addressbook_so::DELETED_TYPE)
{
$GLOBALS['egw']->contenthistory->updateTimeStamp('contacts', $id, 'delete', time());
$this->tracking->track(array('id' => $id), array('id' => $id), null, true);
}
}
else
{
return $ok;
break;
}
}
return true;
//error_log(__METHOD__.'('.array2string($contact).', deny_account_delete='.array2string($deny_account_delete).', check_etag='.array2string($check_etag).' returning '.array2string($ok));
return $ok;
}
/**
@ -747,6 +839,27 @@ class addressbook_bo extends addressbook_so
{
$contact['cat_id'] = implode(',',$contact['cat_id']);
}
// Update country codes
foreach(array('adr_one_', 'adr_two_') as $c_prefix) {
if($contact[$c_prefix.'countryname'] && !$contact[$c_prefix.'countrycode'] &&
$code = $GLOBALS['egw']->country->country_code($contact[$c_prefix.'countryname']))
{
if(strlen($code) == 2)
{
$contact[$c_prefix.'countrycode'] = $code;
}
else
{
$contact[$c_prefix.'countrycode'] = null;
}
}
if($contact[$c_prefix.'countrycode'] != null)
{
$contact[$c_prefix.'countryname'] = null;
}
}
// last modified
$contact['modifier'] = $this->user;
$contact['modified'] = $this->now_su;
@ -754,8 +867,9 @@ class addressbook_bo extends addressbook_so
if (!isset($contact['n_fn']))
{
$contact['n_fn'] = $this->fullname($contact);
if (isset($contact['org_name'])) $contact['n_fileas'] = $this->fileas($contact);
}
if (isset($contact['org_name'])) $contact['n_fileas'] = $this->fileas($contact, null, false);
$to_write = $contact;
// (non-admin) user editing his own account, make sure he does not change fields he is not allowed to (eg. via SyncML or xmlrpc)
if (!$ignore_acl && !$contact['owner'] && !$this->is_admin($contact))
@ -768,6 +882,22 @@ class addressbook_bo extends addressbook_so
}
}
}
// Get old record for tracking changes
if (!isset($old) && $isUpdate)
{
$old = $this->read($contact['id']);
}
// IF THE OLD ENTRY IS A ACCOUNT, dont allow to change the owner/location
// maybe we need that for id and account_id as well.
if (is_array($old) && (!isset($old['owner']) || empty($old['owner'])))
{
if (isset($to_write['owner']) && !empty($to_write['owner']))
{
error_log(__METHOD__.__LINE__." Trying to change account to owner:". $to_write['owner'].' Account affected:'.array2string($old).' Data send:'.array2string($to_write));
unset($to_write['owner']);
}
}
// we dont update the content-history, if we run inside setup (admin-account-creation)
if(!($this->error = parent::save($to_write)) && is_object($GLOBALS['egw']->contenthistory))
{
@ -788,6 +918,18 @@ class addressbook_bo extends addressbook_so
}
// Notify linked apps about changes in the contact data
egw_link::notify_update('addressbook', $contact['id'], $contact);
// Check for restore of deleted contact, restore held links
if($old && $old['tid'] == addressbook_so::DELETED_TYPE && $contact['tid'] != addressbook_so::DELETED_TYPE)
{
egw_link::restore('addressbook', $contact['id']);
}
// Record change history for sql - doesn't work for LDAP accounts
if(!$contact['account_id'] || $contact['account_id'] && $this->account_repository == 'sql') {
$deleted = ($old['tid'] == addressbook_so::DELETED_TYPE || $contact['tid'] == addressbook_so::DELETED_TYPE);
$this->tracking->track($to_write, $old ? $old : null, null, $deleted);
}
}
return $this->error ? false : $contact['id'];
@ -848,31 +990,55 @@ class addressbook_bo extends addressbook_so
{
if (!($data = parent::read($contact_id)))
{
return null; // not found
$data = null; // not found
}
if (!$this->check_perms(EGW_ACL_READ,$data))
elseif (!$this->check_perms(EGW_ACL_READ,$data))
{
return false; // no view perms
$data = false; // no view perms
}
// determine the file-as type
$data['fileas_type'] = $this->fileas_type($data);
else
{
// determine the file-as type
$data['fileas_type'] = $this->fileas_type($data);
// Update country name from code
if($data['adr_one_countrycode'] != null) {
$data['adr_one_countryname'] = $GLOBALS['egw']->country->get_full_name($data['adr_one_countrycode'], true);
}
if($data['adr_two_countrycode'] != null) {
$data['adr_two_countryname'] = $GLOBALS['egw']->country->get_full_name($data['adr_two_countrycode'], true);
}
}
//error_log(__METHOD__.'('.array2string($contact_id).') returning '.array2string($data));
return $data;
}
/**
* Checks if the current user has the necessary ACL rights
*
* If the access of a contact is set to private, one need a private grant for a personal addressbook
* or the group membership for a group-addressbook
*
* @param int $needed necessary ACL right: EGW_ACL_{READ|EDIT|DELETE}
* @param mixed $contact contact as array or the contact-id
* @param boolean $deny_account_delete=false if true never allow to delete accounts
* @return boolean true permission granted, false for permission denied, null for contact does not exist
*/
function check_perms($needed,$contact,$deny_account_delete=false)
* Checks if the current user has the necessary ACL rights
*
* If the access of a contact is set to private, one need a private grant for a personal addressbook
* or the group membership for a group-addressbook
*
* @param int $needed necessary ACL right: EGW_ACL_{READ|EDIT|DELETE}
* @param mixed $contact contact as array or the contact-id
* @param boolean $deny_account_delete=false if true never allow to delete accounts
* @param int $user=null for which user to check, default current user
* @return boolean true permission granted, false for permission denied, null for contact does not exist
*/
function check_perms($needed,$contact,$deny_account_delete=false,$user=null)
{
if (!$user) $user = $this->user;
if ($user == $this->user)
{
$grants = $this->grants;
$memberships = $this->memberships;
}
else
{
$grants = $this->get_grants($user);
$memberships = $GLOBALS['egw']->accounts->memberships($user,true);
}
if ((!is_array($contact) || !isset($contact['owner'])) &&
!($contact = parent::read(is_array($contact) ? $contact['id'] : $contact)))
{
@ -881,25 +1047,43 @@ class addressbook_bo extends addressbook_so
$owner = $contact['owner'];
// allow the user to edit his own account
if (!$owner && $needed == EGW_ACL_EDIT && $contact['account_id'] == $this->user && $this->own_account_acl)
if (!$owner && $needed == EGW_ACL_EDIT && $contact['account_id'] == $user && $this->own_account_acl)
{
return true;
$access = true;
}
// dont allow to delete own account (as admin handels it too)
if (!$owner && $needed == EGW_ACL_DELETE && ($deny_account_delete || $contact['account_id'] == $this->user))
elseif (!$owner && $needed == EGW_ACL_DELETE && ($deny_account_delete || $contact['account_id'] == $user))
{
return false;
$access = false;
}
// for reading accounts (owner == 0) and account_selection == groupmembers, check if current user and contact are groupmembers
if ($owner == 0 && $needed == EGW_ACL_READ &&
elseif ($owner == 0 && $needed == EGW_ACL_READ &&
$GLOBALS['egw_info']['user']['preferences']['common']['account_selection'] == 'groupmembers' &&
!isset($GLOBALS['egw_info']['user']['apps']['admin']))
{
return !!array_intersect($GLOBALS['egw']->accounts->memberships($this->user,true),
$GLOBALS['egw']->accounts->memberships($contact['account_id'],true));
$access = !!array_intersect($memberships,$GLOBALS['egw']->accounts->memberships($contact['account_id'],true));
}
return ($this->grants[$owner] & $needed) &&
(!$contact['private'] || ($this->grants[$owner] & EGW_ACL_PRIVATE) || in_array($owner,$this->memberships));
else
{
$access = ($grants[$owner] & $needed) &&
(!$contact['private'] || ($grants[$owner] & EGW_ACL_PRIVATE) || in_array($owner,$memberships));
}
//error_log(__METHOD__."($needed,$contact[id],$deny_account_delete,$user) returning ".array2string($access));
return $access;
}
/**
* Check access to the file store
*
* @param int|array $id id of entry or entry array
* @param int $check EGW_ACL_READ for read and EGW_ACL_EDIT for write or delete access
* @param string $rel_path=null currently not used in InfoLog
* @param int $user=null for which user to check, default current user
* @return boolean true if access is granted or false otherwise
*/
function file_access($id,$check,$rel_path=null,$user=null)
{
return $this->check_perms($check,$id,false,$user);
}
/**
@ -1186,23 +1370,29 @@ class addressbook_bo extends addressbook_so
* Is called as hook to participate in the linking
*
* @param string|array $pattern pattern to search, or an array with a 'search' key
* @param array $options Array of options for the search
* @return array with id - title pairs of the matching entries
*/
function link_query($pattern)
function link_query($pattern, Array &$options = array())
{
$result = $criteria = array();
$filter = $result = $criteria = array();
$limit = false;
if ($pattern)
{
foreach($this->columns_to_search as $col)
{
$criteria[$col] = is_array($pattern) ? $pattern['search'] : $pattern;
}
$criteria = is_array($pattern) ? $pattern['search'] : $pattern;
}
if (($contacts = parent::search($criteria,false,'org_name,n_family,n_given,cat_id','','%',false,'OR')))
if($options['start'] || $options['num_rows'])
{
$limit = array($options['start'], $options['num_rows']);
}
$filter = (array)$options['filter'];
if ($GLOBALS['egw_info']['user']['preferences']['addressbook']['hide_accounts']) $filter['account_id'] = null;
if (($contacts =& parent::search($criteria,false,'org_name,n_family,n_given,cat_id,contact_email','','%',false,'OR', $limit, $filter)))
{
foreach($contacts as $contact)
{
$result[$contact['id']] = $this->link_title($contact);
$result[$contact['id']] = $this->link_title($contact).
($options['type'] === 'email' ? ' <'.$contact['email'].'>' : '');
// show category color
if ($contact['cat_id'] && ($color = etemplate::cats2color($contact['cat_id'])))
{
@ -1213,19 +1403,30 @@ class addressbook_bo extends addressbook_so
}
}
}
$options['total'] = $this->total;
return $result;
}
/**
* Check access to the projects file store
* Query for subtype email (returns only contacts with email address set)
*
* @param int $id id of entry
* @param int $check EGW_ACL_READ for read and EGW_ACL_EDIT for write or delete access
* @return boolean true if access is granted or false otherwise
* @param string|array $pattern
* @param array $options
* @return Ambigous <multitype:, string, multitype:Ambigous <multitype:, string> string >
*/
function file_access($id,$check,$rel_path)
function link_query_email($pattern, Array &$options = array())
{
return $this->check_perms($check,$id);
if (isset($options['filter']) && !is_array($options['filter']))
{
$options['filter'] = (array)$options['filter'];
}
// return only contacts with email set
$options['filter'][] = "contact_email LIKE '%@%'";
// let link query know, to append email to list
$options['type'] = 'email';
return $this->link_query($pattern,$options);
}
/**
@ -1301,6 +1502,9 @@ class addressbook_bo extends addressbook_so
'id' => $event['id'],
'app' => 'calendar',
'title' => $bocal->link_title($event),
'extra_args' => array(
'date' => date('Ymd',$event['start']),
),
);
if ($extra_title)
{
@ -1319,6 +1523,9 @@ class addressbook_bo extends addressbook_so
'id' => $event['id'],
'app' => 'calendar',
'title' => $bocal->link_title($event),
'extra_args' => array(
'date' => date('Ymd',$event['start']),
),
);
if ($extra_title)
{
@ -1330,7 +1537,6 @@ class addressbook_bo extends addressbook_so
}
}
}
//_debug_array($calendars);
return $calendars;
}
@ -1404,6 +1610,7 @@ class addressbook_bo extends addressbook_so
function merge($ids)
{
$this->error = false;
$account = null;
$custom_fields = config::get_customfields('addressbook', true);
$custom_field_list = $this->read_customfields($ids);
foreach(parent::search(array('id'=>$ids),false) as $contact) // $this->search calls the extended search from ui!
@ -1419,9 +1626,7 @@ class addressbook_bo extends addressbook_so
continue;
}
// Add in custom fields
foreach($custom_field_list[$contact['id']] as $field => $value) {
$contact['#'.$field] = $value;
}
if (is_array($custom_field_list[$contact['id']])) $contact = array_merge($contact, $custom_field_list[$contact['id']]);
$pos = array_search($contact['id'],$ids);
$contacts[$pos] = $contact;
@ -1490,10 +1695,10 @@ class addressbook_bo extends addressbook_so
// info_from and info_link_id (main link)
$newlinkID = egw_link::link('addressbook',$target['id'],$data['app'],$data['id'],$data['remark'],$target['owner']);
//_debug_array(array('newLinkID'=>$newlinkID));
if ($newlinkID)
if ($newlinkID)
{
// update egw_infolog set info_link_id=$newlinkID where info_id=$data['id'] and info_link_id=$data['link_id']
if ($data['app']=='infolog')
if ($data['app']=='infolog')
{
$this->db->update('egw_infolog',array(
'info_link_id' => $newlinkID
@ -1524,42 +1729,45 @@ class addressbook_bo extends addressbook_so
{
$owner = $list_data['list_owner'];
}
//error_log(__METHOD__."($list, $required, $owner) grants[$owner]=".$this->grants[$owner]." returning ".array2string(!!($this->grants[$owner] & $required)));
return !!($this->grants[$owner] & $required);
}
/**
* Adds a distribution list
* Adds / updates a distribution list
*
* @param string $name list-name
* @param string|array $keys list-name or array with column-name => value pairs to specify the list
* @param int $owner user- or group-id
* @param array $contacts=array() contacts to add
* @return list_id or false on error
* @param array $contacts=array() contacts to add (only for not yet existing lists!)
* @param array &$data=array() values for keys 'list_uid', 'list_carddav_name', 'list_name'
* @return int|boolean integer list_id or false on error
*/
function add_list($name,$owner,$contacts=array())
function add_list($keys,$owner,$contacts=array(),array &$data=array())
{
if (!$this->check_list(null,EGW_ACL_ADD,$owner)) return false;
if (!$this->check_list(null,EGW_ACL_ADD|EGW_ACL_EDIT,$owner)) return false;
return parent::add_list($name,$owner,$contacts);
return parent::add_list($keys,$owner,$contacts,$data);
}
/**
* Adds one contact to a distribution list
* Adds contacts to a distribution list
*
* @param int $contact contact_id
* @param int|array $contact contact_id(s)
* @param int $list list-id
* @param array $existing=null array of existing contact-id(s) of list, to not reread it, eg. array()
* @return false on error
*/
function add2list($contact,$list)
function add2list($contact,$list,array $existing=null)
{
if (!$this->check_list($list,EGW_ACL_EDIT)) return false;
return parent::add2list($contact,$list);
return parent::add2list($contact,$list,$existing);
}
/**
* Removes one contact from distribution list(s)
*
* @param int $contact contact_id
* @param int|array $contact contact_id(s)
* @param int $list list-id
* @return false on error
*/
@ -1680,17 +1888,16 @@ class addressbook_bo extends addressbook_so
*/
function find_or_add_categories($catname_list, $contact_id=null)
{
if($contact_id && $contact_id > 0)
if ($contact_id && $contact_id > 0 && ($old_contact = $this->read($contact_id)))
{
// preserve categories without users read access
$old_contact = $this->read($contact_id);
$old_categories = explode(',',$old_contact['cat_id']);
$old_cats_preserve = array();
if(is_array($old_categories) && count($old_categories) > 0)
if (is_array($old_categories) && count($old_categories) > 0)
{
foreach($old_categories as $cat_id)
foreach ($old_categories as $cat_id)
{
if(!$this->categories->check_perms(EGW_ACL_READ, $cat_id))
if (!$this->categories->check_perms(EGW_ACL_READ, $cat_id))
{
$old_cats_preserve[] = $cat_id;
}
@ -1699,11 +1906,10 @@ class addressbook_bo extends addressbook_so
}
$cat_id_list = array();
foreach($catname_list as $cat_name)
foreach ((array)$catname_list as $cat_name)
{
$cat_name = trim($cat_name);
$cat_id = $this->categories->name2id($cat_name, 'X-');
if (!$cat_id)
{
// some SyncML clients (mostly phones) add an X- to the category names
@ -1720,7 +1926,7 @@ class addressbook_bo extends addressbook_so
}
}
if(is_array($old_cats_preserve) && count($old_cats_preserve) > 0)
if (is_array($old_cats_preserve) && count($old_cats_preserve) > 0)
{
$cat_id_list = array_merge($cat_id_list, $old_cats_preserve);
}
@ -1731,6 +1937,7 @@ class addressbook_bo extends addressbook_so
sort($cat_id_list, SORT_NUMERIC);
}
//error_log(__METHOD__."(".array2string($catname_list).", $contact_id) returning ".array2string($cat_id_list));
return $cat_id_list;
}
@ -2003,4 +2210,40 @@ class addressbook_bo extends addressbook_so
}
return $matchingContacts;
}
/**
* Get a ctag (collection tag) for one addressbook or all addressbooks readable by a user
*
* Currently implemented as maximum modification date (1 seconde granularity!)
*
* We have to include deleted entries, as otherwise the ctag will not change if an entry gets deleted!
* (Only works if tracking of deleted entries / history is switched on!)
*
* @param int|array $owner=null 0=accounts, null=all addressbooks or integer account_id of user or group
* @return string
*/
public function get_ctag($owner=null)
{
$filter = array('tid' => null); // tid=null --> use all entries incl. deleted (tid='D')
// show addressbook of a single user?
if (!is_null($owner)) $filter['owner'] = $owner;
// should we hide the accounts addressbook
if (!$owner && $GLOBALS['egw_info']['user']['preferences']['addressbook']['hide_accounts'])
{
$filter['account_id'] = null;
}
$result = $this->search(array(),'contact_modified','contact_modified DESC','','',false,'AND',array(0,1),$filter);
if (!$result || !isset($result[0]['modified']))
{
$ctag = 'empty'; // ctag for empty addressbook
}
else
{
$ctag = $result[0]['modified'];
}
//error_log(__METHOD__.'('.array2string($owner).') returning '.array2string($ctag));
return $ctag;
}
}

View File

@ -7,7 +7,7 @@
* @package addressbook
* @subpackage groupdav
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2007-9 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2007-12 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @version $Id$
*/
@ -16,6 +16,11 @@
*
* Propfind now uses a groupdav_propfind_iterator with a callback to query huge addressbooks in chunk,
* without getting into problems with memory_limit.
*
* Permanent error_log() calls should use $this->groupdav->log($str) instead, to be send to PHP error_log()
* and our request-log (prefixed with "### " after request and response, like exceptions).
*
* @todo check/fix contacts in LDAP (no carddav_name column!)
*/
class addressbook_groupdav extends groupdav_handler
{
@ -31,6 +36,7 @@ class addressbook_groupdav extends groupdav_handler
//'NICKNAME',
'EMAIL' => 'email',
'FN' => 'n_fn',
'ORG' => 'org_name',
);
/**
@ -41,34 +47,54 @@ class addressbook_groupdav extends groupdav_handler
var $charset = 'utf-8';
/**
* What attribute is used to construct the path, default id, can be uid too
* 'addressbook_home_set' preference already exploded as array
*
* A = all available addressbooks
* G = primary group
* D = distribution lists as groups
* O = sync all in one (/<username>/addressbook/)
* or nummerical account_id, but not user itself
*
* @var array
*/
const PATH_ATTRIBUTE = 'id';
var $home_set_pref;
/**
* Constructor
*
* @param string $app 'calendar', 'addressbook' or 'infolog'
* @param int $debug=null debug-level to set
* @param string $base_uri=null base url of handler
* @param string $principalURL=null pricipal url of handler
* @param groupdav $groupdav calling class
*/
function __construct($app,$debug=null,$base_uri=null,$principalURL=null)
function __construct($app, groupdav $groupdav)
{
parent::__construct($app,$debug,$base_uri,$principalURL);
parent::__construct($app, $groupdav);
$this->bo = new addressbook_bo();
}
/**
* Create the path for a contact
*
* @param array $contact
* @return string
*/
static function get_path($contact)
{
return $contact[self::PATH_ATTRIBUTE].'.vcf';
// since 1.9.007 we allow clients to specify the URL when creating a new contact, as specified by CardDAV
// LDAP does NOT have a carddav_name attribute --> stick with id mapped to LDAP attribute uid
if (version_compare($GLOBALS['egw_info']['apps']['phpgwapi']['version'], '1.9.007', '<') ||
$this->bo->contact_repository == 'ldap' ||
$this->bo->account_repository == 'ldap' && strpos($_SERVER['REQUEST_URI'].'/','/addressbook-accounts/') !== false)
{
groupdav_handler::$path_extension = '.vcf';
}
else
{
groupdav_handler::$path_attr = 'carddav_name';
groupdav_handler::$path_extension = '';
}
if ($this->debug) error_log(__METHOD__."() contact_repository={$this->bo->contact_repository}, account_repository={$this->bo->account_repository}, REQUEST_URI=$_SERVER[REQUEST_URI] --> path_attr=".self::$path_attr.", path_extension=".self::$path_extension);
$this->home_set_pref = $GLOBALS['egw_info']['user']['preferences']['groupdav']['addressbook-home-set'];
$this->home_set_pref = $this->home_set_pref ? explode(',',$this->home_set_pref) : array();
// silently switch "Sync all into one" preference on for OS X addressbook, as it only supports one AB
// this restores behavior before Lion (10.7), where AB synced all ABs contained in addressbook-home-set
if (substr(self::get_agent(),0,9) == 'cfnetwork' && !in_array('O',$this->home_set_pref))
{
$this->home_set_pref[] = 'O';
}
}
/**
@ -84,16 +110,27 @@ class addressbook_groupdav extends groupdav_handler
function propfind($path,$options,&$files,$user,$id='')
{
$filter = array();
// If "Sync selected addressbooks into one" is set
if ($user && $user == $GLOBALS['egw_info']['user']['account_id'] && in_array('O',$this->home_set_pref))
{
$filter['owner'] = array_keys($this->get_shared(true)); // true: ignore all-in-one pref
$filter['owner'][] = $user;
}
// show addressbook of a single user?
if ($user && $path != '/addressbook/') $filter['contact_owner'] = $user;
elseif ($user && $path != '/addressbook/' || $user === 0)
{
$filter['owner'] = $user;
}
// should we hide the accounts addressbook
if ($GLOBALS['egw_info']['user']['preferences']['addressbook']['hide_accounts']) $filter['account_id'] = null;
// process REPORT filters or multiget href's
if (($id || $options['root']['name'] != 'propfind') && !$this->_report_filters($options,$filter,$id))
if (($id || $options['root']['name'] != 'propfind') && !$this->_report_filters($options,$filter,$id, $nresults))
{
return false;
}
if ($id) $path = dirname($path).'/'; // carddav_name get's added anyway in the callback
if ($this->debug) error_log(__METHOD__."($path,".array2string($options).",,$user,$id) filter=".array2string($filter));
// check if we have to return the full contact data or just the etag's
@ -109,8 +146,15 @@ class addressbook_groupdav extends groupdav_handler
}
}
}
// return iterator, calling ourself to return result in chunks
$files['files'] = new groupdav_propfind_iterator($this,$path,$filter,$files['files']);
if (isset($nresults))
{
$files['files'] = $this->propfind_callback($path, $filter, array(0, (int)$nresults));
}
else
{
// return iterator, calling ourself to return result in chunks
$files['files'] = new groupdav_propfind_iterator($this,$path,$filter,$files['files']);
}
return true;
}
@ -131,32 +175,74 @@ class addressbook_groupdav extends groupdav_handler
$handler = self::_get_handler();
}
unset($filter['address_data']);
if (isset($filter['order']))
{
$order = $filter['order'];
unset($filter['order']);
}
else
{
$order = 'egw_addressbook.contact_id';
}
$files = array();
// we query etag and modified, as LDAP does not have the strong sql etag
if (($contacts =& $this->bo->search(array(),array('id','uid','etag','modified'),'contact_id','','',False,'AND',$start,$filter)))
$cols = array('id','uid','etag','modified','n_fn');
if (!in_array(self::$path_attr,$cols)) $cols[] = self::$path_attr;
if (($contacts =& $this->bo->search(array(),$cols,$order,'','',False,'AND',$start,$filter)))
{
foreach($contacts as &$contact)
{
$props = array(
HTTP_WebDAV_Server::mkprop('getetag',$this->get_etag($contact)),
HTTP_WebDAV_Server::mkprop('getcontenttype', 'text/vcard'),
// getlastmodified and getcontentlength are required by WebDAV and Cadaver eg. reports 404 Not found if not set
HTTP_WebDAV_Server::mkprop('getlastmodified', $contact['modified']),
'getcontenttype' => HTTP_WebDAV_Server::mkprop('getcontenttype', 'text/vcard'),
'getlastmodified' => $contact['modified'],
'displayname' => $contact['n_fn'],
);
if ($address_data)
{
$content = $handler->getVCard($contact['id'],$this->charset,false);
$props[] = HTTP_WebDAV_Server::mkprop('getcontentlength',bytes($content));
$props['getcontentlength'] = bytes($content);
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'address-data',$content,true);
}
else
$files[] = $this->add_resource($path, $contact, $props);
}
}
// add groups after contacts, but only if enabled and NOT for '/addressbook/' (!isset($filter['owner'])
if (in_array('D',$this->home_set_pref) && (!$start || count($contacts) < $start[1]) && isset($filter['owner']))
{
$where = array(
'list_owner' => isset($filter['owner'])?$filter['owner']:array_keys($this->bo->grants)
);
if (isset($filter[self::$path_attr])) // multiget report?
{
$where['list_'.self::$path_attr] = $filter[self::$path_attr];
}
//error_log(__METHOD__."() filter=".array2string($filter).", do_groups=".in_array('D',$this->home_set_pref).", where=".array2string($where));
if (($lists = $this->bo->read_lists($where,'contact_uid',$where['list_owner']))) // limit to contacts in same AB!
{
foreach($lists as $list)
{
$props[] = HTTP_WebDAV_Server::mkprop('getcontentlength', ''); // expensive to calculate and no CalDAV client uses it
$list['carddav_name'] = $list['list_carddav_name'];
$etag = $list['list_id'].':'.$list['list_etag'];
// for all-in-one addressbook, add selected ABs to etag
if (isset($filter['owner']) && is_array($filter['owner']))
{
$etag .= ':'.implode('-',$filter['owner']);
}
$props = array(
'getcontenttype' => HTTP_WebDAV_Server::mkprop('getcontenttype', 'text/vcard'),
'getlastmodified' => egw_time::to($list['list_modified'],'ts'),
'displayname' => $list['list_name'],
'getetag' => '"'.$etag.'"',
);
if ($address_data)
{
$content = $handler->getGroupVCard($list);
$props['getcontentlength'] = bytes($content);
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'address-data',$content,true);
}
$files[] = $this->add_resource($path, $list, $props);
}
$files[] = array(
'path' => $path.self::get_path($contact),
'props' => $props,
);
}
}
if ($this->debug) error_log(__METHOD__."($path,".array2string($filter).','.array2string($start).") took ".(microtime(true) - $starttime).' to return '.count($files).' items');
@ -169,49 +255,138 @@ class addressbook_groupdav extends groupdav_handler
* @param array $options
* @param array &$cal_filters
* @param string $id
* @return boolean true if filter could be processed, false for requesting not here supported VTODO items
* @param int &$nresult on return limit for number or results or unchanged/null
* @return boolean true if filter could be processed
*/
function _report_filters($options,&$filters,$id)
function _report_filters($options,&$filters,$id, &$nresults)
{
if ($options['filters'])
{
foreach($options['filters'] as $filter)
/* Example of a complex filter used by Mac Addressbook
<B:filter test="anyof">
<B:prop-filter name="FN" test="allof">
<B:text-match collation="i;unicode-casemap" match-type="contains">becker</B:text-match>
<B:text-match collation="i;unicode-casemap" match-type="contains">ralf</B:text-match>
</B:prop-filter>
<B:prop-filter name="EMAIL" test="allof">
<B:text-match collation="i;unicode-casemap" match-type="contains">becker</B:text-match>
<B:text-match collation="i;unicode-casemap" match-type="contains">ralf</B:text-match>
</B:prop-filter>
<B:prop-filter name="NICKNAME" test="allof">
<B:text-match collation="i;unicode-casemap" match-type="contains">becker</B:text-match>
<B:text-match collation="i;unicode-casemap" match-type="contains">ralf</B:text-match>
</B:prop-filter>
</B:filter>
*/
$filter_test = isset($options['filters']['attrs']) && isset($options['filters']['attrs']['test']) ?
$options['filters']['attrs']['test'] : 'anyof';
$prop_filters = array();
foreach($options['filters'] as $n => $filter)
{
switch($filter['name'])
if (!is_int($n)) continue; // eg. attributes of filter xml element
switch((string)$filter['name'])
{
case 'prop-filter':
if ($this->debug > 1) error_log(__METHOD__."($path,...) prop-filter='{$filter['attrs']['name']}'");
$prop_filter = $filter['attrs']['name'];
case 'param-filter':
$this->groupdav->log(__METHOD__."(...) param-filter='{$filter['attrs']['name']}' not (yet) implemented!");
break;
case 'text-match':
if ($this->debug > 1) error_log(__METHOD__."($path,...) text-match: $prop_filter='{$filter['data']}'");
if (!isset($this->filter_prop2cal[strtoupper($prop_filter)]))
case 'prop-filter': // can be multiple prop-filter, see example
if ($matches) $prop_filters[] = implode($prop_test=='allof'?' AND ':' OR ',$matches);
$matches = array();
$prop_filter = strtoupper($filter['attrs']['name']);
$prop_test = isset($filter['attrs']['test']) ? $filter['attrs']['test'] : 'anyof';
if ($this->debug > 1) error_log(__METHOD__."(...) prop-filter='$prop_filter', test='$prop_test'");
break;
case 'is-not-defined':
$matches[] = '('.$column."='' OR ".$column.' IS NULL)';
break;
case 'text-match': // prop-filter can have multiple text-match, see example
if (!isset($this->filter_prop2cal[$prop_filter])) // eg. not existing NICKNAME in EGroupware
{
if ($this->debug) error_log(__METHOD__."($path,".str_replace(array("\n",' '),'',print_r($options,true)).",,$user) unknown property '$prop_filter' --> ignored");
if ($this->debug || $prop_filter != 'NICKNAME') error_log(__METHOD__."(...) text-match: $prop_filter {$filter['attrs']['match-type']} '{$filter['data']}' unknown property '$prop_filter' --> ignored");
$column = false; // to ignore following data too
}
else
{
switch($filter['attrs']['match-type'])
switch($filter['attrs']['collation']) // todo: which other collations allowed, we are allways unicode
{
case 'i;unicode-casemap':
default:
case 'equals':
$filters[$this->filter_prop2cal[strtoupper($prop_filter)]] = $filter['data'];
break;
case 'substr': // ToDo: check RFC4790
$filters[] = $this->filter_prop2cal[strtoupper($prop_filter)].' LIKE '.$GLOBALS['egw']->db->quote($filter['data']);
$comp = ' '.$GLOBALS['egw']->db->capabilities[egw_db::CAPABILITY_CASE_INSENSITIV_LIKE].' ';
break;
}
$column = $this->filter_prop2cal[strtoupper($prop_filter)];
if (strpos($column, '_') === false) $column = 'contact_'.$column;
if (!isset($filters['order'])) $filters['order'] = $column;
$match_type = $filter['attrs']['match-type'];
$negate_condition = isset($filter['attrs']['negate-condition']) && $filter['attrs']['negate-condition'] == 'yes';
}
unset($prop_filter);
break;
case 'param-filter':
if ($this->debug) error_log(__METHOD__."($path,...) param-filter='{$filter['attrs']['name']}' not (yet) implemented!");
break;
case '': // data of text-match element
if (isset($filter['data']) && isset($column))
{
if ($column) // false for properties not known to EGroupware
{
$value = str_replace(array('%', '_'), array('\\%', '\\_'), $filter['data']);
switch($match_type)
{
case 'equals':
$sql_filter = $column . $comp . $GLOBALS['egw']->db->quote($value);
break;
default:
case 'contains':
$sql_filter = $column . $comp . $GLOBALS['egw']->db->quote('%'.$value.'%');
break;
case 'starts-with':
$sql_filter = $column . $comp . $GLOBALS['egw']->db->quote($value.'%');
break;
case 'ends-with':
$sql_filter = $column . $comp . $GLOBALS['egw']->db->quote('%'.$value);
break;
}
$matches[] = ($negate_condition ? 'NOT ' : '').$sql_filter;
if ($this->debug > 1) error_log(__METHOD__."(...) text-match: $prop_filter $match_type' '{$filter['data']}'");
}
unset($column);
break;
}
// fall through
default:
if ($this->debug) error_log(__METHOD__."($path,".array2string($options).",,$user) unknown filter --> ignored");
$this->groupdav->log(__METHOD__."(".array2string($options).",,$id) unknown filter=".array2string($filter).' --> ignored');
break;
}
}
if ($matches) $prop_filters[] = implode($prop_test=='allof'?' AND ':' OR ',$matches);
if ($prop_filters)
{
$filters[] = $filter = '(('.implode($filter_test=='allof'?') AND (':') OR (', $prop_filters).'))';
if ($this->debug) error_log(__METHOD__."($path,...) sql-filter: $filter");
}
}
// parse limit from $options['other']
/* Example limit
<B:limit>
<B:nresults>10</B:nresults>
</B:limit>
*/
foreach((array)$options['other'] as $option)
{
switch($option['name'])
{
case 'nresults':
$nresults = (int)$option['data'];
//error_log(__METHOD__."(...) options[other]=".array2string($options['other'])." --> nresults=$nresults");
break;
case 'limit':
break;
case 'href':
break; // from addressbook-multiget, handled below
default:
$this->groupdav->log(__METHOD__."(...) unknown xml: options[other]=".array2string($options['other']));
break;
}
}
// multiget --> fetch the url's
if ($options['root']['name'] == 'addressbook-multiget')
@ -222,15 +397,18 @@ class addressbook_groupdav extends groupdav_handler
if ($option['name'] == 'href')
{
$parts = explode('/',$option['data']);
if (($id = array_pop($parts))) $ids[] = basename($id,'.vcf');
if (($id = array_pop($parts)))
{
$ids[] = groupdav_handler::$path_extension ? basename($id,groupdav_handler::$path_extension) : $id;
}
}
}
if ($ids) $filters[self::PATH_ATTRIBUTE] = $ids;
if ($this->debug) error_log(__METHOD__."($path,,,$user) addressbook-multiget: ids=".implode(',',$ids));
if ($ids) $filters[self::$path_attr] = $ids;
if ($this->debug) error_log(__METHOD__."(...) addressbook-multiget: ids=".implode(',',$ids));
}
elseif ($id)
{
$filters[self::PATH_ATTRIBUTE] = basename($id,'.vcf');
$filters[self::$path_attr] = groupdav_handler::$path_extension ? basename($id,groupdav_handler::$path_extension) : $id;
}
return true;
}
@ -250,16 +428,17 @@ class addressbook_groupdav extends groupdav_handler
return $contact;
}
$handler = self::_get_handler();
$options['data'] = $handler->getVCard($contact['id'],$this->charset,false);
$options['data'] = $contact['list_id'] ? $handler->getGroupVCard($contact) :
$handler->getVCard($contact['id'],$this->charset,false);
// e.g. Evolution does not understand 'text/vcard'
$options['mimetype'] = 'text/x-vcard; charset='.$this->charset;
header('Content-Encoding: identity');
header('ETag: '.$this->get_etag($contact));
header('ETag: "'.$this->get_etag($contact).'"');
return true;
}
/**
* Handle put request for an event
* Handle put request for a contact
*
* @param array &$options
* @param int $id
@ -274,6 +453,7 @@ class addressbook_groupdav extends groupdav_handler
$oldContact = $this->_common_get_put_delete('PUT',$options,$id);
if (!is_null($oldContact) && !is_array($oldContact))
{
if ($this->debug) error_log(__METHOD__."(,'$id', $user, '$prefix') returning ".array2string($oldContact));
return $oldContact;
}
@ -301,39 +481,26 @@ class addressbook_groupdav extends groupdav_handler
}
}
if (is_array($oldContact))
$contact = $handler->vcardtoegw($vCard, $charset);
if (is_array($oldContact) || ($oldContact = $this->bo->read(array('contact_uid' => $contact['uid']))))
{
$contactId = $oldContact['id'];
$retval = true;
}
else
{
// new entry?
if (($foundContacts = $handler->search($vCard, null, false, $charset)))
{
if (($contactId = array_shift($foundContacts)) &&
($oldContact = $this->bo->read($contactId)))
{
$retval = '301 Moved Permanently';
}
else
{
// to be safe
$contactId = -1;
$retval = '201 Created';
}
}
else
{
// new entry
$contactId = -1;
$retval = '201 Created';
}
// new entry
$contactId = -1;
$retval = '201 Created';
}
$is_group = $contact['##X-ADDRESSBOOKSERVER-KIND'] == 'group';
if ($oldContact && $is_group !== isset($oldContact['list_id']))
{
throw new egw_exception_assertion_failed(__METHOD__."(,'$id',$user,'$prefix') can contact into group or visa-versa!");
}
$contact = $handler->vcardtoegw($vCard, $charset);
if (is_array($contact['cat_id']))
if (!$is_group && is_array($contact['cat_id']))
{
$contact['cat_id'] = implode(',',$this->bo->find_or_add_categories($contact['cat_id'], $contactId));
}
@ -348,22 +515,41 @@ class addressbook_groupdav extends groupdav_handler
$contact['uid'] = $oldContact['uid'];
$contact['owner'] = $oldContact['owner'];
$contact['private'] = $oldContact['private'];
$contact['carddav_name'] = $oldContact['carddav_name'];
$contact['tid'] = $oldContact['tid'];
$contact['creator'] = $oldContact['creator'];
$contact['account_id'] = $oldContact['account_id'];
}
// only set owner, if user is explicitly specified in URL (check via prefix, NOT for /addressbook/ !)
if ($prefix)
else
{
// check for modified owners, if user has an add right for the new addressbook and
// delete rights for the old addressbook (_common_get_put_delete checks for PUT only EGW_ACL_EDIT)
if ($oldContact && $user != $oldContact['owner'] && !($this->bo->grants[$user] & EGW_ACL_ADD) &&
(!$this->bo->grants[$oldContact['owner']] & EGW_ACL_DELETE))
$contact['carddav_name'] = $id;
// only set owner, if user is explicitly specified in URL (check via prefix, NOT for /addressbook/) or sync-all-in-one!)
if ($prefix && !in_array('O',$this->home_set_pref))
{
$contact['owner'] = $user;
}
// check if default addressbook is synced, if not use (always synced) personal addressbook
elseif($this->bo->default_addressbook && !in_array($this->bo->default_addressbook,$this->home_set_pref))
{
$contact['owner'] = $GLOBALS['egw_info']['user']['account_id'];
}
else
{
$contact['owner'] = $this->bo->default_addressbook;
$contact['private'] = $this->bo->default_private;
}
// check if user has add rights for addressbook
// done here again, as _common_get_put_delete knows nothing about default addressbooks...
if (!($this->bo->grants[$contact['owner']] & EGW_ACL_ADD))
{
if ($this->debug) error_log(__METHOD__."(,'$id', $user, '$prefix') returning '403 Forbidden'");
return '403 Forbidden';
}
$contact['owner'] = $user;
}
if ($this->http_if_match) $contact['etag'] = self::etag2value($this->http_if_match);
if (!($save_ok = $this->bo->save($contact)))
if (!($save_ok = $is_group ? $this->save_group($contact, $oldContact) : $this->bo->save($contact)))
{
if ($this->debug) error_log(__METHOD__."(,$id) save(".array2string($contact).") failed, Ok=$save_ok");
if ($save_ok === 0)
@ -375,73 +561,112 @@ class addressbook_groupdav extends groupdav_handler
if (!isset($contact['etag']))
{
$contact = $this->read($save_ok);
$contact = $this->read($save_ok,$options['path']);
}
header('ETag: '.$this->get_etag($contact));
if ($retval !== true)
// send evtl. necessary respose headers: Location, etag, ...
// epl-11.1 needs && !$is_group, as we dont store carddav_name for lists!
$this->put_response_headers($contact, $options['path'], $retval, self::$path_attr != 'id' && !$is_group);
if ($this->debug > 1) error_log(__METHOD__."(,'$id', $user, '$prefix') returning ".array2string($retval));
return $retval;
}
/**
* Save distribition-list / group
*
* @param array $contact
* @param array|false $oldContact
* @param int|boolean $list_id or false on error
*/
function save_group(array &$contact, $oldContact=null)
{
$data = array('list_name' => $contact['n_fn']);
if (!isset($contact['owner'])) $contact['owner'] = $GLOBALS['egw_info']['user']['account_id'];
foreach(array('id','carddav_name','uid','owner') as $name)
{
$path = preg_replace('|(.*)/[^/]*|', '\1/', $options['path']);
header($h='Location: '.$this->base_uri.$path.self::get_path($contact));
if ($this->debug) error_log(__METHOD__."($method,,$id) header('$h'): $retval");
return $retval;
if ($name != self::$path_attr) $data['list_'.$name] = $contact[$name];
}
return true;
//error_log(__METHOD__.'('.array2string($contact).', '.array2string($oldContact).') data='.array2string($data));
if (($list_id=$this->bo->add_list(array('list_'.self::$path_attr => $contact[self::$path_attr]),
$contact['owner'], null, $data)))
{
// update members given in $contact['##X-ADDRESSBOOKSERVER-MEMBER']
$new_members = $contact['##X-ADDRESSBOOKSERVER-MEMBER'];
if ($new_members[1] == ':' && ($n = unserialize($new_members)))
{
$new_members = $n['values'];
}
else
{
$new_members = array($new_members);
}
foreach($new_members as &$uid) $uid = substr($uid,9); // cut off "urn:uuid:" prefix
if ($oldContact)
{
$to_add = array_diff($new_members,$oldContact['members']);
$to_delete = array_diff($oldContact['members'],$new_members);
}
else
{
$to_add = $new_members;
}
//error_log('to_add='.array2string($to_add).', to_delete='.array2string($to_delete));
if ($to_add || $to_delete)
{
$to_add_ids = $to_delete_ids = array();
$filter = array('uid' => $to_delete ? array_merge($to_add, $to_delete) : $to_add);
if (($contacts =& $this->bo->search(array(),'id,uid','','','',False,'AND',false,$filter)))
{
foreach($contacts as $c)
{
if ($to_delete && in_array($c['uid'], $to_delete))
{
$to_delete_ids[] = $c['id'];
}
else
{
$to_add_ids[] = $c['id'];
}
}
}
//error_log('to_add_ids='.array2string($to_add_ids).', to_delete_ids='.array2string($to_delete_ids));
if ($to_add_ids) $this->bo->add2list($to_add_ids, $list_id, array());
if ($to_delete_ids) $this->bo->remove_from_list($to_delete_ids, $list_id);
}
$list_id = $data['list_carddav_name'];
}
if ($this->debug > 1) error_log(__METHOD__.'('.array2string($contact).', '.array2string($oldContact).') on return contact='.array2string($data).' returning '.array2string($list_id));
$contact = $data;
return $list_id;
}
/**
* Query ctag for addressbook
*
* @param string $path
* @param int $user
* @return string
*/
public function getctag($path,$user)
{
$filter = array();
// show addressbook of a single user?
if ($user && $path != '/addressbook/') $filter['contact_owner'] = $user;
// should we hide the accounts addressbook
if ($GLOBALS['egw_info']['user']['preferences']['addressbook']['hide_accounts']) $filter['account_id'] = null;
// not showing addressbook of a single user?
if (is_null($user) || $user === '' || $path == '/addressbook/') $user = null;
$result = $this->bo->search(array(),'MAX(contact_modified) AS contact_modified','','','',false,'AND',false,$filter);
if (empty($result))
// If "Sync selected addressbooks into one" is set --> ctag need to take selected AB's into account too
if ($user && $user == $GLOBALS['egw_info']['user']['account_id'] && in_array('O',$this->home_set_pref))
{
$ctag = 0;
$user = array_merge((array)$user,array_keys($this->get_shared(true))); // true: ignore all-in-one pref
}
else
$ctag = $this->bo->get_ctag($user);
// include lists-ctag, if enabled and NOT in /addressbook/ (we dont sync distribution-lists/groups there)
if (in_array('D',$this->home_set_pref) && $path != '/addressbook/')
{
$ctag = $result[0]['modified'];
$lists_ctag = $this->bo->lists_ctag($user);
}
return 'EGw-'.$ctag.'-wGE';
}
/**
* Add the privileges of the current user
*
* @param array $props=array() regular props by the groupdav handler
* @return array
*/
static function current_user_privilege_set(array $props=array())
{
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::DAV,'current-user-privilege-set',
array(HTTP_WebDAV_Server::mkprop(groupdav::DAV,'privilege',
array(
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'read',''),
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'read-free-busy',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'read-current-user-privilege-set',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'bind',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'unbind',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-post',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-post-vevent',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-respond',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-respond-vevent',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-deliver',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-deliver-vevent',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'write',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'write-properties',''),
HTTP_WebDAV_Server::mkprop(groupdav::DAV,'write-content',''),
))));
return $props;
//error_log(__METHOD__."('$path', ".array2string($user).") ctag=$ctag=".date('Y-m-d H:i:s',$ctag).", lists_ctag=".($lists_ctag ? $lists_ctag.'='.date('Y-m-d H:i:s',$lists_ctag) : '').' returning '.max($ctag,$lists_ctag));
return max($ctag,$lists_ctag);
}
/**
@ -465,16 +690,21 @@ class addressbook_groupdav extends groupdav_handler
* @param array $props=array() regular props by the groupdav handler
* @param string $displayname
* @param string $base_uri=null base url of handler
* @param int $user=null account_id of owner of collection
* @return array
*/
static function extra_properties(array $props=array(), $displayname, $base_uri=null)
public function extra_properties(array $props=array(), $displayname, $base_uri=null, $user=null)
{
// addressbook description
$displayname = translation::convert(lang('Addressbook of') . ' ' .
$displayname,translation::charset(),'utf-8');
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'addressbook-description',$displayname);
if (!isset($props['addressbook-description']))
{
// default addressbook description: can be overwritten via PROPPATCH, in which case it's already set
$props['addressbook-description'] = HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'addressbook-description',$props['displayname']);
}
// setting an max image size, so iOS scales the images before transmitting them
$props['max-image-size'] = HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'max-image-size',4096);
// supported reports (required property for CardDAV)
$props[] = HTTP_WebDAV_Server::mkprop('supported-report-set',array(
$props['supported-report-set'] = HTTP_WebDAV_Server::mkprop('supported-report-set',array(
HTTP_WebDAV_Server::mkprop('supported-report',array(
HTTP_WebDAV_Server::mkprop('report',array(
HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'addressbook-query',''))))),
@ -482,7 +712,6 @@ class addressbook_groupdav extends groupdav_handler
HTTP_WebDAV_Server::mkprop('report',array(
HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'addressbook-multiget',''))))),
));
//$props = self::current_user_privilege_set($props);
return $props;
}
@ -495,12 +724,9 @@ class addressbook_groupdav extends groupdav_handler
{
$handler = new addressbook_vcal('addressbook','text/vcard');
// Apple iOS or OS X addressbook
if ($this->agent = 'cfnetwork' || $this->agent == 'dataaccess')
if ($this->agent == 'cfnetwork' || $this->agent == 'dataaccess')
{
$supportedFields = $handler->supportedFields;
// Apple Addressbook don't support CLASS
unset($supportedFields['CLASS']);
unset($supportedFields['CATEGORIES']);
// use just CELL and IPHONE, CELL;WORK and CELL;HOME are NOT understood
//'TEL;CELL;WORK' => array('tel_cell'),
//'TEL;CELL;HOME' => array('tel_cell_private'),
@ -508,6 +734,19 @@ class addressbook_groupdav extends groupdav_handler
unset($supportedFields['TEL;CELL;WORK']);
$supportedFields['TEL;IPHONE'] = array('tel_cell_private');
unset($supportedFields['TEL;CELL;HOME']);
$supportedFields['X-ABSHOWAS'] = array('fileas_type'); // Horde vCard class uses uppercase prop-names!
// Apple Addressbook pre Lion (OS X 10.7) messes up CLASS and CATEGORIES (Lion cant set them but leaves them alone)
if (preg_match('|CFNetwork/([0-9]+)|i', $_SERVER['HTTP_USER_AGENT'],$matches) && $matches[1] < 520)
{
unset($supportedFields['CLASS']);
unset($supportedFields['CATEGORIES']);
// gd cant parse or resize images stored from snow leopard addressbook: gd-jpeg:
// - JPEG library reports unrecoverable error
// - Passed data is not in 'JPEG' format
// - Couldn't create GD Image Stream out of Data
// FF (10), Safari (5.1.3) and Chrome (17) cant display it either --> ignore images
unset($supportedFields['PHOTO']);
}
}
$handler->setSupportedFields('GroupDAV',$this->agent, isset($supportedFields) ?
$supportedFields : $handler->supportedFields);
@ -528,33 +767,176 @@ class addressbook_groupdav extends groupdav_handler
{
return $contact;
}
if (($Ok = $this->bo->delete($contact['id'],self::etag2value($this->http_if_match))) === 0)
if (($Ok = isset($contact['list_id']) ? $this->bo->delete_list($contact['list_id']) !== false :
$this->bo->delete($contact['id'],self::etag2value($this->http_if_match))) === 0)
{
return '412 Precondition Failed';
}
//return $ok;
return $Ok;
}
/**
* Read a contact
*
* @param string/id $id
* @return array/boolean array with entry, false if no read rights, null if $id does not exist
* We have to make sure to not return or even consider in read deleted contacts, as the might have
* the same UID and/or carddav_name as not deleted contacts and would block access to valid entries
*
* @param string|int $id
* @param string $path=null
* @return array|boolean array with entry, false if no read rights, null if $id does not exist
*/
function read($id)
function read($id, $path=null)
{
return $this->bo->read(self::PATH_ATTRIBUTE == 'id' ? $id : array(self::PATH_ATTRIBUTE => $id));
static $non_deleted_tids;
if (is_null($non_deleted_tids))
{
$non_deleted_tids = $this->bo->content_types;
unset($non_deleted_tids[addressbook_so::DELETED_TYPE]);
$non_deleted_tids = array_keys($non_deleted_tids);
}
$contact = $this->bo->read(array(self::$path_attr => $id, 'tid' => $non_deleted_tids));
// see if we have a distribution-list / group with that id
// bo->read_list(..., true) limits returned uid to same owner's addressbook, as iOS and OS X addressbooks
// only understands/shows that and if return more, save_lists would delete the others ones on update!
$limit_in_ab = true;
list(,$account_lid,$app) = explode('/',$path); // eg. /<username>/addressbook/<id>
// /<username>/addressbook/ with home_set_prefs containing 'O'=all-in-one contains selected ab's
if($account_lid == $GLOBALS['egw_info']['user']['account_lid'] && $app == 'addressbook' && in_array('O',$this->home_set_pref))
{
$limit_in_ab = array_keys($this->get_shared(true));
$limit_in_ab[] = $GLOBALS['egw_info']['user']['account_id'];
}
/* we are currently not syncing distribution-lists/groups to /addressbook/ as
* Apple clients use that only as directory gateway
elseif ($account_lid == 'addressbook') // /addressbook/ contains all readably contacts
{
$limit_in_ab = array_keys($this->bo->grants);
}*/
if (!$contact && ($contact = $this->bo->read_lists(array('list_'.self::$path_attr => $id),'contact_uid',$limit_in_ab)))
{
$contact = array_shift($contact);
$contact['n_fn'] = $contact['n_family'] = $contact['list_name'];
foreach(array('owner','id','carddav_name','modified','modifier','created','creator','etag','uid') as $name)
{
$contact[$name] = $contact['list_'.$name];
}
// if NOT limited to containing AB ($limit_in_ab === true), add that limit to etag
if ($limit_in_ab !== true)
{
$contact['etag'] .= ':'.implode('-',$limit_in_ab);
}
}
elseif($contact === array()) // not found from read_lists()
{
$contact = null;
}
if ($contact && $contact['tid'] == addressbook_so::DELETED_TYPE)
{
$contact = null; // handle deleted events, as not existing (404 Not Found)
}
if ($this->debug > 1) error_log(__METHOD__."('$id') returning ".array2string($contact));
return $contact;
}
/**
* Check if user has the neccessary rights on a contact
*
* @param int $acl EGW_ACL_READ, EGW_ACL_EDIT or EGW_ACL_DELETE
* @param array/int $contact contact-array or id
* @param array|int $contact contact-array or id
* @return boolean null if entry does not exist, false if no access, true if access permitted
*/
function check_access($acl,$contact)
{
return $this->bo->check_perms($acl,$contact);
}
/**
* Return calendars/addressbooks shared from other users with the current one
*
* @param boolean $ignore_all_in_one=false if true, return selected addressbooks and not array() for all-in-one
* @return array account_id => account_lid pairs
*/
function get_shared($ignore_all_in_one=false)
{
$shared = array();
// if "Sync all selected addressbook into one" is set --> no (additional) shared addressbooks
if (!$ignore_all_in_one && in_array('O',$this->home_set_pref)) return array();
// replace symbolic id's with real nummeric id's
foreach(array(
'G' => $GLOBALS['egw_info']['user']['account_primary_group'],
'U' => '0',
) as $sym => $id)
{
if (($key = array_search($sym, $this->home_set_pref)) !== false)
{
$this->home_set_pref[$key] = $id;
}
}
foreach($this->bo->get_addressbooks(EGW_ACL_READ) as $id => $label)
{
if (($id || !$GLOBALS['egw_info']['user']['preferences']['addressbook']['hide_accounts']) &&
$GLOBALS['egw_info']['user']['account_id'] != $id && // no current user and no accounts, if disabled in ab prefs
(in_array('A',$this->home_set_pref) || in_array((string)$id,$this->home_set_pref)) &&
is_numeric($id) && ($owner = $id ? $this->accounts->id2name($id) : 'accounts'))
{
$shared[$id] = $owner;
}
}
return $shared;
}
/**
* Return appliction specific settings
*
* @param array $hook_data
* @return array of array with settings
*/
static function get_settings($hook_data)
{
$addressbooks = array();
if (!isset($hook_data['setup']))
{
$user = $GLOBALS['egw_info']['user']['account_id'];
$addressbook_bo = new addressbook_bo();
$addressbooks = $addressbook_bo->get_addressbooks(EGW_ACL_READ);
unset($addressbooks[$user]); // allways synced
unset($addressbooks[$user.'p']);// ignore (optional) private addressbook for now
}
$addressbooks = array(
'A' => lang('All'),
'G' => lang('Primary Group'),
'U' => lang('Accounts'),
'O' => lang('Sync all selected into one'),
'D' => lang('Distribution lists as groups')
) + $addressbooks;
// rewriting owner=0 to 'U', as 0 get's always selected by prefs
if (!isset($addressbooks[0]))
{
unset($addressbooks['U']);
}
else
{
unset($addressbooks[0]);
}
$settings = array();
$settings['addressbook-home-set'] = array(
'type' => 'multiselect',
'label' => 'Addressbooks to sync in addition to personal addressbook',
'name' => 'addressbook-home-set',
'help' => lang('Only supported by a few fully conformant clients (eg. from Apple). If you have to enter a URL, it will most likly not be suppored!').
'<br/>'.lang('They will be sub-folders in users home (%1 attribute).','CardDAV "addressbook-home-set"').
'<br/>'.lang('Select "%1", if your client does not support multiple addressbooks.',lang('Sync all selected into one')).
'<br/>'.lang('Select "%1", if your client support groups, eg. OS X or iOS addressbook.',lang('Distribution lists as groups')),
'values' => $addressbooks,
'xmlrpc' => True,
'admin' => False,
);
return $settings;
}
}

View File

@ -108,6 +108,7 @@ class addressbook_ldap
'n_fileas' => 'displayname',
'label' => 'postaladdress',
'pubkey' => 'usersmimecertificate',
'uid' => 'entryuuid',
),
#displayName
@ -123,19 +124,21 @@ class addressbook_ldap
'mozillaabpersonalpha' => array(
'adr_one_street2' => 'mozillaworkstreet2',
'adr_one_countryname' => 'c', // 2 letter country code
'adr_one_countrycode' => 'c', // 2 letter country code
'adr_two_street' => 'mozillahomestreet',
'adr_two_street2' => 'mozillahomestreet2',
'adr_two_locality' => 'mozillahomelocalityname',
'adr_two_region' => 'mozillahomestate',
'adr_two_postalcode' => 'mozillahomepostalcode',
'adr_two_countryname' => 'mozillahomecountryname',
'adr_two_countrycode' => 'mozillahomecountryname',
'email_home' => 'mozillasecondemail',
'url_home' => 'mozillahomeurl',
),
// similar to the newer mozillaAbPerson, but uses mozillaPostalAddress2 instead of mozillaStreet2
'mozillaorgperson' => array(
'adr_one_street2' => 'mozillapostaladdress2',
'adr_one_countryname' => 'c', // 2 letter country code
'adr_one_countrycode' => 'c', // 2 letter country code
'adr_one_countryname' => 'co', // human readable country name, must be after 'c' to take precedence on read!
'adr_two_street' => 'mozillahomestreet',
'adr_two_street2' => 'mozillahomepostaladdress2',
@ -284,7 +287,8 @@ class addressbook_ldap
*/
function read($contact_id)
{
if (is_array($contact_id) && isset($contact_id['account_id']) || substr($contact_id,0,8) == 'account:')
if (is_array($contact_id) && isset($contact_id['account_id']) ||
!is_array($contact_id) && substr($contact_id,0,8) == 'account:')
{
$filter = 'uidNumber='.(int)(is_array($contact_id) ? $contact_id['account_id'] : substr($contact_id,8));
}
@ -434,6 +438,7 @@ class addressbook_ldap
$needRecreation = false;
// never allow to change the uidNumber (account_id) on update, as it could be misused by eg. xmlrpc or syncml
unset($ldapContact['uidnumber']);
unset($ldapContact['entryuuid']); // not allowed to modify that, no need either
// add missing objectclasses
if($ldapContact['objectClass'] && array_diff($ldapContact['objectClass'],$oldObjectclasses))
@ -663,6 +668,7 @@ class addressbook_ldap
$sort = 'ASC';
foreach(explode(',',$order_by) as $o)
{
if (substr($o,0,8) == 'contact_') $o = substr($o,8);
if (substr($o,-4) == ' ASC')
{
$sort = 'ASC';
@ -1113,9 +1119,17 @@ class addressbook_ldap
*/
function _egw2mozillaabpersonalpha(&$ldapContact,$data,$isUpdate)
{
if ($data['adr_one_countryname'])
if ($data['adr_one_countrycode'])
{
$ldapContact['c'] = $data['adr_one_countrycode'];
}
elseif ($data['adr_one_countryname'])
{
$ldapContact['c'] = ExecMethod('phpgwapi.country.country_code',$data['adr_one_countryname']);
if ($ldapContact['c'] && strlen($ldapContact['c']) > 2) // Bad countryname when "custom" selected!
{
$ldapContact['c'] = array(); // should return error...
}
}
elseif ($isUpdate)
{
@ -1134,10 +1148,7 @@ class addressbook_ldap
*/
function _mozillaorgperson2egw(&$contact,$data)
{
if ($data['c'] && strlen($contact['adr_one_countryname']) <= 2) // dont overwrite a set human readable name
{
$contact['adr_one_countryname'] = ExecMethod('phpgwapi.country.get_full_name',$data['c'][0]);
}
// no special handling necessary, as it supports two distinct attributes: c, cn
}
/**
@ -1152,14 +1163,24 @@ class addressbook_ldap
*/
function _egw2mozillaorgperson(&$ldapContact,$data,$isUpdate)
{
if ($data['adr_one_countryname'])
if ($data['adr_one_countrycode'])
{
$ldapContact['c'] = $data['adr_one_countrycode'];
if ($isUpdate) $ldapContact['co'] = array();
}
elseif ($data['adr_one_countryname'])
{
$ldapContact['c'] = ExecMethod('phpgwapi.country.country_code',$data['adr_one_countryname']);
if ($ldapContact['c'] && strlen($ldapContact['c']) > 2) // Bad countryname when "custom" selected!
{
$ldapContact['c'] = array(); // should return error...
}
}
elseif ($isUpdate)
{
$ldapContact['c'] = array();
$ldapContact['c'] = $ldapContact['co'] = array();
}
//error_log(__METHOD__."() adr_one_countrycode='{$data['adr_one_countrycode']}', adr_one_countryname='{$data['adr_one_countryname']}' --> c=".array2string($ldapContact['c']).', co='.array2string($ldapContact['co']));
}
/**

View File

@ -6,7 +6,7 @@
* @author Cornelius Weiss <egw-AT-von-und-zu-weiss.de>
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @package addressbook
* @copyright (c) 2005-8 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2005-12 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2005/6 by Cornelius Weiss <egw@von-und-zu-weiss.de>
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
@ -128,7 +128,7 @@ class addressbook_so
* In SQL we can search all columns, though a view make on real sense
*/
var $sql_cols_not_to_search = array(
'jpegphoto','owner','tid','private','id','cat_id','etag',
'jpegphoto','owner','tid','private','cat_id','etag',
'modified','modifier','creator','created','tz','account_id',
'uid',
);
@ -164,6 +164,13 @@ class addressbook_so
*/
var $content_types = array();
/**
* Special content type to indicate a deleted addressbook
*
* @var String;
*/
const DELETED_TYPE = 'D';
/**
* total number of matches of last search
*
@ -172,9 +179,9 @@ class addressbook_so
var $total;
/**
* storage object: sql (socontacts_sql) or ldap (so_ldap) backend class
* storage object: sql (addressbook_sql) or ldap (addressbook_ldap) backend class
*
* @var socontacts_sql
* @var addressbook_sql
*/
var $somain;
/**
@ -192,15 +199,21 @@ class addressbook_so
/**
* custom fields backend
*
* @var so_sql
* @var addressbook_sql
*/
var $soextra;
var $sodistrib_list;
var $backend;
function __construct($contact_app='addressbook')
/**
* Constructor
*
* @param string $contact_app='addressbook' used for acl->get_grants()
* @param egw_db $db=null
*/
function __construct($contact_app='addressbook',egw_db $db=null)
{
$this->db = $GLOBALS['egw']->db;
$this->db = is_null($db) ? $GLOBALS['egw']->db : $db;
$this->user = $GLOBALS['egw_info']['user']['account_id'];
$this->memberships = $GLOBALS['egw']->accounts->memberships($this->user,true);
@ -220,15 +233,6 @@ class addressbook_so
$this->contact_repository = 'ldap';
$this->somain = new addressbook_ldap();
if ($this->user) // not set eg. in setup
{
// static grants from ldap: all rights for the own personal addressbook and the group ones of the meberships
$this->grants = array($this->user => ~0);
foreach($this->memberships as $gid)
{
$this->grants[$gid] = ~0;
}
}
$this->columns_to_search = $this->ldap_search_attributes;
}
else // sql or sql->ldap
@ -237,17 +241,15 @@ class addressbook_so
{
$this->contact_repository = 'sql-ldap';
}
$this->somain = new addressbook_sql();
$this->somain = new addressbook_sql($db);
if ($this->user) // not set eg. in setup
{
// group grants are now grants for the group addressbook and NOT grants for all its members,
// therefor the param false!
$this->grants = $GLOBALS['egw']->acl->get_grants($contact_app,false);
}
// remove some columns, absolutly not necessary to search in sql
$this->columns_to_search = array_diff(array_values($this->somain->db_cols),$this->sql_cols_not_to_search);
}
if ($this->user)
{
$this->grants = $this->get_grants($this->user,$contact_app);
}
if ($this->account_repository == 'ldap' && $this->contact_repository == 'sql')
{
if ($this->account_repository != $this->contact_repository)
@ -290,7 +292,14 @@ class addressbook_so
// ToDo: it should be the other way arround, the backend should set the grants it uses
$this->somain->grants =& $this->grants;
$this->soextra = new so_sql('phpgwapi',$this->extra_table);
if($this->somain instanceof addressbook_sql)
{
$this->soextra =& $this->somain;
}
else
{
$this->soextra = new addressbook_sql($db);
}
$this->customfields = config::get_customfields('addressbook');
$this->content_types = config::get_content_types('addressbook');
@ -303,6 +312,54 @@ class addressbook_so
'icon' => 'navbar.png'
)));
}
// Add in deleted type, if holding deleted contacts
$config = config::read('phpgwapi');
if($config['history'])
{
$this->content_types[self::DELETED_TYPE] = array(
'name' => lang('Deleted'),
'options' => array(
'template' => 'addressbook.edit',
'icon' => 'deleted.png'
)
);
}
}
/**
* Get grants for a given user, taking into account static LDAP ACL
*
* @param int $user
* @param string $contact_app='addressbook'
* @return array
*/
function get_grants($user,$contact_app='addressbook')
{
if ($user)
{
// contacts backend (contacts in LDAP require accounts in LDAP!)
if($GLOBALS['egw_info']['server']['contact_repository'] == 'ldap' && $this->account_repository == 'ldap')
{
// static grants from ldap: all rights for the own personal addressbook and the group ones of the meberships
$grants = array($user => ~0);
foreach($GLOBALS['egw']->accounts->memberships($user,true) as $gid)
{
$grants[$gid] = ~0;
}
}
else // sql or sql->ldap
{
// group grants are now grants for the group addressbook and NOT grants for all its members,
// therefor the param false!
$grants = $GLOBALS['egw']->acl->get_grants($contact_app,false,$user);
}
}
else
{
$grants = array();
}
return $grants;
}
/**
@ -321,42 +378,19 @@ class addressbook_so
/**
* Read all customfields of the given id's
*
* @param int/array $ids
* @param int|array $ids
* @param array $field_names=null custom fields to read, default all
* @return array id => name => value
*/
function read_customfields($ids,$field_names=null)
{
if ($this->contact_repository == 'ldap')
{
return array(); // ldap does not support custom-fields (non-nummeric uid)
}
if (is_null($field_names)) $field_names = array_keys($this->customfields);
if(!is_array($ids) && is_numeric($ids)) {
$ids = array((int)$ids);
}
foreach($ids as $key => $id)
{
if (!(int)$id) unset($ids[$key]);
}
if (!$ids || !$field_names) return array(); // nothing to do, eg. all these contacts are in ldap
$fields = array();
foreach((array)$this->soextra->search(array(
$this->extra_id => $ids,
$this->extra_key => $field_names,
),false) as $data)
{
if ($data) $fields[$data[$this->extra_id]][$data[$this->extra_key]] = $data[$this->extra_value];
}
return $fields;
return $this->soextra->read_customfields($ids,$field_names);
}
/**
* Read all distributionlists of the given id's
*
* @param int/array $ids
* @param int|array $ids
* @return array id => name => value
*/
function read_distributionlist($ids, $dl_allowed=array())
@ -430,7 +464,10 @@ class addressbook_so
if ($this->somain->delete($where))
{
// delete customfields, can return 0 if there are no customfields
$this->soextra->delete(array($this->extra_id => $contact));
if(!($this->somain instanceof addressbook_sql))
{
$this->soextra->delete_customfields(array($this->extra_id => $contact));
}
// delete from distribution list(s)
$this->remove_from_list($contact);
@ -499,35 +536,14 @@ class addressbook_so
}
if($error_nr) return $error_nr;
// save customfields
foreach ((array)$this->customfields as $field => $options)
{
if (!isset($contact['#'.$field])) continue;
$data = array(
$this->extra_id => $contact['id'],
$this->extra_owner => $contact['owner'],
$this->extra_key => $field,
);
if((string) $contact['#'.$field] === '') // dont write empty values
{
$this->soextra->delete($data); // just delete them, in case they were previously set
continue;
}
$data[$this->extra_value] = $contact['#'.$field];
if (($error_nr = $this->soextra->save($data)))
{
return $error_nr;
}
}
return false; // no error
}
/**
* reads contact data including custom fields
*
* @param int/string $contact_id contact_id or 'a'.account_id
* @return array/boolean data if row could be retrived else False
* @param int|string $contact_id contact_id or 'a'.account_id
* @return array|boolean data if row could be retrived else False
*/
function read($contact_id)
{
@ -541,18 +557,6 @@ class addressbook_so
{
return $contact;
}
// try reading customfields only if we have some (none for LDAP!)
if ($this->customfields && $this->contact_repository != 'ldap')
{
$customfields = $this->soextra->search(array(
$this->extra_id => $contact['id'],
$this->extra_key => array_keys($this->customfields),
),false);
foreach ((array)$customfields as $field)
{
$contact['#'.$field[$this->extra_key]] = $field[$this->extra_value];
}
}
$dl_list=$this->read_distributionlist(array($contact['id']));
if (count($dl_list)) $contact['distrib_lists']=implode("\n",$dl_list[$contact['id']]);
return $this->db2data($contact);
@ -563,10 +567,10 @@ class addressbook_so
*
* '*' and '?' are replaced with sql-wildcards '%' and '_'
*
* @param array/string $criteria array of key and data cols, OR string to search over all standard search fields
* @param boolean/string $only_keys=true True returns only keys, False returns all cols. comma seperated list of keys to return
* @param array|string $criteria array of key and data cols, OR string to search over all standard search fields
* @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|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
@ -577,14 +581,35 @@ class addressbook_so
*/
function &search($criteria,$only_keys=True,$order_by='',$extra_cols='',$wildcard='',$empty=False,$op='AND',$start=false,$filter=null,$join='')
{
//echo "<p>socontacts::search(".print_r($criteria,true).",'$only_keys','$order_by','$extra_cols','$wildcard','$empty','$op','$start',".print_r($filter,true).",'$join')</p>\n";
//error_log("socontacts::search(".print_r($criteria,true).",'$only_keys','$order_by','$extra_cols','$wildcard','$empty','$op','$start',".print_r($filter,true).",'$join')");
//echo '<p>'.__METHOD__.'('.array2string($criteria,true).','.array2string($only_keys).",'$order_by','$extra_cols','$wildcard','$empty','$op',$start,".array2string($filter,true).",'$join')</p>\n";
//error_log(__METHOD__.'('.array2string($criteria,true).','.array2string($only_keys).",'$order_by','$extra_cols','$wildcard','$empty','$op',$start,".array2string($filter,true).",'$join')");
// the nextmatch custom-filter-header country-select returns a 2 letter country-code
if (isset($filter['adr_one_countryname']) && strlen($filter['adr_one_countryname']) == 2)
// Handle 'None' country option
if(is_array($filter) && $filter['adr_one_countrycode'] == '-custom-')
{
$filter['adr_one_countryname'] = $GLOBALS['egw']->country->get_full_name($filter['adr_one_countryname']);
$filter[] = 'adr_one_countrycode IS NULL';
unset($filter['adr_one_countrycode']);
}
// Hide deleted items unless type is specifically deleted
if(!is_array($filter)) $filter = $filter ? (array) $filter : array();
// if no tid set or tid==='' do NOT return deleted entries ($tid === null returns all entries incl. deleted)
if(!array_key_exists('tid', $filter) || $filter['tid'] === '')
{
if ($join && strpos($join,'RIGHT JOIN') !== false) // used eg. to search for groups
{
$filter[] = '(contact_tid != \'' . self::DELETED_TYPE . '\' OR contact_tid IS NULL)';
}
else
{
$filter[] = 'contact_tid != \'' . self::DELETED_TYPE . '\'';
}
}
elseif(is_null($filter['tid']))
{
unset($filter['tid']); // return all entries incl. deleted
}
$backend =& $this->get_backend(null,$filter['owner']);
// single string to search for --> create so_sql conformant search criterial for the standard search columns
if ($criteria && !is_array($criteria))
@ -602,14 +627,32 @@ class addressbook_so
{
$cols = $this->account_cols_to_search;
}
// search the customfields only if some exist, but only for sql!
if (get_class($backend) == 'addressbook_sql' && $this->customfields)
if($backend instanceof addressbook_sql)
{
$cols[] = $this->extra_value;
// Keep a string, let the parent handle it
$criteria = $search;
foreach($cols as $key => &$col)
{
if(!array_key_exists($col, $backend->db_cols))
{
if(!($col = array_search($col, $backend->db_cols)))
{
// Can't search this column, it will error if we try
unset($cols[$key]);
}
}
if ($col=='contact_id') $col='egw_addressbook.contact_id';
}
$backend->columns_to_search = $cols;
}
foreach($cols as $col)
else
{
$criteria[$col] = $search;
foreach($cols as $col)
{
$criteria[$col] = $search;
}
}
}
if (is_array($criteria) && count($criteria))
@ -662,9 +705,17 @@ class addressbook_so
{
$search = $param['search'];
$param['search'] = array();
foreach($this->columns_to_search as $col)
if($this->somain instanceof addressbook_sql)
{
if ($col != 'contact_value') $param['search'][$col] = $search; // we dont search the customfields
// Keep the string, let the parent deal with it
$param['search'] = $search;
}
else
{
foreach($this->columns_to_search as $col)
{
if ($col != 'contact_value') $param['search'][$col] = $search; // we dont search the customfields
}
}
}
if (is_array($param['search']) && count($param['search']))
@ -730,7 +781,10 @@ class addressbook_so
if (!$new_owner)
{
$this->somain->delete(array('owner' => $account_id));
$this->soextra->delete(array($this->extra_owner => $account_id));
if(!($this->somain instanceof addressbook_sql))
{
$this->soextra->delete_customfields(array($this->extra_owner => $account_id));
}
}
else
{
@ -746,16 +800,19 @@ class addressbook_so
/**
* return the backend, to be used for the given $contact_id
*
* @param mixed $contact_id=null
* @param array|string|int $keys=null
* @param int $owner=null account_id of owner or 0 for accounts
* @return object
*/
function get_backend($contact_id=null,$owner=null)
function get_backend($keys=null,$owner=null)
{
if ($owner === '') $owner = null;
$contact_id = !is_array($keys) ? $keys :
(isset($keys['id']) ? $keys['id'] : $keys['contact_id']);
if ($this->contact_repository != $this->account_repository && is_object($this->so_accounts) &&
(!is_null($owner) && !$owner || is_array($contact_id) && $contact_id['account_id'] || !is_null($contact_id) &&
(!is_null($owner) && !$owner || is_array($keys) && $keys['account_id'] || !is_null($contact_id) &&
($this->contact_repository == 'sql' && (!is_numeric($contact_id) && !is_array($contact_id) )||
$this->contact_repository == 'ldap' && is_numeric($contact_id))))
{
@ -907,38 +964,55 @@ class addressbook_so
}
/**
* Adds a distribution list
* Get the availible distribution lists for givens users and groups
*
* @param string $name list-name
* @param int $owner user- or group-id
* @param array $contacts=array() contacts to add
* @return list_id or false on error
* @param array $keys column-name => value(s) pairs, eg. array('list_uid'=>$uid)
* @param string $member_attr='contact_uid' null: no members, 'contact_uid', 'contact_id', 'caldav_name' return members as that attribute
* @param boolean $limit_in_ab=false if true only return members from the same owners addressbook
* @return array with list_id => array(list_id,list_name,list_owner,...) pairs
*/
function add_list($name,$owner,$contacts=array())
function read_lists($keys,$member_attr=null,$limit_in_ab=false)
{
if (!method_exists($this->somain,'add_list')) return false;
if (!method_exists($this->somain,'get_lists')) return false;
return $this->somain->add_list($name,$owner,$contacts);
return $this->somain->get_lists($keys,null,$member_attr,$limit_in_ab);
}
/**
* Adds one contact to a distribution list
* Adds / updates a distribution list
*
* @param int $contact contact_id
* @param string|array $keys list-name or array with column-name => value pairs to specify the list
* @param int $owner user- or group-id
* @param array $contacts=array() contacts to add (only for not yet existing lists!)
* @param array &$data=array() values for keys 'list_uid', 'list_carddav_name', 'list_name'
* @return int|boolean integer list_id or false on error
*/
function add_list($keys,$owner,$contacts=array(),array &$data=array())
{
if (!method_exists($this->somain,'add_list')) return false;
return $this->somain->add_list($keys,$owner,$contacts,$data);
}
/**
* Adds contact(s) to a distribution list
*
* @param int|array $contact contact_id(s)
* @param int $list list-id
* @param array $existing=null array of existing contact-id(s) of list, to not reread it, eg. array()
* @return false on error
*/
function add2list($contact,$list)
function add2list($contact,$list,array $existing=null)
{
if (!method_exists($this->somain,'add2list')) return false;
return $this->somain->add2list($contact,$list);
return $this->somain->add2list($contact,$list,$existing);
}
/**
* Removes one contact from distribution list(s)
*
* @param int $contact contact_id
* @param int|array $contact contact_id(s)
* @param int $list=null list-id or null to remove from all lists
* @return false on error
*/
@ -952,7 +1026,7 @@ class addressbook_so
/**
* Deletes a distribution list (incl. it's members)
*
* @param int/array $list list_id(s)
* @param int|array $list list_id(s)
* @return number of members deleted or false if list does not exist
*/
function delete_list($list)
@ -978,7 +1052,7 @@ class addressbook_so
/**
* Check if distribution lists are availible for a given addressbook
*
* @param int/string $owner='' addressbook (eg. 0 = accounts), default '' = "all" addressbook (uses the main backend)
* @param int|string $owner='' addressbook (eg. 0 = accounts), default '' = "all" addressbook (uses the main backend)
* @return boolean
*/
function lists_available($owner='')
@ -987,4 +1061,17 @@ class addressbook_so
return method_exists($backend,'read_list');
}
/**
* Get ctag (max list_modified as timestamp) for lists
*
* @param int|array $owner=null null for all lists user has access too
* @return int
*/
function lists_ctag($owner=null)
{
if (!method_exists($this->somain,'lists_ctag')) return 0;
return $this->somain->lists_ctag($owner);
}
}

View File

@ -5,27 +5,21 @@
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @package addressbook
* @copyright (c) 2006-8 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2006-12 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
include_once(EGW_INCLUDE_ROOT.'/etemplate/inc/class.so_sql.inc.php');
/**
* SQL storage object of the adressbook
*/
class addressbook_sql extends so_sql
class addressbook_sql extends so_sql_cf
{
/**
* name of customefields table
* name of custom fields table
*
* @var string
*/
var $extra_table = 'egw_addressbook_extra';
var $extra_join = ' LEFT JOIN egw_addressbook_extra ON egw_addressbook.contact_id=egw_addressbook_extra.contact_id';
var $extra_join_order = ' LEFT JOIN egw_addressbook_extra extra_order ON egw_addressbook.contact_id=extra_order.contact_id';
var $extra_join_filter = ' JOIN egw_addressbook_extra extra_filter ON egw_addressbook.contact_id=extra_filter.contact_id';
var $account_repository = 'sql';
var $contact_repository = 'sql';
var $grants;
@ -33,7 +27,7 @@ class addressbook_sql extends so_sql
/**
* join to show only active account (and not already expired ones)
*/
const ACOUNT_ACTIVE_JOIN = ' LEFT JOIN egw_accounts ON egw_addressbook.account_id=egw_accounts.account_id';
const ACCOUNT_ACTIVE_JOIN = ' LEFT JOIN egw_accounts ON egw_addressbook.account_id=egw_accounts.account_id';
/**
* filter to show only active account (and not already expired ones)
* UNIX_TIMESTAMP(NOW()) gets replaced with value of time() in the code!
@ -60,9 +54,18 @@ class addressbook_sql extends so_sql
*/
var $ab2list_table = 'egw_addressbook2list';
function __construct()
/**
* Constructor
*
* @param egw_db $db=null
*/
function __construct(egw_db $db=null)
{
$this->so_sql('phpgwapi','egw_addressbook',null,'contact_',true); // true = using the global db object, no clone!
parent::__construct('phpgwapi','egw_addressbook','egw_addressbook_extra','contact_',
$extra_key='_name',$extra_value='_value',$extra_id='_id',$db);
// Get custom fields from addressbook instead of phpgwapi
$this->customfields = config::get_customfields('addressbook');
if ($GLOBALS['egw_info']['server']['account_repository'])
{
@ -105,7 +108,7 @@ class addressbook_sql extends so_sql
if (isset($param['advanced_search']) && !empty($param['advanced_search'])) $advanced_search = true;
$wildcard ='%';
if ($advanced_search || (isset($param['wildcard']) && !empty($param['wildcard']))) $wildcard = ($param['wildcard']?$param['wildcard']:'');
// fix cat_id filter to search in comma-separated multiple cats and return subcats
if ((int)$filter['cat_id'])
{
@ -133,8 +136,8 @@ class addressbook_sql extends so_sql
{
$filter[] = $this->table_name.'.contact_owner != 0'; // in case there have been accounts in sql previously
}
$filter[] = "(contact_owner=".(int)$GLOBALS['egw_info']['user']['account_id'].
" OR contact_private=0 AND contact_owner IN (".
$filter[] = "(".$this->table_name.".contact_owner=".(int)$GLOBALS['egw_info']['user']['account_id'].
" OR contact_private=0 AND ".$this->table_name.".contact_owner IN (".
implode(',',array_keys($this->grants))."))";
}
}
@ -254,10 +257,17 @@ class addressbook_sql extends so_sql
*/
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 ((int) $this->debug >= 4) echo '<p>'.__METHOD__.'('.array2string($criteria).','.array2string($only_keys).",'$order_by','$extra_cols','$wildcard','$empty','$op','$start',".array2string($filter).",'$join')</p>\n";
if ((int) $this->debug >= 4) echo '<p>'.__METHOD__.'('.array2string($criteria,true).','.array2string($only_keys).",'$order_by','$extra_cols','$wildcard','$empty','$op',$start,".array2string($filter,true).",'$join')</p>\n";
//error_log(__METHOD__.'('.array2string($criteria,true).','.array2string($only_keys).",'$order_by','$extra_cols','$wildcard','$empty','$op',$start,".array2string($filter,true).",'$join')");
$owner = isset($filter['owner']) ? $filter['owner'] : (isset($criteria['owner']) ? $criteria['owner'] : null);
// fix cat_id criteria to search in comma-separated multiple cats and return subcats
if (is_array($criteria) && ($cats = $criteria['cat_id']))
{
$criteria = array_merge($criteria, $this->_cat_search($criteria['cat_id']));
unset($criteria['cat_id']);
}
// fix cat_id filter to search in comma-separated multiple cats and return subcats
if (($cats = $filter['cat_id']))
{
@ -323,122 +333,28 @@ class addressbook_sql extends so_sql
implode(',',array_keys($this->grants)).") $groupmember_sql OR $this->table_name.contact_owner IS NULL)";
}
}
$search_customfields = isset($criteria['contact_value']) && !empty($criteria['contact_value']);
if (is_array($criteria))
{
foreach($criteria as $col => $val)
{
if ($col[0] === '#') // search for a value in a certain custom field
{
$valarray=array();
# val may be a list of values, constructed by multiple select fields, to be able to do the contains feature of adv-search
# we split the value and search for each part individually
if ($wildcard !='') {
$valarray=explode(',',$val);
} else {
$valarray[]=$val;
}
$negate = false; //negate the search funktion
if ($criteria[$col][0] == '!') $negate = True;
unset($criteria[$col]);
foreach ($valarray as $vkey => $part)
{
$criteria[] =$this->table_name.'.contact_id'.($negate ? ' not ' :'').' in (select '.$this->extra_table.'.contact_id from '.$this->extra_table.' where '.
"(".$this->extra_table.".contact_name='".substr($col,1)."' AND ".$this->extra_table.".contact_value".(!$wildcard?' = ':' LIKE ')."'".$wildcard.($negate?substr($part,1):$part).$wildcard."'"."))";
}
$search_customfields = true;
}
elseif($col === 'cat_id') // search in comma-sep. cat-column
{
$criteria = array_merge($criteria,$this->_cat_search($val));
unset($criteria[$col]);
}
elseif($col === 'contact_value')
{
if ($order_by[0] == '#')
{
$criteria =array_merge($criteria,array('extra_order.contact_value'=>$val));
unset($criteria[$col]);
}
}
}
}
if ($search_customfields) // search the custom-fields
{
$join .= $this->extra_join;
}
// do we order by a cf?
if ($order_by[0] == '#')
{
list($val) = explode("<>''",$order_by);
$order_by = str_replace($val,'extra_order.contact_value',$order_by);
$join .= $this->extra_join_order.' AND extra_order.contact_name='.$this->db->quote(substr($val,1));
}
// do we filter by a cf?
$extra_filter = '';
foreach($filter as $name => $val)
{
if ($name[0] === '#')
{
if (!empty($val)) // empty -> dont filter
{
$join .= str_replace('extra_filter','extra_filter'.$extra_filter,$this->extra_join_filter.' AND extra_filter.contact_name='.$this->db->quote(substr($name,1)).
' AND extra_filter.contact_value='.$this->db->quote($val));
++$extra_filter;
}
unset($filter[$name]);
}
elseif($val[0] === '#') // lettersearch: #cfname like 's%'
{
list($cf) = explode(' ',$val);
$join .= str_replace('extra_filter','extra_filter'.$extra_filter,$this->extra_join_filter.' AND extra_filter.contact_name='.$this->db->quote(substr($cf,1)).
' AND '.str_replace($cf,'extra_filter.contact_value',$val));
++$extra_filter;
unset($filter[$name]);
}
switch((string)$name)
{
case 'owner':
case 'contact_owner':
$filter[] = $this->db->expression($this->table_name,$this->table_name.'.',array(
'contact_owner' => $val,
));
unset($filter[$name]);
break;
}
}
if (isset($filter['list']))
{
$join .= " JOIN $this->ab2list_table ON $this->table_name.contact_id=$this->ab2list_table.contact_id AND list_id=".(int)$filter['list'];
unset($filter['list']);
}
if ($join)
// add join to show only active accounts (only if accounts are shown and in sql and we not already join the accounts table, eg. used by admin)
if (!$owner && substr($this->account_repository,0,3) == 'sql' &&
strpos($join,$GLOBALS['egw']->accounts->backend->table) === false && !array_key_exists('account_id',$filter))
{
$join .= self::ACCOUNT_ACTIVE_JOIN;
$filter[] = str_replace('UNIX_TIMESTAMP(NOW())',time(),self::ACOUNT_ACTIVE_FILTER);
}
if ($join || $criteria && is_string($criteria)) // search also adds a join for custom fields!
{
switch(gettype($only_keys))
{
case 'boolean':
// only return the egw_addressbook columns, to not generate dublicates by the left join
// and to not return the NULL for contact_{id|owner} of not found custom fields!
$only_keys = (strpos($join,$this->extra_table)!==false?'DISTINCT ':'').$this->table_name.'.'.($only_keys ? 'contact_id AS contact_id' : '*');
// Correctly handled by parent class
break;
case 'string':
$only_keys = explode(',',$only_keys);
// fall through
case 'array':
foreach($only_keys as $key => $val)
{
switch($val)
{
case 'id': case 'contact_id':
$only_keys[$key] = $this->table_name.'.contact_id';
break;
case 'owner': case 'contact_owner':
$only_keys[$key] = $this->table_name.'.contact_owner';
break;
}
}
break;
}
// postgres requires that expressions in order by appear in the columns of a distinct select
if ($this->db->Type != 'mysql' && preg_match_all("/([a-zA-Z_.]+) *(<> *''|IS NULL|IS NOT NULL)? *(ASC|DESC)?(,|$)/ui",$order_by,$all_matches,PREG_SET_ORDER))
@ -471,13 +387,6 @@ class addressbook_sql extends so_sql
//_debug_array($order_by); _debug_array($extra_cols);
}
}
// add join to show only active accounts (only if accounts are shown and in sql and we not already join the accounts table, eg. used by admin)
if (!$owner && substr($this->account_repository,0,3) == 'sql' &&
strpos($join,$GLOBALS['egw']->accounts->backend->table) === false && !array_key_exists('account_id',$filter))
{
$join .= self::ACOUNT_ACTIVE_JOIN;
$filter[] = str_replace('UNIX_TIMESTAMP(NOW())',time(),self::ACOUNT_ACTIVE_FILTER);
}
$rows =& parent::search($criteria,$only_keys,$order_by,$extra_cols,$wildcard,$empty,$op,$start,$filter,$join,$need_full_no_count);
if ($start === false) $this->total = is_array($rows) ? count($rows) : 0; // so_sql sets total only for $start !== false!
@ -549,101 +458,200 @@ class addressbook_sql extends so_sql
/**
* Get the availible distribution lists for givens users and groups
*
* @param array $uids user or group id's
* @param array $uids array of user or group id's for $uid_column='list_owners', or values for $uid_column,
* or whole where array: column-name => value(s) pairs
* @param string $uid_column='list_owner' column-name or null to use $uids as where array
* @param string $member_attr=null null: no members, 'contact_uid', 'contact_id', 'caldav_name' return members as that attribute
* @param boolean|int|array $limit_in_ab=false if true only return members from the same owners addressbook,
* if int|array only return members from the given owners addressbook(s)
* @return array with list_id => array(list_id,list_name,list_owner,...) pairs
*/
function get_lists($uids)
function get_lists($uids,$uid_column='list_owner',$member_attr=null,$limit_in_ab=false)
{
$user = $GLOBALS['egw_info']['user']['account_id'];
$lists = array();
foreach($this->db->select($this->lists_table,'*',array('list_owner'=>$uids),__LINE__,__FILE__,
false,'ORDER BY list_owner<>'.(int)$GLOBALS['egw_info']['user']['account_id'].',list_name') as $row)
if (is_array($uids) && isset($uids['list_carddav_name']))
{
$ids = array();
foreach((array)$uids['list_carddav_name'] as $carddav_name)
{
if (preg_match('/addressbook-lists-([0-9]+)-/',$carddav_name, $matches))
{
$ids[] = $matches[1];
}
}
unset($uids['list_carddav_name']);
if (!$ids) return array();
$uids[] = $this->db->expression($this->lists_table, $this->lists_table.'.',array('list_id' => $ids));
}
$lists = array();
$table_def = $this->db->get_table_definitions('phpgwapi',$this->lists_table);
$group_by = 'GROUP BY '.$this->lists_table.'.'.implode(','.$this->lists_table.'.',array_keys($table_def['fd']));
foreach($this->db->select($this->lists_table,$this->lists_table.'.*,MAX(list_added) AS list_modified',$uid_column?array($uid_column=>$uids):$uids,__LINE__,__FILE__,
false,$group_by.' ORDER BY list_owner<>'.(int)$GLOBALS['egw_info']['user']['account_id'].',list_name',false,0,
"LEFT JOIN $this->ab2list_table ON $this->ab2list_table.list_id=$this->lists_table.list_id") as $row)
{
if (!$row['list_id']) continue; // because of join, no lists at all still return one row of NULL
if ($member_attr) $row['members'] = array();
// generate UID and carddav_name for list_id
$row['list_uid'] = common::generate_uid('addressbook-lists', $row['list_id']);
$row['list_carddav_name'] = $row['list_uid'].'.vcf';
// set list_modified (=MAX(list_added)) as etag
if (!$row['list_modified']) $row['list_modified'] = $row['list_created'];
$row['list_etag'] = $row['list_modified'];
$lists[$row['list_id']] = $row;
}
//echo "<p>socontacts_sql::get_lists(".print_r($uids,true).")</p>\n"; _debug_array($lists);
if ($lists && $member_attr && in_array($member_attr,array('contact_id','contact_uid','caldav_name')))
{
if ($limit_in_ab)
{
$in_ab_join = " JOIN $this->lists_table ON $this->lists_table.list_id=$this->ab2list_table.list_id AND $this->lists_table.";
if (!is_bool($limit_in_ab))
{
$in_ab_join .= $this->db->expression($this->lists_table, array('list_owner'=>$limit_in_ab));
}
else
{
$in_ab_join .= "list_owner=$this->table_name.contact_owner";
}
}
foreach($this->db->select($this->ab2list_table,"$this->ab2list_table.list_id,$this->table_name.$member_attr",
$this->db->expression($this->ab2list_table, $this->ab2list_table.'.', array('list_id'=>array_keys($lists))),
__LINE__,__FILE__,false,$member_attr=='contact_id' ? '' :
'',false,0,"JOIN $this->table_name ON $this->ab2list_table.contact_id=$this->table_name.contact_id".$in_ab_join) as $row)
{
$lists[$row['list_id']]['members'][] = $row[$member_attr];
}
}
//error_log(__METHOD__.'('.array2string($uids).", '$uid_column', '$member_attr') returning ".array2string($lists));
return $lists;
}
/**
* Adds a distribution list
* Adds / updates a distribution list
*
* @param string $name list-name
* @param string|array $keys list-name or array with column-name => value pairs to specify the list
* @param int $owner user- or group-id
* @param array $contacts=array() contacts to add
* @return int/boolean integer list_id, true if the list already exists or false on error
* @param array $contacts=array() contacts to add (only for not yet existing lists!)
* @param array &$data=array() values for keys 'list_uid', 'list_carddav_name', 'list_name'
* @return int|boolean integer list_id or false on error
*/
function add_list($name,$owner,$contacts=array())
function add_list($keys,$owner,$contacts=array(),array &$data=array())
{
if (!$name || !(int)$owner) return false;
//error_log(__METHOD__.'('.array2string($keys).", $owner, ".array2string($contacts).', '.array2string($data).') '.function_backtrace());
if (!$keys && !$data || !(int)$owner) return false;
if ($this->db->select($this->lists_table,'list_id',array(
'list_name' => $name,
'list_owner' => $owner,
),__LINE__,__FILE__)->fetchColumn())
if ($keys && !is_array($keys)) $keys = array('list_name' => $keys);
if ($keys)
{
return true; // return existing list-id
$keys['list_owner'] = $owner;
}
if (!$this->db->insert($this->lists_table,array(
'list_name' => $name,
'list_owner' => $owner,
'list_created' => time(),
'list_creator' => $GLOBALS['egw_info']['user']['account_id'],
),array(),__LINE__,__FILE__)) return false;
if ((int)($list_id = $this->db->get_last_insert_id($this->lists_table,'list_id')) && $contacts)
else
{
foreach($contacts as $contact)
$data['list_owner'] = $owner;
}
if (isset($keys['list_carddav_name']))
{
if (isset($data['list_id'])) // use id if given
{
$this->add2list($list_id,$contact);
$keys = array(
'list_id' => $data['list_id'],
);
unset($data['list_id']);
}
else
{
$keys = false; // cant PUT a name in 11.1
}
}
if (!$keys || !($list_id = $this->db->select($this->lists_table,'list_id',$keys,__LINE__,__FILE__)->fetchColumn()))
{
$data['list_created'] = time();
$data['list_creator'] = $GLOBALS['egw_info']['user']['account_id'];
}
$data['list_modified'] = $data['list_etag'] = time();
$data['list_modifier'] = $GLOBALS['egw_info']['user']['account_id'];
if (!$data['list_id']) unset($data['list_id']);
if (!$this->db->insert($this->lists_table,$data,$keys,__LINE__,__FILE__)) return false;
if (!$list_id && ($list_id = $this->db->get_last_insert_id($this->lists_table,'list_id')))
{
$this->add2list($list_id,$contacts,array());
}
// generate UID and carddav_name for list_id, as we dont store them in 11.1
$data['list_uid'] = common::generate_uid('addressbook-lists', $list_id);
$data['list_carddav_name'] = $data['list_uid'].'.vcf';
if ($keys) $data += $keys;
//error_log(__METHOD__.'('.array2string($keys).", $owner, ...) data=".array2string($data).' returning '.array2string($list_id));
return $list_id;
}
/**
* Adds one contact to a distribution list
* Adds contact(s) to a distribution list
*
* @param int $contact contact_id
* @param int|array $contact contact_id(s)
* @param int $list list-id
* @param array $existing=null array of existing contact-id(s) of list, to not reread it, eg. array()
* @return false on error
*/
function add2list($contact,$list)
function add2list($contact,$list,array $existing=null)
{
if (!(int)$list || !(int)$contact) return false;
if (!(int)$list || !is_array($contact) && !(int)$contact) return false;
if ($this->db->select($this->ab2list_table,'list_id',array(
'contact_id' => $contact,
'list_id' => $list,
),__LINE__,__FILE__)->fetchColumn())
if (!is_array($existing))
{
$existing = array();
foreach($this->db->select($this->ab2list_table,'contact_id',array('list_id'=>$list),__LINE__,__FILE__) as $row)
{
$existing[] = $row['contact_id'];
}
}
if (!($to_add = array_diff((array)$contact,$existing)))
{
return true; // no need to insert it, would give sql error
}
return $this->db->insert($this->ab2list_table,array(
'contact_id' => $contact,
'list_id' => $list,
'list_added' => time(),
'list_added_by' => $GLOBALS['egw_info']['user']['account_id'],
),array(),__LINE__,__FILE__);
foreach($to_add as $contact)
{
$this->db->insert($this->ab2list_table,array(
'contact_id' => $contact,
'list_id' => $list,
'list_added' => time(),
'list_added_by' => $GLOBALS['egw_info']['user']['account_id'],
),array(),__LINE__,__FILE__);
}
}
/**
* Removes one contact from distribution list(s)
*
* @param int $contact contact_id
* @param int|array $contact contact_id(s)
* @param int $list=null list-id or null to remove from all lists
* @return false on error
*/
function remove_from_list($contact,$list=null)
{
if (!(int)$list && !is_null($list) || !(int)$contact) return false;
if (!(int)$list && !is_null($list) || !is_array($contact) && !(int)$contact) return false;
$where = array(
'contact_id' => $contact,
);
if (!is_null($list)) $where['list_id'] = $list;
return $this->db->delete($this->ab2list_table,$where,__LINE__,__FILE__);
if (!is_null($list))
{
$where['list_id'] = $list;
}
else
{
$list = array();
foreach($this->db->select($this->ab2list_table,'list_id',$where,__LINE__,__FILE__) as $row)
{
$list[] = $row['list_id'];
}
}
if (!$this->db->delete($this->ab2list_table,$where,__LINE__,__FILE__))
{
return false;
}
return true;
}
/**
@ -661,6 +669,31 @@ class addressbook_sql extends so_sql
return $this->db->affected_rows();
}
/**
* Get ctag (max list_modified as timestamp) for lists
*
* @param int|array $owner=null null for all lists user has access too
* @return int
*/
function lists_ctag($owner=null)
{
if (is_null($owner)) $owner = array_keys($this->grants);
if (!($modified = $this->db->select($this->lists_table,'MAX(list_added)',array('list_owner'=>$owner),
__LINE__,__FILE__,false,'',false,0,
"JOIN $this->ab2list_table ON $this->ab2list_table.list_id=$this->lists_table.list_id")->fetchColumn()))
{
$modified = 0;
}
if (!($created = $this->db->select($this->lists_table,'MAX(list_created)',array('list_owner'=>$owner),
__LINE__,__FILE__)->fetchColumn()))
{
$created = 0;
}
//error_log(__METHOD__.'('.array2string($owner).") MAX(list_added)=$modified, MAX(list_created)=$created returning ".array2string(max($modified,$created)));
return max($modified,$created);
}
/**
* Reads a contact, reimplemented to use the uid, if a non-numeric key is given
*
@ -682,6 +715,14 @@ class addressbook_sql extends so_sql
$keys = array('contact_uid' => $keys);
}
$contact = parent::read($keys,$extra_cols,$join);
// Change autoinc_id to match $this->db_cols
$this->autoinc_id = $this->db_cols[$this->autoinc_id];
if(($id = (int)$this->data[$this->autoinc_id]) && $cfs = $this->read_customfields($keys)) {
if (is_array($cfs[$id])) $contact = array_merge($contact,$cfs[$id]);
}
$this->autoinc_id = array_search($this->autoinc_id, $this->db_cols);
// enforce a minium uid strength
if (is_array($contact) && (!isset($contact['uid'])
|| strlen($contact['uid']) < $minimum_uid_length)) {
@ -730,12 +771,23 @@ class addressbook_sql extends so_sql
$this->data['etag'] = 0;
}
}
$update = array();
// enforce a minium uid strength
if (!$err && (!isset($this->data['uid'])
|| strlen($this->data['uid']) < $minimum_uid_length)) {
parent::update(array('uid' => common::generate_uid('addressbook',$this->data['id'])));
if (!isset($this->data['uid']) || strlen($this->data['uid']) < $minimum_uid_length)
{
$update['uid'] = common::generate_uid('addressbook',$this->data['id']);
//echo "<p>set uid={$this->data['uid']}, etag={$this->data['etag']}</p>";
}
// set carddav_name, if not given by caller
if (empty($this->data['carddav_name']))
{
$update['carddav_name'] = $this->data['id'].'.vcf';
}
if (!$err && $update)
{
parent::update($update);
}
return $err;
}
@ -752,4 +804,49 @@ class addressbook_sql extends so_sql
return $this->db->select($this->lists_table,'*',array('list_id'=>$list),__LINE__,__FILE__)->fetch();
}
/**
* saves custom field data
* Re-implemented to deal with extra contact_owner column
*
* @param array $data data to save (cf's have to be prefixed with self::CF_PREFIX = #)
* @return bool false on success, errornumber on failure
*/
function save_customfields($data)
{
foreach ((array)$this->customfields as $name => $options)
{
if (!isset($data[$field = $this->get_cf_field($name)])) continue;
$where = array(
$this->extra_id => $data['id'],
$this->extra_key => $name,
);
$is_multiple = $this->is_multiple($name);
// we explicitly need to delete fields, if value is empty or field allows multiple values or we have no unique index
if(empty($data[$field]) || $is_multiple || !$this->extra_has_unique_index)
{
$this->db->delete($this->extra_table,$where,__LINE__,__FILE__,$this->app);
if (empty($data[$field])) continue; // nothing else to do for empty values
}
foreach($is_multiple && !is_array($data[$field]) ? explode(',',$data[$field]) : (array)$data[$field] as $value)
{
if (!$this->db->insert($this->extra_table,array($this->extra_value => $value, 'contact_owner' => $data['owner']),$where,__LINE__,__FILE__,$this->app))
{
return $this->db->Errno;
}
}
}
return false; // no error
}
/**
* Deletes custom field data
* Implemented to deal with LDAP backend, which saves CFs in SQL, but the account record is in LDAP
*/
function delete_customfields($data)
{
$this->db->delete($this->extra_table,$data,__LINE__,__FILE__);
}
}

View File

@ -34,7 +34,7 @@ class addressbook_ui extends addressbook_bo
*
* @var boolean
*/
protected $private_addressbook = false;
public $private_addressbook = false;
protected $org_views;
/**

View File

@ -72,7 +72,9 @@ class addressbook_vcal extends addressbook_bo
'X-ASSISTANT' => array('assistent'),
'X-ASSISTANT-TEL' => array('tel_assistent'),
'UID' => array('uid'),
);
'REV' => array('modified'),
//set for Apple: 'X-ABSHOWAS' => array('fileas_type'), // Horde vCard class uses uppercase prop-names!
);
/**
* VCard version
@ -175,23 +177,6 @@ class addressbook_vcal extends addressbook_bo
{
$contact['cat_id'] = implode(',',$this->find_or_add_categories($contact['cat_id'], -1));
}
if (isset($GLOBALS['egw_info']['user']['preferences']['syncml']['filter_addressbook']))
{
$owner = $GLOBALS['egw_info']['user']['preferences']['syncml']['filter_addressbook'];
switch ($owner)
{
case 'G':
$contact['owner'] = $GLOBALS['egw_info']['user']['account_primary_group'];
break;
case 'P':
case 'N':
case 0:
$contact['owner'] = $this->user;
break;
default:
$contact['owner'] = (int)$owner;
}
}
}
if (isset($contact['owner']) && $contact['owner'] != $this->user)
{
@ -220,8 +205,10 @@ class addressbook_vcal extends addressbook_bo
#Horde::logMessage("vCalAddressbook clientProperties:\n" . print_r($this->clientProperties, true), __FILE__, __LINE__, PEAR_LOG_DEBUG);
$vCard = new Horde_iCalendar_vcard($this->version);
$vCard->setAttribute('PRODID','-//EGroupware//NONSGML EGroupware Addressbook '.$GLOBALS['egw_info']['apps']['phpgwapi']['version'].'//'.
strtoupper($GLOBALS['egw_info']['user']['preferences']['common']['lang']));
$sysCharSet = $GLOBALS['egw']->translation->charset();
$sysCharSet = translation::charset();
// KAddressbook and Funambol4BlackBerry always requires non-ascii chars to be qprint encoded.
if ($this->productName == 'kde' ||
@ -295,6 +282,11 @@ class addressbook_vcal extends addressbook_bo
switch ($databaseField)
{
case 'modified':
$value = gmdate("Y-m-d\TH:i:s\Z",egw_time::user2server($value));
$hasdata++;
break;
case 'private':
$value = $value ? 'PRIVATE' : 'PUBLIC';
$hasdata++;
@ -339,9 +331,9 @@ class addressbook_vcal extends addressbook_bo
break;
case 'cat_id':
if (!empty($value) && ($values = $this->get_categories($value)))
if (!empty($value) && ($values = /*str_replace(',','\\,',*/$this->get_categories($value)))//)
{
$values = (array) $GLOBALS['egw']->translation->convert($values, $sysCharSet, $_charset);
$values = (array) translation::convert($values, $sysCharSet, $_charset);
$value = implode(',', $values); // just for the CHARSET recognition
if (($size > 0) && strlen($value) > $size)
{
@ -388,6 +380,18 @@ class addressbook_vcal extends addressbook_bo
}
break;
case 'n_fn':
case 'fileas_type':
// mark entries with fileas_type == 'org_name' as X-ABSHOWAS:COMPANY (Apple AB specific)
if (isset($this->supportedFields['X-ABSHOWAS']) &&
$entry['org_name'] == $entry['n_fileas'] && $entry['fileas_type'] == 'org_name')
{
if ($vcardField == 'X-ABSHOWAS') $value = 'COMPANY';
if ($databaseField == 'n_fn') $value = $entry['org_name'];
}
//error_log("vcardField='$vcardField', databaseField='$databaseField', this->supportedFields['X-ABSHOWAS']=".array2string($this->supportedFields['X-ABSHOWAS'])." --> value='$value'");
// fall-through
default:
if (($size > 0) && strlen(implode(',', $values) . $value) > $size)
{
@ -425,7 +429,7 @@ class addressbook_vcal extends addressbook_bo
|| in_array($vcardField,array('FN','ORG','N'))
|| ($size >= 0 && !$noTruncate))
{
$value = $GLOBALS['egw']->translation->convert(trim($value), $sysCharSet, $_charset);
$value = translation::convert(trim($value), $sysCharSet, $_charset);
$values[] = $value;
if (preg_match('/[^\x20-\x7F]/', $value))
{
@ -478,7 +482,6 @@ class addressbook_vcal extends addressbook_bo
}
$vCard->setAttribute($vcardField, $value, $options, true, $values);
//$vCard->setParameter($vcardField, $options);
}
$result = $vCard->exportvCalendar($_charset);
@ -535,43 +538,6 @@ class addressbook_vcal extends addressbook_bo
// the horde class does the charset conversion. DO NOT CONVERT HERE.
// be as flexible as possible
$databaseFields = array(
'ADR;WORK' => array('','adr_one_street2','adr_one_street','adr_one_locality','adr_one_region',
'adr_one_postalcode','adr_one_countryname'),
'ADR;HOME' => array('','adr_two_street2','adr_two_street','adr_two_locality','adr_two_region',
'adr_two_postalcode','adr_two_countryname'),
'BDAY' => array('bday'),
'X-CLASS' => array('private'),
'CLASS' => array('private'),
'CATEGORIES' => array('cat_id'),
'EMAIL;WORK' => array('email'),
'EMAIL;HOME' => array('email_home'),
'N' => array('n_family','n_given','n_middle',
'n_prefix','n_suffix'),
'FN' => array('n_fn'),
'NOTE' => array('note'),
'ORG' => array('org_name','org_unit','room'),
'TEL;CELL;WORK' => array('tel_cell'),
'TEL;CELL;HOME' => array('tel_cell_private'),
'TEL;CAR' => array('tel_car'),
'TEL;OTHER' => array('tel_other'),
'TEL;VOICE;WORK' => array('tel_work'),
'TEL;FAX;WORK' => array('tel_fax'),
'TEL;HOME;VOICE' => array('tel_home'),
'TEL;FAX;HOME' => array('tel_fax_home'),
'TEL;PAGER' => array('tel_pager'),
'TITLE' => array('title'),
'URL;WORK' => array('url'),
'URL;HOME' => array('url_home'),
'ROLE' => array('role'),
'NICKNAME' => array('label'),
'FBURL' => array('freebusy_uri'),
'PHOTO' => array('jpegphoto'),
'X-ASSISTANT' => array('assistent'),
'X-ASSISTANT-TEL' => array('tel_assistent'),
'UID' => array('uid'),
);
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" .
@ -589,7 +555,7 @@ class addressbook_vcal extends addressbook_bo
}
$vcardValues = $vCard->getAllAttributes();
if (isset($GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length']))
if (!empty($GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length']))
{
$minimum_uid_length = $GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length'];
}
@ -609,6 +575,7 @@ class addressbook_vcal extends addressbook_bo
$url = 1;
$pref_tel = false;
$rowNames = array();
foreach($vcardValues as $key => $vcardRow)
{
$rowName = strtoupper($vcardRow['name']);
@ -686,7 +653,7 @@ class addressbook_vcal extends addressbook_bo
switch ($pname)
{
case 'PREF':
if ($rowName == 'TEL' && !$pref_tel)
if (substr($rowName,0,3) == 'TEL' && !$pref_tel)
{
$pref_tel = $key;
}
@ -746,6 +713,12 @@ class addressbook_vcal extends addressbook_bo
$rowName = 'URL;X-egw-Ref' . $url++;
}
// current algorithm cant cope with multiple attributes of same name
// --> cumulate them in values, so they can be used later (works only for values, not for parameters!)
if (($k = array_search($rowName, $rowNames)) != false)
{
$vcardValues[$k]['values'] = array_merge($vcardValues[$k]['values'],$vcardValues[$key]['values']);
}
$rowNames[$key] = $rowName;
}
@ -812,7 +785,7 @@ class addressbook_vcal extends addressbook_bo
{
$finalRowNames['TEL;OTHER'] = $vcardKey;
}
break;
break;
case 'TEL;PAGER;WORK':
case 'TEL;PAGER;HOME':
if (!in_array('TEL;PAGER', $rowNames)
@ -820,7 +793,7 @@ class addressbook_vcal extends addressbook_bo
{
$finalRowNames['TEL;PAGER'] = $vcardKey;
}
break;
break;
case 'TEL;CAR;VOICE':
case 'TEL;CAR;CELL':
case 'TEL;CAR;CELL;VOICE':
@ -930,9 +903,9 @@ class addressbook_vcal extends addressbook_bo
foreach ($finalRowNames as $key => $vcardKey)
{
if (isset($databaseFields[$key]))
if (isset($this->supportedFields[$key]))
{
$fieldNames = $databaseFields[$key];
$fieldNames = $this->supportedFields[$key];
foreach ($fieldNames as $fieldKey => $fieldName)
{
if (!empty($fieldName))
@ -968,6 +941,14 @@ class addressbook_vcal extends addressbook_bo
$contact[$fieldName] = str_replace("\r\n", "\n", $vcardValues[$vcardKey]['value']);
break;
case 'fileas_type':
// store Apple's X-ABSHOWAS:COMPANY as fileas_type == 'org_name'
if ($vcardValues[$vcardKey]['value'] == 'COMPANY')
{
$contact[$fieldName] = 'org_name';
}
break;
case 'uid':
if (strlen($value) < $minimum_uid_length) {
// we don't use it
@ -980,10 +961,24 @@ class addressbook_vcal extends addressbook_bo
}
}
}
// add unsupported attributes as with '##' prefix
elseif(($attribute = $vcardValues[$vcardKey]) && !in_array($attribute['name'],array('PRODID','REV')))
{
// for attributes with multiple values in multiple lines, merge the values
if (isset($contact['##'.$attribute['name']]))
{
error_log(__METHOD__."() contact['##$attribute[name]'] = ".array2string($contact['##'.$attribute['name']]));
$attribute['values'] = array_merge(
is_array($contact['##'.$attribute['name']]) ? $contact['##'.$attribute['name']]['values'] : (array)$contact['##'.$attribute['name']],
$attribute['values']);
}
$contact['##'.$attribute['name']] = $attribute['params'] || count($attribute['values']) > 1 ?
serialize($attribute) : $attribute['value'];
}
}
$this->fixup_contact($contact);
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__ .
@ -1026,8 +1021,37 @@ class addressbook_vcal extends addressbook_bo
if (!$file)
{
$GLOBALS['egw']->common->egw_exit();
common::egw_exit();
}
return true;
}
/**
* return a groupVCard
*
* @param array $list values for 'list_uid', 'list_name', 'list_modified', 'members'
* @param string $version='3.0' vcard version
* @return string containing the vcard
*/
function getGroupVCard(array $list,$version='3.0')
{
require_once(EGW_SERVER_ROOT.'/phpgwapi/inc/horde/Horde/iCalendar/vcard.php');
$vCard = new Horde_iCalendar_vcard($version);
$vCard->setAttribute('PRODID','-//EGroupware//NONSGML EGroupware Addressbook '.$GLOBALS['egw_info']['apps']['phpgwapi']['version'].'//'.
strtoupper($GLOBALS['egw_info']['user']['preferences']['common']['lang']));
$vCard->setAttribute('N',$list['list_name'],array(),true,array($list['list_name'],'','','',''));
$vCard->setAttribute('FN',$list['list_name']);
$vCard->setAttribute('X-ADDRESSBOOKSERVER-KIND','group');
foreach($list['members'] as $uid)
{
$vCard->setAttribute('X-ADDRESSBOOKSERVER-MEMBER','urn:uuid:'.$uid);
}
$vCard->setAttribute('REV',egw_time::to($list['list_modified'],'Y-m-d\TH:i:s\Z'));
$vCard->setAttribute('UID',$list['list_uid']);
return $vCard->exportvCalendar();
}
}

File diff suppressed because one or more lines are too long

View File

@ -13,7 +13,7 @@
</hbox>
<styles>.rightPadAdd { width: 30px; }</styles>
</template>
<template id="addressbook.index.rows" template="" lang="" group="0" version="1.7.004">
<template id="addressbook.index.rows" template="" lang="" group="0" version="1.8.004">
<grid width="100%">
<columns>
<column/>
@ -71,7 +71,7 @@
<nextmatch-header id="bday" label="Birthday"/>
<vbox options="0,0">
<nextmatch-header label="Business address" id="business"/>
<nextmatch-customfilter id="adr_one_countryname" options="select-country,Country,1" class="countrySelect"/>
<nextmatch-customfilter id="adr_one_countrycode" options="select-country,Country,1" class="countrySelect"/>
<nextmatch-sortheader id="adr_one_postalcode" label="zip code"/>
</vbox>
<nextmatch-header label="Home address" id="home"/>

View File

@ -0,0 +1,246 @@
<?php
/**
* EGgroupware admin - access- and session-log
*
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @package admin
* @copyright (c) 2009-11 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
/**
* Show EGroupware access- and session-log
*/
class admin_accesslog
{
/**
* Which methods of this class can be called as menuation
*
* @var array
*/
public $public_functions = array(
'index' => true,
'sessions' => true,
);
/**
* Our storage object
*
* @var so_sql
*/
protected $so;
/**
* Name of our table
*/
const TABLE = 'egw_access_log';
/**
* Name of app the table is registered
*/
const APP = 'phpgwapi';
/**
* Constructor
*
*/
function __construct()
{
$this->so = new so_sql(self::APP,self::TABLE,null,'',true);
}
/**
* query rows for the nextmatch widget
*
* @param array $query with keys 'start', 'search', 'order', 'sort', 'col_filter'
* @param array &$rows returned rows/competitions
* @param array &$readonlys eg. to disable buttons based on acl, not use here, maybe in a derived class
* @return int total number of rows
*/
function get_rows($query,&$rows,&$readonlys)
{
$heartbeat_limit = egw_session::heartbeat_limit();
if ($query['session_list']) // filter active sessions
{
$query['col_filter']['lo'] = null; // not logged out
$query['col_filter'][0] = 'session_dla > '.(int)(time() - $GLOBALS['egw_info']['server']['sessions_timeout']);
$query['col_filter'][1] = "(notification_heartbeat IS NULL OR notification_heartbeat > $heartbeat_limit)";
}
$total = $this->so->get_rows($query,$rows,$readonlys);
$no_kill = !$GLOBALS['egw']->acl->check('current_sessions_access',8,'admin') && !$query['session_list'];
foreach($rows as &$row)
{
$row['sessionstatus'] = lang('success');
if ($row['notification_heartbeat'] > $heartbeat_limit)
{
$row['sessionstatus'] = lang('active');
}
if (stripos($row['session_php'],'blocked') !== false ||
stripos($row['session_php'],'bad login') !== false ||
strpos($row['sessioin_php'],' ') !== false)
{
$row['sessionstatus'] = $row['session_php'];
}
if ($row['lo']) {
$row['total'] = ($row['lo'] - $row['li']) / 60;
$row['sessionstatus'] = lang('logged out');
}
// eg. for bad login or password
if (!$row['account_id']) $row['alt_loginid'] = $row['loginid'];
$readonlys['kill['.$row['sessionid'].']'] = $no_kill;
$readonlys['delete['.$row['sessionid'].']'] = $query['session_list'];
// do not allow to kill or select own session
if ($GLOBALS['egw']->session->sessionid_access_log == $row['sessionid'] && $query['session_list'])
{
$readonlys['kill['.$row['sessionid'].']'] = $readonlys['selected['.$row['sessionid'].']'] = true;
}
// do not allow to delete access log off active sessions
if (!$row['lo'] && $row['session_dla'] > time()-$GLOBALS['egw_info']['server']['sessions_timeout'] && !$query['session_list'])
{
$readonlys['delete['.$row['sessionid'].']'] = $readonlys['selected['.$row['sessionid'].']'] = true;
}
unset($row['session_php']); // for security reasons, do NOT give real PHP sessionid to UI
}
if ($query['session_list'])
{
$rows['no_total'] = $rows['no_lo'] = true;
}
$GLOBALS['egw_info']['flags']['app_header'] = lang('Admin').' - '.
($query['session_list'] ? lang('View sessions') : lang('View Access Log')).
($query['col_filter']['account_id'] ? ': '.common::grab_owner_name($query['col_filter']['account_id']) : '');
return $total;
}
/**
* Display the access log or session list
*
* @param array $content=null
* @param string $msg=''
* @param boolean $sessions_list=false
*/
function index(array $content=null, $msg='', $sessions_list=false)
{
//_debug_array($content);
if (is_array($content)) $sessions_list = $content['nm']['session_list'];
// check if user has access to requested functionality
if ($GLOBALS['egw']->acl->check($sessions_list ? 'current_sessions_access' : 'access_log_access',1,'admin'))
{
$GLOBALS['egw']->redirect_link('/index.php');
}
if(!isset($content))
{
$content['nm'] = array(
'get_rows' => 'admin.admin_accesslog.get_rows', // I method/callback to request the data for the rows eg. 'notes.bo.get_rows'
'no_filter' => True, // I disable the 1. filter
'no_filter2' => True, // I disable the 2. filter (params are the same as for filter)
'no_cat' => True, // I disable the cat-selectbox
'header_left' => false, // I template to show left of the range-value, left-aligned (optional)
'header_right' => false, // I template to show right of the range-value, right-aligned (optional)
'never_hide' => True, // I never hide the nextmatch-line if less then maxmatch entries
'lettersearch' => false, // I show a lettersearch
'start' => 0, // IO position in list
'order' => 'li', // IO name of the column to sort after (optional for the sortheaders)
'sort' => 'DESC', // IO direction of the sort: 'ASC' or 'DESC'
//'default_cols' => // I columns to use if there's no user or default pref (! as first char uses all but the named columns), default all columns
'csv_fields' => false, // I false=disable csv export, true or unset=enable it with auto-detected fieldnames,
//or array with name=>label or name=>array('label'=>label,'type'=>type) pairs (type is a eT widget-type)
);
if ((int)$_GET['account_id'])
{
$content['nm']['col_filter']['account_id'] = (int)$_GET['account_id'];
}
if ($sessions_list)
{
$content['nm']['order'] = 'session_dla';
$content['nm']['options-selectcols'] = array(
'lo' => false,
'total' => false,
);
}
$content['nm']['session_list'] = $sessions_list;
}
elseif(isset($content['nm']['rows']['delete']) || isset($content['delete']))
{
if (isset($content['nm']['rows']['delete']))
{
list($sessionid) = each($content['nm']['rows']['delete']);
unset($content['nm']['rows']['delete']);
}
else
{
unset($content['delete']);
$sessionid = $content['nm']['rows']['selected'];
}
if ($sessionid && $this->so->delete(array('sessionid' => $sessionid)))
{
$msg = lang('%1 log entries deleted.',1);
}
else
{
$msg = lang('Error deleting log entry!');
}
}
elseif(isset($content['nm']['rows']['kill']) || isset($content['kill']))
{
if (isset($content['nm']['rows']['kill']))
{
list($sessionid) = each($content['nm']['rows']['kill']);
$sessionid = array($sessionid);
unset($content['nm']['rows']['kill']);
}
else
{
unset($content['kill']);
$sessionid = $content['nm']['rows']['selected'];
}
if (($key = array_search($GLOBALS['egw']->session->sessionid_access_log, $sessionid)))
{
unset($sessionid[$key]); // dont allow to kill own sessions
}
if ($GLOBALS['egw']->acl->check('current_sessions_access',8,'admin'))
{
$msg = lang('Permission denied!');
}
else
{
foreach((array)$sessionid as $id)
{
$GLOBALS['egw']->session->destroy($id);
}
$msg = lang('%1 sessions killed',count($sessionid));
}
}
$readonlys['kill'] = !$sessions_list;
$readonlys['delete'] = $sessions_list;
$content['msg'] = $msg;
$content['percent'] = 100.0 * $GLOBALS['egw']->db->query(
'SELECT ((SELECT COUNT(*) FROM '.self::TABLE.' WHERE lo != 0) / COUNT(*)) FROM '.self::TABLE,
__LINE__,__FILE__)->fetchColumn();
$tmpl = new etemplate('admin.accesslog');
$tmpl->exec('admin.admin_accesslog.index',$content,$sel_options,$readonlys,array(
'nm' => $content['nm'],
));
}
/**
* Display session list
*
* @param array $content=null
* @param string $msg=''
*/
function sessions(array $content=null, $msg='')
{
return $this->index(null,$msg,true);
}
}

View File

@ -78,7 +78,7 @@ class admin_prefs_sidebox_hooks
if (! $GLOBALS['egw']->acl->check('current_sessions_access',1,'admin'))
{
$file['View Sessions'] = egw::link('/index.php','menuaction=admin.uicurrentsessions.list_sessions');
$file['View Sessions'] = egw::link('/index.php','menuaction=admin.admin_accesslog.sessions');
}
if (! $GLOBALS['egw']->acl->check('access_log_access',1,'admin'))

View File

@ -1,71 +0,0 @@
<?php
/**************************************************************************\
* eGroupWare - Administration *
* http://www.egroupware.org *
* This file written by Joseph Engo <jengo@phpgroupware.org> *
* -------------------------------------------- *
* 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$ */
class bocurrentsessions
{
var $ui;
var $so;
var $public_functions = array(
'kill' => True
);
function total()
{
return $GLOBALS['egw']->session->session_count();
}
function list_sessions($start,$order,$sort)
{
$values = $GLOBALS['egw']->session->session_list($start,$sort,$order);
while (list(,$value) = @each($values))
{
if (strpos($value['session_lid'],'@') !== false)
{
$t = explode('@',$value['session_lid']);
$session_lid = $t[0];
}
else
{
$session_lid = $value['session_lid'];
}
$tmp = time() - $value['session_dla'];
$secs = $tmp % 60;
$mins = (($tmp - $secs) % 3600) / 60;
$hours = ($tmp - ($mins * 60) - $secs) / 3600;
$_values[] = array(
'session_id' => $value['session_id'],
'session_lid' => $session_lid,
'session_ip' => $value['session_ip'],
'session_logintime' => $GLOBALS['egw']->common->show_date($value['session_logintime']),
'session_action' => $value['session_action'],
'session_dla' => $value['session_dla'],
'session_idle' => str_pad($hours, 2, '0', STR_PAD_LEFT) . ':' . str_pad($mins, 2, '0', STR_PAD_LEFT) . ':' . str_pad($secs, 2, '0', STR_PAD_LEFT)
);
}
return $_values;
}
function kill()
{
if ($_GET['ksession'] &&
($GLOBALS['sessionid'] != $_GET['ksession']) &&
! $GLOBALS['egw']->acl->check('current_sessions_access',8,'admin'))
{
$GLOBALS['egw']->session->destroy($_GET['ksession'],0);
}
$this->ui =& CreateObject('admin.uicurrentsessions');
$this->ui->list_sessions();
}
}

View File

@ -7,6 +7,7 @@
%1 log entries deleted. admin de %1 Protokolleinträge gelöscht.
%1 not found or not executable !!! admin de %1 nicht gefunden oder nicht ausführbar !!!
%1 rights for %2 and applications %3 admin de %1 Rechte für %2 und Anwendung(en) %3
%1 sessions killed admin de %1 Sitzungen beendet
%1 user %2 admin de %1 Benutzer %2
(default no, leave it off if you dont use it) admin de (Vorgabe Nein, ausgeschaltet lassen, wenn nicht benutzt)
(stored password will not be shown here) admin de (Gespeichertes Passwort wird hier nicht angezeigt)
@ -25,8 +26,8 @@ account preferences admin de Einstellungen der Benutzerkonten
account-id's have to be integers! admin de Konten-ID`s müssen vom Typ Integer (Zahl) sein!
acl manager admin de ACL-Manager
acl rights common de ACL-Rechte
action admin de Aktion
actions admin de Aktionen
action admin de Befehl
actions admin de Befehle
activate wysiwyg-editor admin de WYSIWYG Editor (formatierter Text) aktivieren
add a category admin de Eine Kategorie hinzufügen
add a group admin de Eine Gruppe hinzufügen
@ -147,6 +148,7 @@ delete peer server admin de Server von Serververbund löschen
delete selected entries admin de Ausgewählte Einträge löschen
delete the category admin de Kategorie löschen
delete the group admin de Gruppe löschen
delete the selected entries admin de Die ausgewählten Einträge löschen
delete this category admin de Kategorie löschen
delete this group admin de Gruppe löschen
delete this log entry admin de Diesen Protokolleintrag löschen
@ -305,6 +307,7 @@ kill admin de Beenden
kill session admin de Sitzung beenden
last %1 logins admin de Letze %1 Logins
last %1 logins for %2 admin de Letze %1 Logins für %2
last action admin de Letzte Aktion
last login admin de Letzter Login
last login from admin de Letzer Login von
last submission: admin de Letzte Absendung:

View File

@ -7,6 +7,7 @@
%1 log entries deleted. admin en %1 log entries deleted.
%1 not found or not executable !!! admin en %1 not found or not executable !!!
%1 rights for %2 and applications %3 admin en %1 rights for %2 and applications %3
%1 sessions killed admin en %1 sessions killed
%1 user %2 admin en %1 user %2
(default no, leave it off if you dont use it) admin en (default No, leave it off if you dont use it)
(stored password will not be shown here) admin en (Stored password will not be shown here)
@ -149,6 +150,7 @@ delete peer server admin en Delete peer server
delete selected entries admin en Delete selected entries
delete the category admin en delete the category
delete the group admin en delete the group
delete the selected entries admin en Delete the selected entries
delete this category admin en delete this category
delete this group admin en delete this group
delete this log entry admin en Delete this log entry
@ -310,6 +312,7 @@ kill admin en Kill
kill session admin en Kill session
last %1 logins admin en Last %1 logins
last %1 logins for %2 admin en Last %1 logins for %2
last action admin en Last action
last login admin en last login
last login from admin en last login from
last submission: admin en Last submission:

View File

@ -0,0 +1,75 @@
<?php
/**
* eGroupWare - eTemplates for Application admin
* http://www.egroupware.org
* generated by soetemplate::dump4setup() 2011-04-13 15:34
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package admin
* @subpackage setup
* @version $Id$
*/
$templ_version=1;
$templ_data[] = array('name' => 'admin.accesslog','template' => '','lang' => '','group' => '0','version' => '1.9.001','data' => 'a:1:{i:0;a:5:{s:4:"type";s:4:"grid";s:4:"data";a:4:{i:0;a:1:{s:2:"h1";s:6:",!@msg";}i:1;a:2:{s:1:"A";a:4:{s:4:"span";s:13:"all,redItalic";s:5:"align";s:6:"center";s:4:"name";s:3:"msg";s:4:"type";s:5:"label";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:2;a:2:{s:1:"A";a:4:{s:4:"span";s:3:"all";s:4:"name";s:2:"nm";s:4:"size";s:20:"admin.accesslog.rows";s:4:"type";s:9:"nextmatch";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:3;a:2:{s:1:"A";a:4:{s:4:"size";s:6:"2,,0,0";s:4:"type";s:4:"hbox";i:1;a:3:{s:8:"readonly";s:4:"true";s:4:"type";s:5:"label";s:5:"label";s:32:"Percent of users that logged out";}i:2;a:6:{s:4:"type";s:5:"float";s:9:"precision";s:1:"1";s:5:"label";s:6:": %s %";s:8:"readonly";s:4:"true";s:4:"name";s:7:"percent";s:4:"size";s:1:",";}}s:1:"B";a:6:{s:5:"align";s:5:"right";s:4:"type";s:4:"hbox";i:1;a:6:{s:5:"label";s:6:"Delete";s:7:"onclick";s:46:"return confirm(\'Delete the selected entries\');";s:4:"name";s:6:"delete";s:4:"type";s:6:"button";s:4:"size";s:6:"delete";s:4:"help";s:23:"Delete selected entries";}i:2;a:5:{s:4:"type";s:6:"button";s:4:"size";s:5:"close";s:5:"label";s:4:"Kill";s:4:"name";s:4:"kill";s:7:"onclick";s:63:"return confirm(\'Are you sure you want to kill this session ?\');";}s:4:"size";s:1:"3";i:3;a:4:{s:4:"type";s:10:"buttononly";s:4:"size";s:9:"arrow_ltr";s:5:"label";s:10:"Select all";s:7:"onclick";s:61:"toggle_all(this.form,form::name(\'selected[]\')); return false;";}}}}s:4:"cols";i:2;s:4:"rows";i:3;s:4:"size";s:4:"100%";}}','size' => '100%','style' => '.selectAllArrow { padding-right: 12px; }','modified' => '1302686599',);
$templ_data[] = array('name' => 'admin.accesslog.get_rows','template' => '','lang' => '','group' => '0','version' => '1.3.001','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:3:{i:0;a:2:{s:2:"c1";s:2:"th";s:2:"c2";s:3:"row";}i:1;a:4:{s:1:"A";a:3:{s:4:"type";s:20:"nextmatch-sortheader";s:5:"label";s:7:"LoginID";s:4:"name";s:7:"loginid";}s:1:"B";a:3:{s:4:"type";s:20:"nextmatch-sortheader";s:5:"label";s:2:"IP";s:4:"name";s:2:"ip";}s:1:"C";a:3:{s:4:"type";s:20:"nextmatch-sortheader";s:5:"label";s:9:"Logintime";s:4:"name";s:2:"li";}s:1:"D";a:3:{s:4:"type";s:20:"nextmatch-sortheader";s:5:"label";s:9:"Logoutime";s:4:"name";s:2:"lo";}}i:2;a:4:{s:1:"A";a:3:{s:4:"type";s:14:"select-account";s:4:"name";s:15:"${row}[loginid]";s:8:"readonly";s:1:"1";}s:1:"B";a:3:{s:4:"type";s:5:"label";s:4:"name";s:10:"${row}[ip]";s:7:"no_lang";s:1:"1";}s:1:"C";a:3:{s:4:"type";s:9:"date-time";s:4:"name";s:10:"${row}[li]";s:8:"readonly";s:1:"1";}s:1:"D";a:3:{s:4:"type";s:9:"date-time";s:4:"name";s:10:"${row}[lo]";s:8:"readonly";s:1:"1";}}}s:4:"rows";i:2;s:4:"cols";i:4;s:4:"size";s:4:"100%";s:7:"options";a:1:{i:0;s:4:"100%";}}}','size' => '100%','style' => '','modified' => '1164479930',);
$templ_data[] = array('name' => 'admin.accesslog.rows','template' => '','lang' => '','group' => '0','version' => '1.9.001','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:3:{i:0;a:6:{s:2:"c1";s:2:"th";s:2:"c2";s:3:"row";s:1:"G";s:10:",@no_total";s:1:"F";s:7:",@no_lo";s:1:"B";s:18:",@no_sessionstatus";s:1:"J";s:2:"1%";}i:1;a:10:{s:1:"A";a:3:{s:4:"type";s:23:"nextmatch-accountfilter";s:4:"size";s:7:"LoginID";s:4:"name";s:10:"account_id";}s:1:"B";a:3:{s:4:"type";s:16:"nextmatch-header";s:5:"label";s:12:"Login-Status";s:4:"name";s:13:"sessionstatus";}s:1:"C";a:3:{s:4:"type";s:16:"nextmatch-header";s:5:"label";s:7:"Loginid";s:4:"name";s:7:"loginid";}s:1:"D";a:3:{s:4:"type";s:16:"nextmatch-header";s:5:"label";s:2:"IP";s:4:"name";s:2:"ip";}s:1:"E";a:3:{s:4:"type";s:20:"nextmatch-sortheader";s:5:"label";s:5:"Login";s:4:"name";s:2:"li";}s:1:"F";a:3:{s:4:"type";s:20:"nextmatch-sortheader";s:5:"label";s:6:"Logout";s:4:"name";s:2:"lo";}s:1:"G";a:3:{s:4:"type";s:16:"nextmatch-header";s:5:"label";s:5:"Total";s:4:"name";s:5:"total";}s:1:"H";a:3:{s:4:"type";s:20:"nextmatch-sortheader";s:5:"label";s:4:"Idle";s:4:"name";s:11:"session_dla";}s:1:"I";a:3:{s:4:"type";s:16:"nextmatch-header";s:5:"label";s:11:"Last action";s:4:"name";s:14:"session_action";}s:1:"J";a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";i:1;a:3:{s:4:"type";s:5:"label";s:5:"label";s:7:"Actions";s:5:"align";s:6:"center";}i:2;a:4:{s:4:"type";s:10:"buttononly";s:4:"size";s:5:"check";s:5:"label";s:10:"Select all";s:7:"onclick";s:61:"toggle_all(this.form,form::name(\'selected[]\')); return false;";}}}i:2;a:10:{s:1:"A";a:4:{s:4:"type";s:14:"select-account";s:4:"name";s:18:"${row}[account_id]";s:8:"readonly";s:1:"1";s:5:"label";s:22:"$row_cont[alt_loginid]";}s:1:"B";a:2:{s:4:"type";s:5:"label";s:4:"name";s:21:"${row}[sessionstatus]";}s:1:"C";a:2:{s:4:"type";s:5:"label";s:4:"name";s:15:"${row}[loginid]";}s:1:"D";a:2:{s:4:"type";s:5:"label";s:4:"name";s:10:"${row}[ip]";}s:1:"E";a:3:{s:4:"type";s:9:"date-time";s:4:"name";s:10:"${row}[li]";s:8:"readonly";s:1:"1";}s:1:"F";a:3:{s:4:"type";s:9:"date-time";s:4:"name";s:10:"${row}[lo]";s:8:"readonly";s:1:"1";}s:1:"G";a:4:{s:4:"type";s:13:"date-duration";s:4:"name";s:13:"${row}[total]";s:8:"readonly";s:1:"1";s:4:"size";s:6:",hm,24";}s:1:"H";a:3:{s:4:"type";s:10:"date-since";s:4:"name";s:19:"${row}[session_dla]";s:8:"readonly";s:1:"1";}s:1:"I";a:2:{s:4:"type";s:5:"label";s:4:"name";s:22:"${row}[session_action]";}s:1:"J";a:6:{s:4:"type";s:4:"hbox";s:4:"size";s:6:"3,,0,0";i:1;a:6:{s:4:"type";s:6:"button";s:4:"size";s:6:"delete";s:5:"label";s:6:"Delete";s:4:"name";s:28:"delete[$row_cont[sessionid]]";s:4:"help";s:21:"Delete this log entry";s:7:"onclick";s:40:"return confirm(\'Delete this log entry\');";}i:2;a:5:{s:4:"type";s:6:"button";s:4:"size";s:5:"close";s:5:"label";s:4:"Kill";s:4:"name";s:26:"kill[$row_cont[sessionid]]";s:7:"onclick";s:63:"return confirm(\'Are you sure you want to kill this session ?\');";}s:5:"align";s:6:"center";i:3;a:4:{s:4:"type";s:8:"checkbox";s:4:"size";s:20:"$row_cont[sessionid]";s:4:"name";s:10:"selected[]";s:5:"align";s:5:"right";}}}}s:4:"rows";i:2;s:4:"cols";i:10;s:4:"size";s:4:"100%";s:7:"options";a:1:{i:0;s:4:"100%";}}}','size' => '100%','style' => '','modified' => '1254816462',);
$templ_data[] = array('name' => 'admin.applications','template' => '','lang' => '','group' => '0','version' => '1.7.001','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:3:{i:0;a:0:{}i:1;a:1:{s:1:"A";a:4:{s:4:"type";s:9:"nextmatch";s:4:"size";s:4:"rows";s:4:"span";s:3:"all";s:4:"name";s:2:"nm";}}i:2;a:1:{s:1:"A";a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";i:1;a:3:{s:4:"type";s:6:"button";s:5:"label";s:28:"Number applications serially";s:4:"name";s:6:"number";}i:2;a:2:{s:4:"type";s:5:"label";s:5:"label";s:157:"Number the applications serially. If they are not numbered serially, sorting the applications could work wrong. This will not change the application\'s order.";}}}}s:4:"rows";i:2;s:4:"cols";i:1;s:4:"size";s:7:"100%,,0";s:7:"options";a:2:{i:0;s:4:"100%";i:2;s:1:"0";}}}','size' => '100%,,0','style' => '','modified' => '1276610727',);
$templ_data[] = array('name' => 'admin.applications.rows','template' => '','lang' => '','group' => '0','version' => '1.7.002','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:3:{i:0;a:7:{s:2:"c1";s:2:"th";s:2:"c2";s:3:"row";s:1:"A";s:5:"1px,1";s:1:"E";s:4:"80px";s:1:"D";s:5:"120px";s:1:"F";s:2:"80";s:1:"B";s:5:"120px";}i:1;a:6:{s:1:"A";a:1:{s:4:"type";s:5:"label";}s:1:"B";a:2:{s:4:"type";s:5:"label";s:5:"align";s:6:"center";}s:1:"C";a:3:{s:4:"type";s:16:"nextmatch-header";s:5:"label";s:4:"Name";s:4:"name";s:4:"name";}s:1:"D";a:3:{s:4:"type";s:16:"nextmatch-header";s:5:"label";s:7:"Version";s:4:"name";s:7:"version";}s:1:"E";a:3:{s:4:"type";s:16:"nextmatch-header";s:5:"label";s:5:"Order";s:4:"name";s:5:"order";}s:1:"F";a:3:{s:4:"type";s:16:"nextmatch-header";s:5:"label";s:7:"Actions";s:4:"name";s:7:"actions";}}i:2;a:6:{s:1:"A";a:3:{s:4:"type";s:5:"image";s:5:"align";s:6:"center";s:4:"name";s:14:"${row}[app_id]";}s:1:"B";a:3:{s:4:"type";s:5:"image";s:4:"name";s:13:"${row}[image]";s:5:"align";s:6:"center";}s:1:"C";a:2:{s:4:"type";s:5:"label";s:4:"name";s:16:"${row}[app_name]";}s:1:"D";a:3:{s:4:"type";s:5:"label";s:4:"name";s:19:"${row}[app_version]";s:8:"readonly";s:1:"1";}s:1:"E";a:4:{s:4:"type";s:5:"label";s:4:"name";s:17:"${row}[app_order]";s:7:"no_lang";s:1:"1";s:8:"readonly";s:1:"1";}s:1:"F";a:5:{s:4:"type";s:4:"hbox";s:8:"readonly";s:1:"1";s:4:"size";s:1:"2";i:1;a:4:{s:4:"type";s:6:"button";s:4:"size";s:3:"up2";s:5:"label";s:2:"up";s:4:"name";s:21:"up[$row_cont[app_id]]";}i:2;a:4:{s:4:"type";s:6:"button";s:4:"size";s:5:"down2";s:5:"label";s:4:"down";s:4:"name";s:23:"down[$row_cont[app_id]]";}}}}s:4:"rows";i:2;s:4:"cols";i:6;s:4:"size";s:4:"100%";s:7:"options";a:1:{i:0;s:4:"100%";}}}','size' => '100%','style' => '','modified' => '1275405742',);
$templ_data[] = array('name' => 'admin.categories.delete','template' => '','lang' => '','group' => '0','version' => '1.7.001','data' => 'a:2:{i:0;a:4:{s:4:"type";s:4:"grid";s:4:"data";a:2:{i:0;a:0:{}i:1;a:1:{s:1:"A";a:5:{s:4:"type";s:8:"groupbox";s:4:"size";s:1:"1";s:5:"label";s:20:"Delete this category";i:1;a:5:{s:4:"type";s:4:"grid";s:4:"data";a:4:{i:0;a:3:{s:2:"h3";s:2:"40";s:2:"h1";s:2:"30";s:2:"c2";s:11:"confirmSubs";}i:1;a:2:{s:1:"A";a:4:{s:4:"type";s:5:"label";s:4:"span";s:3:"all";s:5:"label";s:47:"Are you sure you want to delete this category ?";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:5:{s:4:"type";s:8:"checkbox";s:5:"label";s:53:"Do you also want to delete all global subcategories ?";s:4:"name";s:12:"delete[subs]";s:4:"span";s:3:"all";s:5:"align";s:6:"center";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:3;a:2:{s:1:"A";a:4:{s:4:"type";s:6:"button";s:5:"label";s:6:"Delete";s:4:"name";s:14:"delete[delete]";s:5:"align";s:6:"center";}s:1:"B";a:5:{s:4:"type";s:10:"buttononly";s:5:"label";s:6:"Cancel";s:4:"name";s:14:"delete[cancel]";s:5:"align";s:6:"center";s:7:"onclick";s:64:"set_style_by_class(\'fieldset\',\'confirmDelete\',\'display\',\'none\');";}}}s:4:"rows";i:3;s:4:"cols";i:2;s:7:"options";a:0:{}}s:4:"span";s:14:",confirmDelete";}}}s:4:"rows";i:1;s:4:"cols";i:1;}i:1;a:3:{s:4:"type";s:4:"text";s:4:"name";s:14:"delete[cat_id]";s:4:"span";s:12:",hiddenCatid";}}','size' => '','style' => '.confirmDelete {
position: absolute;
left: 120px;
top: 80px;
background-color: white;
display: none;
border: 2px solid black;
}
.hiddenCatid {
display: none;
}
.confirmSubs
{
}','modified' => '1264754384',);
$templ_data[] = array('name' => 'admin.categories.edit','template' => '','lang' => '','group' => '0','version' => '1.7.001','data' => 'a:2:{i:0;a:4:{s:4:"type";s:4:"grid";s:4:"data";a:11:{i:0;a:12:{s:2:"c2";s:2:"th";s:2:"c3";s:3:"row";s:2:"c4";s:7:"row,top";s:2:"c5";s:3:"row";s:2:"c7";s:3:"row";s:2:"c6";s:3:"row";s:2:"h7";s:15:",@appname=phpgw";s:2:"h2";s:2:"25";s:2:"h1";s:6:",!@msg";s:2:"c9";s:3:"row";s:2:"c8";s:3:"row";s:2:"h9";s:11:",!@last_mod";}i:1;a:2:{s:1:"A";a:4:{s:4:"type";s:5:"label";s:4:"span";s:13:"all,redItalic";s:5:"align";s:6:"center";s:4:"name";s:3:"msg";}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:9:",,,parent";s:5:"label";s:15:"Parent category";}s:1:"B";a:3:{s:4:"type";s:10:"select-cat";s:4:"size";s:25:"None,,,$cont[appname],,-1";s:4:"name";s:6:"parent";}}i:3;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:7:",,,name";s:5:"label";s:4:"Name";}s:1:"B";a:3:{s:4:"type";s:4:"text";s:4:"size";s:6:"50,150";s:4:"name";s:4:"name";}}i:4;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:14:",,,description";s:5:"label";s:11:"Description";}s:1:"B";a:3:{s:4:"type";s:8:"textarea";s:4:"size";s:4:"5,50";s:4:"name";s:11:"description";}}i:5;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:18:",,,cat_data[color]";s:5:"label";s:5:"Color";}s:1:"B";a:2:{s:4:"type";s:11:"colorpicker";s:4:"name";s:11:"data[color]";}}i:6;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:13:",,,data[icon]";s:5:"label";s:4:"Icon";}s:1:"B";a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:6:"2,,0,0";i:1;a:4:{s:4:"type";s:6:"select";s:4:"name";s:10:"data[icon]";s:4:"size";s:4:"None";s:8:"onchange";s:73:"document.getElementById(\'icon_url\').src = \'$cont[base_url]\' + this.value;";}i:2;a:3:{s:4:"type";s:5:"image";s:4:"name";s:8:"icon_url";s:4:"span";s:9:",leftPad5";}}}i:7;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:11:"Application";}s:1:"B";a:4:{s:4:"type";s:10:"select-app";s:4:"name";s:7:"appname";s:8:"readonly";s:1:"1";s:4:"size";s:16:"All applications";}}i:8;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:8:",,,owner";s:5:"label";s:19:"Limit to members of";}s:1:"B";a:4:{s:4:"type";s:14:"select-account";s:4:"size";s:16:"All users,groups";s:4:"name";s:5:"owner";s:4:"help";s:51:"Limit global category to members of a certain group";}}i:9;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:8:"Modified";}s:1:"B";a:3:{s:4:"type";s:9:"date-time";s:4:"name";s:8:"last_mod";s:8:"readonly";s:1:"1";}}i:10;a:2:{s:1:"A";a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";i:1;a:3:{s:4:"type";s:6:"button";s:5:"label";s:4:"Save";s:4:"name";s:12:"button[save]";}i:2;a:3:{s:4:"type";s:6:"button";s:5:"label";s:5:"Apply";s:4:"name";s:13:"button[apply]";}}s:1:"B";a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";i:1;a:4:{s:4:"type";s:10:"buttononly";s:4:"name";s:14:"button[cancel]";s:5:"label";s:6:"Cancel";s:7:"onclick";s:15:"window.close();";}i:2;a:6:{s:4:"type";s:10:"buttononly";s:5:"label";s:6:"Delete";s:5:"align";s:5:"right";s:4:"name";s:14:"button[delete]";s:4:"help";s:20:"Delete this category";s:7:"onclick";s:157:"set_style_by_class(\'tr\',\'confirmSubs\',\'visibility\',\'$cont[children]\'?\'visible\':\'collapse\'); set_style_by_class(\'fieldset\',\'confirmDelete\',\'display\',\'block\');";}}}}s:4:"rows";i:10;s:4:"cols";i:2;}i:1;a:2:{s:4:"type";s:8:"template";s:4:"name";s:23:"admin.categories.delete";}}','size' => '','style' => '','modified' => '1264740967',);
$templ_data[] = array('name' => 'admin.categories.index','template' => '','lang' => '','group' => '0','version' => '1.7.001','data' => 'a:2:{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:4:{s:4:"type";s:5:"label";s:4:"name";s:3:"msg";s:5:"align";s:6:"center";s:4:"span";s:13:"all,redItalic";}}i:2;a:1:{s:1:"A";a:3:{s:4:"type";s:9:"nextmatch";s:4:"size";s:4:"rows";s:4:"name";s:2:"nm";}}i:3;a:1:{s:1:"A";a:4:{s:4:"type";s:10:"buttononly";s:5:"label";s:3:"Add";s:4:"name";s:3:"add";s:7:"onclick";s:193:"window.open(egw::link(\'/index.php\',\'menuaction=admin.admin_categories.edit&appname={$cont[nm][appname]}\'),\'_blank\',\'dependent=yes,width=600,height=300,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%";}}i:1;a:2:{s:4:"type";s:8:"template";s:4:"name";s:23:"admin.categories.delete";}}','size' => '100%','style' => '.level0 { font-weight: bold; }','modified' => '1264657515',);
$templ_data[] = array('name' => 'admin.categories.index.rows','template' => '','lang' => '','group' => '0','version' => '1.7.001','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:3:{i:0;a:6:{s:2:"c1";s:2:"th";s:2:"c2";s:13:"$row_cont[id]";s:1:"E";s:2:"40";s:1:"H";s:2:"30";s:1:"I";s:2:"1%";s:1:"F";s:2:"80";}i:1;a:9:{s:1:"A";a:3:{s:4:"type";s:20:"nextmatch-sortheader";s:5:"label";s:4:"Name";s:4:"name";s:4:"name";}s:1:"B";a:3:{s:4:"type";s:20:"nextmatch-sortheader";s:5:"label";s:11:"Description";s:4:"name";s:11:"description";}s:1:"C";a:3:{s:4:"type";s:16:"nextmatch-header";s:4:"name";s:3:"app";s:5:"label";s:11:"Application";}s:1:"D";a:3:{s:4:"type";s:16:"nextmatch-header";s:5:"label";s:19:"Limit to members of";s:4:"name";s:5:"owner";}s:1:"E";a:4:{s:4:"type";s:16:"nextmatch-header";s:5:"label";s:4:"Icon";s:4:"name";s:4:"icon";s:5:"align";s:6:"center";}s:1:"F";a:3:{s:4:"type";s:16:"nextmatch-header";s:5:"label";s:5:"Color";s:4:"name";s:5:"color";}s:1:"G";a:3:{s:4:"type";s:20:"nextmatch-sortheader";s:5:"label";s:8:"Modified";s:4:"name";s:8:"last_mod";}s:1:"H";a:4:{s:4:"type";s:16:"nextmatch-header";s:5:"label";s:8:"Children";s:4:"name";s:4:"subs";s:5:"align";s:6:"center";}s:1:"I";a:2:{s:4:"type";s:5:"label";s:5:"label";s:7:"Actions";}}i:2;a:9:{s:1:"A";a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:6:"2,,0,0";i:1;a:2:{s:4:"type";s:4:"html";s:4:"name";s:20:"${row}[level_spacer]";}i:2;a:3:{s:4:"type";s:5:"label";s:4:"name";s:12:"${row}[name]";s:4:"span";s:17:",$row_cont[class]";}}s:1:"B";a:2:{s:4:"type";s:5:"label";s:4:"name";s:19:"${row}[description]";}s:1:"C";a:3:{s:4:"type";s:10:"select-app";s:4:"name";s:15:"${row}[appname]";s:8:"readonly";s:1:"1";}s:1:"D";a:4:{s:4:"type";s:14:"select-account";s:4:"name";s:13:"${row}[owner]";s:8:"readonly";s:1:"1";s:4:"size";s:16:"All users,groups";}s:1:"E";a:4:{s:4:"type";s:5:"image";s:4:"name";s:16:"${row}[icon_url]";s:5:"label";s:23:"{$row_cont[data][icon]}";s:5:"align";s:6:"center";}s:1:"F";a:2:{s:4:"type";s:5:"label";s:4:"name";s:19:"${row}[data][color]";}s:1:"G";a:3:{s:4:"type";s:9:"date-time";s:4:"name";s:16:"${row}[last_mod]";s:8:"readonly";s:1:"1";}s:1:"H";a:3:{s:4:"type";s:5:"label";s:4:"name";s:12:"${row}[subs]";s:5:"align";s:6:"center";}s:1:"I";a:5:{s:4:"type";s:4:"hbox";s:4:"size";s:6:"3,,0,0";i:1;a:5:{s:4:"type";s:10:"buttononly";s:4:"size";s:4:"edit";s:5:"label";s:4:"Edit";s:4:"name";s:19:"edit[$row_cont[id]]";s:7:"onclick";s:185:"window.open(egw::link(\'/index.php\',\'menuaction=admin.admin_categories.edit&cat_id=$row_cont[id]\'),\'_blank\',\'dependent=yes,width=600,height=380,scrollbars=yes,status=yes\'); return false;";}i:2;a:5:{s:4:"type";s:10:"buttononly";s:4:"size";s:3:"new";s:5:"label";s:7:"Add sub";s:4:"name";s:18:"add[$row_cont[id]]";s:7:"onclick";s:208:"window.open(egw::link(\'/index.php\',\'menuaction=admin.admin_categories.edit&parent=$row_cont[id]&appname=$cont[appname]\'),\'_blank\',\'dependent=yes,width=600,height=380,scrollbars=yes,status=yes\'); return false;";}i:3;a:7:{s:4:"type";s:10:"buttononly";s:4:"size";s:6:"delete";s:5:"label";s:6:"Delete";s:4:"name";s:21:"delete[$row_cont[id]]";s:4:"help";s:20:"Delete this category";s:7:"onclick";s:246:"document.getElementById(\'exec[delete][cat_id]\').value=\'$row_cont[id]\'; set_style_by_class(\'tr\',\'confirmSubs\',\'visibility\',\'$row_cont[children]\'?\'visible\':\'collapse\'); set_style_by_class(\'fieldset\',\'confirmDelete\',\'display\',\'block\'); return false;";s:4:"span";s:9:",leftPad5";}}}}s:4:"rows";i:2;s:4:"cols";i:9;s:4:"size";s:4:"100%";s:7:"options";a:1:{i:0;s:4:"100%";}}}','size' => '100%','style' => '','modified' => '1264657599',);
$templ_data[] = array('name' => 'admin.cmds','template' => '','lang' => '','group' => '0','version' => '1.5.001','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:2:{i:0;a:0:{}i:1;a:1:{s:1:"A";a:3:{s:4:"type";s:9:"nextmatch";s:4:"size";s:15:"admin.cmds.rows";s:4:"name";s:2:"nm";}}}s:4:"rows";i:1;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' => '1195518120',);
$templ_data[] = array('name' => 'admin.cmds.rows','template' => '','lang' => '','group' => '0','version' => '1.5.001','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:3:{i:0;a:3:{s:2:"c1";s:2:"th";s:2:"c2";s:3:"row";s:2:"h2";s:4:",!@1";}i:1;a:10:{s:1:"A";a:3:{s:4:"type";s:16:"nextmatch-header";s:5:"label";s:5:"Title";s:4:"name";s:5:"title";}s:1:"B";a:3:{s:4:"type";s:16:"nextmatch-header";s:5:"label";s:9:"Requested";s:4:"name";s:9:"requested";}s:1:"C";a:3:{s:4:"type";s:20:"nextmatch-sortheader";s:5:"label";s:9:"Scheduled";s:4:"name";s:13:"cmd_scheduled";}s:1:"D";a:3:{s:4:"type";s:22:"nextmatch-filterheader";s:4:"size";s:6:"Remote";s:4:"name";s:9:"remote_id";}s:1:"E";a:3:{s:4:"type";s:20:"nextmatch-sortheader";s:5:"label";s:7:"Created";s:4:"name";s:11:"cmd_created";}s:1:"F";a:3:{s:4:"type";s:23:"nextmatch-accountfilter";s:4:"name";s:7:"creator";s:4:"size";s:7:"Creator";}s:1:"G";a:3:{s:4:"type";s:22:"nextmatch-filterheader";s:4:"name";s:6:"status";s:4:"size";s:6:"Status";}s:1:"H";a:3:{s:4:"type";s:20:"nextmatch-sortheader";s:5:"label";s:8:"Modified";s:4:"name";s:12:"cmd_modified";}s:1:"I";a:3:{s:4:"type";s:23:"nextmatch-accountfilter";s:4:"size";s:8:"Modifier";s:4:"name";s:8:"modifier";}s:1:"J";a:1:{s:4:"type";s:5:"label";}}i:2;a:10:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:4:"name";s:13:"${row}[title]";}s:1:"B";a:4:{s:4:"type";s:9:"url-email";s:4:"name";s:17:"${row}[requested]";s:4:"size";s:29:",,,$row_cont[requested_email]";s:8:"readonly";s:1:"1";}s:1:"C";a:3:{s:4:"type";s:9:"date-time";s:4:"name";s:17:"${row}[scheduled]";s:8:"readonly";s:1:"1";}s:1:"D";a:3:{s:4:"type";s:6:"select";s:4:"name";s:17:"${row}[remote_id]";s:8:"readonly";s:1:"1";}s:1:"E";a:3:{s:4:"type";s:9:"date-time";s:4:"name";s:15:"${row}[created]";s:8:"readonly";s:1:"1";}s:1:"F";a:4:{s:4:"type";s:9:"url-email";s:4:"name";s:15:"${row}[creator]";s:4:"size";s:27:",,,$row_cont[creator_email]";s:8:"readonly";s:1:"1";}s:1:"G";a:4:{s:4:"type";s:4:"vbox";s:4:"size";s:6:"2,,0,0";i:1;a:3:{s:4:"type";s:6:"select";s:4:"name";s:14:"${row}[status]";s:8:"readonly";s:1:"1";}i:2;a:3:{s:4:"type";s:5:"label";s:4:"name";s:13:"${row}[error]";s:4:"span";s:10:",redItalic";}}s:1:"H";a:3:{s:4:"type";s:9:"date-time";s:4:"name";s:16:"${row}[modified]";s:8:"readonly";s:1:"1";}s:1:"I";a:4:{s:4:"type";s:9:"url-email";s:4:"name";s:16:"${row}[modifier]";s:8:"readonly";s:1:"1";s:4:"size";s:28:",,,$row_cont[modifier_email]";}s:1:"J";a:6:{s:4:"type";s:6:"button";s:4:"size";s:6:"delete";s:5:"label";s:6:"Cancel";s:4:"name";s:21:"delete[$row_cont[id]]";s:4:"help";s:29:"Cancel this scheduled command";s:7:"onclick";s:48:"return confirm(\'Cancel this scheduled command\');";}}}s:4:"rows";i:2;s:4:"cols";i:10;s:4:"size";s:4:"100%";s:7:"options";a:1:{i:0;s:4:"100%";}}}','size' => '100%','style' => '','modified' => '1195518170',);
$templ_data[] = array('name' => 'admin.customfields','template' => '','lang' => '','group' => '0','version' => '1.9.001','data' => 'a:1:{i:0;a:4:{s:4:"type";s:4:"grid";s:4:"data";a:6:{i:0;a:1:{s:1:"G";s:3:"80%";}i:1;a:7:{s:1:"A";a:5:{s:4:"type";s:5:"label";s:4:"span";s:13:"all,redItalic";s:4:"name";s:9:"error_msg";s:5:"align";s:6:"center";s:7:"no_lang";s:1:"1";}s:1:"B";a:1:{s:4:"type";s:5:"label";}s:1:"C";a:1:{s:4:"type";s:5:"label";}s:1:"D";a:1:{s:4:"type";s:5:"label";}s:1:"E";a:1:{s:4:"type";s:5:"label";}s:1:"F";a:1:{s:4:"type";s:5:"label";}s:1:"G";a:2:{s:4:"type";s:5:"label";s:7:"no_lang";s:1:"1";}}i:2;a:7:{s:1:"A";a:4:{s:4:"type";s:8:"template";s:4:"size";s:13:"content_types";s:4:"span";s:3:"all";s:4:"name";s:24:"admin.customfields.types";}s:1:"B";a:1:{s:4:"type";s:5:"label";}s:1:"C";a:1:{s:4:"type";s:5:"label";}s:1:"D";a:1:{s:4:"type";s:5:"label";}s:1:"E";a:1:{s:4:"type";s:5:"label";}s:1:"F";a:1:{s:4:"type";s:5:"label";}s:1:"G";a:1:{s:4:"type";s:5:"label";}}i:3;a:7:{s:1:"A";a:2:{s:4:"type";s:8:"template";s:4:"span";s:3:"all";}s:1:"B";a:1:{s:4:"type";s:5:"label";}s:1:"C";a:1:{s:4:"type";s:5:"label";}s:1:"D";a:1:{s:4:"type";s:5:"label";}s:1:"E";a:1:{s:4:"type";s:5:"label";}s:1:"F";a:1:{s:4:"type";s:5:"label";}s:1:"G";a:1:{s:4:"type";s:5:"label";}}i:4;a:7:{s:1:"A";a:4:{s:4:"type";s:8:"template";s:4:"size";s:6:"fields";s:4:"span";s:3:"all";s:4:"name";s:25:"admin.customfields.fields";}s:1:"B";a:1:{s:4:"type";s:5:"label";}s:1:"C";a:1:{s:4:"type";s:5:"label";}s:1:"D";a:1:{s:4:"type";s:5:"label";}s:1:"E";a:1:{s:4:"type";s:5:"label";}s:1:"F";a:1:{s:4:"type";s:5:"label";}s:1:"G";a:1:{s:4:"type";s:5:"label";}}i:5;a:7:{s:1:"A";a:6:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"3";s:4:"span";s:3:"all";i:1;a:4:{s:4:"type";s:6:"button";s:5:"label";s:4:"Save";s:4:"name";s:12:"button[save]";s:4:"help";s:33:"saves the changes made and leaves";}i:2;a:4:{s:4:"type";s:6:"button";s:5:"label";s:5:"Apply";s:4:"name";s:13:"button[apply]";s:4:"help";s:19:"applies the changes";}i:3;a:4:{s:4:"type";s:6:"button";s:5:"label";s:6:"Cancel";s:4:"name";s:14:"button[cancel]";s:4:"help";s:22:"leaves without saveing";}}s:1:"B";a:1:{s:4:"type";s:5:"label";}s:1:"C";a:1:{s:4:"type";s:5:"label";}s:1:"D";a:1:{s:4:"type";s:5:"label";}s:1:"E";a:1:{s:4:"type";s:5:"label";}s:1:"F";a:1:{s:4:"type";s:5:"label";}s:1:"G";a:1:{s:4:"type";s:5:"label";}}}s:4:"rows";i:5;s:4:"cols";i:7;}}','size' => '','style' => '.redItalic { color: red; font-style: italics; }','modified' => '1295986974',);
$templ_data[] = array('name' => 'admin.customfields.fields','template' => '','lang' => '','group' => '0','version' => '1.5.001','data' => 'a:1:{i:0;a:5:{s:4:"type";s:4:"grid";s:4:"data";a:3:{i:0;a:4:{s:2:"c1";s:2:"th";s:2:"c2";s:7:"row,top";s:1:"C";s:8:",!@type2";s:1:"E";s:14:",!@use_private";}i:1;a:9:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:5:"label";s:4:"Name";s:4:"help";s:83:"the name used internaly (<= 20 chars), changeing it makes existing data unavailible";}s:1:"B";a:2:{s:4:"type";s:5:"label";s:5:"label";s:5:"Label";}s:1:"C";a:2:{s:4:"type";s:5:"label";s:5:"label";s:7:"Subtype";}s:1:"D";a:2:{s:4:"type";s:5:"label";s:5:"label";s:4:"Type";}s:1:"E";a:2:{s:4:"type";s:5:"label";s:5:"label";s:7:"Private";}s:1:"F";a:3:{s:4:"type";s:5:"label";s:5:"label";s:7:"Options";s:4:"help";s:40:"each value is a line like <id>[=<label>]";}s:1:"G";a:2:{s:4:"type";s:5:"label";s:5:"label";s:14:"Length<br>Rows";}s:1:"H";a:2:{s:4:"type";s:5:"label";s:5:"label";s:5:"Order";}s:1:"I";a:4:{s:4:"type";s:5:"label";s:5:"label";s:6:"Action";s:5:"align";s:6:"center";s:4:"help";s:18:"deletes this field";}}i:2;a:9:{s:1:"A";a:4:{s:4:"type";s:4:"text";s:4:"size";s:5:"20,32";s:4:"name";s:12:"${row}[name]";s:4:"help";s:83:"the name used internaly (<= 20 chars), changeing it makes existing data unavailible";}s:1:"B";a:4:{s:4:"type";s:4:"vbox";s:4:"size";s:1:"2";i:1;a:4:{s:4:"type";s:4:"text";s:4:"size";s:4:",255";s:4:"name";s:13:"${row}[label]";s:4:"help";s:30:"the text displayed to the user";}i:2;a:2:{s:4:"type";s:5:"label";s:4:"name";s:13:"${row}[label]";}}s:1:"C";a:4:{s:4:"type";s:6:"select";s:4:"size";s:3:"All";s:4:"name";s:13:"${row}[type2]";s:7:"no_lang";s:1:"1";}s:1:"D";a:3:{s:4:"type";s:18:"customfields-types";s:4:"name";s:12:"{$row}[type]";s:4:"help";s:19:"Type of customfield";}s:1:"E";a:5:{s:4:"type";s:14:"select-account";s:4:"name";s:15:"${row}[private]";s:4:"help";s:60:"Select accounts for which the custom field should be visible";s:5:"align";s:6:"center";s:4:"size";s:6:"3,both";}s:1:"F";a:4:{s:4:"type";s:8:"textarea";s:4:"size";s:4:"2,30";s:4:"name";s:14:"${row}[values]";s:4:"help";s:36:"each value is a line like id[=label]";}s:1:"G";a:4:{s:4:"type";s:4:"vbox";s:4:"size";s:1:"2";i:1;a:4:{s:4:"type";s:4:"text";s:4:"size";s:1:"5";s:4:"name";s:11:"${row}[len]";s:4:"help";s:63:"max length of the input [, length of the inputfield (optional)]";}i:2;a:5:{s:4:"type";s:3:"int";s:4:"size";s:6:"0,10,2";s:4:"name";s:12:"${row}[rows]";s:4:"help";s:70:"number of row for a multiline inputfield or line of a multi-select-box";s:4:"blur";s:1:"1";}}s:1:"H";a:4:{s:4:"type";s:3:"int";s:4:"size";s:4:"1,,3";s:4:"name";s:13:"${row}[order]";s:4:"help";s:45:"determines the order the fields are displayed";}s:1:"I";a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";i:1;a:4:{s:4:"type";s:6:"button";s:5:"label";s:6:"Delete";s:4:"name";s:23:"delete[$row_cont[name]]";s:4:"help";s:18:"deletes this field";}i:2;a:4:{s:4:"type";s:6:"button";s:5:"label";s:6:"Create";s:4:"name";s:21:"create$row_cont[name]";s:4:"help";s:19:"creates a new field";}}}}s:4:"rows";i:2;s:4:"cols";i:9;s:7:"options";a:0:{}}}','size' => '','style' => '','modified' => '1131454776',);
$templ_data[] = array('name' => 'admin.customfields.types','template' => '','lang' => '','group' => '0','version' => '1.2','data' => 'a:1:{i:0;a:4:{s:4:"type";s:4:"grid";s:4:"data";a:2:{i:0;a:4:{s:1:"D";s:15:",@non_deletable";s:1:"E";s:8:",@no_add";s:1:"F";s:8:",@no_add";s:2:"h1";s:15:",@no_edit_types";}i:1;a:6:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:4:"name";s:8:"app-name";}s:1:"B";a:2:{s:4:"type";s:5:"label";s:5:"label";s:7:"- type";}s:1:"C";a:4:{s:4:"type";s:6:"select";s:4:"name";s:5:"types";s:8:"onchange";s:1:"1";s:7:"no_lang";s:1:"1";}s:1:"D";a:4:{s:4:"type";s:6:"button";s:5:"label";s:6:"Delete";s:4:"name";s:6:"delete";s:7:"onclick";s:110:"return confirm(\'WARNING: You are about to delete this type. Entries of this type won\\\'t be accessable then.\');";}s:1:"E";a:3:{s:4:"type";s:4:"text";s:4:"name";s:4:"name";s:4:"blur";s:8:"new name";}s:1:"F";a:3:{s:4:"type";s:6:"button";s:5:"label";s:6:"Create";s:4:"name";s:6:"create";}}}s:4:"rows";i:1;s:4:"cols";i:6;}}','size' => '','style' => '','modified' => '1139823458',);
$templ_data[] = array('name' => 'admin.export_users_csv_selectors','template' => '','lang' => '','group' => '0','version' => '1.9.001','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:2:{i:0;a:0:{}i:1;a:1:{s:1:"A";a:4:{s:4:"type";s:14:"select-account";s:4:"size";s:10:"All,groups";s:5:"label";s:5:"Group";s:4:"name";s:8:"group_id";}}}s:4:"rows";i:1;s:4:"cols";i:1;s:4:"name";s:9:"selection";s:7:"options";a:0:{}}}','size' => '','style' => '','modified' => '1302620448',);
$templ_data[] = array('name' => 'admin.passwordreset','template' => '','lang' => '','group' => '0','version' => '1.9.001','data' => 'a:1:{i:0;a:4:{s:4:"type";s:4:"grid";s:4:"data";a:7:{i:0;a:2:{s:2:"h1";s:6:",!@msg";s:2:"c5";s:4:",top";}i:1;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"span";s:13:"all,redItalic";s:4:"name";s:3:"msg";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:2;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:12:"Select users";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:3;a:2:{s:1:"A";a:3:{s:4:"type";s:14:"select-account";s:4:"size";s:2:"15";s:4:"name";s:5:"users";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:4;a:2:{s:1:"A";a:8:{s:4:"type";s:8:"groupbox";s:4:"size";s:1:"5";s:5:"label";s:7:"Actions";i:1;a:3:{s:4:"type";s:8:"checkbox";s:5:"label";s:21:"Set a random password";s:4:"name";s:9:"random_pw";}i:2;a:5:{s:4:"type";s:11:"select-bool";s:4:"size";s:15:"Leave unchanged";s:5:"label";s:36:"Must change password upon next login";s:4:"name";s:18:"mustchangepassword";s:8:"onchange";s:85:"if (this.value==\'1\') document.getElementById(form::name(\'changepassword\')).value=\'1\';";}i:3;a:5:{s:4:"type";s:11:"select-bool";s:4:"size";s:15:"Leave unchanged";s:5:"label";s:19:"Can change password";s:4:"name";s:14:"changepassword";s:8:"onchange";s:136:"var mustchange=document.getElementById(form::name(\'mustchangepassword\')); if (this.value==\'0\' && mustchange.value) mustchange.value=\'0\';";}i:4;a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:6:"2,,0,0";i:1;a:4:{s:4:"type";s:6:"select";s:4:"size";s:15:"Leave unchanged";s:4:"name";s:4:"hash";s:5:"label";s:23:"Change password hash to";}i:2;a:5:{s:4:"type";s:4:"text";s:5:"label";s:12:"Current hash";s:4:"name";s:12:"current_hash";s:4:"span";s:14:",leftPad5 gray";s:8:"readonly";s:1:"1";}}i:5;a:3:{s:4:"type";s:8:"checkbox";s:5:"label";s:20:"Notify user by email";s:4:"name";s:6:"notify";}}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:5;a:2:{s:1:"A";a:5:{s:4:"type";s:8:"groupbox";s:4:"size";s:1:"2";s:5:"label";s:17:"Notification mail";i:1;a:4:{s:4:"type";s:4:"text";s:4:"size";s:2:"64";s:4:"name";s:7:"subject";s:4:"blur";s:7:"Subject";}i:2;a:3:{s:4:"type";s:8:"textarea";s:4:"size";s:5:"15,64";s:4:"name";s:4:"body";}}s:1:"B";a:4:{s:4:"type";s:4:"vbox";s:4:"size";s:1:"2";i:1;a:3:{s:4:"type";s:5:"label";s:4:"span";s:5:",gray";s:5:"label";s:22:"Available placeholders";}i:2;a:6:{s:4:"type";s:4:"grid";s:4:"span";s:5:",gray";s:4:"name";s:12:"replacements";s:4:"data";a:2:{i:0;a:0:{}i:1;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:4:"name";s:12:"${row}[name]";}s:1:"B";a:2:{s:4:"type";s:5:"label";s:4:"name";s:13:"${row}[label]";}}}s:4:"rows";i:1;s:4:"cols";i:2;}}}i:6;a:2:{s:1:"A";a:3:{s:4:"type";s:6:"button";s:5:"label";s:5:"Start";s:4:"name";s:5:"start";}s:1:"B";a:3:{s:4:"type";s:6:"button";s:5:"label";s:12:"Download CSV";s:4:"name";s:12:"download_csv";}}}s:4:"rows";i:6;s:4:"cols";i:2;}}','size' => '','style' => '','modified' => '1301655701',);
$templ_data[] = array('name' => 'admin.remotes','template' => '','lang' => '','group' => '0','version' => '1.5.001','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:5:{i:0;a:3:{s:2:"h2";s:9:",!@remote";s:2:"h1";s:6:",!@msg";s:2:"h3";s:2:",1";}i:1;a:1:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"span";s:13:"all,redItalic";s:4:"name";s:3:"msg";}}i:2;a:1:{s:1:"A";a:4:{s:4:"type";s:8:"template";s:4:"size";s:6:"remote";s:4:"span";s:10:"all,border";s:4:"name";s:18:"admin.remotes.edit";}}i:3;a:1:{s:1:"A";a:3:{s:4:"type";s:8:"template";s:5:"align";s:5:"right";s:4:"name";s:26:"admin.remotes.header_right";}}i:4;a:1:{s:1:"A";a:3:{s:4:"type";s:9:"nextmatch";s:4:"name";s:2:"nm";s:4:"size";s:18:"admin.remotes.rows";}}}s:4:"rows";i:4;s:4:"cols";i:1;s:4:"size";s:4:"100%";s:7:"options";a:1:{i:0;s:4:"100%";}}}','size' => '100%','style' => '.border { border: black solid 2px; }','modified' => '1195926693',);
$templ_data[] = array('name' => 'admin.remotes.edit','template' => '','lang' => '','group' => '0','version' => '1.5.001','data' => 'a:1:{i:0;a:4:{s:4:"type";s:4:"grid";s:4:"data";a:9:{i:0;a:8:{s:2:"c2";s:2:"th";s:2:"c3";s:3:"row";s:2:"c5";s:3:"row";s:2:"c6";s:3:"row";s:2:"c4";s:3:"row";s:2:"c7";s:3:"row";s:2:"h5";s:14:",!@remote_hash";s:2:"h1";s:11:",@remote_id";}i:1;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"span";s:13:"all,redItalic";s:5:"label";s:97:"Remote administration need to be enabled in the remote instance under Admin > Site configuration!";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:2;a:2:{s:1:"A";a:4:{s:4:"type";s:5:"label";s:4:"size";s:14:",,,remote_name";s:5:"label";s:4:"Name";s:6:"needed";s:1:"1";}s:1:"B";a:4:{s:4:"type";s:4:"text";s:4:"size";s:5:"64,64";s:4:"name";s:11:"remote_name";s:6:"needed";s:1:"1";}}i:3;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:13:",,,install_id";s:5:"label";s:10:"Install ID";}s:1:"B";a:4:{s:4:"type";s:4:"text";s:4:"size";s:24:"40,32,/^[a-f0-9]{0,32}$/";s:4:"name";s:10:"install_id";s:4:"help";s:75:"The install ID of an instance can be found under Admin > Site configuration";}}i:4;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:16:",,,config_passwd";s:5:"label";s:8:"Password";}s:1:"B";a:4:{s:4:"type";s:4:"text";s:4:"size";s:5:"40,32";s:4:"name";s:13:"config_passwd";s:4:"help";s:51:"Config password or md5 hash from the header.inc.php";}}i:5;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:4:"Hash";}s:1:"B";a:2:{s:4:"type";s:5:"label";s:4:"name";s:11:"remote_hash";}}i:6;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:13:",,,remote_url";s:5:"label";s:3:"URL";}s:1:"B";a:5:{s:4:"type";s:4:"text";s:4:"size";s:62:"64,128,/^https?:\\/\\/[a-z0-9._-]+(\\:[0-9]+)?(\\/[a-z0-9._-]+)*$/";s:4:"name";s:10:"remote_url";s:6:"needed";s:1:"1";s:4:"help";s:68:"URL of the eGroupWare installation, eg. http://domain.com/egroupware";}}i:7;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:16:",,,remote_domain";s:5:"label";s:8:"Instance";}s:1:"B";a:4:{s:4:"type";s:4:"text";s:4:"size";s:5:"64,64";s:4:"name";s:13:"remote_domain";s:4:"help";s:44:"Name of the eGroupWare instance, eg. default";}}i:8;a:2:{s:1:"A";a:6:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"3";i:1;a:4:{s:4:"type";s:6:"button";s:5:"label";s:4:"Save";s:4:"name";s:12:"button[save]";s:4:"help";s:16:"Saves this entry";}s:4:"span";s:3:"all";i:2;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:17:"Apply the changes";}i:3;a:4:{s:4:"type";s:6:"button";s:5:"label";s:6:"Cancel";s:4:"name";s:14:"button[cancel]";s:4:"help";s:31:"leave without saveing the entry";}}s:1:"B";a:1:{s:4:"type";s:5:"label";}}}s:4:"rows";i:8;s:4:"cols";i:2;}}','size' => '','style' => '','modified' => '1195927476',);
$templ_data[] = array('name' => 'admin.remotes.header_right','template' => '','lang' => '','group' => '0','version' => '1.5.001','data' => 'a:1:{i:0;a:7:{s:4:"type";s:6:"button";s:4:"data";a:2:{i:0;a:0:{}i:1;a:1:{s:1:"A";a:1:{s:4:"type";s:5:"label";}}}s:4:"rows";i:1;s:4:"cols";i:1;s:4:"name";s:3:"add";s:5:"label";s:3:"Add";s:4:"help";s:25:"Add a new remote instance";}}','size' => '','style' => '','modified' => '1195931282',);
$templ_data[] = array('name' => 'admin.remotes.rows','template' => '','lang' => '','group' => '0','version' => '1.5.001','data' => 'a:1:{i:0;a:4:{s:4:"type";s:4:"grid";s:4:"data";a:3:{i:0;a:2:{s:2:"c1";s:2:"th";s:2:"c2";s:3:"row";}i:1;a:4:{s:1:"A";a:3:{s:4:"type";s:20:"nextmatch-sortheader";s:5:"label";s:4:"Name";s:4:"name";s:11:"remote_name";}s:1:"B";a:3:{s:4:"type";s:20:"nextmatch-sortheader";s:5:"label";s:3:"URL";s:4:"name";s:10:"remote_url";}s:1:"C";a:3:{s:4:"type";s:20:"nextmatch-sortheader";s:5:"label";s:8:"Instance";s:4:"name";s:13:"remote_domain";}s:1:"D";a:2:{s:4:"type";s:5:"label";s:5:"label";s:7:"Actions";}}i:2;a:4:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:4:"name";s:19:"${row}[remote_name]";}s:1:"B";a:3:{s:4:"type";s:5:"label";s:4:"size";s:64:",$row_cont[remote_url]/?domain=$row_cont[remote_domain],,,_blank";s:4:"name";s:18:"${row}[remote_url]";}s:1:"C";a:3:{s:4:"type";s:5:"label";s:4:"name";s:21:"${row}[remote_domain]";s:7:"no_lang";s:1:"1";}s:1:"D";a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"1";i:1;a:5:{s:4:"type";s:6:"button";s:4:"size";s:4:"edit";s:5:"label";s:4:"Edit";s:4:"name";s:26:"edit[$row_cont[remote_id]]";i:1;a:1:{s:4:"type";s:4:"hbox";}}s:5:"align";s:6:"center";}}}s:4:"rows";i:2;s:4:"cols";i:4;}}','size' => '','style' => '','modified' => '1195925625',);
$templ_data[] = array('name' => 'admin.statistics','template' => '','lang' => '','group' => '0','version' => '1.7.001','data' => 'a:2:{i:0;a:9:{s:4:"type";s:8:"groupbox";s:4:"size";s:1:"5";s:4:"span";s:10:"all,bigger";s:5:"label";s:35:"Official EGroupware usage statistic";i:1;a:2:{s:4:"type";s:5:"label";s:5:"label";s:131:"We ask for the data to improve our profile in the press and to get a better understanding of EGroupware\'s user base and it\'s needs.";}i:2;a:4:{s:4:"type";s:4:"html";s:5:"label";s:63:"The cumulated and anonymised data will be publically available:";i:1;a:2:{s:4:"type";s:4:"vbox";s:5:"label";s:44:"The statistics will be publically available:";}s:4:"name";s:13:"statistic_url";}i:3;a:3:{s:4:"type";s:5:"label";s:5:"label";s:91:"We hope you understand the importance for this voluntary statistic and not deny it lightly.";s:4:"span";s:5:",bold";}i:4;a:3:{s:4:"type";s:5:"label";s:5:"label";s:61:"Only below displayed information is directly submitted to %s.";s:4:"name";s:11:"submit_host";}i:5;a:4:{s:4:"type";s:4:"text";s:5:"label";s:104:"To allow us to track the growth of your individual installation use this submit ID, otherwise delete it:";s:4:"name";s:9:"submit_id";s:4:"size";s:5:"40,40";}}i:1;a:4:{s:4:"type";s:4:"grid";s:4:"data";a:14:{i:0;a:4:{s:1:"A";s:3:"140";s:3:"c10";s:4:",top";s:3:"h11";s:17:",!@last_submitted";s:3:"h12";s:6:",!@msg";}i:1;a:2:{s:1:"A";a:1:{s:4:"type";s:5:"label";}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:10:",,,country";s:5:"label";s:7:"Country";}s:1:"B";a:3:{s:4:"type";s:14:"select-country";s:4:"name";s:7:"country";s:4:"size";s:17:"International use";}}i:3;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:5:"label";s:5:"Usage";s:4:"size";s:13:",,,usage_type";}s:1:"B";a:2:{s:4:"type";s:6:"select";s:4:"name";s:10:"usage_type";}}i:4;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:5:"label";s:15:"Number of users";s:4:"size";s:8:",,,users";}s:1:"B";a:5:{s:4:"type";s:4:"text";s:4:"name";s:5:"users";s:4:"help";s:22:"number of active users";s:4:"size";s:2:"-8";s:8:"readonly";s:1:"1";}}i:5;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:11:",,,sessions";s:5:"label";s:21:"Sessions last 30 days";}s:1:"B";a:5:{s:4:"type";s:4:"text";s:4:"name";s:8:"sessions";s:4:"help";s:58:"Number of sessions / EGroupware logins in the last 30 days";s:4:"size";s:2:"-8";s:8:"readonly";s:1:"1";}}i:6;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:18:"EGroupware Version";}s:1:"B";a:4:{s:4:"type";s:4:"text";s:4:"name";s:7:"version";s:4:"size";s:2:"-8";s:8:"readonly";s:1:"1";}}i:7;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:16:"Operating System";}s:1:"B";a:4:{s:4:"type";s:4:"text";s:4:"name";s:2:"os";s:4:"size";s:3:"-40";s:8:"readonly";s:1:"1";}}i:8;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"size";s:6:",,,php";s:5:"label";s:11:"PHP Version";}s:1:"B";a:4:{s:4:"type";s:4:"text";s:4:"name";s:3:"php";s:4:"size";s:3:"-20";s:8:"readonly";s:1:"1";}}i:9;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:17:"Installation Type";}s:1:"B";a:2:{s:4:"type";s:6:"select";s:4:"name";s:12:"install_type";}}i:10;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:12:"Applications";}s:1:"B";a:4:{s:4:"type";s:8:"textarea";s:4:"size";s:5:"10,40";s:4:"name";s:4:"apps";s:4:"help";s:80:"Installed applications, percentage of allowed users and total number of entries.";}}i:11;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:16:"Last submission:";}s:1:"B";a:3:{s:4:"type";s:9:"date-time";s:8:"readonly";s:1:"1";s:4:"name";s:14:"last_submitted";}}i:12;a:2:{s:1:"A";a:4:{s:4:"type";s:5:"label";s:4:"span";s:13:"all,redItalic";s:5:"align";s:6:"center";s:4:"name";s:3:"msg";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:13;a:2:{s:1:"A";a:5:{s:4:"type";s:6:"button";s:5:"label";s:6:"Submit";s:4:"help";s:24:"Submit to egroupware.org";s:7:"onclick";s:14:"$cont[onclick]";s:4:"name";s:6:"submit";}s:1:"B";a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";i:1;a:4:{s:4:"type";s:6:"select";s:4:"name";s:8:"postpone";s:4:"size";s:12:"Postpone for";s:8:"onchange";i:1;}i:2;a:5:{s:4:"type";s:6:"button";s:4:"name";s:6:"cancel";s:5:"label";s:6:"Cancel";s:4:"help";s:84:"Go directly to admin menu, returning here the next time you click on administration.";s:5:"align";s:5:"right";}}}}s:4:"rows";i:13;s:4:"cols";i:2;}}','size' => '','style' => '.bold { font-weight: bold; }
fieldset.bigger legend {
font-weight: bold;
font-size: 125%;
padding-left: 5px;
padding-right: 5px;
}','modified' => '1257091720',);

View File

@ -0,0 +1,86 @@
<?xml version="1.0"?>
<!-- $Id$ -->
<overlay>
<template id="admin.accesslog.rows" template="" lang="" group="0" version="1.9.001">
<grid width="100%">
<columns>
<column/>
<column disabled="@no_sessionstatus"/>
<column/>
<column/>
<column/>
<column disabled="@no_lo"/>
<column disabled="@no_total"/>
<column/>
<column/>
<column width="1%"/>
</columns>
<rows>
<row class="th">
<nextmatch-accountfilter options="LoginID" id="account_id"/>
<nextmatch-header label="Login-Status" id="sessionstatus"/>
<nextmatch-header label="Loginid" id="loginid"/>
<nextmatch-header label="IP" id="ip"/>
<nextmatch-sortheader label="Login" id="li"/>
<nextmatch-sortheader label="Logout" id="lo"/>
<nextmatch-header label="Total" id="total"/>
<nextmatch-sortheader label="Idle" id="session_dla"/>
<nextmatch-header label="Last action" id="session_action"/>
<hbox>
<description value="Actions" align="center"/>
<buttononly options="check" label="Select all" onclick="toggle_all(this.form,form::name('selected[]')); return false;"/>
</hbox>
</row>
<row class="row">
<menulist>
<menupopup type="select-account" id="${row}[account_id]" readonly="true" label="$row_cont[alt_loginid]"/>
</menulist>
<description id="${row}[sessionstatus]"/>
<description id="${row}[loginid]"/>
<description id="${row}[ip]"/>
<date-time id="${row}[li]" readonly="true"/>
<date-time id="${row}[lo]" readonly="true"/>
<date-duration id="${row}[total]" readonly="true" options=",hm,24"/>
<date-since id="${row}[session_dla]" readonly="true"/>
<description id="${row}[session_action]"/>
<hbox options="0,0" align="center">
<button image="delete" label="Delete" id="delete[$row_cont[sessionid]]" statustext="Delete this log entry" onclick="return confirm('Delete this log entry');"/>
<button image="close" label="Kill" id="kill[$row_cont[sessionid]]" onclick="return confirm('Are you sure you want to kill this session ?');"/>
<checkbox options="$row_cont[sessionid]" id="selected[]" align="right"/>
</hbox>
</row>
</rows>
</grid>
</template>
<template id="admin.accesslog" template="" lang="" group="0" version="1.9.001">
<grid width="100%">
<columns>
<column/>
<column/>
</columns>
<rows>
<row disabled="!@msg">
<description span="all" class="redItalic" align="center" id="msg"/>
<description/>
</row>
<row>
<nextmatch span="all" id="nm" options="admin.accesslog.rows"/>
</row>
<row>
<hbox options="0,0">
<description readonly="true" value="Percent of users that logged out"/>
<textbox type="float" precision="1" label=": %s %" readonly="true" id="percent"/>
</hbox>
<hbox align="right">
<button label="Delete" onclick="return confirm('Delete the selected entries');" id="delete" image="delete" statustext="Delete selected entries"/>
<button image="close" label="Kill" id="kill" onclick="return confirm('Are you sure you want to kill this session ?');"/>
<buttononly options="arrow_ltr" label="Select all" onclick="toggle_all(this.form,form::name('selected[]')); return false;"/>
</hbox>
</row>
</rows>
</grid>
<styles>
.selectAllArrow { padding-right: 12px; }
</styles>
</template>
</overlay>

View File

@ -1,12 +1,12 @@
<?php
/**
* eGroupWare - Calendar's buisness-object - access only
* EGroupware - Calendar's buisness-object - access only
*
* @link http://www.egroupware.org
* @package calendar
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @author Joerg Lehrke <jlehrke@noc.de>
* @copyright (c) 2004-9 by RalfBecker-At-outdoor-training.de
* @copyright (c) 2004-11 by RalfBecker-At-outdoor-training.de
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
@ -67,7 +67,7 @@ class calendar_bo
var $debug=false;
/**
* @var int $now servertime
* @var int $now timestamp in server-time
*/
var $now;
@ -142,7 +142,7 @@ class calendar_bo
'NON-PARTICIPANT' => 'None',
);
/**
* @var array $resources registered scheduling resources of the calendar (gets chached in the session for performance reasons)
* @var array $resources registered scheduling resources of the calendar (gets cached in the session for performance reasons)
*/
var $resources;
/**
@ -155,6 +155,10 @@ class calendar_bo
* @var array $cached_holidays holidays plus birthdays (gets cached in the session for performance reasons)
*/
var $cached_holidays;
/**
* @var boholiday
*/
var $holidays;
/**
* Instance of the socal class
*
@ -181,12 +185,25 @@ class calendar_bo
*/
public $require_acl_invite = false;
/**
* if the number of selected users for a view exeeds this number a view is consolidated (5 is set as default)
* @var int
*/
public $calview_no_consolidate = 5;
/**
* Warnings to show in regular UI
*
* @var array
*/
var $warnings = array();
/**
* Constructor
*/
function __construct()
{
if ($this->debug > 0) $this->debug_message('bocal::bocal() started',True,$param);
if ($this->debug > 0) $this->debug_message('calendar_bo::bocal() started',True,$param);
$this->so = new calendar_so();
$this->datetime = $GLOBALS['egw']->datetime;
@ -195,7 +212,7 @@ class calendar_bo
$this->cal_prefs =& $GLOBALS['egw_info']['user']['preferences']['calendar'];
$this->now = time();
$this->now_su = egw_time::to('now','ts');
$this->now_su = egw_time::server2user($this->now,'ts');
$this->user = $GLOBALS['egw_info']['user']['account_id'];
@ -220,12 +237,16 @@ class calendar_bo
'info' => __CLASS__.'::email_info',
'app' => 'email',
);
$this->resources[''] = array(
'type' => '',
'app' => 'home-accounts',
);
$GLOBALS['egw']->session->appsession('resources','calendar',$this->resources);
}
//echo "registered resources="; _debug_array($this->resources);
$this->config = config::read('calendar'); // only used for horizont, regular calendar config is under phpgwapi
$this->calview_no_consolidate = ($GLOBALS['egw_info']['server']['calview_no_consolidate']?$GLOBALS['egw_info']['server']['calview_no_consolidate']:5);
$this->require_acl_invite = $GLOBALS['egw_info']['server']['require_acl_invite'];
$this->categories = new categories($this->user,'calendar');
@ -242,7 +263,7 @@ class calendar_bo
if (!$ids) return null;
$data = array();
foreach(!is_array($ids) ? array($ids) : $ids as $id)
foreach((array)$ids as $id)
{
$email = $id;
$name = '';
@ -258,7 +279,7 @@ class calendar_bo
'name' => $name,
);
}
//echo "<p>email_info(".print_r($ids,true).")="; _debug_array($data);
//error_log(__METHOD__.'('.array2string($ids).')='.array2string($data).' '.function_backtrace());
return $data;
}
@ -386,6 +407,7 @@ class calendar_bo
* cols string|array columns to select, if set an iterator will be returned
* append string to append to the query, eg. GROUP BY
* cfs array if set, query given custom fields or all for empty array, none are returned, if not set (default)
* master_only boolean default false, true only take into account participants/status from master (for AS)
* @param string $sql_filter=null sql to be and'ed into query (fully quoted), default none
* @return iterator|array|boolean array of events or array with YYYYMMDD strings / array of events pairs (depending on $daywise param)
* or false if there are no read-grants from _any_ of the requested users or iterator/recordset if cols are given
@ -444,18 +466,25 @@ class calendar_bo
$this->check_move_horizont($end);
}
$daywise = !isset($params['daywise']) ? False : !!$params['daywise'];
$enum_recuring = $daywise || !isset($params['enum_recuring']) || !!$params['enum_recuring'];
$params['enum_recuring'] = $enum_recuring = $daywise || !isset($params['enum_recuring']) || !!$params['enum_recuring'];
$cat_id = isset($params['cat_id']) ? $params['cat_id'] : 0;
$filter = isset($params['filter']) ? $params['filter'] : 'all';
$offset = isset($params['offset']) && $params['offset'] !== false ? (int) $params['offset'] : false;
// socal::search() returns rejected group-invitations, as only the user not also the group is rejected
// as we cant remove them efficiantly in SQL, we kick them out here, but only if just one user is displayed
$users_in = (array)$params_in['users'];
$remove_rejected_by_user = !in_array($filter,array('all','rejected')) &&
count($users_in) == 1 && $users_in[0] > 0 ? $users_in[0] : null;
//error_log(__METHOD__.'('.array2string($params_in).", $sql_filter) params[users]=".array2string($params['users']).' --> remove_rejected_by_user='.array2string($remove_rejected_by_user));
if ($this->debug && ($this->debug > 1 || $this->debug == 'search'))
{
$this->debug_message('bocal::search(%1) start=%2, end=%3, daywise=%4, cat_id=%5, filter=%6, query=%7, offset=%8, num_rows=%9, order=%10, sql_filter=%11)',
$this->debug_message('calendar_bo::search(%1) start=%2, end=%3, daywise=%4, cat_id=%5, filter=%6, query=%7, offset=%8, num_rows=%9, order=%10, sql_filter=%11)',
True,$params,$start,$end,$daywise,$cat_id,$filter,$params['query'],$offset,(int)$params['num_rows'],$params['order'],$params['sql_filter']);
}
// date2ts(,true) converts to server time, db2data converts again to user-time
$events =& $this->so->search(isset($start) ? $this->date2ts($start,true) : null,isset($end) ? $this->date2ts($end,true) : null,
$users,$cat_id,$filter,$params['query'],$offset,(int)$params['num_rows'],$params['order'],$params['sql_filter'],$params['cols'],$params['append'],$params['cfs']);
$users,$cat_id,$filter,$offset,(int)$params['num_rows'],$params,$remove_rejected_by_user);
if (isset($params['cols']))
{
@ -464,29 +493,22 @@ class calendar_bo
$this->total = $this->so->total;
$this->db2data($events,isset($params['date_format']) ? $params['date_format'] : 'ts');
// socal::search() returns rejected group-invitations, as only the user not also the group is rejected
// as we cant remove them efficiantly in SQL, we kick them out here, but only if just one user is displayed
$remove_rejected_by_user = !in_array($filter,array('all','rejected','owner')) && count($params['users']) == 1 ? $params['users'][0] : false;
//echo "<p align=right>remove_rejected_by_user=$remove_rejected_by_user, filter=$filter, params[users]=".print_r($param['users'])."</p>\n";
foreach($events as $id => $event)
{
if (isset($start) && $event['end'] < $start)
{
unset($events[$id]); // remove former events (e.g. whole day)
$this->total--;
continue;
}
if ($remove_rejected_by_user && $event['participants'][$remove_rejected_by_user] == 'R')
{
unset($events[$id]); // remove the rejected event
$this->total--;
continue;
}
if ($params['enum_groups'] && $this->enum_groups($event))
{
$events[$id] = $event;
}
if (!$this->check_perms(EGW_ACL_READ,$event) || (!$event['public'] && $filter == 'hideprivate'))
if (!(int)$event['id'] && preg_match('/^([a-z_]+)([0-9]+)$/',$event['id'],$matches))
{
$is_private = self::integration_get_private($matches[1],$matches[2],$event);
}
else
{
$is_private = !$this->check_perms(EGW_ACL_READ,$event);
}
if ($is_private || (!$event['public'] && $filter == 'hideprivate'))
{
$this->clear_private_infos($events[$id],$users);
}
@ -529,29 +551,11 @@ class calendar_bo
$this->debug_message('socalendar::search daywise events=%1',False,$events);
}
}
elseif(!$enum_recuring)
{
$recur_ids = array();
foreach($events as $k => $event)
{
if ($event['recur_type'] != MCAL_RECUR_NONE)
{
if (!in_array($event['id'],$recur_ids))
{
$recur_ids[] = $event['id'];
}
unset($events[$k]);
}
}
if (count($recur_ids))
{
$events = array_merge($this->read($recur_ids,null,false,$params['date_format']),$events);
}
}
if ($this->debug && ($this->debug > 0 || $this->debug == 'search'))
{
$this->debug_message('bocal::search(%1)=%2',True,$params,$events);
$this->debug_message('calendar_bo::search(%1)=%2',True,$params,$events);
}
//error_log(__METHOD__."() returning ".count($events)." entries, total=$this->total ".function_backtrace());
return $events;
}
@ -618,6 +622,8 @@ class calendar_bo
*/
function clear_private_infos(&$event,$allowed_participants = array())
{
if (!is_array($event['participants'])) error_log(__METHOD__.'('.array2string($event).', '.array2string($allowed_participants).') NO PARTICIPANTS '.function_backtrace());
$event = array(
'id' => $event['id'],
'start' => $event['start'],
@ -646,18 +652,24 @@ class calendar_bo
{
if ((int) $this->debug >= 2 || $this->debug == 'check_move_horizont')
{
$this->debug_message('bocal::check_move_horizont(%1) horizont=%2',true,$new_horizont,$this->config['horizont']);
$this->debug_message('calendar_bo::check_move_horizont(%1) horizont=%2',true,$new_horizont,(int)$this->config['horizont']);
}
$new_horizont = $this->date2ts($new_horizont,true); // now we are in server-time, where this function operates
if ($new_horizont > time()+1000*DAY_s) // some user tries to "look" more then 1000 days in the future
{
if ($this->debug == 'check_move_horizont') $this->debug_message('bocal::check_move_horizont(%1) horizont=%2 new horizont more then 1000 days from now --> ignoring it',true,$new_horizont,$this->config['horizont']);
return;
}
if ($new_horizont <= $this->config['horizont']) // no move necessary
{
if ($this->debug == 'check_move_horizont') $this->debug_message('bocal::check_move_horizont(%1) horizont=%2 is bigger ==> nothing to do',true,$new_horizont,$this->config['horizont']);
if ($this->debug == 'check_move_horizont') $this->debug_message('calendar_bo::check_move_horizont(%1) horizont=%2 is bigger ==> nothing to do',true,$new_horizont,(int)$this->config['horizont']);
return;
}
if (!empty($GLOBALS['egw_info']['server']['calendar_horizont']))
{
$maxdays = abs($GLOBALS['egw_info']['server']['calendar_horizont']);
}
if (empty($maxdays)) $maxdays = 1000; // old default
if ($new_horizont > time()+$maxdays*DAY_s) // some user tries to "look" more then the maximum number of days in the future
{
if ($this->debug == 'check_move_horizont') $this->debug_message('calendar_bo::check_move_horizont(%1) horizont=%2 new horizont more then %3 days from now --> ignoring it',true,$new_horizont,(int)$this->config['horizont'],$maxdays);
$this->warnings['horizont'] = lang('Requested date %1 outside allowed range of %2 days: recurring events obmitted!', egw_time::to($new_horizont,true), $maxdays);
return;
}
if ($new_horizont < time()+31*DAY_s)
@ -670,21 +682,21 @@ class calendar_bo
// create further recurrences for all recurring and not yet (at the old horizont) ended events
if (($recuring = $this->so->unfinished_recuring($old_horizont)))
{
@set_time_limit(0); // disable time-limit, in case it takes longer to calculate the recurrences
foreach($this->read(array_keys($recuring)) as $cal_id => $event)
{
if ($this->debug == 'check_move_horizont')
{
$this->debug_message('bocal::check_move_horizont(%1): calling set_recurrences(%2,%3)',true,$new_horizont,$event,$old_horizont);
$this->debug_message('calendar_bo::check_move_horizont(%1): calling set_recurrences(%2,%3)',true,$new_horizont,$event,$old_horizont);
}
// insert everything behind max(cal_start), which can be less then $old_horizont because of bugs in the past
$this->set_recurrences($event,egw_time::server2user($recuring[$cal_id]+1)); // set_recurences operates in user-time!
}
}
// update the horizont
$config = CreateObject('phpgwapi.config','calendar');
$config->save_value('horizont',$this->config['horizont'],'calendar');
config::save_value('horizont',$this->config['horizont'],'calendar');
if ($this->debug == 'check_move_horizont') $this->debug_message('bocal::check_move_horizont(%1) new horizont=%2, exiting',true,$new_horizont,$this->config['horizont']);
if ($this->debug == 'check_move_horizont') $this->debug_message('calendar_bo::check_move_horizont(%1) new horizont=%2, exiting',true,$new_horizont,(int)$this->config['horizont']);
}
/**
@ -699,7 +711,7 @@ class calendar_bo
{
if ($this->debug && ((int) $this->debug >= 2 || $this->debug == 'set_recurrences' || $this->debug == 'check_move_horizont'))
{
$this->debug_message('bocal::set_recurrences(%1,%2)',true,$event,$start);
$this->debug_message('calendar_bo::set_recurrences(%1,%2)',true,$event,$start);
}
// check if the caller gave us enough information and if not read it from the DB
if (!isset($event['participants']) || !isset($event['start']) || !isset($event['end']))
@ -725,13 +737,24 @@ class calendar_bo
//error_log('set_recurrences: days=' . array2string($days) );
foreach($events as $event)
{
$start_servertime = $this->date2ts($event['start'],true);
if (in_array($start_servertime, (array)$days))
$start = $this->date2ts($event['start'],true);
if (in_array($start, (array)$days))
{
// we don't change the stati of recurrence exceptions
$event['participants'] = array();
}
$this->so->recurrence($event['id'],$start_servertime,$this->date2ts($event['end'],true),$event['participants']);
if ($event['whole_day'])
{
$time = new egw_time($event['end'], egw_time::$user_timezone);
$time =& $this->so->startOfDay($time);
$time->setTime(23, 59, 59);
$end = $this->date2ts($time,true);
}
else
{
$end = $this->date2ts($event['end'],true);
}
$this->so->recurrence($event['id'],$start,$end,$event['participants']);
}
}
@ -746,11 +769,11 @@ class calendar_bo
*/
function db2data(&$events,$date_format='ts')
{
if (!is_array($events)) echo "<p>bocal::db2data(\$events,$date_format) \$events is no array<br />\n".function_backtrace()."</p>\n";
if (!is_array($events)) echo "<p>calendar_bo::db2data(\$events,$date_format) \$events is no array<br />\n".function_backtrace()."</p>\n";
foreach ($events as &$event)
{
// convert timezone id of event to tzid (iCal id like 'Europe/Berlin')
if (!$event['tz_id'] || !($event['tzid'] = calendar_timezones::id2tz($event['tz_id'])))
if (empty($event['tzid']) && (!$event['tz_id'] || !($event['tzid'] = calendar_timezones::id2tz($event['tz_id']))))
{
$event['tzid'] = egw_time::$server_timezone->getName();
}
@ -759,7 +782,7 @@ class calendar_bo
// (this will fail on 32bit systems for times > 2038!)
$event['start'] = (int)$event['start']; // this is for isWholeDay(), which also calls egw_time
$event['end'] = (int)$event['end'];
$event['whole_day'] = $this->isWholeDay($event);
$event['whole_day'] = self::isWholeDay($event);
if ($event['whole_day'] && $date_format != 'server')
{
// Adjust dates to user TZ
@ -783,11 +806,11 @@ class calendar_bo
$time->setTime(23, 59, 59);
$event['recur_enddate'] = egw_time::to($time, $date_format);
}
$timestamps = array('modified','created');
$timestamps = array('modified','created','max_user_modified');
}
else
{
$timestamps = array('start','end','modified','created','recur_enddate','recurrence');
$timestamps = array('start','end','modified','created','recur_enddate','recurrence','max_user_modified');
}
// we convert here from the server-time timestamps to user-time and (optional) to a different date-format!
foreach ($timestamps as $ts)
@ -847,15 +870,20 @@ class calendar_bo
* @param mixed $date=null date to specify a single event of a series
* @param boolean $ignore_acl should we ignore the acl, default False for a single id, true for multiple id's
* @param string $date_format='ts' date-formats: 'ts'=timestamp, 'server'=timestamp in servertime, 'array'=array, or string with date-format
* @param array|int $clear_privat_infos_users=null if not null, return events with EGW_ACL_FREEBUSY too,
* but call clear_private_infos() with the given users
* @return boolean|array event or array of id => event pairs, false if the acl-check went wrong, null if $ids not found
*/
function read($ids,$date=null,$ignore_acl=False,$date_format='ts')
function read($ids,$date=null,$ignore_acl=False,$date_format='ts',$clear_private_infos_users=null)
{
if (!$ids) return false;
if ($date) $date = $this->date2ts($date);
$return = null;
if ($ignore_acl || is_array($ids) || ($return = $this->check_perms(EGW_ACL_READ,$ids,0,$date_format,$date)))
$check = $clear_private_infos_users ? EGW_ACL_FREEBUSY : EGW_ACL_READ;
if ($ignore_acl || is_array($ids) || ($return = $this->check_perms($check,$ids,0,$date_format,$date)))
{
if (is_array($ids) || !isset(self::$cached_event['id']) || self::$cached_event['id'] != $ids ||
self::$cached_event_date_format != $date_format ||
@ -876,7 +904,7 @@ class calendar_bo
self::$cached_event = array_shift($events);
self::$cached_event_date_format = $date_format;
self::$cached_event_date = $date;
$return =& self::$cached_event;
$return = self::$cached_event;
}
}
}
@ -885,9 +913,13 @@ class calendar_bo
$return = self::$cached_event;
}
}
if ($clear_private_infos_users && !is_array($ids) && !$this->check_perms(EGW_ACL_READ,$return))
{
$this->clear_private_infos($return, (array)$clear_private_infos_users);
}
if ($this->debug && ($this->debug > 1 || $this->debug == 'read'))
{
$this->debug_message('bocal::read(%1,%2,%3,%4)=%5',True,$ids,$date,$ignore_acl,$date_format,$return);
$this->debug_message('calendar_bo::read(%1,%2,%3,%4,%5)=%6',True,$ids,$date,$ignore_acl,$date_format,$clear_private_infos_users,$return);
}
return $return;
}
@ -921,10 +953,11 @@ class calendar_bo
if (!$event['recur_enddate'] || $this->date2ts($event['recur_enddate']) > $this->date2ts($end))
{
//echo "<p>recur_enddate={$event['recur_enddate']}=".egw_time::to($event['recur_enddate'])." > end=$end=".egw_time::to($end)." --> using end instead of recur_enddate</p>\n";
$event['recur_enddate'] = $end;
// insert at least the event itself, if it's behind the horizont
$event['recur_enddate'] = $this->date2ts($end) < $this->date2ts($event['end']) ? $event['end'] : $end;
}
// loop over all recurrences and insert them, if they are after $start
$rrule = calendar_rrule::event2rrule($event,true); // true = we operate in usertime, like the rest of calendar_bo
$rrule = calendar_rrule::event2rrule($event, true); // true = we operate in usertime, like the rest of calendar_bo
foreach($rrule as $time)
{
$time->setUser(); // $time is in timezone of event, convert it to usertime used here
@ -993,7 +1026,7 @@ class calendar_bo
if ($this->debug && ($this->debug > 2 || $this->debug == 'add_adjust_event'))
{
$this->debug_message('bocal::add_adjust_event(,%1,%2) as %3',True,$event_in,$date_ymd,$event);
$this->debug_message('calendar_bo::add_adjust_event(,%1,%2) as %3',True,$event_in,$date_ymd,$event);
}
}
@ -1009,6 +1042,8 @@ class calendar_bo
{
static $res_info_cache = array();
if (!is_scalar($uid)) throw new egw_exception_wrong_parameter(__METHOD__.'('.array2string($uid).') parameter must be scalar');
if (!isset($res_info_cache[$uid]))
{
if (is_numeric($uid))
@ -1019,6 +1054,7 @@ class calendar_bo
'name' => trim($GLOBALS['egw']->accounts->id2name($uid,'account_firstname'). ' ' .
$GLOBALS['egw']->accounts->id2name($uid,'account_lastname')),
'type' => $GLOBALS['egw']->accounts->get_type($uid),
'app' => 'accounts',
);
}
else
@ -1031,13 +1067,14 @@ class calendar_bo
{
$info['email'] = $GLOBALS['egw']->accounts->id2name($info['responsible'],'account_email');
}
$info['app'] = $this->resources[$uid[0]]['app'];
}
}
$res_info_cache[$uid] = $info;
}
if ($this->debug && ($this->debug > 2 || $this->debug == 'resource_info'))
{
$this->debug_message('bocal::resource_info(%1) = %2',True,$uid,$res_info_cache[$uid]);
$this->debug_message('calendar_bo::resource_info(%1) = %2',True,$uid,$res_info_cache[$uid]);
}
return $res_info_cache[$uid];
}
@ -1055,10 +1092,21 @@ class calendar_bo
* @param int $other uid to check (if event==0) or 0 to check against $this->user
* @param string $date_format='ts' date-format used for reading: 'ts'=timestamp, 'array'=array, 'string'=iso8601 string for xmlrpc
* @param mixed $date_to_read=null date used for reading, internal param for the caching
* @param int $user=null for which user to check, default current user
* @return boolean true permission granted, false for permission denied or null if event not found
*/
function check_perms($needed,$event=0,$other=0,$date_format='ts',$date_to_read=null)
function check_perms($needed,$event=0,$other=0,$date_format='ts',$date_to_read=null,$user=null)
{
if (!$user) $user = $this->user;
if ($user == $this->user)
{
$grants = $this->grants;
}
else
{
$grants = $GLOBALS['egw']->acl->get_grants('calendar',true,$user);
}
$event_in = $event;
if ($other && !is_numeric($other))
{
@ -1067,7 +1115,7 @@ class calendar_bo
}
if (is_int($event) && $event == 0)
{
$owner = $other ? $other : $this->user;
$owner = $other ? $other : $user;
}
else
{
@ -1086,10 +1134,10 @@ class calendar_bo
$owner = $event['owner'];
$private = !$event['public'];
}
$grants = $this->grants[$owner];
$grant = $grants[$owner];
// now any ACL rights (but invite rights!) implicate FREEBUSY rights (at least READ has to include FREEBUSY)
if ($grants & ~EGW_ACL_INVITE) $grants |= EGW_ACL_FREEBUSY;
if ($grant & ~EGW_ACL_INVITE) $grant |= EGW_ACL_FREEBUSY;
if (is_array($event) && ($needed == EGW_ACL_READ || $needed == EGW_ACL_FREEBUSY))
{
@ -1100,25 +1148,25 @@ class calendar_bo
{
foreach($event['participants'] as $uid => $accept)
{
if ($uid == $this->user || $uid < 0 && in_array($this->user,$GLOBALS['egw']->accounts->members($uid,true)))
if ($uid == $user || $uid < 0 && in_array($user,$GLOBALS['egw']->accounts->members($uid,true)))
{
// if we are a participant, we have an implicite FREEBUSY, READ and PRIVAT grant
$grants |= EGW_ACL_FREEBUSY | EGW_ACL_READ | EGW_ACL_PRIVATE;
$grant |= EGW_ACL_FREEBUSY | EGW_ACL_READ | EGW_ACL_PRIVATE;
break;
}
elseif ($this->grants[$uid] & EGW_ACL_READ)
elseif ($grants[$uid] & EGW_ACL_READ)
{
// if we have a READ grant from a participant, we dont give an implicit privat grant too
$grants |= EGW_ACL_READ;
$grant |= EGW_ACL_READ;
// we cant break here, as we might be a participant too, and would miss the privat grant
}
elseif (!is_numeric($uid))
{
// if the owner only grants EGW_ACL_BUSY we are not interested in the recources explicit rights
if ($grants == EGW_ACL_FREEBUSY) break;
// if the owner only grants EGW_ACL_FREEBUSY we are not interested in the recources explicit rights
if ($grant == EGW_ACL_FREEBUSY) break;
// if we have a resource as participant
$resource = $this->resource_info($uid);
$grants |= $resource['rights'];
$grant |= $resource['rights'];
}
}
}
@ -1129,14 +1177,21 @@ class calendar_bo
}
else
{
$access = $this->user == $owner || $grants & $needed
&& ($needed == EGW_ACL_FREEBUSY || !$private || $grants & EGW_ACL_PRIVATE);
$access = $user == $owner || $grant & $needed
&& ($needed == EGW_ACL_FREEBUSY || !$private || $grant & EGW_ACL_PRIVATE);
}
// do NOT allow users to purge deleted events, if we dont have 'user_purge' enabled
if ($access && $needed == EGW_ACL_DELETE && $event['deleted'] &&
!$GLOBALS['egw_info']['user']['apps']['admin'] && $user != $this->user &&
$GLOBALS['egw_info']['server']['calendar_delete_history'] != 'user_purge')
{
$access = false;
}
if ($this->debug && ($this->debug > 2 || $this->debug == 'check_perms'))
{
$this->debug_message('bocal::check_perms(%1,%2,%3)=%4',True,ACL_TYPE_IDENTIFER.$needed,$event,$other,$access);
$this->debug_message('calendar_bo::check_perms(%1,%2,other=%3,%4,%5,user=%6)=%7',True,ACL_TYPE_IDENTIFER.$needed,$event,$other,$date_format,$date_to_read,$user,$access);
}
//error_log(__METHOD__."($needed,".array2string($event).",$other) returning ".array2string($access));
//error_log(__METHOD__."($needed,".array2string($event).",$other,...,$user) returning ".array2string($access));
return $access;
}
@ -1266,7 +1321,9 @@ class calendar_bo
}
$msg = str_replace('%'.($i-1),$param,$msg);
}
echo '<p>'.$msg."<br>\n".($backtrace ? 'Backtrace: '.function_backtrace(1)."</p>\n" : '').str_repeat(' ',4096);
//echo '<p>'.$msg."<br>\n".($backtrace ? 'Backtrace: '.function_backtrace(1)."</p>\n" : '').str_repeat(' ',4096);
error_log($msg);
if ($backtrace) error_log(function_backtrace(1));
}
/**
@ -1377,11 +1434,11 @@ class calendar_bo
if ($end_m == 24*60-1) ++$duration;
$duration = floor($duration/60).lang('h').($duration%60 ? $duration%60 : '');
$timespan = $t = $GLOBALS['egw']->common->formattime(sprintf('%02d',$start_m/60),sprintf('%02d',$start_m%60));
$timespan = $t = common::formattime(sprintf('%02d',$start_m/60),sprintf('%02d',$start_m%60));
if ($both) // end-time too
{
$timespan .= ' - '.$GLOBALS['egw']->common->formattime(sprintf('%02d',$end_m/60),sprintf('%02d',$end_m%60));
$timespan .= ' - '.common::formattime(sprintf('%02d',$end_m/60),sprintf('%02d',$end_m%60));
// dont double am/pm if they are the same in both times
if ($this->common_prefs['timeformat'] == 12 && substr($timespan,-2) == substr($t,-2))
{
@ -1396,12 +1453,14 @@ class calendar_bo
* Converts a participant into a (readable) user- or resource-name
*
* @param string|int $id id of user or resource
* @param string|boolean type-letter or false
* @param string|boolean $use_type=false type-letter or false
* @param boolean $append_email=false append email (Name <email>)
* @return string with name
*/
function participant_name($id,$use_type=false)
function participant_name($id,$use_type=false, $append_email=false)
{
static $id2lid = array();
static $id2email = array();
if ($use_type && $use_type != 'u') $id = $use_type.$id;
@ -1413,14 +1472,16 @@ class calendar_bo
if (($info = $this->resource_info($id)))
{
$id2lid[$id] = $info['name'] ? $info['name'] : $info['email'];
if ($info['name']) $id2email[$id] = $info['email'];
}
}
else
{
$id2lid[$id] = common::grab_owner_name($id);
$id2email[$id] = $GLOBALS['egw']->accounts->id2name($id,'account_email');
}
}
return $id2lid[$id];
return $id2lid[$id].($append_email && $id2email[$id] ? ' <'.$id2email[$id].'>' : '');
}
/**
@ -1435,7 +1496,7 @@ class calendar_bo
{
//_debug_array($event);
$names = array();
foreach($event['participants'] as $id => $status)
foreach((array)$event['participants'] as $id => $status)
{
calendar_so::split_status($status,$quantity,$role);
@ -1531,8 +1592,11 @@ class calendar_bo
*/
function _list_cals_add($id,&$users,&$groups)
{
$name = $GLOBALS['egw']->common->grab_owner_name($id);
$egw_name = $GLOBALS['egw']->accounts->id2name($id);
$name = common::grab_owner_name($id);
if (!($egw_name = $GLOBALS['egw']->accounts->id2name($id)))
{
return; // do not return no longer existing accounts which eg. still mentioned in acl
}
if (($type = $GLOBALS['egw']->accounts->get_type($id)) == 'g')
{
$arr = &$groups;
@ -1541,7 +1605,7 @@ class calendar_bo
{
$arr = &$users;
}
$arr[$name] = Array(
$arr[$id] = array(
'grantor' => $id,
'value' => ($type == 'g' ? 'g_' : '') . $id,
'name' => $name,
@ -1552,7 +1616,7 @@ class calendar_bo
/**
* generate list of user- / group-calendars for the selectbox in the header
*
* @return array alphabeticaly sorted array with groups first and then users: $name => array('grantor'=>$id,'value'=>['g_'.]$id,'name'=>$name)
* @return array alphabeticaly sorted array with users first and then groups: array('grantor'=>$id,'value'=>['g_'.]$id,'name'=>$name)
*/
function list_cals()
{
@ -1561,7 +1625,7 @@ class calendar_bo
{
$this->_list_cals_add($id,$users,$groups);
}
if ($memberships = $GLOBALS['egw']->accounts->membership($GLOBALS['egw_info']['user']['account_id']))
if (($memberships = $GLOBALS['egw']->accounts->membership($GLOBALS['egw_info']['user']['account_id'])))
{
foreach($memberships as $group_info)
{
@ -1576,17 +1640,22 @@ class calendar_bo
}
}
}
foreach ($groups as $name => $group)
{
foreach ($users as $user)
{
if ($user['sname'] == $group['sname']) unset($groups[$name]);
}
}
uksort($users,'strnatcasecmp');
uksort($groups,'strnatcasecmp');
usort($users,array($this,'name_cmp'));
usort($groups,array($this,'name_cmp'));
return $users + $groups; // users first and then groups, both alphabeticaly
return array_merge($users, $groups); // users first and then groups, both alphabeticaly
}
/**
* Compare function for sort by value of key 'name'
*
* @param array $a
* @param array $b
* @return int
*/
function name_cmp(array $a, array $b)
{
return strnatcasecmp($a['name'], $b['name']);
}
/**
@ -1643,12 +1712,12 @@ class calendar_bo
'bday' => "!''",
);
$bdays =& $contacts->search('',array('id','n_family','n_given','n_prefix','n_middle','bday'),
'contact_bday ASC',$extra_cols='',$wildcard='',$empty=False,$op='AND',$start=false,$filter);
'bday ASC',$extra_cols='',$wildcard='',$empty=False,$op='AND',$start=false,$filter);
// search accounts too, if not stored in contacts repository
$extra_accounts_search = $contacts->account_repository == 'ldap' && !is_null($contacts->so_accounts) &&
!$GLOBALS['egw_info']['user']['preferences']['addressbook']['hide_accounts'];
if ($extra_accounts_search && ($bdays2 =& $contacts->search('',array('id','n_family','n_given','n_prefix','n_middle','bday'),
'contact_bday ASC',$extra_cols='',$wildcard='',$empty=False,$op='AND',$start=false,$filter+array('owner' => 0))))
'bday ASC',$extra_cols='',$wildcard='',$empty=False,$op='AND',$start=false,$filter+array('owner' => 0))))
{
$bdays = !$bdays ? $bdays2 : array_merge($bdays,$bdays2);
}
@ -1681,7 +1750,7 @@ class calendar_bo
}
if ((int) $this->debug >= 2 || $this->debug == 'read_holidays')
{
$this->debug_message('bocal::read_holidays(%1)=%2',true,$year,$this->cached_holidays[$year]);
$this->debug_message('calendar_bo::read_holidays(%1)=%2',true,$year,$this->cached_holidays[$year]);
}
return $this->cached_holidays[$year];
}
@ -1696,7 +1765,12 @@ class calendar_bo
*/
function link_title($event)
{
if (!is_array($event) && (int) $event > 0)
if (!is_array($event) && strpos($event, '-') !== false)
{
list($id, $recur) = explode('-', $event, 2);
$event = $this->read($id, $recur);
}
else if (!is_array($event) && (int) $event > 0)
{
$event = $this->read($event);
}
@ -1715,26 +1789,36 @@ class calendar_bo
* @param string $pattern pattern to search
* @return array with cal_id - title pairs of the matching entries
*/
function link_query($pattern)
function link_query($pattern, Array &$options = array())
{
$result = array();
foreach((array) $this->search(array('query' => $pattern)) as $event)
$query = array(
'query' => $pattern,
'offset' => $options['start'],
);
if($options['num_rows']) {
$query['num_rows'] = $options['num_rows'];
}
foreach((array) $this->search($query) as $event)
{
$result[$event['id']] = $this->link_title($event);
}
$options['total'] = $this->total;
return $result;
}
/**
* Check access to the projects file store
* Check access to the file store
*
* @param int $id id of entry
* @param int $check EGW_ACL_READ for read and EGW_ACL_EDIT for write or delete access
* @param string $rel_path=null currently not used in calendar
* @param int $user=null for which user to check, default current user
* @return boolean true if access is granted or false otherwise
*/
function file_access($id,$check,$rel_path)
function file_access($id,$check,$rel_path,$user=null)
{
return $this->check_perms($check,$id);
return $this->check_perms($check,$id,0,'ts',null,$user);
}
/**
@ -1794,14 +1878,25 @@ class calendar_bo
* @param int|string $user account_id or account_lid
* @param string $pw=null password
*/
static function freebusy_url($user,$pw=null)
static function freebusy_url($user='',$pw=null)
{
if (is_numeric($user)) $user = $GLOBALS['egw']->accounts->id2name($user);
$credentials = '';
if ($pw)
{
$credentials = '&password='.urlencode($pw);
}
elseif ($GLOBALS['egw_info']['user']['preferences']['calendar']['freebusy'] == 2)
{
$credentials = $GLOBALS['egw_info']['user']['account_lid']
. ':' . $GLOBALS['egw_info']['user']['passwd'];
$credentials = '&cred=' . base64_encode($credentials);
}
return (!$GLOBALS['egw_info']['server']['webserver_url'] || $GLOBALS['egw_info']['server']['webserver_url'][0] == '/' ?
($_SERVER['HTTPS'] ? 'https://' : 'http://').$_SERVER['HTTP_HOST'] : '').
$GLOBALS['egw_info']['server']['webserver_url'].'/calendar/freebusy.php?user='.urlencode($user).
($pw ? '&password='.urlencode($pw) : '');
$GLOBALS['egw_info']['server']['webserver_url'].'/calendar/freebusy.php/?user='.urlencode($user).$credentials;
}
/**
@ -1810,11 +1905,11 @@ class calendar_bo
* @param array $event event
* @return boolean true if whole day event, false othwerwise
*/
function isWholeDay($event)
public static function isWholeDay($event)
{
// check if the event is the whole day
$start = $this->date2array($event['start']);
$end = $this->date2array($event['end']);
$start = self::date2array($event['start']);
$end = self::date2array($event['end']);
return !$start['hour'] && !$start['minute'] && $end['hour'] == 23 && $end['minute'] == 59;
}
@ -1823,10 +1918,13 @@ class calendar_bo
* Get the etag for an entry
*
* @param array|int|string $event array with event or cal_id, or cal_id:recur_date for virtual exceptions
* @param string &$schedule_tag=null on return schedule-tag (egw_cal.cal_id:egw_cal.cal_etag, no participant modifications!)
* @param boolean $client_share_uid_excpetions Does client understand exceptions to be included in VCALENDAR component of series master sharing its UID
* @param boolean $master_only=false only take into account recurrance masters
* (for ActiveSync which does not support different participants/status on recurrences/exceptions!)
* @return string|boolean string with etag or false
*/
function get_etag($entry,$client_share_uid_excpetions=true)
function get_etag($entry, &$schedule_tag=null, $client_share_uid_excpetions=true,$master_only=false)
{
if (!is_array($entry))
{
@ -1834,7 +1932,7 @@ class calendar_bo
if (!$this->check_perms(EGW_ACL_FREEBUSY, $entry, 0, 'server')) return false;
$entry = $this->read($entry, $recur_date, true, 'server');
}
$etag = $entry['id'].':'.$entry['etag'];
$etag = $schedule_tag = $entry['id'].':'.$entry['etag'];
// use new MAX(modification date) of egw_cal_user table (deals with virtual exceptions too)
if (isset($entry['max_user_modified']))
@ -1843,7 +1941,7 @@ class calendar_bo
}
else
{
$modified = max($this->so->max_user_modified($entry['id']), $entry['modified']);
$modified = max($this->so->max_user_modified($entry['id'],false,$master_only), $entry['modified']);
}
$etag .= ':' . $modified;
// include exception etags into our own etag, if exceptions are included
@ -1863,9 +1961,17 @@ class calendar_bo
{
if ($recurrence['reference'] && $recurrence['id'] != $entry['id']) // ignore series master
{
$etag .= ':'.$this->get_etag($recurrence);
$exception_etag = $this->get_etag($recurrence, $nul);
// if $master_only, only add cal_etag, not max. user modification date
if ($master_only) list(,$exception_etag) = explode(':',$exception_etag);
$exception_etags .= ':'.$this->get_etag($recurrence, $nul);
}
}
if ($exception_etags)
{
$etag .= ':'.md5($exception_etags); // limit size, as there can be many exceptions
}
}
//error_log(__METHOD__ . "($entry[id],$client_share_uid_excpetions) entry=".array2string($entry)." --> etag=$etag");
return $etag;
@ -1875,19 +1981,45 @@ class calendar_bo
* Query ctag for calendar
*
* @param int|array $user integer user-id or array of user-id's to use, defaults to the current user
* @param $filter='owner'
* @return string $filter='owner' all (not rejected), accepted, unknown, tentative, rejected or hideprivate
* @todo use MAX(modified) to query everything in one sql query, currently we do one query per event (more then the search)
* @param string $filter='owner' all (not rejected), accepted, unknown, tentative, rejected or hideprivate
* @param boolean $master_only=false only check recurance master (egw_cal_user.recur_date=0)
* @return integer
*/
public function get_ctag($user,$filter='owner')
public function get_ctag($user, $filter='owner', $master_only=false)
{
if ($this->debug > 1) $startime = microtime(true);
// resolve users to add memberships for users and members for groups
$users = $this->resolve_users($user);
$ctag = $users ? $this->so->get_ctag($users, $filter == 'owner') : 0; // no rights, return 0 as ctag (otherwise we get SQL error!)
$ctag = $users ? $this->so->get_ctag($users, $filter == 'owner', $master_only) : 0; // no rights, return 0 as ctag (otherwise we get SQL error!)
if ($this->debug > 1) error_log(__METHOD__. "($user, '$filter') = $ctag = ".date('Y-m-d H:i:s',$ctag)." took ".(microtime(true)-$startime)." secs");
return $ctag;
}
/**
* Hook for timesheet to set some extra data and links
*
* @param array $data
* @param int $data[id] cal_id:recurrence
* @return array with key => value pairs to set in new timesheet and link_app/link_id arrays
*/
function timesheet_set($data)
{
$set = array();
list($id,$recurrence) = explode(':',$data['id']);
if ((int)$id && ($event = $this->read($id,$recurrence)))
{
$set['ts_start'] = $event['start'];
$set['ts_title'] = $this->link_title($event);
$set['start_time'] = egw_time::to($event['start'],'H:i');
$set['ts_description'] = $event['description'];
if ($this->isWholeDay($event)) $event['end']++; // whole day events are 1sec short
$set['ts_duration'] = ($event['end'] - $event['start']) / 60;
$set['ts_quantity'] = ($event['end'] - $event['start']) / 3600;
$set['end_time'] = null; // unset end-time
$set['cat_id'] = (int)$event['category'];
}
return $set;
}
}

View File

@ -1,12 +1,12 @@
<?php
/**
* eGroupWare - Calendar's buisness-object - access + update
* EGroupware - Calendar's buisness-object - access + update
*
* @link http://www.egroupware.org
* @package calendar
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @author Joerg Lehrke <jlehrke@noc.de>
* @copyright (c) 2005-9 by RalfBecker-At-outdoor-training.de
* @copyright (c) 2005-11 by RalfBecker-At-outdoor-training.de
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
@ -111,7 +111,7 @@ class calendar_boupdate extends calendar_bo
* + + + C + which is clearly wrong for everything with a maximum quantity > 1
* ++++++++ ++++++++
*/
function update(&$event,$ignore_conflicts=false,$touch_modified=true,$ignore_acl=false,$updateTS=true,&$messages=null)
function update(&$event,$ignore_conflicts=false,$touch_modified=true,$ignore_acl=false,$updateTS=true,&$messages=null, $skip_notification=false)
{
//error_log(__METHOD__."(".array2string($event).",$ignore_conflicts,$touch_modified,$ignore_acl)");
@ -130,6 +130,8 @@ class calendar_boupdate extends calendar_bo
return false;
}
$status_reset_to_unknown = false;
if (($new_event = !$event['id'])) // some defaults for new entries
{
// if no owner given, set user to owner
@ -356,19 +358,28 @@ class calendar_boupdate extends calendar_bo
$event = $this->read($cal_id); // we re-read the event, in case only partial information was update and we need the full info for the notifies
//echo "new $cal_id="; _debug_array($event);
if($old_event['deleted'] && $event['deleted'] == null)
{
// Restored, bring back links
egw_link::restore('calendar', $cal_id);
}
if ($this->log_file)
{
$this->log2file($event2save,$event,$old_event);
}
// send notifications
if ($new_event)
if(!$skip_notification)
{
$this->send_update(MSG_ADDED,$event['participants'],'',$event);
}
else // update existing event
{
$this->check4update($event,$old_event);
if ($new_event)
{
$this->send_update(MSG_ADDED,$event['participants'],'',$event);
}
else // update existing event
{
$this->check4update($event,$old_event);
}
}
// notify the link-class about the update, as other apps may be subscribt to it
egw_link::notify_update('calendar',$cal_id,$event);
@ -393,7 +404,7 @@ class calendar_boupdate extends calendar_bo
$old_event = $this->read($event['id']);
}
$removed = array();
foreach($event['participants'] as $uid => $status)
foreach((array)$event['participants'] as $uid => $status)
{
if ((is_null($old_event) || !isset($old_event['participants'][$uid])) && !$this->check_acl_invite($uid))
{
@ -492,9 +503,10 @@ class calendar_boupdate extends calendar_bo
* @param int $msg_type type of the notification: MSG_ADDED, MSG_MODIFIED, MSG_ACCEPTED, ...
* @param array $old_event Event before the change
* @param array $new_event Event after the change
* @return boolean true = update requested, flase otherwise
* @param string $role we treat CHAIR like event owners
* @return boolean true = update requested, false otherwise
*/
function update_requested($userid,$part_prefs,$msg_type,$old_event,$new_event)
function update_requested($userid,$part_prefs,$msg_type,$old_event,$new_event,$role)
{
if ($msg_type == MSG_ALARM)
{
@ -517,6 +529,7 @@ class calendar_boupdate extends calendar_bo
}
case 'time_change_4h':
case 'time_change':
default:
$diff = max(abs($this->date2ts($old_event['start'])-$this->date2ts($new_event['start'])),
abs($this->date2ts($old_event['end'])-$this->date2ts($new_event['end'])));
$check = $ru == 'time_change_4h' ? 4 * 60 * 60 - 1 : 0;
@ -525,13 +538,17 @@ class calendar_boupdate extends calendar_bo
++$want_update;
}
case 'add_cancel':
if ($old_event['owner'] == $userid && $msg_is_response ||
if ($msg_is_response && ($old_event['owner'] == $userid || $role == 'CHAIR') ||
$msg_type == MSG_DELETED || $msg_type == MSG_ADDED || $msg_type == MSG_DISINVITE)
{
++$want_update;
}
break;
case 'no':
if ($msg_is_response && $role == 'CHAIR') // always notify chairs!
{
++$want_update;
}
break;
}
//error_log(__METHOD__."(userid=$userid,,msg_type=$msg_type,...) msg_is_response=$msg_is_response, want_update=$want_update");
@ -672,12 +689,31 @@ class calendar_boupdate extends calendar_bo
if ($old_event != False) $olddate = new egw_time($old_event['start']);
foreach($to_notify as $userid => $statusid)
{
if ($this->debug > 0) error_log(__METHOD__." trying to notify $userid, with $statusid");
unset($res_info);
calendar_so::split_status($statusid, $quantity, $role);
if ($this->debug > 0) error_log(__METHOD__." trying to notify $userid, with $statusid ($role)");
if (!is_numeric($userid))
{
$res_info = $this->resource_info($userid);
$userid = $res_info['responsible'];
if (!isset($userid)) continue;
if (!isset($userid))
{
if (empty($res_info['email'])) continue; // no way to notify
// check if event-owner wants non-EGroupware users notified
if (is_null($owner_prefs))
{
$preferences = new preferences($old_event['owner']);
$owner_prefs = $preferences->read_repository();
}
if ($role != 'CHAIR' && // always notify externals CHAIRs
(empty($owner_prefs['calendar']['notify_externals']) ||
$owner_prefs['calendar']['notify_externals'] == 'no'))
{
continue;
}
$userid = $res_info['email'];
}
}
if ($statusid == 'R' || $GLOBALS['egw']->accounts->get_type($userid) == 'g')
@ -690,16 +726,30 @@ class calendar_boupdate extends calendar_bo
$user_prefs['calendar']['receive_own_updates']==1) ||
$msg_type == MSG_ALARM)
{
$preferences = CreateObject('phpgwapi.preferences',$userid);
$part_prefs = $preferences->read_repository();
if (is_numeric($userid))
{
$preferences = new preferences($userid);
$part_prefs = $preferences->read_repository();
if (!$this->update_requested($userid,$part_prefs,$msg_type,$old_event,$new_event))
$GLOBALS['egw']->accounts->get_account_name($userid,$lid,$details['to-firstname'],$details['to-lastname']);
$details['to-fullname'] = common::display_fullname('',$details['to-firstname'],$details['to-lastname']);
}
else // external email address: use preferences of event-owner, plus some hardcoded settings (eg. ical notification)
{
if (is_null($owner_prefs))
{
$preferences = new preferences($old_event['owner']);
$owner_prefs = $preferences->read_repository();
}
$part_prefs = $owner_prefs;
$part_prefs['calendar']['receive_updates'] = $owner_prefs['calendar']['notify_externals'];
$part_prefs['calendar']['update_format'] = 'ical'; // use ical format
$details['to-fullname'] = $res_info && !empty($res_info['name']) ? $res_info['name'] : $userid;
}
if (!$this->update_requested($userid,$part_prefs,$msg_type,$old_event,$new_event,$role))
{
continue;
}
$GLOBALS['egw']->accounts->get_account_name($userid,$lid,$details['to-firstname'],$details['to-lastname']);
$details['to-fullname'] = $GLOBALS['egw']->common->display_fullname('',$details['to-firstname'],$details['to-lastname']);
// event is in user-time of current user, now we need to calculate the tz-difference to the notified user and take it into account
if (!isset($part_prefs['common']['tz'])) $part_prefs['common']['tz'] = $GLOBALS['egw_info']['server']['server_timezone'];
$timezone = new DateTimeZone($part_prefs['common']['tz']);
@ -735,27 +785,28 @@ class calendar_boupdate extends calendar_bo
switch($part_prefs['calendar']['update_format'])
{
case 'ical':
if ($method == 'REQUEST')
if (is_null($ics))
{
if (is_null($ics))
{
$calendar_ical = new calendar_ical();
$calendar_ical->setSupportedFields('full'); // full iCal fields+event TZ
$ics = $calendar_ical->exportVCal($event['id'],'2.0',$method);
unset($calendar_ical);
}
$attachment = array( 'string' => $ics,
'filename' => 'cal.ics',
'encoding' => '8bit',
'type' => 'text/calendar; method='.$method,
);
$calendar_ical = new calendar_ical();
$calendar_ical->setSupportedFields('full'); // full iCal fields+event TZ
// we need to pass $event[id] so iCal class reads event again,
// as event is in user TZ, but iCal class expects server TZ!
$ics = $calendar_ical->exportVCal(array($event['id']),'2.0',$method);
unset($calendar_ical);
}
$attachment = array(
'string' => $ics,
'filename' => 'cal.ics',
'encoding' => '8bit',
'type' => 'text/calendar; method='.$method,
);
// fall through
case 'extended':
$body .= "\n\n".lang('Event Details follow').":\n";
foreach($event_arr as $key => $val)
{
if(strlen($details[$key])) {
if(!empty($details[$key]))
{
switch($key){
case 'access':
case 'priority':
@ -770,7 +821,8 @@ class calendar_boupdate extends calendar_bo
break;
}
// send via notification_app
if($GLOBALS['egw_info']['apps']['notifications']['enabled']) {
if($GLOBALS['egw_info']['apps']['notifications']['enabled'])
{
try {
$notification = new notifications();
$notification->set_receivers(array($userid));
@ -785,7 +837,9 @@ class calendar_boupdate extends calendar_bo
error_log(__METHOD__.' error while notifying user '.$userid.':'.$exception->getMessage());
continue;
}
} else {
}
else
{
error_log(__METHOD__.' cannot send any notifications because notifications is not installed');
}
}
@ -872,12 +926,59 @@ class calendar_boupdate extends calendar_bo
return false;
}
// invalidate the read-cache if it contains the event we store now
if ($event['id'] && $event['id'] == self::$cached_event['id']) self::$cached_event = array();
if ($event['id'])
{
// invalidate the read-cache if it contains the event we store now
if ($event['id'] == self::$cached_event['id']) self::$cached_event = array();
$old_event = $this->read($event['id'], $event['recurrence'], false, 'server');
}
else
{
$old_event = null;
}
if (!isset($event['whole_day'])) $event['whole_day'] = $this->isWholeDay($event);
$save_event = $event;
if ($event['whole_day'])
{
if (!empty($event['start']))
{
$time = new egw_time($event['start'], egw_time::$user_timezone);
$time =& $this->so->startOfDay($time);
$event['start'] = egw_time::to($time, 'ts');
$save_event['start'] = $time;
}
if (!empty($event['end']))
{
$time = new egw_time($event['end'], egw_time::$user_timezone);
$time =& $this->so->startOfDay($time);
$time->setTime(23, 59, 59);
$event['end'] = egw_time::to($time, 'ts');
}
if (!empty($event['recurrence']))
{
$time = new egw_time($event['recurrence'], egw_time::$user_timezone);
$time =& $this->so->startOfDay($time);
$event['recurrence'] = egw_time::to($time, 'ts');
}
if (!empty($event['recur_enddate']))
{
$time = new egw_time($event['recur_enddate'], egw_time::$user_timezone);
$time =& $this->so->startOfDay($time);
$event['recur_enddate'] = egw_time::to($time, 'ts');
$time->setUser();
$save_event['recur_enddate'] = egw_time::to($time, 'ts');
}
$timestamps = array('modified','created');
// all-day events are handled in server time
$event['tzid'] = $save_event['tzid'] = egw_time::$server_timezone->getName();
}
else
{
$timestamps = array('start','end','modified','created','recur_enddate','recurrence');
}
// we run all dates through date2ts, to adjust to server-time and the possible date-formats
foreach(array('start','end','modified','created','recur_enddate','recurrence') as $ts)
foreach($timestamps as $ts)
{
// we convert here from user-time to timestamps in server-time!
if (isset($event[$ts])) $event[$ts] = $event[$ts] ? $this->date2ts($event[$ts],true) : 0;
@ -892,7 +993,16 @@ class calendar_boupdate extends calendar_bo
{
foreach($event['recur_exception'] as $n => $date)
{
$event['recur_exception'][$n] = $this->date2ts($date,true);
if ($event['whole_day'])
{
$time = new egw_time($date, egw_time::$user_timezone);
$time =& $this->so->startOfDay($time);
$date = egw_time::to($time, 'ts');
}
else
{
$event['recur_exception'][$n] = $this->date2ts($date,true);
}
}
}
// same with the alarms
@ -935,7 +1045,12 @@ class calendar_boupdate extends calendar_bo
unset($save_event['participants']);
$this->set_recurrences($save_event, $set_recurrences_start);
}
if ($updateTS) $GLOBALS['egw']->contenthistory->updateTimeStamp('calendar',$cal_id,$event['id'] ? 'modify' : 'add',time());
if ($updateTS) $GLOBALS['egw']->contenthistory->updateTimeStamp('calendar', $cal_id, $event['id'] ? 'modify' : 'add', $this->now);
// Update history
$tracking = new calendar_tracking($this);
if (empty($event['id']) && !empty($cal_id)) $event['id']=$cal_id;
$tracking->track($event, $old_event);
return $cal_id;
}
@ -1119,7 +1234,7 @@ class calendar_boupdate extends calendar_bo
* @param boolean $updateTS=true update the content history of the event
* @return int number of changed recurrences
*/
function set_status($event,$uid,$status,$recur_date=0,$ignore_acl=false,$updateTS=true)
function set_status($event,$uid,$status,$recur_date=0,$ignore_acl=false,$updateTS=true,$skip_notification=false)
{
$cal_id = is_array($event) ? $event['id'] : $event;
//echo "<p>calendar_boupdate::set_status($cal_id,$uid,$status,$recur_date)</p>\n";
@ -1133,11 +1248,15 @@ class calendar_boupdate extends calendar_bo
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
"($cal_id, $uid, $status, $recur_date)\n",3,$this->logfile);
}
$old_event = $this->read($cal_id, $recur_date, false, 'server');
if (($Ok = $this->so->set_status($cal_id,is_numeric($uid)?'u':$uid[0],
is_numeric($uid)?$uid:substr($uid,1),$status,
$recur_date?$this->date2ts($recur_date,true):0,$role)))
{
if ($updateTS) $GLOBALS['egw']->contenthistory->updateTimeStamp('calendar',$cal_id,'modify',time());
if ($updateTS)
{
$GLOBALS['egw']->contenthistory->updateTimeStamp('calendar', $cal_id, 'modify', $this->now);
}
static $status2msg = array(
'R' => MSG_REJECTED,
@ -1145,13 +1264,18 @@ class calendar_boupdate extends calendar_bo
'A' => MSG_ACCEPTED,
'D' => MSG_DELEGATED,
);
if (isset($status2msg[$status]))
if (isset($status2msg[$status]) && !$skip_notification)
{
if (!is_array($event)) $event = $this->read($cal_id);
if (isset($recur_date)) $event = $this->read($event['id'],$recur_date); //re-read the actually edited recurring event
$this->send_update($status2msg[$status],$event['participants'],$event);
}
// Update history
$event = $this->read($cal_id, $recur_date, false, 'server');
$tracking = new calendar_tracking($this);
$tracking->track($event, $old_event);
}
return $Ok;
}
@ -1195,22 +1319,47 @@ class calendar_boupdate extends calendar_bo
* @param boolean $ignore_acl=false true for no ACL check, default do ACL check
* @return boolean true on success, false on error (usually permission denied)
*/
function delete($cal_id,$recur_date=0,$ignore_acl=false)
function delete($cal_id,$recur_date=0,$ignore_acl=false,$skip_notification=false)
{
if (!($event = $this->read($cal_id,$recur_date)) ||
!$ignore_acl && !$this->check_perms(EGW_ACL_DELETE,$event))
{
return false;
}
$this->send_update(MSG_DELETED,$event['participants'],$event);
// Don't send notification if the event has already been deleted
if(!$event['deleted'] && !$skip_notification)
{
$this->send_update(MSG_DELETED,$event['participants'],$event);
}
if (!$recur_date || $event['recur_type'] == MCAL_RECUR_NONE)
{
$this->so->delete($cal_id);
$GLOBALS['egw']->contenthistory->updateTimeStamp('calendar',$cal_id,'delete',time());
$config = config::read('phpgwapi');
if(!$config['calendar_delete_history'] || $event['deleted'])
{
$this->so->delete($cal_id);
// delete all links to the event
egw_link::unlink(0,'calendar',$cal_id);
// delete all links to the event
egw_link::unlink(0,'calendar',$cal_id);
}
elseif ($config['calendar_delete_history'])
{
// mark all links to the event as deleted, but keep them
egw_link::unlink(0,'calendar',$cal_id,'','','',true);
$event['deleted'] = $this->now;
$this->save($event, $ignore_acl);
// Actually delete alarms
if (isset($event['alarm']) && is_array($event['alarm']))
{
foreach($event['alarm'] as $id => $alarm)
{
$this->delete_alarm($id);
}
}
}
$GLOBALS['egw']->contenthistory->updateTimeStamp('calendar', $cal_id, 'delete', $this->now);
}
else // delete an exception
{
@ -1384,7 +1533,7 @@ class calendar_boupdate extends calendar_bo
echo "<p>error opening '$this->log_file' !!!</p>\n";
return false;
}
fwrite($f,$type.': '.$GLOBALS['egw']->common->grab_owner_name($this->user).': '.date('r')."\n");
fwrite($f,$type.': '.common::grab_owner_name($this->user).': '.date('r')."\n");
fwrite($f,"Time: time to save / saved time read back / old time before save\n");
foreach(array('start','end') as $name)
{
@ -1418,9 +1567,9 @@ class calendar_boupdate extends calendar_bo
}
$alarm['time'] = $this->date2ts($alarm['time'],true); // user to server-time
$GLOBALS['egw']->contenthistory->updateTimeStamp('calendar',$cal_id, 'modify', time());
$GLOBALS['egw']->contenthistory->updateTimeStamp('calendar', $cal_id, 'modify', $this->now);
return $this->so->save_alarm($cal_id,$alarm, $this->now_su);
return $this->so->save_alarm($cal_id,$alarm, $this->now);
}
/**
@ -1438,9 +1587,9 @@ class calendar_boupdate extends calendar_bo
return false; // no rights to delete the alarm
}
$GLOBALS['egw']->contenthistory->updateTimeStamp('calendar',$cal_id, 'modify', time());
$GLOBALS['egw']->contenthistory->updateTimeStamp('calendar', $cal_id, 'modify', $this->now);
return $this->so->delete_alarm($id, $this->now_su);
return $this->so->delete_alarm($id, $this->now);
}
/**
@ -1448,16 +1597,16 @@ class calendar_boupdate extends calendar_bo
* currently used for ical/sif import
*
* @param array $catname_list names of the categories which should be found or added
* @param int $cal_id=-1 match against existing event and expand the returned category ids
* @param int|array $old_event=null match against existing event and expand the returned category ids
* by the ones the user normally does not see due to category permissions - used to preserve categories
* @return array category ids (found, added and preserved categories)
*/
function find_or_add_categories($catname_list, $cal_id=-1)
function find_or_add_categories($catname_list, $old_event=null)
{
if ($cal_id && $cal_id > 0)
if (is_array($old_event) || $old_event > 0)
{
// preserve categories without users read access
$old_event = $this->read($cal_id);
if (!is_array($old_event)) $old_event = $this->read($old_event);
$old_categories = explode(',',$old_event['category']);
$old_cats_preserve = array();
if (is_array($old_categories) && count($old_categories) > 0)
@ -1548,13 +1697,25 @@ class calendar_boupdate extends calendar_bo
"($filter)[EVENT]:" . array2string($event)."\n",3,$this->logfile);
}
if (!isset($event['recurrence'])) $event['recurrence'] = 0;
if ($filter == 'master')
{
$query[] = 'recur_type!='. MCAL_RECUR_NONE;
$query['cal_recurrence'] = 0;
}
if (!isset($event['recurrence'])) $event['recurrence'] = 0;
elseif ($filter == 'exact')
{
if ($event['recur_type'] != MCAL_RECUR_NONE)
{
$query[] = 'recur_type='.$event['recur_type'];
}
else
{
$query[] = 'recur_type IS NULL';
}
$query['cal_recurrence'] = $event['recurrence'];
}
if ($event['id'])
{
@ -1679,7 +1840,6 @@ class calendar_boupdate extends calendar_bo
}
elseif (isset($event['start']))
{
if ($filter == 'relax')
{
$query[] = ('cal_start>' . ($event['start'] - 3600));
@ -1700,7 +1860,6 @@ class calendar_boupdate extends calendar_bo
{
$matchFields = array('priority', 'public');
}
$matchFields[] = 'recurrence';
foreach ($matchFields as $key)
{
if (isset($event[$key])) $query['cal_'.$key] = $event[$key];
@ -1723,7 +1882,7 @@ class calendar_boupdate extends calendar_bo
'[QUERY]: ' . array2string($query)."\n",3,$this->logfile);
}
if (!count($users) || !($foundEvents =
$this->so->search(null, null, $users, 0, 'owner', $query)))
$this->so->search(null, null, $users, 0, 'owner', false, 0, array('query' => $query))))
{
if ($this->log)
{
@ -1821,7 +1980,7 @@ class calendar_boupdate extends calendar_bo
continue;
}
}
// check times
if ($filter != 'relax')
{
@ -1925,8 +2084,15 @@ class calendar_boupdate extends calendar_bo
unset($egwEvent['participants'][$attendee]);
}
}
// ORGANIZER is maybe missing
// ORGANIZER and Groups may be missing
unset($egwEvent['participants'][$egwEvent['owner']]);
foreach ($egwEvent['participants'] as $attendee => $status)
{
if (is_numeric($attendee) && $attendee < 0)
{
unset($egwEvent['participants'][$attendee]);
}
}
if (!empty($egwEvent['participants']))
{
if ($this->log)
@ -1978,7 +2144,7 @@ class calendar_boupdate extends calendar_bo
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__.
'() missing event[recur_exception]: ' .
array2string($event['recur_exception']));
array2string($event['recur_exception'])."\n",3,$this->logfile);
}
continue;
}
@ -2002,12 +2168,10 @@ class calendar_boupdate extends calendar_bo
}
$matchingEvents[] = $egwEvent['id']; // exact match
}
if (!empty($event['uid']) &&
count($matchingEvents) > 1 || $filter != 'master' &&
$egwEvent['recur_type'] != MCAL_RECUR_NONE &&
empty($event['recur_type']))
if ($filter == 'exact' && !empty($event['uid']) && count($matchingEvents) > 1
|| $filter != 'master' && !empty($egwEvent['recur_type']) && empty($event['recur_type']))
{
// Unknown exception for existing series
if ($this->log)
{
@ -2170,7 +2334,7 @@ class calendar_boupdate extends calendar_bo
// check for changed data
foreach (array('start','end','uid','title','location','description',
'priority','public','special','non_blocking') as $key)
{
{
if (!empty($event[$key]) && $recurrence_event[$key] != $event[$key])
{
if ($wasPseudo)
@ -2190,7 +2354,7 @@ class calendar_boupdate extends calendar_bo
}
break;
}
}
}
// the event id here is always the id of the master event
// unset it to prevent confusion of stored event and master event
unset($event['id']);
@ -2252,4 +2416,15 @@ class calendar_boupdate extends calendar_bo
}
}
}
/**
* Delete events that are more than $age years old
*
* Purges old events from the database
*
* @param int|float $age How many years old the event must be before it is deleted
*/
function purge($age)
{
$this->so->purge(time() - 365*24*3600*(float)$age);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
<?php
/**
* iCal import and export via Horde iCalendar classes
* EGroupware - Calendar iCal import and export via Horde iCalendar classes
*
* @link http://www.egroupware.org
* @author Lars Kneschke <lkneschke@egroupware.org>
@ -228,10 +228,10 @@ class calendar_ical extends calendar_boupdate
}
$vcal = new Horde_iCalendar;
$vcal->setAttribute('PRODID','-//eGroupWare//NONSGML eGroupWare Calendar '.$GLOBALS['egw_info']['apps']['calendar']['version'].'//'.
$vcal->setAttribute('PRODID','-//EGroupware//NONSGML EGroupware Calendar '.$GLOBALS['egw_info']['apps']['calendar']['version'].'//'.
strtoupper($GLOBALS['egw_info']['user']['preferences']['common']['lang']));
$vcal->setAttribute('VERSION', $version);
$vcal->setAttribute('METHOD', $method);
if ($method) $vcal->setAttribute('METHOD', $method);
$events_exported = false;
if (!is_array($events)) $events = array($events);
@ -354,42 +354,15 @@ class calendar_ical extends calendar_boupdate
if ($tzid && $tzid != 'UTC' && !in_array($tzid,$vtimezones_added))
{
// check if we have vtimezone component data for tzid of event, if not default to user timezone (default to server tz)
if (!($vtimezone = calendar_timezones::tz2id($tzid,'component')))
if (calendar_timezones::add_vtimezone($vcal, $tzid) ||
!in_array($tzid = egw_time::$user_timezone->getName(), $vtimezones_added) &&
calendar_timezones::add_vtimezone($vcal, $tzid))
{
error_log(__METHOD__."() unknown TZID='$tzid', defaulting to user timezone '".egw_time::$user_timezone->getName()."'!");
$vtimezone = calendar_timezones::tz2id($tzid=egw_time::$user_timezone->getName(),'component');
$tzid = null;
}
if (!isset(self::$tz_cache[$tzid]))
{
self::$tz_cache[$tzid] = calendar_timezones::DateTimeZone($tzid);
}
//error_log("in_array('$tzid',\$vtimezones_added)=".array2string(in_array($tzid,$vtimezones_added)).", component=$vtimezone");;
if (!in_array($tzid,$vtimezones_added))
{
// $vtimezone is a string with a single VTIMEZONE component, afaik Horde_iCalendar can not add it directly
// --> we have to parse it and let Horde_iCalendar add it again
$horde_vtimezone = Horde_iCalendar::newComponent('VTIMEZONE',$container=false);
$horde_vtimezone->parsevCalendar($vtimezone,'VTIMEZONE');
// DTSTART must be in local time!
$standard = $horde_vtimezone->findComponent('STANDARD');
if (is_a($standard, 'Horde_iCalendar'))
{
$dtstart = $standard->getAttribute('DTSTART');
$dtstart = new egw_time($dtstart, egw_time::$server_timezone);
$dtstart->setTimezone(self::$tz_cache[$tzid]);
$standard->setAttribute('DTSTART', $dtstart->format('Ymd\THis'), array(), false);
}
$daylight = $horde_vtimezone->findComponent('DAYLIGHT');
if (is_a($daylight, 'Horde_iCalendar'))
{
$dtstart = $daylight->getAttribute('DTSTART');
$dtstart = new egw_time($dtstart, egw_time::$server_timezone);
$dtstart->setTimezone(self::$tz_cache[$tzid]);
$daylight->setAttribute('DTSTART', $dtstart->format('Ymd\THis'), array(), false);
}
$vcal->addComponent($horde_vtimezone);
$vtimezones_added[] = $tzid;
if (!isset(self::$tz_cache[$tzid]))
{
self::$tz_cache[$tzid] = calendar_timezones::DateTimeZone($tzid);
}
}
}
if ($this->productManufacturer != 'file' && $this->uidExtension)
@ -434,7 +407,6 @@ class calendar_ical extends calendar_boupdate
}
$event['recur_exception'] = $exceptions;
}
foreach ($egwSupportedFields as $icalFieldName => $egwFieldName)
{
if (!isset($this->supportedFields[$egwFieldName]))
@ -451,13 +423,14 @@ class calendar_ical extends calendar_boupdate
switch ($icalFieldName)
{
case 'ATTENDEE':
$attendees = count($event['participants']);
foreach ((array)$event['participants'] as $uid => $status)
{
calendar_so::split_status($status, $quantity, $role);
if ($attendees == 1 &&
$uid == $this->user && $status == 'A') continue;
// do not include event owner/ORGANIZER as participant in his own calendar, if he is only participant
if (count($event['participants']) == 1 && $event['owner'] == $uid) continue;
if (!($info = $this->resource_info($uid))) continue;
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__ .
@ -491,11 +464,7 @@ class calendar_ical extends calendar_boupdate
{
case 'g':
$cutype = 'GROUP';
if ($this->productManufacturer == 'groupdav')
{
$participantURL = 'invalid:nomail';
$cutype = 'INDIVIDUAL';
}
$participantURL = 'urn:uuid:'.common::generate_uid('accounts', $uid);
$members = $GLOBALS['egw']->accounts->members($uid, true);
if (!isset($event['participants'][$this->user]) && in_array($this->user, $members))
{
@ -508,13 +477,16 @@ class calendar_ical extends calendar_boupdate
'CUTYPE' => 'INDIVIDUAL',
'RSVP' => 'TRUE',
'X-EGROUPWARE-UID' => $this->user,
'EMAIL' => $user['email'],
);
);
$event['participants'][$this->user] = true;
}
break;
case 'r':
$cutype = 'RESOURCE';
$participantURL = 'urn:uuid:'.common::generate_uid('resources', substr($uid, 1));
$cutype = groupdav_principals::resource_is_location(substr($uid, 1)) ? 'ROOM' : 'RESOURCE';
// unset resource email (email of responsible user) as iCal at least has problems,
// if resonpsible is also pariticipant or organizer
unset($info['email']);
break;
case 'u': // account
case 'c': // contact
@ -524,7 +496,12 @@ class calendar_ical extends calendar_boupdate
default:
$cutype = 'UNKNOWN';
break;
};
}
// generate urn:uuid, if we have no other participant URL
if (empty($participantURL) && $info && $info['app'])
{
$participantURL = 'urn:uuid:'.common::generate_uid($info['app'], substr($uid, 1));
}
// ROLE={CHAIR|REQ-PARTICIPANT|OPT-PARTICIPANT|NON-PARTICIPANT|X-*}
$options = array();
if (!empty($participantCN)) $options['CN'] = $participantCN;
@ -532,20 +509,24 @@ class calendar_ical extends calendar_boupdate
if (!empty($status)) $options['PARTSTAT'] = $status;
if (!empty($cutype)) $options['CUTYPE'] = $cutype;
if (!empty($rsvp)) $options['RSVP'] = $rsvp;
if (!empty($info['email'])) $options['EMAIL'] = $info['email'];
if (!empty($info['email']) && $participantURL != 'MAILTO:'.$info['email'])
{
$options['EMAIL'] = $info['email']; // only add EMAIL attribute, if not already URL, as eg. Akonadi is reported to have problems with it
}
if ($info['type'] != 'e') $options['X-EGROUPWARE-UID'] = $uid;
if ($quantity > 1) $options['X-EGROUPWARE-QUANTITY'] = $quantity;
$attributes['ATTENDEE'][] = $participantURL;
$attributes['ATTENDEE'][] = $participantURL;
$parameters['ATTENDEE'][] = $options;
}
break;
case 'CLASS':
$attributes['CLASS'] = $event['public'] ? 'PUBLIC' : 'PRIVATE';
// Apple iCal on OS X uses X-CALENDARSERVER-ACCESS: CONFIDENTIAL on VCALANDAR (not VEVENT!)
if (!$event['public']) $vcal->setAttribute('X-CALENDARSERVER-ACCESS', 'CONFIDENTIAL');
break;
case 'ORGANIZER':
// according to iCalendar standard, ORGANIZER not used for events in the own calendar
if (!$organizerCN)
{
$organizerCN = '"' . trim($GLOBALS['egw']->accounts->id2name($event['owner'],'account_firstname')
@ -575,7 +556,8 @@ class calendar_ical extends calendar_boupdate
$parameters['ATTENDEE'][] = $options;
}
}
if ($this->productManufacturer != 'groupdav' || !$this->check_perms(EGW_ACL_EDIT,$event))
// do NOT use ORGANIZER for events without further participants or a different organizer
if (count($event['participants']) > 1 || !isset($event['participants'][$event['owner']]))
{
$attributes['ORGANIZER'] = $organizerURL;
$parameters['ORGANIZER']['CN'] = $organizerCN;
@ -873,7 +855,11 @@ class calendar_ical extends calendar_boupdate
{
$attributes['CREATED'] = $event['created'] ? $event['created'] : $event['modified'];
}
if ($event['modified'])
if ($event['max_user_modified'])
{
$attributes['LAST-MODIFIED'] = max($event['modified'], $event['max_user_modified']);
}
elseif ($event['modified'])
{
$attributes['LAST-MODIFIED'] = $event['modified'];
}
@ -968,12 +954,12 @@ class calendar_ical extends calendar_boupdate
{
foreach (is_array($value) && $parameters[$key]['VALUE']!='DATE' ? $value : array($value) as $valueID => $valueData)
{
$valueData = $GLOBALS['egw']->translation->convert($valueData,$GLOBALS['egw']->translation->charset(),$charset);
$paramData = (array) $GLOBALS['egw']->translation->convert(is_array($value) ?
$valueData = translation::convert($valueData,translation::charset(),$charset);
$paramData = (array) translation::convert(is_array($value) ?
$parameters[$key][$valueID] : $parameters[$key],
$GLOBALS['egw']->translation->charset(),$charset);
$valuesData = (array) $GLOBALS['egw']->translation->convert($values[$key],
$GLOBALS['egw']->translation->charset(),$charset);
translation::charset(),$charset);
$valuesData = (array) translation::convert($values[$key],
translation::charset(),$charset);
$content = $valueData . implode(';', $valuesData);
if (preg_match('/[^\x20-\x7F]/', $content) ||
@ -1080,10 +1066,12 @@ class calendar_ical extends calendar_boupdate
* @param int $user=null account_id of owner, default null
* @param string $charset The encoding charset for $text. Defaults to
* utf-8 for new format, iso-8859-1 for old format.
* @param string $caldav_name=null name from CalDAV client or null (to use default)
* @return int|boolean cal_id > 0 on success, false on failure or 0 for a failed etag|permission denied
*/
function importVCal($_vcalData, $cal_id=-1, $etag=null, $merge=false, $recur_date=0, $principalURL='', $user=null, $charset=null)
function importVCal($_vcalData, $cal_id=-1, $etag=null, $merge=false, $recur_date=0, $principalURL='', $user=null, $charset=null, $caldav_name=null)
{
//error_log(__METHOD__."(, $cal_id, $etag, $merge, $recur_date, $principalURL, $user, $charset, $caldav_name)");
$this->events_imported = 0;
$replace = $delete_exceptions= false;
@ -1286,7 +1274,9 @@ class calendar_ical extends calendar_boupdate
// participants OR the event no longer contains participants, add them back
unset($event['participants']);
}
else
// since we export now all participants in CalDAV as urn:uuid, if they have no email,
// we dont need and dont want that special treatment anymore, as it keeps client from changing resources
elseif ($this->productManufacturer != 'groupdav')
{
foreach ($event_info['stored_event']['participants'] as $uid => $status)
{
@ -1304,15 +1294,20 @@ class calendar_ical extends calendar_boupdate
}
}
if (!empty($event['whole_day']) && $event['tzid'] != $event_info['stored_event']['tzid'])
/* Modifying an existing event with timezone different from default timezone of user
* to a whole-day event (no timezone allowed according to iCal rfc)
* --> code to modify start- and end-time here creates a one day longer event!
* Skipping that code, creates the event correct in default timezone of user
if (!empty($event['whole_day']) && $event['tzid'] != $event_info['stored_event']['tzid'])
{
// Adjust dates to original TZ
$time = new egw_time($event['start'],egw_time::$server_timezone);
$time =& $this->so->startOfDay($time, $event_info['stored_event']['tzid']);
$event['start'] = egw_time::to($time,'server');
$time = new egw_time($event['end'],egw_time::$server_timezone);
$time =& $this->so->startOfDay($time, $event_info['stored_event']['tzid']);
$time->setTime(23, 59, 59);
//$time = new egw_time($event['end'],egw_time::$server_timezone);
//$time =& $this->so->startOfDay($time, $event_info['stored_event']['tzid']);
//$time->setTime(23, 59, 59);
$time->modify('+'.round(($event['end']-$event['start'])/DAY_s).' day');
$event['end'] = egw_time::to($time,'server');
if ($event['recur_type'] != MCAL_RECUR_NONE)
{
@ -1329,7 +1324,8 @@ class calendar_ical extends calendar_boupdate
$time =& $this->so->startOfDay($time, $event_info['stored_event']['tzid']);
$event['recurrence'] = egw_time::to($time,'server');
}
}
error_log(__METHOD__."() TZ adjusted {$event_info['stored_event']['tzid']} --> {$event['tzid']} event=".array2string($event));
}*/
calendar_rrule::rrule2tz($event, $event_info['stored_event']['start'],
$event_info['stored_event']['tzid']);
@ -1338,10 +1334,19 @@ class calendar_ical extends calendar_boupdate
// avoid that iCal changes the organizer, which is not allowed
$event['owner'] = $event_info['stored_event']['owner'];
}
$event['caldav_name'] = $event_info['stored_event']['caldav_name'];
// as we no longer export event owner/ORGANIZER as only participant, we have to re-add owner as participant
// to not loose him, as EGroupware knows events without owner/ORGANIZER as participant
if (isset($event_info['stored_event']['participants'][$event['owner']]) && !isset($event['participants'][$event['owner']]))
{
$event['participant'][$event['owner']] = $event_info['stored_event']['participants'][$event['owner']];
}
}
else // common adjustments for new events
{
unset($event['id']);
if ($caldav_name) $event['caldav_name'] = $caldav_name;
// set non blocking all day depending on the user setting
if (!empty($event['whole_day']) && $this->nonBlockingAllday)
{
@ -1380,11 +1385,15 @@ class calendar_ical extends calendar_boupdate
if (!$event['participants']
|| !is_array($event['participants'])
|| !count($event['participants']))
|| !count($event['participants'])
// for new events, allways add owner as participant. Users expect to participate too, if they invite further participants.
// They can now only remove themselfs, if that is desired, after storing the event first.
|| !isset($event['participants'][$event['owner']]))
{
$status = $event['owner'] == $this->user ? 'A' : 'U';
$status = calendar_so::combine_status($status, 1, 'CHAIR');
$event['participants'] = array($event['owner'] => $status);
if (!is_array($event['participants'])) $event['participants'] = array();
$event['participants'][$event['owner']] = $status;
}
else
{
@ -1730,7 +1739,8 @@ class calendar_ical extends calendar_boupdate
}
}
date_default_timezone_set($GLOBALS['egw_info']['server']['server_timezone']);
return $return_id;
return $updated_id === 0 ? 0 : $return_id;
}
/**
@ -2165,7 +2175,7 @@ class calendar_ical extends calendar_boupdate
{
if (($event = $this->_ical2egw_callback($component,$this->tzid,$principalURL)))
{
$events[] = $event;
$events[] = $event;
}
}
date_default_timezone_set($GLOBALS['egw_info']['server']['server_timezone']);
@ -2191,7 +2201,7 @@ class calendar_ical extends calendar_boupdate
}
if (!is_a($component, 'Horde_iCalendar_vevent') ||
!($event = $this->vevent2egw($component, $component->getAttribute('VERSION'), $this->supportedFields, $principalURL)))
!($event = $this->vevent2egw($component, $component->_container->getAttribute('VERSION'), $this->supportedFields, $principalURL)))
{
return false;
}
@ -2230,13 +2240,14 @@ class calendar_ical extends calendar_boupdate
* @param array $component VEVENT
* @param string $version vCal version (1.0/2.0)
* @param array $supportedFields supported fields of the device
* @param string $principalURL='' Used for CalDAV imports
* @param string $principalURL='' Used for CalDAV imports, no longer used in favor of groupdav_principals::url2uid()
* @param string $check_component='Horde_iCalendar_vevent'
*
* @return array|boolean event on success, false on failure
*/
function vevent2egw(&$component, $version, $supportedFields, $principalURL='')
function vevent2egw(&$component, $version, $supportedFields, $principalURL='', $check_component='Horde_iCalendar_vevent')
{
if (!is_a($component, 'Horde_iCalendar_vevent'))
if ($check_component && !is_a($component, $check_component))
{
if ($this->log)
{
@ -2352,6 +2363,16 @@ class calendar_ical extends calendar_boupdate
{
switch ($attributes['name'])
{
case 'DURATION': // clients can use DTSTART+DURATION, instead of DTSTART+DTEND
if (!isset($vcardData['end']))
{
$vcardData['end'] = $vcardData['start'] + $attributes['value'];
}
else
{
error_log(__METHOD__."() find DTEND AND DURATION --> ignoring DURATION");
}
break;
case 'X-MICROSOFT-CDO-ALLDAYEVENT':
$event['whole_day'] = true;
break;
@ -2671,8 +2692,18 @@ class calendar_ical extends calendar_boupdate
$vcardData['category'] = array();
}
break;
case 'ATTENDEE':
case 'ORGANIZER':
$event['organizer'] = $attributes['value']; // no egw field, but needed in AS
if (strtoupper(substr($event['organizer'],0,7)) == 'MAILTO:')
{
$event['organizer'] = substr($event['organizer'],7);
}
if (!empty($attributes['params']['CN']))
{
$event['organizer'] = $attributes['params']['CN'].' <'.$event['organizer'].'>';
}
// fall throught
case 'ATTENDEE':
if (isset($attributes['params']['PARTSTAT']))
{
$attributes['params']['STATUS'] = $attributes['params']['PARTSTAT'];
@ -2693,29 +2724,6 @@ class calendar_ical extends calendar_boupdate
{
$role = $attributes['params']['ROLE'];
}
// try pricipal url from CalDAV
if (strpos($attributes['value'], 'http') === 0)
{
if (!empty($principalURL) && strstr($attributes['value'], $principalURL) !== false)
{
$uid = $this->user;
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
. "(): Found myself: '$uid'\n",3,$this->logfile);
}
}
else
{
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__
. '(): Unknown URI: ' . $attributes['value']
. "\n",3,$this->logfile);
}
$attributes['value'] = '';
}
}
// parse email and cn from attendee
if (preg_match('/MAILTO:([@.a-z0-9_-]+)|MAILTO:"?([.a-z0-9_ -]*)"?[ ]*<([@.a-z0-9_-]*)>/i',
$attributes['value'],$matches))
@ -2755,6 +2763,9 @@ class calendar_ical extends calendar_boupdate
// we use the current user
$uid = $this->user;
}
// check principal url from CalDAV here after X-EGROUPWARE-UID and to get optional X-EGROUPWARE-QUANTITY
if (!$uid) $uid = groupdav_principals::url2uid($attributes['value']);
// try to find an email address
if (!$uid && $email && ($uid = $GLOBALS['egw']->accounts->name2id($email, 'account_email')))
{
@ -2990,6 +3001,14 @@ class calendar_ical extends calendar_boupdate
$event['recur_enddate'] = egw_time::to($last, 'server');
}
// Apple iCal on OS X uses X-CALENDARSERVER-ACCESS: CONFIDENTIAL on VCALANDAR (not VEVENT!)
if (($x_calendarserver_access = $component->_container->getAttribute('X-CALENDARSERVER-ACCESS')) &&
!is_a($x_calendarserver_access, 'PEAR_Error'))
{
$event['public'] = (int)(strtoupper($x_calendarserver_access) == 'PUBLIC');
}
//error_log(__METHOD__."() X-CALENDARSERVER-ACCESS=".array2string($x_calendarserver_access).' --> public='.array2string($event['public']));
// if no end is given in iCal we use the default lenght from user prefs
// whole day events get one day in calendar_boupdate::save()
if (!isset($event['end']))
@ -3044,52 +3063,47 @@ class calendar_ical extends calendar_boupdate
* @param mixed $end=null end-date, default now+1 month
* @param boolean $utc=true if false, use severtime for dates
* @param string $charset='UTF-8' encoding of the vcalendar, default UTF-8
* @return string
* @param mixed $start=null default now
* @param string $method='PUBLISH' or eg. 'REPLY'
* @param array $extra=null extra attributes to add
* X-CALENDARSERVER-MASK-UID can be used to not include an event specified by this uid as busy
*/
function freebusy($user,$end=null,$utc=true, $charset='UTF-8')
function freebusy($user,$end=null,$utc=true, $charset='UTF-8', $start=null, $method='PUBLISH', array $extra=null)
{
if (!$end) $end = $this->now_su + 100*DAY_s; // default next 100 days
if (!$start) $start = time(); // default now
if (!$end) $end = time() + 100*DAY_s; // default next 100 days
$vcal = new Horde_iCalendar;
$vcal->setAttribute('PRODID','-//eGroupWare//NONSGML eGroupWare Calendar '.$GLOBALS['egw_info']['apps']['calendar']['version'].'//'.
$vcal->setAttribute('PRODID','-//EGroupware//NONSGML EGroupware Calendar '.$GLOBALS['egw_info']['apps']['calendar']['version'].'//'.
strtoupper($GLOBALS['egw_info']['user']['preferences']['common']['lang']));
$vcal->setAttribute('VERSION','2.0');
$vcal->setAttribute('METHOD',$method);
$vfreebusy = Horde_iCalendar::newComponent('VFREEBUSY',$vcal);
$parameters = array(
'ORGANIZER' => $GLOBALS['egw']->translation->convert(
$GLOBALS['egw']->accounts->id2name($user,'account_firstname').' '.
$GLOBALS['egw']->accounts->id2name($user,'account_lastname'),
$GLOBALS['egw']->translation->charset(),$charset),
if ($uid) $vfreebusy->setAttribute('UID', $uid);
$attributes = array(
'DTSTAMP' => time(),
'DTSTART' => $this->date2ts($start,true), // true = server-time
'DTEND' => $this->date2ts($end,true), // true = server-time
);
if ($utc)
if (!$utc)
{
foreach (array(
'URL' => $this->freebusy_url($user),
'DTSTART' => $this->date2ts($this->now_su,true), // true = server-time
'DTEND' => $this->date2ts($end,true), // true = server-time
'ORGANIZER' => $GLOBALS['egw']->accounts->id2name($user,'account_email'),
'DTSTAMP' => time(),
) as $attr => $value)
foreach ($attributes as $attr => $value)
{
$vfreebusy->setAttribute($attr, $value);
$attributes[$attr] = date('Ymd\THis', $value);
}
}
else
if (is_null($extra)) $extra = array(
'URL' => $this->freebusy_url($user),
'ORGANIZER' => 'mailto:'.$GLOBALS['egw']->accounts->id2name($user,'account_email'),
);
foreach($attributes+$extra as $attr => $value)
{
foreach (array(
'URL' => $this->freebusy_url($user),
'DTSTART' => date('Ymd\THis',$this->date2ts($this->now_su,true)), // true = server-time
'DTEND' => date('Ymd\THis',$this->date2ts($end,true)), // true = server-time
'ORGANIZER' => $GLOBALS['egw']->accounts->id2name($user,'account_email'),
'DTSTAMP' => date('Ymd\THis',time()),
) as $attr => $value)
{
$vfreebusy->setAttribute($attr, $value);
}
$vfreebusy->setAttribute($attr, $value);
}
$fbdata = parent::search(array(
'start' => $this->now_su,
'start' => $start,
'end' => $end,
'users' => $user,
'date_format' => 'server',
@ -3100,20 +3114,23 @@ class calendar_ical extends calendar_boupdate
foreach ($fbdata as $event)
{
if ($event['non_blocking']) continue;
if ($event['uid'] === $extra['X-CALENDARSERVER-MASK-UID']) continue;
$fbtype = $event['participants'][$user] == 'T' ? 'BUSY-TENTATIVE' : 'BUSY';
if ($utc)
{
$vfreebusy->setAttribute('FREEBUSY',array(array(
'start' => $event['start'],
'end' => $event['end'],
)));
)), array('FBTYPE' => $fbtype));
}
else
{
$vfreebusy->setAttribute('FREEBUSY',array(array(
'start' => date('Ymd\THis',$event['start']),
'end' => date('Ymd\THis',$event['end']),
)));
)), array('FBTYPE' => $fbtype));
}
}
}

View File

@ -0,0 +1,794 @@
<?php
/**
* eGroupWare - Calendar recurrence rules
*
* @link http://www.egroupware.org
* @package calendar
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @author Joerg Lehrke <jlehrke@noc.de>
* @copyright (c) 2009 by RalfBecker-At-outdoor-training.de
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
/**
* Recurrence rule iterator
*
* The constructor accepts times only as DateTime (or decendents like egw_date) to work timezone-correct.
* The timezone of the event is determined by timezone of the startime, other times get converted to that timezone.
*
* There's a static factory method calendar_rrule::event2rrule(array $event,$usertime=true), which converts an
* event read by calendar_bo::read() or calendar_bo::search() to a rrule iterator.
*
* The rrule iterator object can be casted to string, to get a human readable description of the rrule.
*
* There's an interactive test-form, if the class get's called directly: http://localhost/egroupware/calendar/inc/class.calendar_rrule.inc.php
*
* @todo Integrate iCal import and export, so all recurrence code resides just in this class
* @todo Implement COUNT, can be stored in enddate assuming counts are far smaller then timestamps (eg. < 1000 is a count)
* @todo Implement WKST (week start day), currently WKST=SU is used (this is not stored in current DB schema, it's a user preference)
*/
class calendar_rrule implements Iterator
{
/**
* No recurrence
*/
const NONE = 0;
/**
* Daily recurrence
*/
const DAILY = 1;
/**
* Weekly recurrance on day(s) specified by bitfield in $data
*/
const WEEKLY = 2;
/**
* Monthly recurrance iCal: monthly_bymonthday
*/
const MONTHLY_MDAY = 3;
/**
* Monthly recurrance iCal: BYDAY (by weekday, eg. 1st Friday of month)
*/
const MONTHLY_WDAY = 4;
/**
* Yearly recurrance
*/
const YEARLY = 5;
/**
* Translate recure types to labels
*
* @var array
*/
static public $types = Array(
self::NONE => 'None',
self::DAILY => 'Daily',
self::WEEKLY => 'Weekly',
self::MONTHLY_WDAY => 'Monthly (by day)',
self::MONTHLY_MDAY => 'Monthly (by date)',
self::YEARLY => 'Yearly'
);
/**
* @var array $recur_egw2ical_2_0 converstaion of egw recur-type => ical FREQ
*/
static private $recur_egw2ical_2_0 = array(
self::DAILY => 'DAILY',
self::WEEKLY => 'WEEKLY',
self::MONTHLY_WDAY => 'MONTHLY', // BYDAY={1..7, -1}{MO..SO, last workday}
self::MONTHLY_MDAY => 'MONTHLY', // BYMONHTDAY={1..31, -1 for last day of month}
self::YEARLY => 'YEARLY',
);
/**
* @var array $recur_egw2ical_1_0 converstaion of egw recur-type => ical FREQ
*/
static private $recur_egw2ical_1_0 = array(
self::DAILY => 'D',
self::WEEKLY => 'W',
self::MONTHLY_WDAY => 'MP', // BYDAY={1..7,-1}{MO..SO, last workday}
self::MONTHLY_MDAY => 'MD', // BYMONHTDAY={1..31,-1}
self::YEARLY => 'YM',
);
/**
* RRule type: NONE, DAILY, WEEKLY, MONTHLY_MDAY, MONTHLY_WDAY, YEARLY
*
* @var int
*/
public $type = self::NONE;
/**
* Interval
*
* @var int
*/
public $interval = 1;
/**
* Number for monthly byday: 1, ..., 5, -1=last weekday of month
*
* EGroupware Calendar does NOT explicitly store it, it's only implicitly defined by series start date
*
* @var int
*/
public $monthly_byday_num;
/**
* Number for monthly bymonthday: 1, ..., 31, -1=last day of month
*
* EGroupware Calendar does NOT explicitly store it, it's only implicitly defined by series start date
*
* @var int
*/
public $monthly_bymonthday;
/**
* Enddate of recurring event or null, if not ending
*
* @var DateTime
*/
public $enddate;
/**
* Enddate of recurring event, as Ymd integer (eg. 20091111)
*
* @var int
*/
public $enddate_ymd;
const SUNDAY = 1;
const MONDAY = 2;
const TUESDAY = 4;
const WEDNESDAY = 8;
const THURSDAY = 16;
const FRIDAY = 32;
const SATURDAY = 64;
const WORKDAYS = 62; // Mo, ..., Fr
const ALLDAYS = 127;
/**
* Translate weekday bitmasks to labels
*
* @var array
*/
static public $days = array(
self::MONDAY => 'Monday',
self::TUESDAY => 'Tuesday',
self::WEDNESDAY => 'Wednesday',
self::THURSDAY => 'Thursday',
self::FRIDAY => 'Friday',
self::SATURDAY => 'Saturday',
self::SUNDAY => 'Sunday',
);
/**
* Bitmask of valid weekdays for weekly repeating events: self::SUNDAY|...|self::SATURDAY
*
* @var integer
*/
public $weekdays;
/**
* Array of exception dates (Ymd strings)
*
* @var array
*/
public $exceptions=array();
/**
* Array of exceptions as DateTime/egw_time objects
*
* @var array
*/
public $exceptions_objs=array();
/**
* Starttime of series
*
* @var DateTime
*/
public $time;
/**
* Current "position" / time
*
* @var DateTime
*/
public $current;
/**
* Last day of the week according to user preferences
*
* @var int
*/
protected $lastdayofweek;
/**
* Cached timezone data
*
* @var array id => data
*/
protected static $tz_cache = array();
/**
* Constructor
*
* The constructor accepts on DateTime (or decendents like egw_date) for all times, to work timezone-correct.
* The timezone of the event is determined by timezone of $time, other times get converted to that timezone.
*
* @param DateTime $time start of event in it's own timezone
* @param int $type self::NONE, self::DAILY, ..., self::YEARLY
* @param int $interval=1 1, 2, ...
* @param DateTime $enddate=null enddate or null for no enddate (in which case we user '+5 year' on $time)
* @param int $weekdays=0 self::SUNDAY=1|self::MONDAY=2|...|self::SATURDAY=64
* @param array $exceptions=null DateTime objects with exceptions
*/
public function __construct(DateTime $time,$type,$interval=1,DateTime $enddate=null,$weekdays=0,array $exceptions=null)
{
switch($GLOBALS['egw_info']['user']['preferences']['calendar']['weekdaystarts'])
{
case 'Sunday':
$this->lastdayofweek = self::SATURDAY;
break;
case 'Saturday':
$this->lastdayofweek = self::FRIDAY;
break;
default: // Monday
$this->lastdayofweek = self::SUNDAY;
}
$this->time = $time;
if (!in_array($type,array(self::NONE, self::DAILY, self::WEEKLY, self::MONTHLY_MDAY, self::MONTHLY_WDAY, self::YEARLY)))
{
throw new egw_exception_wrong_parameter(__METHOD__."($time,$type,$interval,$enddate,$data,...) type $type is NOT valid!");
}
$this->type = $type;
// determine only implicit defined rules for RRULE=MONTHLY,BYDAY={-1, 1, ..., 5}{MO,..,SU}
if ($type == self::MONTHLY_WDAY)
{
// check for last week of month
if (($day = $this->time->format('d')) >= 21 && $day > self::daysInMonth($this->time)-7)
{
$this->monthly_byday_num = -1;
}
else
{
$this->monthly_byday_num = 1 + floor(($this->time->format('d')-1) / 7);
}
}
elseif($type == self::MONTHLY_MDAY)
{
$this->monthly_bymonthday = (int)$this->time->format('d');
// check for last day of month
if ($this->monthly_bymonthday >= 28)
{
$test = clone $this->time;
$test->modify('1 day');
if ($test->format('m') != $this->time->format('m'))
{
$this->monthly_bymonthday = -1;
}
}
}
if ((int)$interval < 1)
{
$interval = 1; // calendar stores no (extra) interval as null, so using default 1 here
}
$this->interval = (int)$interval;
$this->enddate = $enddate;
// no recurrence --> current date is enddate
if ($type == self::NONE)
{
$enddate = clone $this->time;
}
// set a maximum of 5 years if no enddate given
elseif (is_null($enddate))
{
$enddate = clone $this->time;
$enddate->modify('5 year');
}
// convert enddate to timezone of time, if necessary
else
{
$enddate->setTimezone($this->time->getTimezone());
}
$this->enddate_ymd = (int)$enddate->format('Ymd');
// if no valid weekdays are given for weekly repeating, we use just the current weekday
if (!($this->weekdays = (int)$weekdays) && ($type == self::WEEKLY || $type == self::MONTHLY_WDAY))
{
$this->weekdays = self::getWeekday($this->time);
}
if ($exceptions)
{
foreach($exceptions as $exception)
{
$exception->setTimezone($this->time->getTimezone());
$this->exceptions[] = $exception->format('Ymd');
}
$this->exceptions_objs = $exceptions;
}
}
/**
* Get number of days in month of given date
*
* @param DateTime $time
* @return int
*/
private static function daysInMonth(DateTime $time)
{
list($year,$month) = explode('-',$time->format('Y-m'));
$last_day = new egw_time();
$last_day->setDate($year,$month+1,0);
return (int)$last_day->format('d');
}
/**
* Return the current element
*
* @return DateTime
*/
public function current()
{
return clone $this->current;
}
/**
* Return the key of the current element, we use a Ymd integer as key
*
* @return int
*/
public function key()
{
return (int)$this->current->format('Ymd');
}
/**
* Move forward to next recurence, not caring for exceptions
*/
public function next_no_exception()
{
switch($this->type)
{
case self::NONE: // need to add at least one day, to end "series", as enddate == current date
case self::DAILY:
$this->current->modify($this->interval.' day');
break;
case self::WEEKLY:
// advance to next valid weekday
do
{
// interval in weekly means event runs on valid days eg. each 2. week
// --> on the last day of the week we have to additionally advance interval-1 weeks
if ($this->interval > 1 && self::getWeekday($this->current) == $this->lastdayofweek)
{
$this->current->modify(($this->interval-1).' week');
}
$this->current->modify('1 day');
//echo __METHOD__.'() '.$this->current->format('l').', '.$this->current.": $this->weekdays & ".self::getWeekday($this->current)."<br />\n";
}
while(!($this->weekdays & self::getWeekday($this->current)));
break;
case self::MONTHLY_WDAY: // iCal: BYDAY={1, ..., 5, -1}{MO..SO}
// advance to start of next month
list($year,$month) = explode('-',$this->current->format('Y-m'));
$month += $this->interval+($this->monthly_byday_num < 0 ? 1 : 0);
$this->current->setDate($year,$month,$this->monthly_byday_num < 0 ? 0 : 1);
//echo __METHOD__."() $this->monthly_byday_num".substr(self::$days[$this->monthly_byday_wday],0,2).": setDate($year,$month,1): ".$this->current->format('l').', '.$this->current."<br />\n";
// now advance to n-th week
if ($this->monthly_byday_num > 1)
{
$this->current->modify(($this->monthly_byday_num-1).' week');
//echo __METHOD__."() $this->monthly_byday_num".substr(self::$days[$this->monthly_byday_wday],0,2).': modify('.($this->monthly_byday_num-1).' week): '.$this->current->format('l').', '.$this->current."<br />\n";
}
// advance to given weekday
while(!($this->weekdays & self::getWeekday($this->current)))
{
$this->current->modify(($this->monthly_byday_num < 0 ? -1 : 1).' day');
//echo __METHOD__."() $this->monthly_byday_num".substr(self::$days[$this->monthly_byday_wday],0,2).': modify(1 day): '.$this->current->format('l').', '.$this->current."<br />\n";
}
break;
case self::MONTHLY_MDAY: // iCal: monthly_bymonthday={1, ..., 31, -1}
list($year,$month) = explode('-',$this->current->format('Y-m'));
$day = $this->monthly_bymonthday+($this->monthly_bymonthday < 0 ? 1 : 0);
$month += $this->interval+($this->monthly_bymonthday < 0 ? 1 : 0);
$this->current->setDate($year,$month,$day);
//echo __METHOD__."() setDate($year,$month,$day): ".$this->current->format('l').', '.$this->current."<br />\n";
break;
case self::YEARLY:
$this->current->modify($this->interval.' year');
break;
default:
throw new egw_exception_assertion_failed(__METHOD__."() invalid type #$this->type !");
}
}
/**
* Move forward to next recurence, taking into account exceptions
*/
public function next()
{
do
{
$this->next_no_exception();
}
while($this->exceptions && in_array($this->current->format('Ymd'),$this->exceptions));
}
/**
* Get weekday of $time as self::SUNDAY=1, ..., self::SATURDAY=64 integer mask
*
* @param DateTime $time
* @return int self::SUNDAY=1, ..., self::SATURDAY=64
*/
static protected function getWeekday(DateTime $time)
{
//echo __METHOD__.'('.$time->format('l').' '.$time.') 1 << '.$time->format('w').' = '.(1 << (int)$time->format('w'))."<br />\n";
return 1 << (int)$time->format('w');
}
/**
* Rewind the Iterator to the first element (called at beginning of foreach loop)
*/
public function rewind()
{
$this->current = clone $this->time;
while ($this->valid() &&
$this->exceptions &&
in_array($this->current->format('Ymd'),$this->exceptions))
{
$this->next_no_exception();
}
}
/**
* Checks if current position is valid
*
* @return boolean
*/
public function valid ()
{
return $this->current->format('Ymd') <= $this->enddate_ymd;
}
/**
* Return string represenation of RRule
*
* @return string
*/
function __toString( )
{
$str = '';
// Repeated Events
if($this->type != self::NONE)
{
list($str) = explode(' (',lang(self::$types[$this->type])); // remove (by day/date) from Monthly
$str_extra = array();
switch ($this->type)
{
case self::MONTHLY_MDAY:
$str_extra[] = ($this->monthly_bymonthday == -1 ? lang('last') : $this->monthly_bymonthday.'.').' '.lang('day');
break;
case self::WEEKLY:
case self::MONTHLY_WDAY:
$repeat_days = array();
if ($this->weekdays == self::ALLDAYS)
{
$repeat_days[] = $this->type == self::WEEKLY ? lang('all') : lang('day');
}
elseif($this->weekdays == self::WORKDAYS)
{
$repeat_days[] = $this->type == self::WEEKLY ? lang('workdays') : lang('workday');
}
else
{
foreach (self::$days as $mask => $label)
{
if ($this->weekdays & $mask)
{
$repeat_days[] = lang($label);
}
}
}
if($this->type == self::WEEKLY && count($repeat_days))
{
$str_extra[] = lang('days repeated').': '.implode(', ',$repeat_days);
}
elseif($this->type == self::MONTHLY_WDAY)
{
$str_extra[] = ($this->monthly_byday_num == -1 ? lang('last') : $this->monthly_byday_num.'.').' '.implode(', ',$repeat_days);
}
break;
}
if($this->interval > 1)
{
$str_extra[] = lang('Interval').': '.$this->interval;
}
if ($this->enddate)
{
if ($this->enddate->getTimezone()->getName() != egw_time::$user_timezone->getName())
{
$this->enddate->setTimezone(egw_time::$user_timezone);
}
$str_extra[] = lang('ends').': '.lang($this->enddate->format('l')).', '.$this->enddate->format(egw_time::$user_dateformat);
}
if ($this->time->getTimezone()->getName() != egw_time::$user_timezone->getName())
{
$str_extra[] = $this->time->getTimezone()->getName();
}
if(count($str_extra))
{
$str .= ' ('.implode(', ',$str_extra).')';
}
}
return $str;
}
/**
* Generate a VEVENT RRULE
* @param string $version='1.0' could be '2.0', too
*
* $return array vCalendar RRULE
*/
public function generate_rrule($version='1.0')
{
$repeat_days = array();
$rrule = array();
if ($this->type == self::NONE) return false; // no recuring event
if ($version == '1.0')
{
$rrule['FREQ'] = self::$recur_egw2ical_1_0[$this->type] . $this->interval;
switch ($this->type)
{
case self::WEEKLY:
foreach (self::$days as $mask => $label)
{
if ($this->weekdays & $mask)
{
$repeat_days[] = strtoupper(substr($label,0,2));
}
}
$rrule['BYDAY'] = implode(' ', $repeat_days);
$rrule['FREQ'] = $rrule['FREQ'].' '.$rrule['BYDAY'];
break;
case self::MONTHLY_MDAY: // date of the month: BYMONTDAY={1..31}
break;
case self::MONTHLY_WDAY: // weekday of the month: BDAY={1..5}+ {MO..SO}
$rrule['BYDAY'] = abs($this->monthly_byday_num);
$rrule['BYDAY'] .= ($this->monthly_byday_num < 0) ? '- ' : '+ ';
$rrule['BYDAY'] .= strtoupper(substr($this->time->format('l'),0,2));
$rrule['FREQ'] = $rrule['FREQ'].' '.$rrule['BYDAY'];
break;
}
if (!$this->enddate)
{
$rrule['UNTIL'] = '#0';
}
}
else // $version == '2.0'
{
$rrule['FREQ'] = self::$recur_egw2ical_2_0[$this->type];
switch ($this->type)
{
case self::WEEKLY:
foreach (self::$days as $mask => $label)
{
if ($this->weekdays & $mask)
{
$repeat_days[] = strtoupper(substr($label,0,2));
}
}
$rrule['BYDAY'] = implode(',', $repeat_days);
break;
case self::MONTHLY_MDAY: // date of the month: BYMONTDAY={1..31}
$rrule['BYMONTHDAY'] = $this->monthly_bymonthday;
break;
case MCAL_RECUR_MONTHLY_WDAY: // weekday of the month: BDAY={1..5}{MO..SO}
$rrule['BYDAY'] = $this->monthly_byday_num .
strtoupper(substr($this->time->format('l'),0,2));
break;
}
if ($this->interval > 1)
{
$rrule['INTERVAL'] = $this->interval;
}
}
if ($this->enddate)
{
$this->rewind();
$enddate = $this->current();
do
{
$this->next_no_exception();
$occurrence = $this->current();
}
while ($this->valid() && ($enddate = $occurrence));
$rrule['UNTIL'] = $enddate;
}
return $rrule;
}
/**
* Get instance for a given event array
*
* @param array $event
* @param boolean $usertime=true true: event timestamps are usertime (default for calendar_bo::(read|search), false: servertime
* @param string $to_tz timezone for exports (null for event's timezone)
*
* @return calendar_rrule false on error
*/
public static function event2rrule(array $event,$usertime=true,$to_tz=null)
{
if (!is_array($event) || !isset($event['tzid'])) return false;
if (!$to_tz) $to_tz = $event['tzid'];
$timestamp_tz = $usertime ? egw_time::$user_timezone : egw_time::$server_timezone;
$time = is_a($event['start'],'DateTime') ? $event['start'] : new egw_time($event['start'],$timestamp_tz);
if (!isset(self::$tz_cache[$to_tz]))
{
self::$tz_cache[$to_tz] = calendar_timezones::DateTimeZone($to_tz);
}
self::rrule2tz($event, $time, $to_tz);
$time->setTimezone(self::$tz_cache[$to_tz]);
if ($event['recur_enddate'])
{
$enddate = is_a($event['recur_enddate'],'DateTime') ? $event['recur_enddate'] : new egw_time($event['recur_enddate'],$timestamp_tz);
}
if (is_array($event['recur_exception']))
{
foreach($event['recur_exception'] as $exception)
{
$exceptions[] = is_a($exception,'DateTime') ? $exception : new egw_time($exception,$timestamp_tz);
}
}
return new calendar_rrule($time,$event['recur_type'],$event['recur_interval'],$enddate,$event['recur_data'],$exceptions);
}
/**
* Get recurrence data (keys 'recur_*') to merge into an event
*
* @return array
*/
public function rrule2event()
{
return array(
'recur_type' => $this->type,
'recur_interval' => $this->interval,
'recur_enddate' => $this->enddate ? $this->enddate->format('ts') : null,
'recur_data' => $this->weekdays,
'recur_exception' => $this->exceptions,
);
}
/**
* Shift a recurrence rule to a new timezone
*
* @param array $event recurring event
* @param DateTime/string starttime of the event (in servertime)
* @param string $to_tz new timezone
*/
public static function rrule2tz(array &$event,$starttime,$to_tz)
{
// We assume that the difference between timezones can result
// in a maximum of one day
if (!is_array($event) ||
!isset($event['recur_type']) ||
$event['recur_type'] == MCAL_RECUR_NONE ||
empty($event['recur_data']) || $event['recur_data'] == ALLDAYS ||
empty($event['tzid']) || empty($to_tz) ||
$event['tzid'] == $to_tz) return;
if (!isset(self::$tz_cache[$event['tzid']]))
{
self::$tz_cache[$event['tzid']] = calendar_timezones::DateTimeZone($event['tzid']);
}
if (!isset(self::$tz_cache[$to_tz]))
{
self::$tz_cache[$to_tz] = calendar_timezones::DateTimeZone($to_tz);
}
$time = is_a($starttime,'DateTime') ?
$starttime : new egw_time($starttime, egw_time::$server_timezone);
$time->setTimezone(self::$tz_cache[$event['tzid']]);
$remote = clone $time;
$remote->setTimezone(self::$tz_cache[$to_tz]);
$delta = (int)$remote->format('w') - (int)$time->format('w');
if ($delta)
{
// We have to generate a shifted rrule
switch ($event['recur_type'])
{
case self::MONTHLY_WDAY:
case self::WEEKLY:
$mask = (int)$event['recur_data'];
if ($delta == 1 || $delta == -6)
{
$mask = $mask << 1;
if ($mask & 128) $mask = $mask - 127; // overflow
}
else
{
if ($mask & 1) $mask = $mask + 128; // underflow
$mask = $mask >> 1;
}
$event['recur_data'] = $mask;
}
}
}
}
if (isset($_SERVER['SCRIPT_FILENAME']) && $_SERVER['SCRIPT_FILENAME'] == __FILE__) // some tests
{
ini_set('display_errors',1);
error_reporting(E_ALL & ~E_NOTICE);
function lang($str) { return $str; }
$GLOBALS['egw_info']['user']['preferences']['common']['tz'] = $_REQUEST['user-tz'] ? $_REQUEST['user-tz'] : 'Europe/Berlin';
require_once('../../phpgwapi/inc/class.egw_time.inc.php');
require_once('../../phpgwapi/inc/class.html.inc.php');
require_once('../../phpgwapi/inc/class.egw_exception.inc.php');
if (!isset($_REQUEST['time']))
{
$now = new egw_time('now',new DateTimeZone($_REQUEST['tz'] = 'UTC'));
$_REQUEST['time'] = $now->format();
$_REQUEST['type'] = calendar_rrule::WEEKLY;
$_REQUEST['interval'] = 2;
$now->modify('2 month');
$_REQUEST['enddate'] = $now->format('Y-m-d');
$_REQUEST['user-tz'] = 'Europe/Berlin';
}
echo "<html>\n<head>\n\t<title>Test calendar_rrule class</title>\n</head>\n<body>\n<form method='GET'>\n";
echo "<p>Date+Time: ".html::input('time',$_REQUEST['time']).
html::select('tz',$_REQUEST['tz'],egw_time::getTimezones())."</p>\n";
echo "<p>Type: ".html::select('type',$_REQUEST['type'],calendar_rrule::$types)."\n".
"Interval: ".html::input('interval',$_REQUEST['interval'])."</p>\n";
echo "<table><tr><td>\n";
echo "Weekdays:<br />".html::checkbox_multiselect('weekdays',$_REQUEST['weekdays'],calendar_rrule::$days,false,'','7',false,'height: 150px;')."\n";
echo "</td><td>\n";
echo "<p>Exceptions:<br />".html::textarea('exceptions',$_REQUEST['exceptions'],'style="height: 150px;"')."\n";
echo "</td></tr></table>\n";
echo "<p>Enddate: ".html::input('enddate',$_REQUEST['enddate'])."</p>\n";
echo "<p>Display recurances in ".html::select('user-tz',$_REQUEST['user-tz'],egw_time::getTimezones())."</p>\n";
echo "<p>".html::submit_button('calc','Calculate')."</p>\n";
echo "</form>\n";
$tz = new DateTimeZone($_REQUEST['tz']);
$time = new egw_time($_REQUEST['time'],$tz);
if ($_REQUEST['enddate']) $enddate = new egw_time($_REQUEST['enddate'],$tz);
$weekdays = 0; foreach((array)$_REQUEST['weekdays'] as $mask) $weekdays |= $mask;
if ($_REQUEST['exceptions']) foreach(preg_split("/[,\r\n]+ ?/",$_REQUEST['exceptions']) as $exception) $exceptions[] = new egw_time($exception);
$rrule = new calendar_rrule($time,$_REQUEST['type'],$_REQUEST['interval'],$enddate,$weekdays,$exceptions);
echo "<h3>".$time->format('l').', '.$time.' ('.$tz->getName().') '.$rrule."</h3>\n";
foreach($rrule as $rtime)
{
$rtime->setTimezone(egw_time::$user_timezone);
echo ++$n.': '.$rtime->format('l').', '.$rtime."<br />\n";
}
echo "</body>\n</html>\n";
}

View File

@ -1,6 +1,6 @@
<?php
/**
* eGroupWare - Calendar's storage-object
* EGroupware - Calendar's storage-object
*
* @link http://www.egroupware.org
* @package calendar
@ -64,6 +64,9 @@ define('WEEK_s',7*DAY_s);
* UI only operates in user-time, so there have to be no conversation at all !!!
* BO's functions take and return user-time only (!), they convert internaly everything to servertime, because
* SO operates only on server-time
*
* DB-model uses egw_cal_user.cal_status='X' for participants who got deleted. They never get returned by
* read or search methods, but influence the ctag of the deleted users calendar!
*/
class calendar_so
{
@ -98,7 +101,6 @@ class calendar_so
*/
protected static $tz_cache = array();
/**
* Constructor of the socal class
*/
@ -122,7 +124,7 @@ class calendar_so
*
* @param int|array|string $ids id or array of id's of the entries to read, or string with a single uid
* @param int $recur_date=0 if set read the next recurrence at or after the timestamp, default 0 = read the initital one
* @return array|boolean array with id => data pairs or false if entry not found
* @return array|boolean array with cal_id => event array pairs or false if entry not found
*/
function read($ids,$recur_date=0)
{
@ -143,23 +145,29 @@ class calendar_so
$group_by_cols .= ','.$this->repeats_table.'.'.implode(','.$this->repeats_table.'.',array_keys($table_def['fd']));
$where = array();
if (is_array($ids))
{
array_walk($ids,create_function('&$val,$key','$val = (int) $val;'));
$where[] = $this->cal_table.'.cal_id IN ('.implode(',',$ids).')';
}
elseif (is_numeric($ids))
{
$where[] = $this->cal_table.'.cal_id = '.(int) $ids;
}
else
if (is_scalar($ids) && !is_numeric($ids)) // a single uid
{
// We want only the parents to match
$where['cal_uid'] = $ids;
$where['cal_reference'] = 0;
$where['cal_recurrence'] = 0;
}
elseif(is_array($ids) && isset($ids[count($ids)-1]) || is_scalar($ids)) // one or more cal_id's
{
$where['cal_id'] = $ids;
}
else // array with column => value pairs
{
$where = $ids;
unset($ids); // otherwise users get not read!
}
if (isset($where['cal_id'])) // prevent non-unique column-name cal_id
{
$where[] = $this->db->expression($this->cal_table, $this->cal_table.'.',array(
'cal_id' => $where['cal_id'],
));
unset($where['cal_id']);
}
if ((int) $recur_date)
{
$where[] = 'cal_start >= '.(int)$recur_date;
@ -196,11 +204,17 @@ class calendar_so
sort($event['recur_exception']);
if ($event['recur_exception'][0] < $event['start'])
{
// leading exceptions => move start and end
$event['end'] -= $event['start'] - $event['recur_exception'][0];
$event['start'] = $event['recur_exception'][0];
/* Do NOT move start- and end-date, to the earliest exception, as they will NOT be found in CalDAV or ActiveSync, because
* we only recognice recuring events which start before or in the current timerange and end in or after it or have no end-date.
* --> give an error message, as it is a debuging/support nightmare, if this get's silently fixed when reading events.
* No idea how this situation (exceptions before startdate) can be created anyway.
*
* $event['end'] -= $event['start'] - $event['recur_exception'][0];
* $event['start'] = $event['recur_exception'][0];
*/
error_log(__METHOD__."() recuring event #$event[id]: $event[title] has exceptions before it's startdate ".date('Y-m-d H:i:s',$event['start']));
}
}
}//*/
}
// check if we have a real recurance, if not set $recur_date=0
@ -217,11 +231,13 @@ class calendar_so
}
}
$need_max_user_modified = array();
// participants, if a recur_date give, we read that recurance, plus the one users from the default entry with recur_date=0
// sorting by cal_recur_date ASC makes sure recurence status always overwrites series status
foreach($this->db->select($this->user_table,'*',array(
'cal_id' => $ids,
'cal_recur_date' => $recur_date,
"cal_status != 'X'",
),__LINE__,__FILE__,false,'ORDER BY cal_user_type DESC,cal_recur_date ASC,'.self::STATUS_SORT,'calendar') as $row) // DESC puts users before resources and contacts
{
// combine all participant data in uid and status values
@ -230,6 +246,23 @@ class calendar_so
$events[$row['cal_id']]['participants'][$uid] = $status;
$events[$row['cal_id']]['participant_types'][$row['cal_user_type']][$row['cal_user_id']] = $status;
if ($events[$row['cal_id']]['recur_type'])
{
$need_max_user_modified[$row['cal_id']] = $row['cal_id'];
}
elseif (($modified = $this->db->from_timestamp($row['cal_user_modified'])) > $events[$row['cal_id']]['max_user_modified'])
{
$events[$row['cal_id']]['max_user_modified'] = $modified;
}
}
// max_user_modified for recurring events has to include all recurrences, above code only querys $recur_date!
if ($need_max_user_modified)
{
foreach($this->max_user_modified($need_max_user_modified) as $id => $modified)
{
$events[$id]['max_user_modified'] = $modified;
}
}
// custom fields
@ -260,18 +293,21 @@ class calendar_so
* This includes ALL recurences of an event series
*
* @param int|array $ids one or multiple cal_id's
* @param booelan $return_maximum=false if true return only the maximum, even for multiple ids
* @param boolean $return_maximum=false if true return only the maximum, even for multiple ids
* @param boolean $master_only=false only check recurance master (egw_cal_user.recur_date=0)
* @return int|array (array of) modification timestamp(s)
*/
function max_user_modified($ids, $return_maximum=false)
function max_user_modified($ids, $return_maximum=false, $master_only=false)
{
if (!is_array($ids) || count($ids) == 1) $return_maximum = true;
if (!is_array($ids)) $return_maximum = true;
$where = array('cal_id' => $ids);
if ($master_only) $where['cal_recur_date'] = 0;
if ($return_maximum)
{
if (($etags = $this->db->select($this->user_table,'MAX(cal_user_modified)',array(
'cal_id' => $ids,
),__LINE__,__FILE__,false,'','calendar')->fetchColumn()))
if (($etags = $this->db->select($this->user_table,'MAX(cal_user_modified)',$where,
__LINE__,__FILE__,false,'','calendar')->fetchColumn()))
{
$etags = $this->db->from_timestamp($etags);
}
@ -279,15 +315,13 @@ class calendar_so
else
{
$etags = array();
foreach($this->db->select($this->user_table,'cal_id,MAX(cal_user_modified) AS user_etag',array(
'cal_id' => $ids,
),__LINE__,__FILE__,false,'GROUP BY cal_id','calendar') as $row)
foreach($this->db->select($this->user_table,'cal_id,MAX(cal_user_modified) AS user_etag',$where,
__LINE__,__FILE__,false,'GROUP BY cal_id','calendar') as $row)
{
$etags[$row['cal_id']] = $this->db->from_timestamp($row['user_etag']);
}
}
//echo "<p>".__METHOD__.'('.array2string($ids).','.array($return_maximum).') = '.array2string($etags)."</p>\n";
//error_log(__METHOD__.'('.array2string($ids).','.array2string($return_maximum).') = '.array2string($etags));
//error_log(__METHOD__.'('.array2string($ids).', '.array2string($return_maximum).', '.array2string($master_only).') = '.array2string($etags).' '.function_backtrace());
return $etags;
}
@ -298,14 +332,19 @@ class calendar_so
*
* @param int|array $users one or mulitple calendar users
* @param booelan $owner_too=false if true return also events owned by given users
* @param boolean $master_only=false only check recurance master (egw_cal_user.recur_date=0)
* @return int maximum modification timestamp
*/
function get_ctag($users, $owner_too=false)
function get_ctag($users, $owner_too=false,$master_only=false)
{
$where = array(
'cal_user_type' => 'u',
'cal_user_id' => $users,
);
if ($master_only)
{
$where['cal_recur_date'] = 0;
}
if ($owner_too)
{
// owner can only by users, no groups
@ -341,7 +380,7 @@ class calendar_so
{
$cats = $GLOBALS['egw']->categories->return_all_children($cat_id);
array_walk($cats,create_function('&$val,$key','$val = (int) $val;'));
if (is_array($cat_id) && count($cat_id)==1) $cat_id = $cat_id[0];
$sql = '(cal_category'.(count($cats) > 1 ? " IN ('".implode("','",$cats)."')" : '='.$this->db->quote((int)$cat_id));
foreach($cats as $cat)
{
@ -360,36 +399,43 @@ class calendar_so
* @param int|array $users user-id or array of user-id's, !$users means all entries regardless of users
* @param int|array $cat_id=0 mixed category-id or array of cat-id's (incl. all sub-categories), default 0 = all
* @param string $filter='all' string filter-name: all (not rejected), accepted, unknown, tentative, rejected or hideprivate (handled elsewhere!)
* @param string $query='' pattern so search for, if unset or empty all matching entries are returned (no search)
* Please Note: a search never returns repeating events more then once AND does not honor start+end date !!!
* @param int|boolean $offset=False offset for a limited query or False (default)
* @param int $num_rows=0 number of rows to return if offset set, default 0 = use default in user prefs
* @param string $order='cal_start' column-names plus optional DESC|ASC separted by comma
* @param string $sql_filter='' sql to be and'ed into query (fully quoted)
* @param string|array $_cols=null what to select, default "$this->repeats_table.*,$this->cal_table.*,cal_start,cal_end,cal_recur_date",
* @param array $params=array()
* @param string|array $params['query'] string: pattern so search for, if unset or empty all matching entries are returned (no search)
* Please Note: a search never returns repeating events more then once AND does not honor start+end date !!!
* array: everything is directly used as $where
* @param string $params['order']='cal_start' column-names plus optional DESC|ASC separted by comma
* @param string $params['sql_filter'] sql to be and'ed into query (fully quoted)
* @param string|array $params['cols'] what to select, default "$this->repeats_table.*,$this->cal_table.*,cal_start,cal_end,cal_recur_date",
* if specified and not false an iterator for the rows is returned
* @param string $append='' SQL to append to the query before $order, eg. for a GROUP BY clause
* @param array $cfs=null custom fields to query, null = none, array() = all, or array with cfs names
* @param string $params['append'] SQL to append to the query before $order, eg. for a GROUP BY clause
* @param array $params['cfs'] custom fields to query, null = none, array() = all, or array with cfs names
* @param array $params['users'] raw parameter as passed to calendar_bo::search() no memberships resolved!
* @param boolean $params['master_only']=false, true only take into account participants/status from master (for AS)
* @param int $remove_rejected_by_user=null add join to remove entry, if given user has rejected it
* @return array of cal_ids, or false if error in the parameters
*
* ToDo: search custom-fields too
*/
function &search($start,$end,$users,$cat_id=0,$filter='all',$query='',$offset=False,$num_rows=0,$order='cal_start',$sql_filter='',$_cols=null,$append='',$cfs=null)
function &search($start,$end,$users,$cat_id=0,$filter='all',$offset=False,$num_rows=0,array $params=array(),$remove_rejected_by_user=null)
{
//echo '<p>'.__METHOD__.'('.($start ? date('Y-m-d H:i',$start) : '').','.($end ? date('Y-m-d H:i',$end) : '').','.array2string($users).','.array2string($cat_id).",'$filter',".array2string($query).",$offset,$num_rows,$order,$show_rejected,".array2string($_cols).",$append,".array2string($cfs).")</p>\n";
//error_log(__METHOD__.'('.($start ? date('Y-m-d H:i',$start) : '').','.($end ? date('Y-m-d H:i',$end) : '').','.array2string($users).','.array2string($cat_id).",'$filter',".array2string($offset).",$num_rows,".array2string($params).') '.function_backtrace());
$cols = !is_null($_cols) ? $_cols : "$this->repeats_table.*,$this->cal_table.*,cal_start,cal_end,cal_recur_date";
$cols = self::get_columns('calendar', $this->cal_table);
$cols[0] = $this->db->to_varchar($this->cal_table.'.cal_id');
$cols = isset($params['cols']) ? $params['cols'] : "$this->repeats_table.recur_type,$this->repeats_table.recur_enddate,$this->repeats_table.recur_interval,$this->repeats_table.recur_data,$this->repeats_table.recur_exception,".implode(',',$cols).",cal_start,cal_end,$this->user_table.cal_recur_date";
$where = array();
if (is_array($query))
if (is_array($params['query']))
{
$where = $query;
$where = $params['query'];
}
elseif ($query)
elseif ($params['query'])
{
foreach(array('cal_title','cal_description','cal_location') as $col)
{
$to_or[] = $col.' '.$this->db->capabilities[egw_db::CAPABILITY_CASE_INSENSITIV_LIKE].' '.$this->db->quote('%'.$query.'%');
$to_or[] = $col.' '.$this->db->capabilities[egw_db::CAPABILITY_CASE_INSENSITIV_LIKE].' '.$this->db->quote('%'.$params['query'].'%');
}
$where[] = '('.implode(' OR ',$to_or).')';
@ -402,9 +448,9 @@ class calendar_so
$private_filter = '(cal_public=1 OR cal_public=0 AND '.$this->db->expression($this->cal_table, array('cal_owner' => $params['private_grants'])) . ')';
$where[] = $private_filter;
}
if (!empty($sql_filter) && is_string($sql_filter))
if (!empty($params['sql_filter']) && is_string($params['sql_filter']))
{
$where[] = $sql_filter;
$where[] = $params['sql_filter'];
}
if ($users)
{
@ -420,7 +466,8 @@ class calendar_so
$users_by_type[$user[0]][] = (int) substr($user,1);
}
}
$to_or = $user_or = $owner_or = array();
$to_or = $user_or = array();
$owner_or = null;
$useUnionQuery = $this->db->capabilities['distinct_on_text'] && $this->db->capabilities['union'];
$table_def = $this->db->get_table_definitions('calendar',$this->user_table);
foreach($users_by_type as $type => $ids)
@ -428,20 +475,22 @@ class calendar_so
// when we are able to use Union Querys, we do not OR our query, we save the needed parts for later construction of the union
if ($useUnionQuery)
{
$user_or[] = $this->db->expression($table_def,array(
$user_or[] = $this->db->expression($table_def,$this->user_table.'.',array(
'cal_user_type' => $type,
),' AND '.$this->user_table.'.',array(
'cal_user_id' => $ids,
));
if ($type == 'u' && ($filter == 'owner'))
if ($type == 'u' && $filter == 'owner')
{
$cal_table_def = $this->db->get_table_definitions('calendar',$this->cal_table);
$owner_or[] = $this->db->expression($cal_table_def,array('cal_owner' => $ids));
$owner_or = $this->db->expression($cal_table_def,array('cal_owner' => $ids));
}
}
else
{
$to_or[] = $this->db->expression($table_def,array(
$to_or[] = $this->db->expression($table_def,$this->user_table.'.',array(
'cal_user_type' => $type,
),' AND '.$this->user_table.'.',array(
'cal_user_id' => $ids,
));
if ($type == 'u' && ($filter == 'owner'))
@ -454,27 +503,35 @@ class calendar_so
// this is only used, when we cannot use UNIONS
if (!$useUnionQuery) $where[] = '('.implode(' OR ',$to_or).')';
if($filter != 'deleted')
{
$where[] = 'cal_deleted IS NULL';
}
switch($filter)
{
case 'showonlypublic':
$where[] = "cal_public=1";
$where[] = "cal_status != 'R'"; break;
$where['cal_public'] = 1;
$where[] = "$this->user_table.cal_status NOT IN ('R','X')"; break;
case 'deleted':
$where[] = 'cal_deleted IS NOT NULL'; break;
case 'unknown':
$where[] = "cal_status='U'"; break;
$where[] = "$this->user_table.cal_status='U'"; break;
case 'not-unknown':
$where[] = "$this->user_table.cal_status NOT IN ('U','X')"; break;
case 'accepted':
$where[] = "cal_status='A'"; break;
$where[] = "$this->user_table.cal_status='A'"; break;
case 'tentative':
$where[] = "cal_status='T'"; break;
$where[] = "$this->user_table.cal_status='T'"; break;
case 'rejected':
$where[] = "cal_status='R'"; break;
$where[] = "$this->user_table.cal_status='R'"; break;
case 'delegated':
$where[] = "cal_status='D'"; break;
$where[] = "$this->user_table.cal_status='D'"; break;
case 'all':
case 'owner':
$where[] = "$this->user_table.cal_status!='X'"; break;
break;
default:
//if (!$show_rejected) // not longer used
$where[] = "cal_status != 'R'";
$where[] = "$this->user_table.cal_status NOT IN ('R','X')";
break;
}
}
@ -482,105 +539,126 @@ class calendar_so
{
$where[] = $this->cat_filter($cat_id);
}
if ($start) $where[] = (int)$start.' < cal_end';
if ($start)
{
if ($params['enum_recuring'])
{
$where[] = (int)$start.' < cal_end';
}
else
{
// we check recur_endate!=0, because it can be NULL, 0 or !=0 !!!
$where[] = (int)$start.' < (CASE WHEN recur_type IS NULL THEN cal_end ELSE (CASE WHEN recur_enddate!=0 THEN recur_enddate ELSE 9999999999 END) END)';
}
}
// if not enum recuring events, we have to use minimum start- AND end-dates, otherwise we get more then one event per cal_id!
if (!$params['enum_recuring'])
{
$where[] = "$this->user_table.cal_recur_date=0";
$group_by = 'GROUP BY '.str_replace(array('cal_start,','cal_end,'),'',implode(', ',(array)$cols));
$cols = str_replace(array('cal_start','cal_end'),array('MIN(cal_start) AS cal_start','MIN(cal_end) AS cal_end'),$cols);
}
if ($end) $where[] = 'cal_start < '.(int)$end;
if (!preg_match('/^[a-z_ ,]+$/i',$order)) $order = 'cal_start'; // gard against SQL injection
if (!preg_match('/^[a-z_ ,c]+$/i',$params['order'])) $params['order'] = 'cal_start'; // gard against SQL injection
if ($remove_rejected_by_user)
{
$rejected_by_user_join = "LEFT JOIN $this->user_table rejected_by_user".
" ON $this->cal_table.cal_id=rejected_by_user.cal_id".
" AND rejected_by_user.cal_user_type='u'".
" AND rejected_by_user.cal_user_id=".$this->db->quote($remove_rejected_by_user).
" AND (recur_type IS NULL AND rejected_by_user.cal_recur_date=0 OR cal_start=rejected_by_user.cal_recur_date)";
$or_required = array(
'rejected_by_user.cal_status IS NULL',
"rejected_by_user.cal_status NOT IN ('R','X')",
);
if ($filter == 'owner') $or_required[] = 'cal_owner='.(int)$remove_rejected_by_user;
$where[] = '('.implode(' OR ',$or_required).')';
}
//$starttime = microtime(true);
if ($useUnionQuery)
{
// allow apps to supply participants and/or icons
if (!isset($params['cols'])) $cols .= ',NULL AS participants,NULL AS icons';
// changed the original OR in the query into a union, to speed up the query execution under MySQL 5
$select = array(
'table' => $this->cal_table,
'join' => "JOIN $this->dates_table ON $this->cal_table.cal_id=$this->dates_table.cal_id JOIN $this->user_table ON $this->cal_table.cal_id=$this->user_table.cal_id LEFT JOIN $this->repeats_table ON $this->cal_table.cal_id=$this->repeats_table.cal_id",
'join' => "JOIN $this->dates_table ON $this->cal_table.cal_id=$this->dates_table.cal_id JOIN $this->user_table ON $this->cal_table.cal_id=$this->user_table.cal_id LEFT JOIN $this->repeats_table ON $this->cal_table.cal_id=$this->repeats_table.cal_id $rejected_by_user_join",
'cols' => $cols,
'where' => $where,
'app' => 'calendar',
'append'=> $append,
'append'=> $params['append'].' '.$group_by,
);
$selects = array();
// we check if there are parts to use for the construction of our UNION query,
// as replace the OR by construction of a suitable UNION for performance reasons
if (!empty($owner_or)||!empty($user_or))
if ($owner_or || $user_or)
{
if (!empty($owner_or) && !empty($user_or))
foreach($user_or as $user_sql)
{
// if the query is to be filtered by owner OR user we need 4 selects for the union
//_debug_array($owner_or);
$selects = array();
foreach(array_keys($user_or) as $key)
$selects[] = $select;
$selects[count($selects)-1]['where'][] = $user_sql;
if ($params['enum_recuring'])
{
$selects[count($selects)-1]['where'][] = "recur_type IS NULL AND $this->user_table.cal_recur_date=0";
$selects[] = $select;
$selects[count($selects)-1]['where'][] = $user_or[$key];
$selects[count($selects)-1]['where'][] = 'recur_type IS NULL AND cal_recur_date=0';
$selects[] = $select;
$selects[count($selects)-1]['where'][] = $user_or[$key];
$selects[count($selects)-1]['where'][] = 'cal_recur_date=cal_start';
$selects[count($selects)-1]['where'][] = $user_sql;
$selects[count($selects)-1]['where'][] = "$this->user_table.cal_recur_date=cal_start";
}
$selects[] = $select;
$selects[count($selects)-1]['where'][] = $owner_or;
$selects[count($selects)-1]['where'][] = 'recur_type IS NULL AND cal_recur_date=0';
$selects[] = $select;
$selects[count($selects)-1]['where'][] = $owner_or;
$selects[count($selects)-1]['where'][] = 'cal_recur_date=cal_start';
}
else
// if the query is to be filtered by owner we need to add more selects for the union
if ($owner_or)
{
// if the query is to be filtered only by user we need 2 selects for the union
$selects = array();
foreach(array_keys($user_or) as $key)
$selects[] = $select;
$selects[count($selects)-1]['where'][] = $owner_or;
if ($params['enum_recuring'])
{
$selects[count($selects)-1]['where'][] = "recur_type IS NULL AND $this->user_table.cal_recur_date=0";
$selects[] = $select;
$selects[count($selects)-1]['where'][] = $user_or[$key];
$selects[count($selects)-1]['where'][] = 'recur_type IS NULL AND cal_recur_date=0';
$selects[] = $select;
$selects[count($selects)-1]['where'][] = $user_or[$key];
$selects[count($selects)-1]['where'][] = 'cal_recur_date=cal_start';
$selects[count($selects)-1]['where'][] = $owner_or;
$selects[count($selects)-1]['where'][] = "$this->user_table.cal_recur_date=cal_start";
}
}
}
else
{
// if the query is to be filtered by neither by user nor owner (should not happen?) we need 2 selects for the union
$selects = array($select,$select);
$selects[0]['where'][] = 'recur_type IS NULL AND cal_recur_date=0';
$selects[1]['where'][] = 'cal_recur_date=cal_start';
$selects[] = $select;
if ($params['enum_recuring'])
{
$selects[count($selects)-1]['where'][] = "recur_type IS NULL AND $this->user_table.cal_recur_date=0";
$selects[] = $select;
$selects[count($selects)-1]['where'][] = "$this->user_table.cal_recur_date=cal_start";
}
}
if (is_numeric($offset)) // get the total too
if (is_numeric($offset) && !$params['no_total']) // get the total too
{
$save_selects = $selects;
// we only select cal_table.cal_id (and not cal_table.*) to be able to use DISTINCT (eg. MsSQL does not allow it for text-columns)
$countSelects = count($selects);
foreach(array_keys($selects) as $key)
{
$selects[$key]['cols'] = "DISTINCT $this->repeats_table.*,$this->cal_table.cal_id,cal_start,cal_end,cal_recur_date";
//$selects[0]['cols'] = $selects[1]['cols'] = "DISTINCT $this->repeats_table.*,$this->cal_table.cal_id,cal_start,cal_end,cal_recur_date";
$selects[$key]['cols'] = "DISTINCT $this->repeats_table.recur_type,$this->repeats_table.recur_enddate,$this->repeats_table.recur_interval,$this->repeats_table.recur_data,$this->repeats_table.recur_exception,".$this->db->to_varchar($this->cal_table.'.cal_id').",cal_start,cal_end,$this->user_table.cal_recur_date";
if (!$params['enum_recuring'])
{
$selects[$key]['cols'] = str_replace('cal_start','MIN(cal_start) AS cal_start',$selects[$key]['cols']);
}
}
if (!isset($param['cols'])) self::get_union_selects($selects,$start,$end,$users,$cat_id,$filter,$params['query'],$params['users']);
$this->total = $this->db->union($selects,__LINE__,__FILE__)->NumRows();
$i = 0;
foreach(array_keys($selects) as $key)
{
if ($i >= $countSelects) continue;
$i++;
$selects[$key]['cols'] = $select['cols']; // restore the original cols
//$selects[0]['cols'] = $selects[1]['cols'] = $select['cols']; // restore the original cols
}
$i = 0;
$selections = array();
foreach(array_keys($selects) as $key)
{
if ($i >= $countSelects) continue;
$i++;
$selections[] = $selects[$key];
}
$selects = $selections;
// restore original cols / selects
$selects = $save_selects; unset($save_selects);
}
// error_log("calendar_so_search:\n" . print_r($selects, true));
$rs = $this->db->union($selects,__LINE__,__FILE__,$order,$offset,$num_rows);
if (!isset($param['cols'])) self::get_union_selects($selects,$start,$end,$users,$cat_id,$filter,$params['query'],$params['users']);
$rs = $this->db->union($selects,__LINE__,__FILE__,$params['order'],$offset,$num_rows);
}
else // MsSQL oder MySQL 3.23
{
$where[] = '(recur_type IS NULL AND cal_recur_date=0 OR cal_recur_date=cal_start)';
$where[] = "(recur_type IS NULL AND $this->user_table.cal_recur_date=0 OR $this->user_table.cal_recur_date=cal_start)";
//_debug_array($where);
if (is_numeric($offset)) // get the total too
@ -588,13 +666,14 @@ class calendar_so
// we only select cal_table.cal_id (and not cal_table.*) to be able to use DISTINCT (eg. MsSQL does not allow it for text-columns)
$this->total = $this->db->select($this->cal_table,"DISTINCT $this->repeats_table.*,$this->cal_table.cal_id,cal_start,cal_end,cal_recur_date",
$where,__LINE__,__FILE__,false,'','calendar',0,
"JOIN $this->dates_table ON $this->cal_table.cal_id=$this->dates_table.cal_id JOIN $this->user_table ON $this->cal_table.cal_id=$this->user_table.cal_id LEFT JOIN $this->repeats_table ON $this->cal_table.cal_id=$this->repeats_table.cal_id")->NumRows();
"JOIN $this->dates_table ON $this->cal_table.cal_id=$this->dates_table.cal_id JOIN $this->user_table ON $this->cal_table.cal_id=$this->user_table.cal_id $rejected_by_user_join LEFT JOIN $this->repeats_table ON $this->cal_table.cal_id=$this->repeats_table.cal_id")->NumRows();
}
$rs = $this->db->select($this->cal_table,($this->db->capabilities['distinct_on_text'] ? 'DISTINCT ' : '').$cols,
$where,__LINE__,__FILE__,$offset,$append.' ORDER BY '.$order,'calendar',$num_rows,
"JOIN $this->dates_table ON $this->cal_table.cal_id=$this->dates_table.cal_id JOIN $this->user_table ON $this->cal_table.cal_id=$this->user_table.cal_id LEFT JOIN $this->repeats_table ON $this->cal_table.cal_id=$this->repeats_table.cal_id");
$where,__LINE__,__FILE__,$offset,$params['append'].' ORDER BY '.$params['order'],'calendar',$num_rows,
"JOIN $this->dates_table ON $this->cal_table.cal_id=$this->dates_table.cal_id JOIN $this->user_table ON $this->cal_table.cal_id=$this->user_table.cal_id $rejected_by_user_join LEFT JOIN $this->repeats_table ON $this->cal_table.cal_id=$this->repeats_table.cal_id");
}
if (!is_null($_cols))
//error_log(__METHOD__."() useUnionQuery=$useUnionQuery --> query took ".(microtime(true)-$starttime));
if (isset($params['cols']))
{
return $rs; // if colums are specified we return the recordset / iterator
}
@ -630,15 +709,18 @@ class calendar_so
//_debug_array($events);
if (count($ids))
{
$ids = array_unique($ids);
$need_max_user_modified = array();
// now ready all users with the given cal_id AND (cal_recur_date=0 or the fitting recur-date)
// This will always read the first entry of each recuring event too, we eliminate it later
$recur_dates[] = 0;
$utcal_id_view = " (SELECT * FROM ".$this->user_table." WHERE cal_id IN (".implode(',',array_unique($ids)).")) utcalid ";
$utcal_id_view = " (SELECT * FROM ".$this->user_table." WHERE cal_id IN (".implode(',',$ids).") AND cal_status!='X') utcalid ";
//$utrecurdate_view = " (select * from ".$this->user_table." where cal_recur_date in (".implode(',',array_unique($recur_dates)).")) utrecurdates ";
foreach($this->db->select($utcal_id_view,'*',array(
//'cal_id' => array_unique($ids),
'cal_recur_date' => $recur_dates,
),__LINE__,__FILE__,false,'ORDER BY cal_id,cal_user_type DESC,'.self::STATUS_SORT,'calendar',$num_rows=0,$join='',
),__LINE__,__FILE__,false,'ORDER BY cal_id,cal_user_type DESC,'.self::STATUS_SORT,'calendar',$num_rows,$join='',
$this->db->get_table_definitions('calendar',$this->user_table)) as $row) // DESC puts users before resources and contacts
{
$id = $row['cal_id'];
@ -662,12 +744,30 @@ class calendar_so
// set data, if recurrence is requested
if (isset($events[$id])) $events[$id]['participants'][$uid] = $status;
// fill max_user_modified:
if (!$params['master_only'] && $events[$id]['recur_type'])
{
$need_max_user_modified[$id] = $id;
}
elseif (isset($events[$id]) && ($modified = $this->db->from_timestamp($row['cal_user_modified'])) > $events[$id]['max_user_modified'])
{
$events[$id]['max_user_modified'] = $modified;
}
}
// max_user_modified for recurring events has to include all recurrences, above code only querys $recur_date!
if (!$params['enum_recuring'] && $need_max_user_modified)
{
foreach($this->max_user_modified($need_max_user_modified) as $id => $modified)
{
$events[$id]['max_user_modified'] = $modified;
}
}
//custom fields are not shown in the regular views, so we only query them, if explicitly required
if (!is_null($cfs))
if (!is_null($params['cfs']))
{
$where = array('cal_id' => $ids);
if ($cfs) $where['cal_extra_name'] = $cfs;
if ($params['cfs']) $where['cal_extra_name'] = $params['cfs'];
foreach($this->db->select($this->extra_table,'*',$where,
__LINE__,__FILE__,false,'','calendar') as $row)
{
@ -702,9 +802,140 @@ class calendar_so
}
}
//echo "<p>socal::search\n"; _debug_array($events);
//error_log(__METHOD__."(,filter=".array2string($params['query']).",offset=$offset, num_rows=$num_rows) returning ".count($events)." entries".($offset!==false?" total=$this->total":'').' '.function_backtrace());
return $events;
}
/**
* Data returned by calendar_search_union hook
*/
private static $integration_data;
/**
* Ask other apps if they want to participate in calendar search / display
*
* @param &$selects parts of union query
* @param $start see search()
* @param $end
* @param $users as used in calendar_so ($users_raw plus all members and memberships added by calendar_bo)
* @param $cat_id
* @param $filter
* @param $query
* @param $users_raw as passed to calendar_bo::search (no members and memberships added)
*/
private static function get_union_selects(array &$selects,$start,$end,$users,$cat_id,$filter,$query,$users_raw)
{
if (in_array(basename($_SERVER['SCRIPT_FILENAME']),array('groupdav.php','rpc.php','xmlrpc.php','/activesync/index.php')) ||
!in_array($GLOBALS['egw_info']['flags']['currentapp'],array('calendar','home')))
{
return; // disable integration for GroupDAV, SyncML, ...
}
self::$integration_data = $GLOBALS['egw']->hooks->process(array(
'location' => 'calendar_search_union',
'cols' => $selects[0]['cols'], // cols to return
'start' => $start,
'end' => $end,
'users' => $users,
'users_raw' => $users_raw,
'cat_id'=> $cat_id,
'filter'=> $filter,
'query' => $query,
));
foreach(self::$integration_data as $app => $data)
{
if (is_array($data['selects']))
{
//echo $app; _debug_array($data);
$selects = array_merge($selects,$data['selects']);
}
}
}
/**
* Get data from last 'calendar_search_union' hook call
*
* @return array
*/
public static function get_integration_data()
{
return self::$integration_data;
}
/**
* Return union cols constructed from application cols and required cols
*
* Every col not supplied in $app_cols get returned as NULL.
*
* @param array $app_cols required name => own name pairs
* @param string|array $required array or comma separated column names or table.*
* @param string $required_app='calendar'
* @return string cols for union query to match ones supplied in $required
*/
public static function union_cols(array $app_cols,$required,$required_app='calendar')
{
// remove evtl. used DISTINCT, we currently dont need it
if (($distinct = substr($required,0,9) == 'DISTINCT '))
{
$required = substr($required,9);
}
$return_cols = array();
foreach(is_array($required) ? $required : explode(',',$required) as $cols)
{
if (substr($cols,-2) == '.*')
{
$cols = self::get_columns($required_app,substr($cols,0,-2));
}
elseif (strpos($cols,' AS ') !== false)
{
list(,$cols) = explode(' AS ',$cols);
}
foreach((array)$cols as $col)
{
if (substr($col,0,7) == 'egw_cal') // remove table name
{
$col = preg_replace('/^egw_cal[a-z_]*\./','',$col);
}
if (isset($app_cols[$col]))
{
$return_cols[] = $app_cols[$col];
}
else
{
$return_cols[] = 'NULL';
}
}
}
return implode(',',$return_cols);
}
/**
* Get columns of given table, taking into account historically different column order of egw_cal table
*
* @param string $app
* @param string $table
* @return array of column names
*/
static private function get_columns($app,$table)
{
if ($table != 'egw_cal')
{
$table_def = $GLOBALS['egw']->db->get_table_definitions($app,$table);
$cols = array_keys($table_def['fd']);
}
else
{
// special handling for egw_cal, as old databases have a different column order!!!
$cols =& egw_cache::getSession(__CLASS__,$table);
if (is_null($cols))
{
$meta = $GLOBALS['egw']->db->metadata($table,true);
$cols = array_keys($meta['meta']);
}
}
return $cols;
}
/**
* Checks for conflicts
*/
@ -756,6 +987,7 @@ ORDER BY cal_user_type, cal_usre_id
if (isset($GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length']))
{
$minimum_uid_length = $GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length'];
if (empty($minimum_uid_length) || $minimum_uid_length<=1) $minimum_uid_length = 8; // we just do not accept no uid, or uid way to short!
}
else
{
@ -779,7 +1011,7 @@ ORDER BY cal_user_type, cal_usre_id
// add colum prefix 'cal_' if there's not already a 'recur_' prefix
foreach($event as $col => $val)
{
if ($col[0] != '#' && substr($col,0,6) != 'recur_' && $col != 'alarm' && $col != 'tz_id')
if ($col[0] != '#' && substr($col,0,6) != 'recur_' && $col != 'alarm' && $col != 'tz_id' && $col != 'caldav_name')
{
$event['cal_'.$col] = $val;
unset($event[$col]);
@ -834,15 +1066,38 @@ ORDER BY cal_user_type, cal_usre_id
}
$etag = 0;
}
$update = array();
// event without uid or not strong enough uid
if (!isset($event['cal_uid']) || strlen($event['cal_uid']) < $minimum_uid_length)
{
// event (without uid), not strong enough uid
$event['cal_uid'] = $GLOBALS['egw']->common->generate_uid('calendar',$cal_id);
$this->db->update($this->cal_table, array('cal_uid' => $event['cal_uid']),
array('cal_id' => $event['cal_id']),__LINE__,__FILE__,'calendar');
$update['cal_uid'] = $event['cal_uid'] = $GLOBALS['egw']->common->generate_uid('calendar',$cal_id);
}
// write information about recuring event, if recur_type is present in the array
if ($event['recur_type'] != MCAL_RECUR_NONE)
// set caldav_name, if not given by caller
if (empty($event['caldav_name']) && version_compare($GLOBALS['egw_info']['apps']['calendar']['version'], '1.9.003', '>='))
{
$update['caldav_name'] = $event['caldav_name'] = $cal_id.'.ics';
}
if ($update)
{
$this->db->update($this->cal_table, $update, array('cal_id' => $cal_id),__LINE__,__FILE__,'calendar');
}
if ($event['recur_type'] == MCAL_RECUR_NONE)
{
$this->db->delete($this->dates_table,array(
'cal_id' => $cal_id),
__LINE__,__FILE__,'calendar');
// delete all user-records, with recur-date != 0
$this->db->delete($this->user_table,array(
'cal_id' => $cal_id, 'cal_recur_date != 0'),
__LINE__,__FILE__,'calendar');
$this->db->delete($this->repeats_table,array(
'cal_id' => $cal_id),
__LINE__,__FILE__,'calendar');
}
else // write information about recuring event, if recur_type is present in the array
{
// fetch information about the currently saved (old) event
$old_min = (int) $this->db->select($this->dates_table,'MIN(cal_start)',array('cal_id'=>$cal_id),__LINE__,__FILE__,false,'','calendar')->fetchColumn();
@ -951,14 +1206,7 @@ ORDER BY cal_user_type, cal_usre_id
// write the repeats table
$event['recur_exception'] = empty($event['recur_exception']) ? null : implode(',',$event['recur_exception']);
unset($event[0]); // unset the 'etag=etag+1', as it's not in the repeats table
if($event['recur_type'] != MCAL_RECUR_NONE)
{
$this->db->insert($this->repeats_table,$event,array('cal_id' => $cal_id),__LINE__,__FILE__,'calendar');
}
else
{
$this->db->delete($this->repeats_table,array('cal_id' => $cal_id),__LINE__,__FILE__,'calendar');
}
$this->db->insert($this->repeats_table,$event,array('cal_id' => $cal_id),__LINE__,__FILE__,'calendar');
}
// update start- and endtime if present in the event-array, evtl. we need to move all recurrences
if (isset($event['cal_start']) && isset($event['cal_end']))
@ -975,6 +1223,12 @@ ORDER BY cal_user_type, cal_usre_id
{
if ($name[0] == '#')
{
if (is_array($value) && array_key_exists('id',$value))
{
//error_log(__METHOD__.__LINE__."$name => ".array2string($value).function_backtrace());
$value = $value['id'];
//error_log(__METHOD__.__LINE__."$name => ".array2string($value));
}
if ($value)
{
$this->db->insert($this->extra_table,array(
@ -993,13 +1247,12 @@ ORDER BY cal_user_type, cal_usre_id
}
}
}
// updating or saving the alarms, new alarms have a temporary numeric id!
// ToDo: recuring events !!!
// updating or saving the alarms; new alarms have a temporary numeric id!
if (is_array($event['alarm']))
{
foreach ($event['alarm'] as $id => $alarm)
{
if (is_numeric($id)) unset($alarm['id']); // unset the temporary id, to add the alarm
if (is_numeric($id)) unset($alarm['id']); // unset the temporary id to add the alarm
if(!isset($alarm['offset']))
{
@ -1268,7 +1521,7 @@ ORDER BY cal_user_type, cal_usre_id
));
}
$where[1] = '('.implode(' OR ',$to_or).')';
$this->db->delete($this->user_table,$where,__LINE__,__FILE__,'calendar');
$this->db->update($this->user_table,array('cal_status'=>'X'),$where,__LINE__,__FILE__,'calendar');
unset($where[1]);
}
}
@ -1376,6 +1629,10 @@ ORDER BY cal_user_type, cal_usre_id
'cal_start' => $start,
),__LINE__,__FILE__,'calendar');
if (!is_array($participants))
{
error_log(__METHOD__."($cal_id, $start, $end, ".array2string($participants).") participants is NO array! ".function_backtrace());
}
foreach($participants as $uid => $status)
{
if ($status == 'G') continue; // dont save group-invitations
@ -1433,6 +1690,50 @@ ORDER BY cal_user_type, cal_usre_id
}
}
/**
* Delete all events that were before the given date.
*
* Recurring events that finished before the date will be deleted.
* Recurring events that span the date will be ignored. Non-recurring
* events before the date will be deleted.
*
* @param int $date
*/
function purge($date)
{
// Start with egw_cal, it's the easiest
$sql = "(SELECT egw_cal.cal_id FROM egw_cal
LEFT JOIN egw_cal_repeats ON
egw_cal_repeats.cal_id = egw_cal.cal_id
JOIN egw_cal_dates ON
egw_cal.cal_id = egw_cal_dates.cal_id
WHERE cal_end < $date AND (egw_cal_repeats.cal_id IS NULL OR (recur_enddate < $date AND recur_enddate != 0))) AS TOPROCESS";
// Get what we want to delete for all tables and links
foreach($this->db->select(
$sql,
array('cal_id'),
null,
__LINE__, __FILE__, false
) as $row)
{
//echo __METHOD__." About to delete".$row['cal_id']."\r\n";
foreach($this->all_tables as $table)
{
$this->db->delete($table, array('cal_id'=>$row['cal_id']), __LINE__, __FILE__, 'calendar');
}
// handle sync
$this->db->update('egw_api_content_history',array(
'sync_deleted' => time(),
),array(
'sync_appname' => 'calendar',
'sync_contentid' => $row['cal_id'], // sync_contentid is varchar(60)!
), __LINE__, __FILE__);
// handle links
egw_link::unlink('', 'calendar', $row['cal_id']);
}
}
/**
* read the alarms of a calendar-event specified by $cal_id
*
@ -1485,12 +1786,13 @@ ORDER BY cal_user_type, cal_usre_id
*
* @param int $cal_id Id of the calendar-entry
* @param array $alarm array with fields: text, owner, enabled, ..
* @param timestamp $now_su=0 timestamp for modification of related event
* @param timestamp $now=0 timestamp for modification of related event
* @return string id of the alarm
*/
function save_alarm($cal_id, $alarm, $now_su = 0)
function save_alarm($cal_id, $alarm, $now=0)
{
//echo "<p>save_alarm(cal_id=$cal_id, alarm="; print_r($alarm); echo ")</p>\n";
//error_log(__METHOD__."(.$cal_id,$now,".array2string($alarm).')');
if (!($id = $alarm['id']))
{
$alarms = $this->read_alarms($cal_id); // find a free alarm#
@ -1515,8 +1817,7 @@ ORDER BY cal_user_type, cal_usre_id
}
// update the modification information of the related event
$datetime = $GLOBALS['egw']->datetime;
$now = ($now_su ? $now_su : time() + $datetime->this->tz_offset);
if (!$now) $now = time();
$modifier = $GLOBALS['egw_info']['user']['account_id'];
$this->db->update($this->cal_table, array('cal_modified' => $now, 'cal_modifier' => $modifier),
array('cal_id' => $cal_id), __LINE__, __FILE__, 'calendar');
@ -1545,17 +1846,16 @@ ORDER BY cal_user_type, cal_usre_id
* delete one alarms identified by its id
*
* @param string $id alarm-id is a string of 'cal:'.$cal_id.':'.$alarm_nr, it is used as the job-id too
* @param timestamp $now_su=0 timestamp for modification of related event
* @param timestamp $now=0 timestamp for modification of related event
* @return int number of alarms deleted
*/
function delete_alarm($id, $now_su = 0)
function delete_alarm($id, $now=0)
{
// update the modification information of the related event
list(,$cal_id) = explode(':',$id);
if ($cal_id)
{
$datetime = $GLOBALS['egw']->datetime;
$now = ($now_su ? $now_su : time() + $datetime->this->tz_offset);
if (!$now) $now = time();
$modifier = $GLOBALS['egw_info']['user']['account_id'];
$this->db->update($this->cal_table, array('cal_modified' => $now, 'cal_modifier' => $modifier),
array('cal_id' => $cal_id), __LINE__, __FILE__, 'calendar');
@ -2062,7 +2362,7 @@ ORDER BY cal_user_type, cal_usre_id
*
* @return DateTime
*/
function &startOfDay(egw_time $time, $tz_id)
function &startOfDay(egw_time $time, $tz_id=null)
{
if (empty($tz_id))
{
@ -2078,4 +2378,19 @@ ORDER BY cal_user_type, cal_usre_id
}
return new egw_time($time->format('Y-m-d 00:00:00'), $timezone);
}
/**
* Udates the modification timestamp
*
* @param id event id
* @param time new timestamp
* @param modifier uid of the modifier
*
*/
function updateModified($id, $time, $modifier)
{
$this->db->update($this->cal_table,
array('cal_modified' => $time, 'cal_modifier' => $modifier),
array('cal_id' => $id), __LINE__,__FILE__, 'calendar');
}
}

View File

@ -1,13 +1,13 @@
<?php
/**
* eGroupWare - Calendar's timezone information
* EGroupware - Calendar's timezone information
*
* Timezone information get imported from SQLite database, "borrowed" of Lighting TB extension.
*
* @link http://www.egroupware.org
* @package calendar
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2009 by RalfBecker-At-outdoor-training.de
* @copyright (c) 2009-11 by RalfBecker-At-outdoor-training.de
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
@ -67,7 +67,7 @@ class calendar_timezones
* @return DateTimeZone
* @throws Exception if called with an unknown TZID
*/
public function DateTimeZone($tzid)
public static function DateTimeZone($tzid)
{
if (($id = self::tz2id($tzid,'alias')))
{
@ -321,6 +321,94 @@ class calendar_timezones
'<h3>'.self::import_tz_aliases()."</h3>\n",
lang('Update timezones'),true);
}
/**
* Add VTIMEZONE component to VCALENDAR
*
* @param Horde_iCalendar $vcal
* @param string $tzid
* @return boolean false if no vtimezone component available, true on success
*/
public static function add_vtimezone($vcal, $tzid)
{
include_once EGW_SERVER_ROOT.'/phpgwapi/inc/horde/lib/core.php';
// checking type of $val, now we included the object definition (no need to always include it!)
if (!$vcal instanceof Horde_iCalendar)
{
throw new egw_exception_wrong_parameter(__METHOD__.'('.array2string($val).", '$tzid') no Horde_iCalendar!");
}
// check if we have vtimezone component data for $tzid
if (!($vtimezone = calendar_timezones::tz2id($tzid, 'component')))
{
return false;
}
// $vtimezone is a string with a single VTIMEZONE component, afaik Horde_iCalendar can not add it directly
// --> we have to parse it and let Horde_iCalendar add it again
$horde_vtimezone = Horde_iCalendar::newComponent('VTIMEZONE',$container=false);
$horde_vtimezone->parsevCalendar($vtimezone,'VTIMEZONE');
// DTSTART is in UTC time, Horde_iCalendar parses it in server timezone, which we need to set again for printing
$standard = $horde_vtimezone->findComponent('STANDARD');
if (is_a($standard, 'Horde_iCalendar'))
{
$dtstart = $standard->getAttribute('DTSTART');
$dtstart = new egw_time($dtstart, egw_time::$server_timezone);
$dtstart->setTimezone(egw_time::$server_timezone);
$standard->setAttribute('DTSTART', $dtstart->format('Ymd\THis'), array(), false);
}
$daylight = $horde_vtimezone->findComponent('DAYLIGHT');
if (is_a($daylight, 'Horde_iCalendar'))
{
$dtstart = $daylight->getAttribute('DTSTART');
$dtstart = new egw_time($dtstart, egw_time::$server_timezone);
$dtstart->setTimezone(egw_time::$server_timezone);
$daylight->setAttribute('DTSTART', $dtstart->format('Ymd\THis'), array(), false);
}
//error_log($vtimezone); error_log($horde_vtimezone->_exportvData('VTIMEZONE'));
$vcal->addComponent($horde_vtimezone);
return true;
}
/**
* Query timezone of a given user, returns 'tzid' or VTIMEZONE 'component'
*
* @param int $user=null
* @param string $type='vcalendar' 'tzid' or everything tz2id supports, default 'vcalendar' = full vcalendar component
* @return string
*/
public static function user_timezone($user=null, $type='vcalendar')
{
if (!$user || $user == $GLOBALS['egw_info']['user']['account_id'])
{
$tzid = $GLOBALS['egw_info']['user']['preferences']['common']['tz'];
}
else
{
$prefs_obj = new preferences($user);
$prefs = $prefs_obj->read();
$tzid = $prefs['common']['tz'];
}
if (!$tzid) $tzid = egw_time::$server_timezone->getName();
switch ($type)
{
case 'vcalendar':
include_once EGW_SERVER_ROOT.'/phpgwapi/inc/horde/lib/core.php';
// checking type of $val, now we included the object definition (no need to always include it!)
$vcal = new Horde_iCalendar;
$vcal->setAttribute('PRODID','-//EGroupware//NONSGML EGroupware Calendar '.$GLOBALS['egw_info']['apps']['calendar']['version'].'//'.
strtoupper($GLOBALS['egw_info']['user']['preferences']['common']['lang']));
self::add_vtimezone($vcal, $tzid);
$tzid = $vcal->exportvCalendar('utf-8');
break;
case 'tzid':
break;
default:
$tzid = self::tz2id($tzid,$type == 'vcalendar' ? 'component' : $type);
break;
}
return $tzid;
}
}
/*
if (isset($_SERVER['SCRIPT_FILENAME']) && $_SERVER['SCRIPT_FILENAME'] == __FILE__) // some tests

View File

@ -10,7 +10,7 @@
*/
$setup_info['calendar']['name'] = 'calendar';
$setup_info['calendar']['version'] = '1.8';
$setup_info['calendar']['version'] = '1.9.004';
$setup_info['calendar']['app_order'] = 3;
$setup_info['calendar']['enable'] = 1;
$setup_info['calendar']['index'] = 'calendar.calendar_uiviews.index';
@ -41,6 +41,9 @@ $setup_info['calendar']['hooks']['preferences'] = 'calendar_hooks::preferences';
$setup_info['calendar']['hooks']['settings'] = 'calendar_hooks::settings';
$setup_info['calendar']['hooks']['sidebox_menu'] = 'calendar.calendar_ui.sidebox_menu';
$setup_info['calendar']['hooks']['search_link'] = 'calendar_hooks::search_link';
$setup_info['calendar']['hooks']['config_validate'] = 'calendar_hooks::config_validate';
$setup_info['calendar']['hooks']['timesheet_set'] = 'calendar.calendar_bo.timesheet_set';
$setup_info['calendar']['hooks']['export_limit'] = 'calendar_hooks::getAppExportLimit';
/* Dependencies for this app to work */
$setup_info['calendar']['depends'][] = array(
@ -65,3 +68,5 @@ $setup_info['calendar']['check_install'] = array(
'from' => 'Calendar',
),
);

View File

@ -0,0 +1,124 @@
<?php
/**
* eGroupWare - Calendar setup
*
* @link http://www.egroupware.org
* @package calendar
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
$phpgw_baseline = array(
'egw_cal' => array(
'fd' => array(
'cal_id' => array('type' => 'auto','nullable' => False),
'cal_uid' => array('type' => 'varchar','precision' => '255','nullable' => False,'comment' => 'unique id of event(-series)'),
'cal_owner' => array('type' => 'int','precision' => '4','nullable' => False,'comment' => 'event owner / calendar'),
'cal_category' => array('type' => 'varchar','precision' => '30','comment' => 'category id'),
'cal_modified' => array('type' => 'int','precision' => '8','comment' => 'ts of last modification'),
'cal_priority' => array('type' => 'int','precision' => '2','nullable' => False,'default' => '2'),
'cal_public' => array('type' => 'int','precision' => '2','nullable' => False,'default' => '1','comment' => '1=public, 0=private event'),
'cal_title' => array('type' => 'varchar','precision' => '255','nullable' => False,'default' => '1'),
'cal_description' => array('type' => 'text'),
'cal_location' => array('type' => 'varchar','precision' => '255'),
'cal_reference' => array('type' => 'int','precision' => '4','nullable' => False,'default' => '0','comment' => 'cal_id of series for exception'),
'cal_modifier' => array('type' => 'int','precision' => '4','comment' => 'user who last modified event'),
'cal_non_blocking' => array('type' => 'int','precision' => '2','default' => '0','comment' => '1 for non-blocking events'),
'cal_special' => array('type' => 'int','precision' => '2','default' => '0'),
'cal_etag' => array('type' => 'int','precision' => '4','default' => '0','comment' => 'etag for optimistic locking'),
'cal_creator' => array('type' => 'int','precision' => '4','nullable' => False,'comment' => 'creating user'),
'cal_created' => array('type' => 'int','precision' => '8','nullable' => False,'comment' => 'creation time of event'),
'cal_recurrence' => array('type' => 'int','precision' => '8','nullable' => False,'default' => '0','comment' => 'cal_start of original recurrence for exception'),
'tz_id' => array('type' => 'int','precision' => '4','comment' => 'key into egw_cal_timezones'),
'cal_deleted' => array('type' => 'int','precision' => '8','comment' => 'ts when event was deleted'),
'caldav_name' => array('type' => 'varchar','precision' => '64','comment' => 'name part of CalDAV URL, if specified by client')
),
'pk' => array('cal_id'),
'fk' => array(),
'ix' => array('cal_uid','cal_owner','cal_modified','cal_deleted','caldav_name'),
'uc' => array()
),
'egw_cal_holidays' => array(
'fd' => array(
'hol_id' => array('type' => 'auto','nullable' => False),
'hol_locale' => array('type' => 'char','precision' => '2','nullable' => False),
'hol_name' => array('type' => 'varchar','precision' => '50','nullable' => False),
'hol_mday' => array('type' => 'int','precision' => '8','nullable' => False,'default' => '0'),
'hol_month_num' => array('type' => 'int','precision' => '8','nullable' => False,'default' => '0'),
'hol_occurence' => array('type' => 'int','precision' => '8','nullable' => False,'default' => '0'),
'hol_dow' => array('type' => 'int','precision' => '8','nullable' => False,'default' => '0'),
'hol_observance_rule' => array('type' => 'int','precision' => '8','nullable' => False,'default' => '0')
),
'pk' => array('hol_id'),
'fk' => array(),
'ix' => array('hol_locale'),
'uc' => array()
),
'egw_cal_repeats' => array(
'fd' => array(
'cal_id' => array('type' => 'int','precision' => '4','nullable' => False),
'recur_type' => array('type' => 'int','precision' => '2','nullable' => False),
'recur_enddate' => array('type' => 'int','precision' => '8'),
'recur_interval' => array('type' => 'int','precision' => '2','default' => '1'),
'recur_data' => array('type' => 'int','precision' => '2','default' => '1'),
'recur_exception' => array('type' => 'text','comment' => 'comma-separated start timestamps of exceptions')
),
'pk' => array('cal_id'),
'fk' => array(),
'ix' => array(),
'uc' => array()
),
'egw_cal_user' => array(
'fd' => array(
'cal_id' => array('type' => 'int','precision' => '4','nullable' => False),
'cal_recur_date' => array('type' => 'int','precision' => '8','default' => '0'),
'cal_user_type' => array('type' => 'varchar','precision' => '1','nullable' => False,'default' => 'u','comment' => 'u=user, g=group, c=contact, r=resource, e=email'),
'cal_user_id' => array('type' => 'varchar','precision' => '128','nullable' => False,'comment' => 'id or email-address for type=e'),
'cal_status' => array('type' => 'char','precision' => '1','default' => 'A','comment' => 'U=unknown, A=accepted, R=rejected, T=tentative'),
'cal_quantity' => array('type' => 'int','precision' => '4','default' => '1','comment' => 'only for certain types (eg. resources)'),
'cal_role' => array('type' => 'varchar','precision' => '64','default' => 'REQ-PARTICIPANT','comment' => 'CHAIR, REQ-PARTICIPANT, OPT-PARTICIPANT, NON-PARTICIPANT, X-CAT-$cat_id'),
'cal_user_modified' => array('type' => 'timestamp','default' => 'current_timestamp','comment' => 'automatic timestamp of last update')
),
'pk' => array('cal_id','cal_recur_date','cal_user_type','cal_user_id'),
'fk' => array(),
'ix' => array('cal_user_modified',array('cal_user_type','cal_user_id')),
'uc' => array()
),
'egw_cal_extra' => array(
'fd' => array(
'cal_id' => array('type' => 'int','precision' => '4','nullable' => False),
'cal_extra_name' => array('type' => 'varchar','precision' => '40','nullable' => False),
'cal_extra_value' => array('type' => 'varchar','precision' => '255','nullable' => False,'default' => '')
),
'pk' => array('cal_id','cal_extra_name'),
'fk' => array(),
'ix' => array(),
'uc' => array()
),
'egw_cal_dates' => array(
'fd' => array(
'cal_id' => array('type' => 'int','precision' => '4','nullable' => False),
'cal_start' => array('type' => 'int','precision' => '8','nullable' => False,'comment' => 'starttime in server time'),
'cal_end' => array('type' => 'int','precision' => '8','nullable' => False,'comment' => 'endtime in server time')
),
'pk' => array('cal_id','cal_start'),
'fk' => array(),
'ix' => array(),
'uc' => array()
),
'egw_cal_timezones' => array(
'fd' => array(
'tz_id' => array('type' => 'auto','nullable' => False),
'tz_tzid' => array('type' => 'varchar','precision' => '128','nullable' => False),
'tz_alias' => array('type' => 'int','precision' => '4','comment' => 'tz_id for data'),
'tz_latitude' => array('type' => 'int','precision' => '4'),
'tz_longitude' => array('type' => 'int','precision' => '4'),
'tz_component' => array('type' => 'text','comment' => 'iCal VTIMEZONE component')
),
'pk' => array('tz_id'),
'fk' => array(),
'ix' => array('tz_alias'),
'uc' => array('tz_tzid')
)
);

View File

@ -1630,6 +1630,7 @@ function calendar_upgrade1_5_002()
return $GLOBALS['setup_info']['calendar']['currentver'] = '1.6';
}
/**
* Adjust UIDs of series exceptions to RFC standard
* Propagate cal_reference field to temporarily contain RECURRENCE-ID
@ -1968,24 +1969,50 @@ function calendar_upgrade1_7_009()
function calendar_upgrade1_7_010()
{
return $GLOBALS['setup_info']['calendar']['currentver'] = '1.8';
$GLOBALS['egw_setup']->oProc->AddColumn('egw_cal','cal_deleted',array(
'type' => 'bool',
'nullable' => False,
'default' => '0',
'comment' => '1 if the event has been deleted, but you want to keep it around'
));
return $GLOBALS['setup_info']['calendar']['currentver'] = '1.9.001'; // was 1.7.011
}
function calendar_upgrade1_7_011()
{
return $GLOBALS['setup_info']['calendar']['currentver'] = '1.9.001';
}
function calendar_upgrade1_8()
{
calendar_upgrade1_7_010();
return $GLOBALS['setup_info']['calendar']['currentver'] = '1.9.001';
}
/**
* Downgrade from Trunk / EPL 10.1 to 1.8: droping cal_deleted
*
* Convert bool column cal_deleted with egw_api_content_history table to a unix timestamp
*
* Using cal_modified as deleted-timestamp, as querying it from SyncML tables creates too many problems (refresh table stops before copying all rows!)
*
* @return string
*/
function calendar_upgrade1_7_011()
function calendar_upgrade1_9_001()
{
$deleted = 'cal_deleted=1 OR cal_deleted IS NOT NULL AND cal_deleted != 0';
foreach(array('egw_cal_dates','egw_cal_extra','egw_cal_repeats','egw_cal_user') as $table)
{
$GLOBALS['egw_setup']->db->delete($table,"cal_id IN (SELECT cal_id FROM egw_cal WHERE $deleted)",__LINE__,__FILE__,'calendar');
}
$GLOBALS['egw_setup']->db->delete('egw_cal',$deleted,__LINE__,__FILE__);
$GLOBALS['egw_setup']->oProc->DropColumn('egw_cal',array(
// delete in the past wrongly created entries for a single recurrence, which mess up the update, beside being wrong anyway
$GLOBALS['egw_setup']->db->delete('egw_api_content_history',array(
'sync_appname' => 'calendar',
"sync_contentid LIKE '%:%'",
), __LINE__, __FILE__);
/* done by RefreshTable() anyway
$GLOBALS['egw_setup']->oProc->AlterColumn('egw_cal','cal_deleted',array(
'type' => 'int',
'precision' => '8',
'comment' => 'ts when event was deleted'
));*/
$GLOBALS['egw_setup']->oProc->RefreshTable('egw_cal',array(
'fd' => array(
'cal_id' => array('type' => 'auto','nullable' => False),
'cal_uid' => array('type' => 'varchar','precision' => '255','nullable' => False,'comment' => 'unique id of event(-series)'),
@ -2012,15 +2039,42 @@ function calendar_upgrade1_7_011()
'fk' => array(),
'ix' => array('cal_uid','cal_owner','cal_deleted'),
'uc' => array()
),'cal_deleted');
),array(
// for deleted rows use cal_modified as deleted date, NULL for not deleted ones
'cal_deleted' => 'CASE cal_deleted WHEN '.$GLOBALS['egw_setup']->db->quote(true,'bool').' THEN cal_modified ELSE NULL END',
));
return $GLOBALS['setup_info']['calendar']['currentver'] = '1.8';
}
function calendar_upgrade1_9_001()
{
return calendar_upgrade1_7_011();
return $GLOBALS['setup_info']['calendar']['currentver'] = '1.9.002';
}
/**
* Add column to store CalDAV name given by client
*/
function calendar_upgrade1_9_002()
{
return calendar_upgrade1_7_011();
}
$GLOBALS['egw_setup']->oProc->AddColumn('egw_cal','caldav_name',array(
'type' => 'varchar',
'precision' => '64',
'comment' => 'name part of CalDAV URL, if specified by client'
));
$GLOBALS['egw_setup']->db->query($sql='UPDATE egw_cal SET caldav_name='.
$GLOBALS['egw_setup']->db->concat(
$GLOBALS['egw_setup']->db->to_varchar('cal_id'),"'.ics'"),__LINE__,__FILE__);
$GLOBALS['egw_setup']->oProc->CreateIndex('egw_cal','caldav_name');
return $GLOBALS['setup_info']['calendar']['currentver'] = '1.9.003';
}
/**
* Add index for cal_modified and cal_user_modified to improve ctag and etag generation on big installtions
*/
function calendar_upgrade1_9_003()
{
$GLOBALS['egw_setup']->oProc->CreateIndex('egw_cal','cal_modified');
$GLOBALS['egw_setup']->oProc->CreateIndex('egw_cal_user','cal_user_modified');
return $GLOBALS['setup_info']['calendar']['currentver'] = '1.9.004';
}

View File

@ -18,6 +18,8 @@ $tz_aliases = array(
'Arabic Standard Time' => 'Asia/Baghdad',
'Argentina Standard Time' => 'America/Argentina/Buenos_Aires', // was 'America/Buenos_Aires',
'Armenian Standard Time' => 'Asia/Yerevan',
'Asia/Katmandu' => 'Asia/Kathmandu',
'Asia/Calcutta' => 'Asia/Kolkata',
'Atlantic Standard Time' => 'America/Halifax',
'Azerbaijan Standard Time' => 'Asia/Baku',
'Azores Standard Time' => 'Atlantic/Azores',
@ -50,7 +52,7 @@ $tz_aliases = array(
'Greenland Standard Time' => 'America/Godthab',
'Greenwich Standard Time' => 'Atlantic/Reykjavik', // was 'Africa/Reykjavik',
'Hawaiian Standard Time' => 'Pacific/Honolulu',
'India Standard Time' => 'Asia/Calcutta',
'India Standard Time' => 'Asia/Kolkata',
'Iran Standard Time' => 'Asia/Tehran',
'Israel Standard Time' => 'Asia/Jerusalem',
'Jordan Standard Time' => 'Asia/Amman',
@ -67,7 +69,7 @@ $tz_aliases = array(
'Myanmar Standard Time' => 'Asia/Rangoon',
'N. Central Asia Standard Time' => 'Asia/Novosibirsk',
'Namibia Standard Time' => 'Africa/Windhoek',
'Nepal Standard Time' => 'Asia/Katmandu',
'Nepal Standard Time' => 'Asia/Kathmandu',
'New Zealand Standard Time' => 'Pacific/Auckland',
'Newfoundland Standard Time' => 'America/St_Johns',
'North Asia East Standard Time' => 'Asia/Irkutsk',
@ -100,4 +102,4 @@ $tz_aliases = array(
'West Asia Standard Time' => 'Asia/Tashkent',
'West Pacific Standard Time' => 'Pacific/Port_Moresby',
'Yakutsk Standard Time' => 'Asia/Yakutsk',
);
);

View File

@ -1,24 +1,38 @@
<?php
//
// +----------------------------------------------------------------------+
// | PHP Version 4 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2003 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.02 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available at through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Authors: Hartmut Holzgraefe <hholzgra@php.net> |
// | Christian Stocker <chregu@bitflux.ch> |
// +----------------------------------------------------------------------+
//
// $Id: Server.php,v 1.56 2006/10/10 11:53:16 hholzgra Exp $
//
<?php // $Id$
/*
+----------------------------------------------------------------------+
| Copyright (c) 2002-2007 Christian Stocker, Hartmut Holzgraefe |
| All rights reserved |
| |
| Redistribution and use in source and binary forms, with or without |
| modification, are permitted provided that the following conditions |
| are met: |
| |
| 1. Redistributions of source code must retain the above copyright |
| notice, this list of conditions and the following disclaimer. |
| 2. Redistributions in binary form must reproduce the above copyright |
| notice, this list of conditions and the following disclaimer in |
| the documentation and/or other materials provided with the |
| distribution. |
| 3. The names of the authors may not be used to endorse or promote |
| products derived from this software without specific prior |
| written permission. |
| |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
| FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
| COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
| INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
| BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
| ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| POSSIBILITY OF SUCH DAMAGE. |
+----------------------------------------------------------------------+
*/
require_once "HTTP/WebDAV/Tools/_parse_propfind.php";
require_once "HTTP/WebDAV/Tools/_parse_proppatch.php";
require_once "HTTP/WebDAV/Tools/_parse_lockinfo.php";
@ -169,23 +183,23 @@ class HTTP_WebDAV_Server
// default uri is the complete request uri
$uri = (@$this->_SERVER["HTTPS"] === "on" ? "https:" : "http:") . '//'.$this->_SERVER['HTTP_HOST'];
}
// support for PHP running as (F)CGI
if (!isset($this->_SERVER['PATH_INFO']) && isset($this->_SERVER['ORIG_PATH_INFO']))
{
$this->_SERVER['PATH_INFO'] = $this->_SERVER['ORIG_PATH_INFO'];
}
// we cant use SCRIPT_NAME, because it fails, if there's any url rewriting
//error_log("pathinfo:\n". $this->_urldecode($this->_SERVER['REQUEST_URI']).":\n".$this->_SERVER['PATH_INFO']);
$uri .= $this->_urldecode($this->_SERVER['REQUEST_URI']);
if (!empty($this->_SERVER["PATH_INFO"]))
{
$uri = substr($uri,0,-strlen($this->_SERVER["PATH_INFO"]));
$uri .= $this->_SERVER["SCRIPT_NAME"];
// WebDAV has no concept of a query string and clients (including cadaver)
// seem to pass '?' unencoded, so we need to extract the path info out
// of the request URI ourselves
$path_info = substr($this->_SERVER["REQUEST_URI"], strlen($this->_SERVER["SCRIPT_NAME"]));
// just in case the path came in empty ...
if (empty($path_info)) {
$path_info = "/";
}
$path_info = empty($this->_SERVER["PATH_INFO"]) ? "/" : $this->_SERVER["PATH_INFO"];
$path_info = $this->_urldecode($path_info);
$this->base_uri = $uri;
$this->uri = $uri . $path_info;
// set path
// $_SERVER['PATH_INFO'] is already urldecoded
//$this->path = $this->_urldecode($path_info);
@ -546,7 +560,7 @@ class HTTP_WebDAV_Server
* OPTIONS method handler
*
* The OPTIONS method handler creates a valid OPTIONS reply
* including Dav: and Allowed: heaers
* including Dav: and Allowed: headers
* based on the implemented methods found in the actual instance
*
* @param void
@ -824,8 +838,10 @@ class HTTP_WebDAV_Server
/* TODO right now the user implementation has to make sure
collections end in a slash, this should be done in here
by checking the resource attribute */
// path needs to be urlencoded (only basic version of this class!)
$href = $this->_urlencode($this->_mergePathes($this->base_uri, $path));
$href = $this->_mergePaths($this->base_uri, $path);
/* minimal urlencoding is needed for the resource path */
$href = $this->_urlencode($href);
if ($this->crrnd)
{
@ -876,6 +892,17 @@ class HTTP_WebDAV_Server
echo $prop["val"];
echo ' </'.($this->crrnd?'':'D:')."lockdiscovery>\n";
break;
// the following are non-standard Microsoft extensions to the DAV namespace
case "lastaccessed":
echo ' <'.($this->crrnd?'':'D:')."lastaccessed ns0:dt=\"dateTime.rfc1123\">"
. gmdate("D, d M Y H:i:s ", $prop['val'])
. 'GMT</'.($this->crrnd?'':'D:')."lastaccessed>\n";
break;
case "ishidden":
echo ' <'.($this->crrnd?'':'D:')."ishidden>"
. is_string($prop['val']) ? $prop['val'] : ($prop['val'] ? 'true' : 'false')
. '</'.($this->crrnd?'':'D:')."</D:ishidden>\n";
break;
default:
$ns_defs = '';
if (is_array($prop['val']))
@ -885,7 +912,7 @@ class HTTP_WebDAV_Server
} elseif (isset($prop['raw'])) {
$val = $this->_prop_encode('<![CDATA['.$prop['val'].']]>');
} else {
$val = $this->_prop_encode(htmlspecialchars($prop['val']));
$val = $this->_prop_encode(htmlspecialchars($prop['val'], ENT_NOQUOTES, 'utf-8'));
}
echo ' <'.($this->crrnd?'':'D:')."$prop[name]$ns_defs>$val".
'</'.($this->crrnd?'':'D:')."$prop[name]>\n";
@ -919,13 +946,22 @@ class HTTP_WebDAV_Server
$ns_name = '';
}
$vals .= "<$ns_name$subprop[name]";
if (is_array($subprop['val'])) // val contains only attributes, no value
if (is_array($subprop['val']))
{
foreach($subprop['val'] as $attr => $val)
{
$vals .= ' '.$attr.'="'.htmlspecialchars($val).'"';
}
$vals .= '/>';
if (isset($subprop['val'][0]))
{
$vals .= '>';
$vals .= $this->_hierarchical_prop_encode($subprop['val'], $subprop['ns'], $ns_defs, $ns_hash);
$vals .= "</$ns_name$subprop[name]>";
}
else // val contains only attributes, no value
{
foreach($subprop['val'] as $attr => $val)
{
$vals .= ' '.$attr.'="'.htmlspecialchars($val, ENT_NOQUOTES, 'utf-8').'"';
}
$vals .= '/>';
}
}
else
{
@ -933,7 +969,7 @@ class HTTP_WebDAV_Server
if (isset($subprop['raw'])) {
$vals .= '<![CDATA['.$subprop['val'].']]>';
} else {
$vals .= htmlspecialchars($subprop['val']);
$vals .= htmlspecialchars($subprop['val'], ENT_NOQUOTES, 'utf-8');
}
$vals .= "</$ns_name$subprop[name]>";
}
@ -944,7 +980,7 @@ class HTTP_WebDAV_Server
{
$val = '<![CDATA['.$prop['val'].']]>';
} else {
$val = htmlspecialchars($prop['val']);
$val = htmlspecialchars($prop['val'], ENT_NOQUOTES, 'utf-8');
}
$val = $this->_prop_encode($val);
// properties from namespaces != "DAV:" or without any namespace
@ -1049,7 +1085,7 @@ class HTTP_WebDAV_Server
echo "<D:multistatus xmlns:D=\"DAV:\">\n";
echo ' <'.($this->crrnd?'':'D:')."response>\n";
echo ' <'.($this->crrnd?'':'D:')."href>".$this->_urlencode($this->_mergePathes($this->_SERVER["SCRIPT_NAME"], $this->path)).'</'.($this->crrnd?'':'D:')."href>\n";
echo ' <'.($this->crrnd?'':'D:')."href>".$this->_urlencode($this->_mergePaths($this->_SERVER["SCRIPT_NAME"], $this->path)).'</'.($this->crrnd?'':'D:')."href>\n";
foreach ($options["props"] as $prop) {
echo ' <'.($this->crrnd?'':'D:')."propstat>\n";
@ -1060,7 +1096,7 @@ class HTTP_WebDAV_Server
if ($responsedescr) {
echo ' <'.($this->crrnd?'':'D:')."responsedescription>".
$this->_prop_encode(htmlspecialchars($responsedescr)).
$this->_prop_encode(htmlspecialchars($responsedescr, ENT_NOQUOTES, 'utf-8')).
'</'.($this->crrnd?'':'D:')."responsedescription>\n";
}
@ -1225,7 +1261,7 @@ class HTTP_WebDAV_Server
if (false === $status) {
$this->http_status("404 not found");
} else {
// TODO: check setting of headers in various code pathes above
// TODO: check setting of headers in various code paths above
$this->http_status("$status");
}
}
@ -1363,8 +1399,6 @@ class HTTP_WebDAV_Server
$options = Array();
$options['path'] = $this->path;
error_log('WebDAV POST: ' . $this->path);
if (isset($this->_SERVER['CONTENT_LENGTH']))
{
$options['content_length'] = $this->_SERVER['CONTENT_LENGTH'];
@ -1488,8 +1522,6 @@ class HTTP_WebDAV_Server
}
}
$options['stream'] = fopen('php://input', 'r');
if (method_exists($this, 'POST')) {
$status = $this->POST($options);
@ -1557,7 +1589,7 @@ class HTTP_WebDAV_Server
// for now we do not support any sort of multipart requests
if (!strncmp($this->_SERVER["CONTENT_TYPE"], "multipart/", 10)) {
$this->http_status("501 not implemented");
echo "The service does not support mulipart PUT requests";
echo "The service does not support multipart PUT requests";
return;
}
$options["content_type"] = $this->_SERVER["CONTENT_TYPE"];
@ -1636,11 +1668,16 @@ class HTTP_WebDAV_Server
return;
}
$range = array("start"=>$matches[1], "end"=>$matches[2]);
$range = array("start" => $matches[1], "end" => $matches[2]);
if (is_numeric($matches[3])) {
$range["total_length"] = $matches[3];
}
$option["ranges"][] = $range;
if (!isset($options['ranges'])) {
$options['ranges'] = array();
}
$options["ranges"][] = $range;
// TODO make sure the implementation supports partial PUT
// this has to be done in advance to avoid data being overwritten
@ -1665,8 +1702,6 @@ class HTTP_WebDAV_Server
}
}
$options["stream"] = fopen("php://input", "r");
$stat = $this->PUT($options);
if ($stat === false) {
@ -1678,17 +1713,36 @@ class HTTP_WebDAV_Server
if (!empty($options["ranges"])) {
// TODO multipart support is missing (see also above)
if (0 == fseek($stream, $range[0]["start"], SEEK_SET)) {
$length = $range[0]["end"]-$range[0]["start"]+1;
if (!fwrite($stream, fread($options["stream"], $length))) {
$stat = "403 Forbidden";
if (0 == fseek($stream, $options['ranges'][0]["start"], SEEK_SET)) {
$length = $options['ranges'][0]["end"] - $options['ranges'][0]["start"]+1;
while (!feof($options['stream'])) {
if ($length <= 0) {
break;
}
if ($length <= 8192) {
$data = fread($options['stream'], $length);
} else {
$data = fread($options['stream'], 8192);
}
if ($data === false) {
$stat = "400 Bad request";
} elseif (strlen($data)) {
if (false === fwrite($stream, $data)) {
$stat = "403 Forbidden";
break;
}
$length -= strlen($data);
}
}
} else {
$stat = "403 Forbidden";
}
} else {
while (!feof($options["stream"])) {
if (false === fwrite($stream, fread($options["stream"], 4096))) {
if (false === fwrite($stream, fread($options["stream"], 8192))) {
$stat = "403 Forbidden";
break;
}
@ -1854,26 +1908,32 @@ class HTTP_WebDAV_Server
if (is_bool($stat)) {
$http_stat = $stat ? "200 OK" : "423 Locked";
} else {
$http_stat = $stat;
$http_stat = (string)$stat;
}
$this->http_status($http_stat);
if ($http_stat{0} == 2) { // 2xx states are ok
if ($options["timeout"]) {
if (is_numeric($options["timeout"]))
{
// more than a million is considered an absolute timestamp
// less is more likely a relative value
if ($options["timeout"]>1000000) {
$timeout = "Second-".($options['timeout']-time());
} else {
$timeout = "Second-$options[timeout]";
}
}
else
{
$timeout = $options[timeout];
}
// if multiple timeout values were given we take the first only
if (is_array($options["timeout"])) {
reset($options["timeout"]);
$options["timeout"] = current($options["timeout"]);
}
// if the timeout is numeric only we need to reformat it
if (is_numeric($options["timeout"])) {
// more than a million is considered an absolute timestamp
// less is more likely a relative value
if ($options["timeout"]>1000000) {
$timeout = "Second-".($options['timeout']-time());
} else {
$timeout = "Second-$options[timeout]";
}
} else {
// non-numeric values are passed on verbatim,
// no error checking is performed here in this case
// TODO: send "Infinite" on invalid timeout strings?
$timeout = $options["timeout"];
}
} else {
$timeout = "Infinite";
}
@ -1987,14 +2047,21 @@ class HTTP_WebDAV_Server
$options["depth"] = "infinity";
}
extract(parse_url($this->_SERVER["HTTP_DESTINATION"]));
$path = urldecode($path);
$http_host = $host;
if (isset($port) && $port != 80)
$http_host.= ":$port";
$http_header_host = preg_replace("/:80$/", "", $this->_SERVER["HTTP_HOST"]);
$url = parse_url($this->_SERVER["HTTP_DESTINATION"]);
$path = urldecode($url["path"]);
if (isset($url["host"])) {
// TODO check url scheme, too
$http_host = $url["host"];
if (isset($url["port"]) && $url["port"] != 80)
$http_host.= ":".$url["port"];
} else {
// only path given, set host to self
$http_host == $http_header_host;
}
if ($http_host == $http_header_host &&
!strncmp($this->_SERVER["SCRIPT_NAME"], $path,
strlen($this->_SERVER["SCRIPT_NAME"]))) {
@ -2491,7 +2558,7 @@ class HTTP_WebDAV_Server
/**
* private minimalistic version of PHP urlencode()
*
* only blanks and XML special chars must be encoded here
* only blanks, percent and XML special chars must be encoded here
* full urlencode() encoding confuses some clients ...
*
* @param string URL to encode
@ -2511,6 +2578,7 @@ class HTTP_WebDAV_Server
}
//error_log( __METHOD__."\n" .print_r($url,true));
return strtr($url, array(' ' => '%20',
'%' => '%25',
'&' => '%26',
'<' => '%3C',
'>' => '%3E',
@ -2528,7 +2596,7 @@ class HTTP_WebDAV_Server
*/
function _urldecode($path)
{
return urldecode($path);
return rawurldecode($path);
}
/**
@ -2584,7 +2652,7 @@ class HTTP_WebDAV_Server
foreach($subprop as $attr => $val)
{
$vals .= ' '.$attr.'="'.htmlspecialchars($val).'"';
$vals .= ' '.$attr.'="'.htmlspecialchars($val, ENT_NOQUOTES, 'utf-8').'"';
}
$ret .= '<'.($prop['ns'] == $ns ? ($this->cnrnd ? $ns_hash[$ns].':' : '') : $ns_hash[$prop['ns']].':').$prop['name'].
@ -2603,8 +2671,14 @@ class HTTP_WebDAV_Server
{
$val = $this->_prop_encode('<![CDATA['.$prop['val'].']]>');
} else {
$val = $this->_prop_encode(htmlspecialchars($prop['val']));
} }
$val = $this->_prop_encode(htmlspecialchars($prop['val'], ENT_NOQUOTES, 'utf-8'));
// for href properties we need (minimalistic) urlencoding, eg. space
if ($prop['name'] == 'href')
{
$val = $this->_urlencode($val);
}
}
}
$ret .= '<'.($prop['ns'] == $ns ? ($this->cnrnd ? $ns_hash[$ns].':' : '') : $ns_hash[$prop['ns']].':').$prop['name'].
(empty($prop['val']) ? ' />' : '>'.$val.'</'.($prop['ns'] == $ns ? ($this->cnrnd ? $ns_hash[$ns].':' : '') : ($this->crrnd ? '' : $ns_hash[$prop['ns']].':')).$prop['name'].'>');
@ -2672,17 +2746,17 @@ class HTTP_WebDAV_Server
}
/**
* Merge two pathes, make sure there is exactly one slash between them
* Merge two paths, make sure there is exactly one slash between them
*
* @param string parent path
* @param string child path
* @return string merged path
*/
function _mergePathes($parent, $child)
function _mergePaths($parent, $child)
{
//error_log("merge called :\n$parent \n$child\n" . function_backtrace());
//error_log("merge :\n".print_r($this->_mergePathes($this->_SERVER["SCRIPT_NAME"], $this->path)true));
if ($child{0} == '/') {
//error_log("merge called :\n$parent \n$child\n" . function_backtrace());
//error_log("merge :\n".print_r($this->_mergePaths($this->_SERVER["SCRIPT_NAME"], $this->path)true));
if ($child{0} == '/') {
return $this->_unslashify($parent).$child;
} else {
return $this->_slashify($parent).$child;

View File

@ -1,4 +1,37 @@
<?php
<?php // $Id: Filesystem.php 312282 2011-06-19 13:39:05Z clockwerx $
/*
+----------------------------------------------------------------------+
| Copyright (c) 2002-2007 Christian Stocker, Hartmut Holzgraefe |
| All rights reserved |
| |
| Redistribution and use in source and binary forms, with or without |
| modification, are permitted provided that the following conditions |
| are met: |
| |
| 1. Redistributions of source code must retain the above copyright |
| notice, this list of conditions and the following disclaimer. |
| 2. Redistributions in binary form must reproduce the above copyright |
| notice, this list of conditions and the following disclaimer in |
| the documentation and/or other materials provided with the |
| distribution. |
| 3. The names of the authors may not be used to endorse or promote |
| products derived from this software without specific prior |
| written permission. |
| |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
| FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
| COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
| INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
| BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
| ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| POSSIBILITY OF SUCH DAMAGE. |
+----------------------------------------------------------------------+
*/
require_once "HTTP/WebDAV/Server.php";
require_once "System.php";
@ -73,11 +106,9 @@ class HTTP_WebDAV_Server_Filesystem extends HTTP_WebDAV_Server
// special treatment for litmus compliance test
// reply on its identifier header
// not needed for the test itself but eases debugging
foreach (apache_request_headers() as $key => $value) {
if (stristr($key, "litmus")) {
error_log("Litmus test $value");
header("X-Litmus-reply: ".$value);
}
if (isset($this->_SERVER['HTTP_X_LITMUS'])) {
error_log("Litmus test ".$this->_SERVER['HTTP_X_LITMUS']);
header("X-Litmus-reply: ".$this->_SERVER['HTTP_X_LITMUS']);
}
// set root directory, defaults to webserver document root if not set
@ -135,13 +166,13 @@ class HTTP_WebDAV_Server_Filesystem extends HTTP_WebDAV_Server
$files["files"][] = $this->fileinfo($options["path"]);
// information for contained resources requested?
if (!empty($options["depth"])) { // TODO check for is_dir() first?
if (!empty($options["depth"]) && is_dir($fspath) && $this->_is_readable($fspath)) {
// make sure path ends with '/'
$options["path"] = $this->_slashify($options["path"]);
// try to open directory
$handle = @opendir($fspath);
$handle = opendir($fspath);
if ($handle) {
// ok, now get all its contents
@ -183,15 +214,19 @@ class HTTP_WebDAV_Server_Filesystem extends HTTP_WebDAV_Server
$info["props"][] = $this->mkprop("creationdate", filectime($fspath));
$info["props"][] = $this->mkprop("getlastmodified", filemtime($fspath));
// Microsoft extensions: last access time and 'hidden' status
$info["props"][] = $this->mkprop("lastaccessed", fileatime($fspath));
$info["props"][] = $this->mkprop("ishidden", ('.' === substr(basename($fspath), 0, 1)));
// type and size (caller already made sure that path exists)
if (is_dir($fspath)) {
// directory (WebDAV collection)
$info["props"][] = $this->mkprop("resourcetype", array($this->mkprop('collection', '')));
$info["props"][] = $this->mkprop("resourcetype", "collection");
$info["props"][] = $this->mkprop("getcontenttype", "httpd/unix-directory");
} else {
// plain file (WebDAV resource)
$info["props"][] = $this->mkprop("resourcetype", "");
if (is_readable($fspath)) {
if ($this->_is_readable($fspath)) {
$info["props"][] = $this->mkprop("getcontenttype", $this->_mimetype($fspath));
} else {
$info["props"][] = $this->mkprop("getcontenttype", "application/x-non-readable");
@ -255,6 +290,31 @@ class HTTP_WebDAV_Server_Filesystem extends HTTP_WebDAV_Server
return false;
}
/**
* Check if path is readable by current user
*
* Allow extending classes to overwrite it
*
* @param string $fspath
* @return boolean
*/
function _is_readable($fspath)
{
return is_readable($fspath);
}
/**
* Check if path is writable by current user
*
* Allow extending classes to overwrite it
*
* @param string $fspath
* @return boolean
*/
function _is_writable($fspath)
{
return is_writable($fspath);
}
/**
* try to detect the mime type of a file
@ -264,7 +324,7 @@ class HTTP_WebDAV_Server_Filesystem extends HTTP_WebDAV_Server
*/
function _mimetype($fspath)
{
if (@is_dir($fspath)) {
if (is_dir($fspath)) {
// directories are easy
return "httpd/unix-directory";
} else if (function_exists("mime_content_type")) {
@ -325,12 +385,12 @@ class HTTP_WebDAV_Server_Filesystem extends HTTP_WebDAV_Server
}
/**
* GET method handler
* HEAD method handler
*
* @param array parameter passing array
* @return bool true on success
*/
function GET(&$options)
function HEAD(&$options)
{
// get absolute fs path to requested resource
$fspath = $this->base . $options["path"];
@ -338,11 +398,6 @@ class HTTP_WebDAV_Server_Filesystem extends HTTP_WebDAV_Server
// sanity check
if (!file_exists($fspath)) return false;
// is this a collection?
if (is_dir($fspath)) {
return $this->GetDir($fspath, $options);
}
// detect resource type
$options['mimetype'] = $this->_mimetype($fspath);
@ -355,11 +410,33 @@ class HTTP_WebDAV_Server_Filesystem extends HTTP_WebDAV_Server
// detect resource size
$options['size'] = filesize($fspath);
// no need to check result here, it is handled by the base class
if (!($options['stream'] = fopen($fspath, "r")))
{
return '403 Forbidden';
return true;
}
/**
* GET method handler
*
* @param array parameter passing array
* @return bool true on success
*/
function GET(&$options)
{
// get absolute fs path to requested resource
$fspath = $this->base . $options["path"];
// is this a collection?
if (is_dir($fspath)) {
return $this->GetDir($fspath, $options);
}
// the header output is the same as for HEAD
if (!$this->HEAD($options)) {
return false;
}
// no need to check result here, it is handled by the base class
$options['stream'] = fopen($fspath, "r");
return true;
}
@ -383,12 +460,16 @@ class HTTP_WebDAV_Server_Filesystem extends HTTP_WebDAV_Server
// fixed width directory column format
$format = "%15s %-19s %-s\n";
$handle = @opendir($fspath);
if (!$this->_is_readable($fspath)) {
return false;
}
$handle = opendir($fspath);
if (!$handle) {
return false;
}
echo "<html><head><title>Index of ".htmlspecialchars($options['path'])."</title></head>\n";
echo "<html><head><title>Index of ".htmlspecialchars(urldecode($options['path']))."</title></head>\n";
echo "<h1>Index of ".htmlspecialchars($options['path'])."</h1>\n";
@ -403,7 +484,7 @@ class HTTP_WebDAV_Server_Filesystem extends HTTP_WebDAV_Server
printf($format,
number_format(filesize($fullpath)),
strftime("%Y-%m-%d %H:%M:%S", filemtime($fullpath)),
'<a href="'.$name.'">'.$name.'</a>');
'<a href="'.$name.'">'.urldecode($name).'</a>');
}
}
@ -426,12 +507,23 @@ class HTTP_WebDAV_Server_Filesystem extends HTTP_WebDAV_Server
{
$fspath = $this->base . $options["path"];
if (!@is_dir(dirname($fspath))) {
return "409 Conflict";
$dir = dirname($fspath);
if (!file_exists($dir) || !is_dir($dir)) {
return "409 Conflict"; // TODO right status code for both?
}
$options["new"] = ! file_exists($fspath);
if ($options["new"] && !$this->_is_writable($dir)) {
return "403 Forbidden";
}
if (!$options["new"] && !$this->_is_writable($fspath)) {
return "403 Forbidden";
}
if (!$options["new"] && is_dir($fspath)) {
return "403 Forbidden";
}
$fp = fopen($fspath, "w");
return $fp;
@ -493,7 +585,7 @@ class HTTP_WebDAV_Server_Filesystem extends HTTP_WebDAV_Server
$query = "DELETE FROM {$this->db_prefix}properties
WHERE path LIKE '".$this->_slashify($options["path"])."%'";
mysql_query($query);
System::rm("-rf $path");
System::rm(array("-rf", $path));
} else {
unlink($path);
}
@ -535,10 +627,34 @@ class HTTP_WebDAV_Server_Filesystem extends HTTP_WebDAV_Server
return "502 bad gateway";
}
$source = $this->base .$options["path"];
if (!file_exists($source)) return "404 Not found";
$source = $this->base . $options["path"];
if (!file_exists($source)) {
return "404 Not found";
}
if (is_dir($source)) { // resource is a collection
switch ($options["depth"]) {
case "infinity": // valid
break;
case "0": // valid for COPY only
if ($del) { // MOVE?
return "400 Bad request";
}
break;
case "1": // invalid for both COPY and MOVE
default:
return "400 Bad request";
}
}
$dest = $this->base . $options["dest"];
$destdir = dirname($dest);
if (!file_exists($destdir) || !is_dir($destdir)) {
return "409 Conflict";
}
$new = !file_exists($dest);
$existing_col = false;
@ -568,11 +684,6 @@ class HTTP_WebDAV_Server_Filesystem extends HTTP_WebDAV_Server
}
}
if (is_dir($source) && ($options["depth"] != "infinity")) {
// RFC 2518 Section 9.2, last paragraph
return "400 Bad request";
}
if ($del) {
if (!rename($source, $dest)) {
return "500 Internal server error";
@ -590,7 +701,7 @@ class HTTP_WebDAV_Server_Filesystem extends HTTP_WebDAV_Server
WHERE path = '".$options["path"]."'";
mysql_query($query);
} else {
if (is_dir($source)) {
if (is_dir($source) && $options["depth"] == "infinity") { // no find for depth="0"
$files = System::find($source);
$files = array_reverse($files);
} else {
@ -610,14 +721,19 @@ class HTTP_WebDAV_Server_Filesystem extends HTTP_WebDAV_Server
$destfile = str_replace($source, $dest, $file);
if (is_dir($file)) {
if (!is_dir($destfile)) {
// TODO "mkdir -p" here? (only natively supported by PHP 5)
if (!@mkdir($destfile)) {
if (!file_exists($destfile)) {
if (!$this->_is_writable(dirname($destfile))) {
return "403 Forbidden";
}
if (!mkdir($destfile)) {
return "409 Conflict";
}
} else if (!is_dir($destfile)) {
return "409 Conflict";
}
} else {
if (!@copy($file, $destfile)) {
if (!copy($file, $destfile)) {
return "409 Conflict";
}
}
@ -683,6 +799,7 @@ class HTTP_WebDAV_Server_Filesystem extends HTTP_WebDAV_Server
$fspath = $this->base . $options["path"];
// TODO recursive locks on directories not supported yet
// makes litmus test "32. lock_collection" fail
if (is_dir($fspath) && !empty($options["depth"])) {
return "409 Conflict";
}

View File

@ -0,0 +1,251 @@
<?php // $Id: _parse_lockinfo.php 246152 2007-11-14 10:49:27Z hholzgra $
/*
+----------------------------------------------------------------------+
| Copyright (c) 2002-2007 Christian Stocker, Hartmut Holzgraefe |
| All rights reserved |
| |
| Redistribution and use in source and binary forms, with or without |
| modification, are permitted provided that the following conditions |
| are met: |
| |
| 1. Redistributions of source code must retain the above copyright |
| notice, this list of conditions and the following disclaimer. |
| 2. Redistributions in binary form must reproduce the above copyright |
| notice, this list of conditions and the following disclaimer in |
| the documentation and/or other materials provided with the |
| distribution. |
| 3. The names of the authors may not be used to endorse or promote |
| products derived from this software without specific prior |
| written permission. |
| |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
| FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
| COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
| INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
| BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
| ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| POSSIBILITY OF SUCH DAMAGE. |
+----------------------------------------------------------------------+
*/
/**
* helper class for parsing LOCK request bodies
*
* @package HTTP_WebDAV_Server
* @author Hartmut Holzgraefe <hholzgra@php.net>
* @version @package-version@
*/
class _parse_lockinfo
{
/**
* success state flag
*
* @var bool
* @access public
*/
var $success = false;
/**
* lock type, currently only "write"
*
* @var string
* @access public
*/
var $locktype = "";
/**
* lock scope, "shared" or "exclusive"
*
* @var string
* @access public
*/
var $lockscope = "";
/**
* lock owner information
*
* @var string
* @access public
*/
var $owner = "";
/**
* flag that is set during lock owner read
*
* @var bool
* @access private
*/
var $collect_owner = false;
/**
* constructor
*
* @param string path of stream to read
* @access public
*/
function _parse_lockinfo($path)
{
// we assume success unless problems occur
$this->success = true;
// remember if any input was parsed
$had_input = false;
// open stream
$f_in = fopen($path, "r");
if (!$f_in) {
$this->success = false;
return;
}
// create namespace aware parser
$xml_parser = xml_parser_create_ns("UTF-8", " ");
// set tag and data handlers
xml_set_element_handler($xml_parser,
array(&$this, "_startElement"),
array(&$this, "_endElement"));
xml_set_character_data_handler($xml_parser,
array(&$this, "_data"));
// we want a case sensitive parser
xml_parser_set_option($xml_parser,
XML_OPTION_CASE_FOLDING, false);
// parse input
while ($this->success && !feof($f_in)) {
$line = fgets($f_in);
if (is_string($line)) {
$had_input = true;
$this->success &= xml_parse($xml_parser, $line, false);
}
}
// finish parsing
if ($had_input) {
$this->success &= xml_parse($xml_parser, "", true);
}
// check if required tags where found
$this->success &= !empty($this->locktype);
$this->success &= !empty($this->lockscope);
// free parser resource
xml_parser_free($xml_parser);
// close input stream
fclose($f_in);
}
/**
* tag start handler
*
* @param resource parser
* @param string tag name
* @param array tag attributes
* @return void
* @access private
*/
function _startElement($parser, $name, $attrs)
{
// namespace handling
if (strstr($name, " ")) {
list($ns, $tag) = explode(" ", $name);
} else {
$ns = "";
$tag = $name;
}
if ($this->collect_owner) {
// everything within the <owner> tag needs to be collected
$ns_short = "";
$ns_attr = "";
if ($ns) {
if ($ns == "DAV:") {
$ns_short = "D:";
} else {
$ns_attr = " xmlns='$ns'";
}
}
$this->owner .= "<$ns_short$tag$ns_attr>";
} else if ($ns == "DAV:") {
// parse only the essential tags
switch ($tag) {
case "write":
$this->locktype = $tag;
break;
case "exclusive":
case "shared":
$this->lockscope = $tag;
break;
case "owner":
$this->collect_owner = true;
break;
}
}
}
/**
* data handler
*
* @param resource parser
* @param string data
* @return void
* @access private
*/
function _data($parser, $data)
{
// only the <owner> tag has data content
if ($this->collect_owner) {
$this->owner .= $data;
}
}
/**
* tag end handler
*
* @param resource parser
* @param string tag name
* @return void
* @access private
*/
function _endElement($parser, $name)
{
// namespace handling
if (strstr($name, " ")) {
list($ns, $tag) = explode(" ", $name);
} else {
$ns = "";
$tag = $name;
}
// <owner> finished?
if (($ns == "DAV:") && ($tag == "owner")) {
$this->collect_owner = false;
}
// within <owner> we have to collect everything
if ($this->collect_owner) {
$ns_short = "";
$ns_attr = "";
if ($ns) {
if ($ns == "DAV:") {
$ns_short = "D:";
} else {
$ns_attr = " xmlns='$ns'";
}
}
$this->owner .= "</$ns_short$tag$ns_attr>";
}
}
}
?>

View File

@ -1,24 +1,37 @@
<?php
//
// +----------------------------------------------------------------------+
// | PHP Version 4 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2003 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.02 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available at through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Authors: Hartmut Holzgraefe <hholzgra@php.net> |
// | Christian Stocker <chregu@bitflux.ch> |
// +----------------------------------------------------------------------+
//
// $Id: _parse_propfind.php,v 1.4 2006/10/10 11:53:17 hholzgra Exp $
//
<?php // $Id: _parse_propfind.php 246152 2007-11-14 10:49:27Z hholzgra $
/*
+----------------------------------------------------------------------+
| Copyright (c) 2002-2007 Christian Stocker, Hartmut Holzgraefe |
| All rights reserved |
| |
| Redistribution and use in source and binary forms, with or without |
| modification, are permitted provided that the following conditions |
| are met: |
| |
| 1. Redistributions of source code must retain the above copyright |
| notice, this list of conditions and the following disclaimer. |
| 2. Redistributions in binary form must reproduce the above copyright |
| notice, this list of conditions and the following disclaimer in |
| the documentation and/or other materials provided with the |
| distribution. |
| 3. The names of the authors may not be used to endorse or promote |
| products derived from this software without specific prior |
| written permission. |
| |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
| FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
| COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
| INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
| BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
| ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| POSSIBILITY OF SUCH DAMAGE. |
+----------------------------------------------------------------------+
*/
/**
* helper class for parsing PROPFIND request bodies
@ -183,9 +196,8 @@ class _parse_propfind
// record root tag
if ($this->depth == 0) {
$this->root = array('name' => $tag, 'xmlns' => $ns);
$this->root = array('name' => $tag, 'xmlns' => $ns, 'attrs' => $attrs);
}
// special tags at level 1: <allprop> and <propname>
if ($this->depth == 1) {
$this->use = 'props';
@ -201,27 +213,41 @@ class _parse_propfind
break;
case 'filter':
$this->use = 'filters';
$this->filters['attrs'] = $attrs; // need attrs eg. <filters test="(anyof|alloff)">
break;
default:
$this->use = 'other';
break;
}
}
//echo "$this->depth: use=$this->use $ns:$tag attrs=".array2string($attrs)."\n";
// requested properties are found at level 2
// CalDAV filters can be at deeper levels too and we need the attrs, same for other tags (eg. multiget href's)
if ($this->depth == 2 || $this->use == 'filters' && $this->depth >= 2 || $this->use == 'other') {
if ($this->depth == 2 || $this->use == 'filters' && $this->depth >= 2 || $this->use == 'other' ||
$this->use == 'props' && $this->depth >= 2) {
$prop = array("name" => $tag);
if ($ns)
$prop["xmlns"] = $ns;
if ($this->use != 'props') {
if ($this->use != 'props' || $this->depth > 2) {
$prop['attrs'] = $attrs;
$prop['depth'] = $this->depth;
}
// this can happen if we have allprop and prop in one propfind:
// <allprop /><prop><blah /></prop>, eg. blah is not automatic returned by allprop
if (!is_array($this->{$this->use}) && $this->{$this->use}) $this->{$this->use} = array($this->{$this->use});
$this->{$this->use}[] = $prop;
// collect sub-elements of props in the original props children attribute
// eg. required for CalDAV <calendar-data><expand start="..." end="..."/></calendar-data>
if ($this->use == 'props' && $this->depth > 2)
{
$this->last_prop['children'][$tag] = $prop;
}
else
{
// this can happen if we have allprop and prop in one propfind:
// <allprop /><prop><blah /></prop>, eg. blah is not automatic returned by allprop
if (!is_array($this->{$this->use}) && $this->{$this->use}) $this->{$this->use} = array($this->{$this->use});
$this->{$this->use}[] =& $prop;
$this->last_prop =& $prop;
unset($prop);
}
}
// increment depth count

View File

@ -1,24 +1,38 @@
<?php
//
// +----------------------------------------------------------------------+
// | PHP Version 4 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2003 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.02 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available at through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Authors: Hartmut Holzgraefe <hholzgra@php.net> |
// | Christian Stocker <chregu@bitflux.ch> |
// +----------------------------------------------------------------------+
//
// $Id: _parse_proppatch.php,v 1.6 2006/10/10 11:53:17 hholzgra Exp $
//
<?php // $Id: _parse_proppatch.php 246152 2007-11-14 10:49:27Z hholzgra $
/*
+----------------------------------------------------------------------+
| Copyright (c) 2002-2007 Christian Stocker, Hartmut Holzgraefe |
| All rights reserved |
| |
| Redistribution and use in source and binary forms, with or without |
| modification, are permitted provided that the following conditions |
| are met: |
| |
| 1. Redistributions of source code must retain the above copyright |
| notice, this list of conditions and the following disclaimer. |
| 2. Redistributions in binary form must reproduce the above copyright |
| notice, this list of conditions and the following disclaimer in |
| the documentation and/or other materials provided with the |
| distribution. |
| 3. The names of the authors may not be used to endorse or promote |
| products derived from this software without specific prior |
| written permission. |
| |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
| FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
| COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
| INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
| BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
| ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| POSSIBILITY OF SUCH DAMAGE. |
+----------------------------------------------------------------------+
*/
/**
* helper class for parsing PROPPATCH request bodies

File diff suppressed because it is too large Load Diff

View File

@ -187,6 +187,7 @@ class so_sql_cf extends so_sql
),__LINE__,__FILE__,false,'',$this->app) as $row)
{
$entry =& $entries[$row[$this->extra_id]];
if (!is_array($entry)) $entry = array();
$field = $this->get_cf_field($row[$this->extra_key]);
if ($this->allow_multiple_values && $this->is_multiple($row[$this->extra_key]))
@ -417,10 +418,12 @@ class so_sql_cf extends so_sql
// if string given as criteria --> search in all (or $this->columns_to_search) columns including custom fields
if ($criteria && is_string($criteria))
{
$criteria = $this->search2criteria($criteria,$wildcard,$op,$this->extra_value);
$criteria = $this->search2criteria($criteria,$wildcard,$op);
}
if ($criteria && is_array($criteria))
{
$join .= $this->extra_join;
// check if we search in the custom fields
if (isset($criteria[$this->extra_value]))
{
@ -432,7 +435,6 @@ class so_sql_cf extends so_sql
$this->db->capabilities[egw_db::CAPABILITY_CASE_INSENSITIV_LIKE]. ' ' .
$this->db->quote($wildcard.$criteria[$this->extra_value].$wildcard);
unset($criteria[$this->extra_value]);
$join .= $this->extra_join;
}
// replace ambiguous auto-id with (an exact match of) table_name.autoid
if (isset($criteria[$this->autoinc_id]))
@ -444,15 +446,76 @@ class so_sql_cf extends so_sql
}
unset($criteria[$this->autoinc_id]);
}
// replace ambiguous column with (an exact match of) table_name.column
foreach($criteria as $name => $val)
{
$extra_columns = $this->db->get_table_definitions($app, $this->extra_table);
if(is_string($name) && $extra_columns['fd'][array_search($name, $this->db_cols)])
{
$criteria[] = $this->db->expression($this->table_name,$this->table_name.'.',array(
array_search($name, $this->db_cols) => $val,
));
unset($criteria[$name]);
}
elseif (is_string($name) && $this->is_cf($name))
{
if ($op != 'AND')
{
$name = substr($name, 1);
if (($negate = $criteria[$name][0] === '!'))
{
$val = substr($val,1);
}
$cfcriteria[] = '(' . $this->extra_table.'.'.$this->extra_value . ' ' .($negate ? 'NOT ' : '').
$this->db->capabilities[egw_db::CAPABILITY_CASE_INSENSITIV_LIKE]. ' ' .
$this->db->quote($wildcard.$val.$wildcard) . ' AND ' .
$this->extra_table.'.'.$this->extra_key . ' = ' . $this->db->quote($name) .
')';
unset($criteria[self::CF_PREFIX.$name]);
}
else
{
// criteria operator is AND we remap the criteria to be transformed to filters
$filter[$name] = $val;
unset($criteria[$name]);
}
}
}
if ($cfcriteria && $op =='OR') $criteria[] = implode(' OR ',$cfcriteria);
}
if($only_keys === true) {
// Expand to keys here, so table_name can be prepended below
$only_keys = array_values($this->db_key_cols);
}
// replace ambiguous column with (an exact match of) table_name.column
if(is_array($only_keys))
{
foreach($only_keys as $key => &$col)
{
if(is_numeric($key) && in_array($col, $this->db_cols))
{
$col = $this->table_name .'.'.array_search($col, $this->db_cols).' AS '.$col;
}
}
}
// check if we order by a custom field --> join cf table for given cf and order by it's value
if (strpos($order_by,self::CF_PREFIX) !== false &&
preg_match('/'.self::CF_PREFIX.'([^ ]+) (asc|desc)/i',$order_by,$matches))
if (strpos($order_by,self::CF_PREFIX) !== false)
{
$order_by = str_replace($matches[0],'extra_order.'.$this->extra_value.' IS NULL,extra_order.'.$this->extra_value.' '.$matches[2],$order_by);
$join .= $this->extra_join_order.' AND extra_order.'.$this->extra_key.'='.$this->db->quote($matches[1]);
// fields to order by, as cutomfields may have names with spaces, we examine each order by criteria
$fields2order = explode(',',$order_by);
foreach($fields2order as $k => $v)
{
if (strpos($v,self::CF_PREFIX) !== false)
{
// we found a customfield, so we split that part by space char in order to get Sorting Direction and Fieldname
$buff = explode(' ',trim($v));
$orderDir = array_pop($buff);
$key = trim(implode(' ',$buff));
$order_by = str_replace($v,'extra_order.'.$this->extra_value.' IS NULL,extra_order.'.$this->extra_value.' '.$orderDir,$order_by);
$join .= $this->extra_join_order.' AND extra_order.'.$this->extra_key.'='.$this->db->quote(substr($key,1));
}
}
}
// check if we filter by a custom field
if (is_array($filter))
{
@ -470,6 +533,17 @@ class so_sql_cf extends so_sql
}
unset($filter[$this->autoinc_id]);
}
// replace ambiguous column with (an exact match of) table_name.column
elseif (is_string($name) && $val!=null && in_array($name, $this->db_cols))
{
$extra_columns = $this->db->get_table_definitions($app, $this->extra_table);
if($extra_columns['fd'][array_search($name, $this->db_cols)]) {
$filter[] = $this->db->expression($this->table_name,$this->table_name.'.',array(
array_search($name, $this->db_cols) => $val,
));
unset($filter[$name]);
}
}
elseif (is_string($name) && $this->is_cf($name))
{
if (!empty($val)) // empty -> dont filter
@ -480,13 +554,21 @@ class so_sql_cf extends so_sql
}
else // using egw_db::expression to allow to use array() with possible values or NULL
{
if($this->customfields[$this->get_cf_name($name)]['type'] == 'select' &&
if($this->customfields[$this->get_cf_name($name)]['type'] == 'select' &&
$this->customfields[$this->get_cf_name($name)]['rows'] > 1)
{
// Multi-select - any entry with the filter value selected matches
$sql_filter = str_replace($this->extra_value,'extra_filter.'.
$this->extra_value,$this->db->expression($this->extra_table,array(
"CONCAT(',',{$this->extra_value},',') LIKE '%,$val,%'"
$this->db->concat("','",$this->extra_value,"','").' '.$this->db->capabilities[egw_db::CAPABILITY_CASE_INSENSITIV_LIKE].' '.$this->db->quote('%,'.$val.',%')
))
);
}
elseif ($this->customfields[$this->get_cf_name($name)]['type'] == 'text')
{
$sql_filter = str_replace($this->extra_value,'extra_filter.'.$this->extra_value,
$this->db->expression($this->extra_table,array(
$this->extra_value.' '.$this->db->capabilities[egw_db::CAPABILITY_CASE_INSENSITIV_LIKE].' '.$this->db->quote($wildcard.$val.$wildcard)
))
);
}
@ -495,7 +577,6 @@ class so_sql_cf extends so_sql
$sql_filter = str_replace($this->extra_value,'extra_filter.'.
$this->extra_value,$this->db->expression($this->extra_table,array($this->extra_value => $val)));
}
}
// need to use a LEFT JOIN for negative search or to allow NULL values
$need_left_join = $val[0] === '!' || strpos($sql_filter,'IS NULL') !== false ? ' LEFT ' : '';
@ -534,6 +615,45 @@ class so_sql_cf extends so_sql
return parent::search($criteria,$only_keys,$order_by,$extra_cols,$wildcard,$empty,$op,$start,$filter,$join,$need_full_no_count);
}
/**
* Return criteria array for a given search pattern
* Reimplemented to search custom fields
*
* @param string $pattern search pattern incl. * or ? as wildcard, if no wildcards used we append and prepend one!
* @param string &$wildcard='' on return wildcard char to use, if pattern does not already contain wildcards!
* @param string &$op='AND' on return boolean operation to use, if pattern does not start with ! we use OR else AND
* @param string $extra_col=null extra column to search
* @param array $search_cols=array() List of columns to search. If not provided, all columns in $this->db_cols will be considered
* @return array or column => value pairs
*/
public function search2criteria($pattern,&$wildcard='',&$op='AND',$extra_col=null, $search_cols = array())
{
// This function can get called multiple times. Make sure it doesn't re-process.
if (empty($pattern) || is_array($pattern)) return $pattern;
if(strpos($pattern, 'CAST(COALESCE(') !== false)
{
return $pattern;
}
$pattern = trim($pattern);
$filter = array();
if(!$search_cols)
{
$search_cols = $this->get_default_search_columns();
}
// Add in custom field column, if it is not already there
if(!in_array($this->extra_table.'.'.$this->extra_value, $search_cols))
{
$search_cols[] = $this->extra_table.'.'.$this->extra_value;
}
// Let parent deal with the normal stuff
$criteria = parent::search2criteria($pattern, $wildcard, $op, $extra_col, $search_cols);
return $criteria;
}
/**
* Function to test if $field is a custom field: check for the prefix
*

View File

@ -1,6 +1,6 @@
<?php
/**
* eGroupWare - GroupDAV access
* EGroupware - CalDAV/CardDAV/GroupDAV server
*
* Using the PEAR HTTP/WebDAV/Server class (which need to be installed!)
*
@ -9,10 +9,19 @@
* @package api
* @subpackage groupdav
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2007-10 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2007-11 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @version $Id$
*/
// switching off output compression for Lighttpd and HTTPS, as it makes problems with TB Lightning
if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' &&
strpos($_SERVER['SERVER_SOFTWARE'],'lighttpd/1.4') === 0 &&
strpos($_SERVER['HTTP_USER_AGENT'],'Lightning') !== false)
{
ini_set('zlib.output_compression',0);
}
//error_log("HTTPS='$_SERVER[HTTPS]', SERVER_SOFTWARE='$_SERVER[SERVER_SOFTWARE]', HTTP_USER_AGENT='$_SERVER[HTTP_USER_AGENT]', REQUEST_METHOD='$_SERVER[REQUEST_METHOD]' --> zlib.output_compression=".ini_get('zlib.output_compression'));
$starttime = microtime(true);
$GLOBALS['egw_info'] = array(
@ -35,4 +44,4 @@ $headertime = microtime(true);
$groupdav = new groupdav();
$groupdav->ServeRequest();
//error_log(sprintf("GroupDAV %s request took %5.3f s (header include took %5.3f s)",$_SERVER['REQUEST_METHOD'],microtime(true)-$starttime,$headertime-$starttime));
//error_log(sprintf('GroupDAV %s: status "%s", took %5.3f s'.($headertime?' (header include took %5.3f s)':''),$_SERVER['REQUEST_METHOD'].($_SERVER['REQUEST_METHOD']=='REPORT'?' '.$groupdav->propfind_options['root']['name']:'').' '.$_SERVER['PATH_INFO'],$groupdav->_http_status,microtime(true)-$starttime,$headertime-$starttime));

View File

@ -1,12 +1,12 @@
<?php
/**
* InfoLog - Business object
* EGroupware - InfoLog - Business object
*
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @author Joerg Lehrke <jlehrke@noc.de>
* @package infolog
* @copyright (c) 2003-10 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2003-12 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
@ -28,8 +28,6 @@ class infolog_bo
var $so;
var $vfs;
var $vfs_basedir='/infolog';
var $link_pathes = array();
var $send_file_ips = array();
/**
* Set Logging
*
@ -54,13 +52,19 @@ class infolog_bo
*
* @var array
*/
var $timestamps = array('info_startdate','info_enddate','info_datemodified','info_datecompleted');
var $timestamps = array('info_startdate','info_enddate','info_datemodified','info_datecompleted','info_created');
/**
* fields the responsible user can change
*
* @var array
*/
var $responsible_edit=array('info_status','info_percent','info_datecompleted');
/**
* fields a user may exclude from copy, if an entry is copied, the ones below are excluded by default.
*
* @var array
*/
var $copy_excludefields = array('info_id', 'info_uid', 'info_etag', 'caldav_name', 'info_created', 'info_creator', 'info_datemodified', 'info_modifier');
/**
* implicit ACL rights of the responsible user: read or edit
*
@ -117,17 +121,21 @@ class infolog_bo
'responsible-open-today' => 'responsible open',
'responsible-open-overdue' => 'responsible overdue',
'responsible-upcoming' => 'responsible upcoming',
'responsible-open-upcoming'=> 'responsible open and upcoming',
'delegated' => 'delegated',
'delegated-open-today' => 'delegated open',
'delegated-open-overdue' => 'delegated overdue',
'delegated-upcoming' => 'delegated upcomming',
'delegated-open-upcoming' => 'delegated open and upcoming',
'own' => 'own',
'own-open-today' => 'own open',
'own-open-overdue' => 'own overdue',
'own-upcoming' => 'own upcoming',
'open-today' => 'open',
'own-open-upcoming' => 'own open and upcoming',
'open-today' => 'open(status)',
'open-overdue' => 'overdue',
'upcoming' => 'upcoming',
'open-upcoming' => 'open and upcoming',
'bydate' => 'startdate',
);
@ -180,9 +188,6 @@ class infolog_bo
);
if (($config_data = config::read('infolog')))
{
$this->link_pathes = $config_data['link_pathes'];
$this->send_file_ips = $config_data['send_file_ips'];
if (isset($config_data['status']) && is_array($config_data['status']))
{
foreach($config_data['status'] as $key => $data)
@ -227,6 +232,10 @@ class infolog_bo
{
$this->responsible_edit = array_merge($this->responsible_edit,$config_data['responsible_edit']);
}
if (is_array($config_data['copy_excludefields']))
{
$this->copy_excludefields = array_merge($this->copy_excludefields,$config_data['copy_excludefields']);
}
if ($config_data['implicit_rights'] == 'edit')
{
$this->implicit_rights = 'edit';
@ -286,53 +295,73 @@ class infolog_bo
* @param int|array $info data or info_id of infolog entry to check
* @param int $required_rights EGW_ACL_{READ|EDIT|ADD|DELETE}
* @param int $other uid to check (if info==0) or 0 to check against $this->user
* @param int $user=null user whos rights to check, default current user
* @return boolean
*/
function check_access($info,$required_rights,$other=0)
function check_access($info,$required_rights,$other=0,$user=null)
{
static $cache = array();
if (!$info)
{
$owner = $other ? $other : $this->user;
$grants = $this->grants[$owner];
return $grants & $required_rights;
}
$info_id = is_array($info) ? $info['info_id'] : $info;
if (isset($cache[$info_id][$required_rights]))
if (!$user) $user = $this->user;
if ($user == $this->user)
{
return $cache[$info_id][$required_rights];
$grants = $this->grants;
if ($info_id) $access =& $cache[$info_id][$required_rights]; // we only cache the current user!
}
// handle delete for the various history modes
if ($this->history)
else
{
if (!is_array($info) && !($info = $this->so->read($info_id))) return false;
$grants = $GLOBALS['egw']->acl->get_grants('infolog',$this->group_owners ? $this->group_owners : true,$user);
}
if (!$info)
{
$owner = $other ? $other : $user;
$grant = $grants[$owner];
return $grant & $required_rights;
}
if ($info['info_status'] == 'deleted' &&
($required_rights == EGW_ACL_EDIT || // no edit rights for deleted entries
$required_rights == EGW_ACL_ADD || // no add rights for deleted entries
$required_rights == EGW_ACL_DELETE && ($this->history == 'history_no_delete' || // no delete at all!
$this->history == 'history_admin_delete' && !isset($GLOBALS['egw_info']['user']['apps']['admin'])))) // delete only for admins
{
return $cache[$info_id][$required_rights] = false;
}
if ($required_rights == EGW_ACL_UNDELETE)
{
if ($info['info_status'] != 'deleted')
{
return $cache[$info_id][$required_rights] = false; // can only undelete deleted items
}
// undelete requires edit rights
return $cache[$info_id][$required_rights] = $this->so->check_access( $info,EGW_ACL_EDIT,$this->implicit_rights == 'edit' );
}
}
elseif ($required_rights == EGW_ACL_UNDELETE)
if (!isset($access))
{
return $cache[$info_id][$required_rights] = false;
// handle delete for the various history modes
if ($this->history)
{
if (!is_array($info) && !($info = $this->so->read(array('info_id' => $info_id)))) return false;
if ($info['info_status'] == 'deleted' &&
($required_rights == EGW_ACL_EDIT || // no edit rights for deleted entries
$required_rights == EGW_ACL_ADD || // no add rights for deleted entries
$required_rights == EGW_ACL_DELETE && ($this->history == 'history_no_delete' || // no delete at all!
$this->history == 'history_admin_delete' && (!isset($GLOBALS['egw_info']['user']['apps']['admin']) || $user!=$this->user)))) // delete only for admins
{
$access = false;
}
elseif ($required_rights == EGW_ACL_UNDELETE)
{
if ($info['info_status'] != 'deleted')
{
$access = false; // can only undelete deleted items
}
else
{
// undelete requires edit rights
$access = $this->so->check_access( $info,EGW_ACL_EDIT,$this->implicit_rights == 'edit',$grants,$user );
}
}
}
elseif ($required_rights == EGW_ACL_UNDELETE)
{
$access = false;
}
if (!isset($access))
{
$access = $this->so->check_access( $info,$required_rights,$this->implicit_rights == 'edit',$grants,$user );
}
}
return $cache[$info_id][$required_rights] = $this->so->check_access( $info,$required_rights,$this->implicit_rights == 'edit' );
// else $cached = ' (from cache)';
// error_log(__METHOD__."($info_id,$required_rights,$other,$user) returning$cached ".array2string($access));
return $access;
}
/**
@ -517,7 +546,7 @@ class infolog_bo
/**
* Read an infolog entry specified by $info_id
*
* @param int|array $info_id integer id or array with key 'info_id' of the entry to read
* @param int|array $info_id integer id or array with id's or array with column=>value pairs of the entry to read
* @param boolean $run_link_id2from=true should link_id2from run, default yes,
* need to be set to false if called from link-title to prevent an infinit recursion
* @param string $date_format='ts' date-formats: 'ts'=timestamp, 'server'=timestamp in server-time,
@ -527,9 +556,17 @@ class infolog_bo
*/
function &read($info_id,$run_link_id2from=true,$date_format='ts')
{
if (is_array($info_id))
//error_log(__METHOD__.'('.array2string($info_id).', '.array2string($run_link_id2from).", '$date_format') ".function_backtrace());
if (is_scalar($info_id) || isset($info_id[count($info_id)-1]))
{
$info_id = isset($info_id['info_id']) ? $info_id['info_id'] : $info_id[0];
if (is_scalar($info_id) && !is_numeric($info_id))
{
$info_id = array('info_uid' => $info_id);
}
else
{
$info_id = array('info_id' => $info_id);
}
}
if (($data = $this->so->read($info_id)) === False)
@ -574,19 +611,20 @@ class infolog_bo
* @param int|array $info_id int id
* @param boolean $delete_children should the children be deleted
* @param int|boolean $new_parent parent to use for not deleted children if > 0
* @param boolean $skip_notification Do not send notification of delete
* @return boolean True if delete was successful, False otherwise ($info_id does not exist or no rights)
*/
function delete($info_id,$delete_children=False,$new_parent=False)
function delete($info_id,$delete_children=False,$new_parent=False, $skip_notification=False)
{
if (is_array($info_id))
{
$info_id = (int)(isset($info_id[0]) ? $info_id[0] : (isset($info_id['info_id']) ? $info_id['info_id'] : $info_id['info_id']));
}
if ($this->so->read($info_id) === False)
if (($info = $this->so->read(array('info_id' => $info_id), true, 'server')) === False)
{
return False;
}
if (!$this->check_access($info_id,EGW_ACL_DELETE))
if (!$this->check_access($info,EGW_ACL_DELETE))
{
return False;
}
@ -597,7 +635,7 @@ class infolog_bo
{
if ($delete_children && $this->so->grants[$owner] & EGW_ACL_DELETE)
{
$this->delete($id,$delete_children,$new_parent); // call ourself recursive to delete the child
$this->delete($id,$delete_children,$new_parent,$skip_notification); // call ourself recursive to delete the child
}
else // dont delete or no rights to delete the child --> re-parent it
{
@ -608,8 +646,6 @@ class infolog_bo
}
}
}
if (!($info = $this->read($info_id, true, 'server'))) return false; // should not happen
$deleted = $info;
$deleted['info_status'] = 'deleted';
$deleted['info_datemodified'] = time();
@ -622,7 +658,7 @@ class infolog_bo
$this->so->write($deleted);
egw_link::unlink(0,'infolog',$info_id,'','!file'); // keep the file attachments, only delete the rest
egw_link::unlink(0,'infolog',$info_id,'','!file','',true); // keep the file attachments, hide the rest
}
else
{
@ -635,11 +671,14 @@ class infolog_bo
$GLOBALS['egw']->contenthistory->updateTimeStamp('infolog_'.$info['info_type'], $info_id, 'delete', time());
// send email notifications and do the history logging
if (!is_object($this->tracking))
if(!$skip_notification)
{
$this->tracking = new infolog_tracking($this);
if (!is_object($this->tracking))
{
$this->tracking = new infolog_tracking($this);
}
$this->tracking->track($deleted,$info,$this->user,true);
}
$this->tracking->track($deleted,$info,$this->user,true);
}
return True;
}
@ -654,27 +693,35 @@ class infolog_bo
* @param boolean $touch_modified=true touch the modification data and sets the modiefier's user-id
* @param boolean $user2server=true conversion between user- and server-time necessary
* @param boolean $skip_notification=false true = do NOT send notification, false (default) = send notifications
* @param boolean $throw_exception=false Throw an exception (if required fields are not set)
* @param string $purge_cfs=null null=dont, 'ical'=only iCal X-properties (cfs name starting with "#"), 'all'=all cfs
*
* @return int/boolean info_id on a successfull write or false
* @return int|boolean info_id on a successfull write or false
*/
function write(&$values, $check_defaults=true, $touch_modified=true, $user2server=true, $skip_notification=false)
function write(&$values_in, $check_defaults=true, $touch_modified=true, $user2server=true,
$skip_notification=false, $throw_exception=false, $purge_cfs=null)
{
$values = $values_in;
//echo "boinfolog::write()values="; _debug_array($values);
if (!$values['info_id'] && !$this->check_access(0,EGW_ACL_EDIT,$values['info_owner']) &&
!$this->check_access(0,EGW_ACL_ADD,$values['info_owner']))
{
return false;
}
// we need to get the old values to update the links in customfields and for the tracking
if ($values['info_id'])
{
$old = $this->read($values['info_id'], false, 'server');
}
if (($status_only = $values['info_id'] && !$this->check_access($values['info_id'],EGW_ACL_EDIT)))
{
if (!isset($values['info_responsible']))
{
if (!($values_read = $this->read($values['info_id']))) return false;
$responsible =& $values_read['info_responsible'];
$responsible = $old['info_responsible'];
}
else
{
$responsible =& $values['info_responsible'];
$responsible = $values['info_responsible'];
}
if (!($status_only = in_array($this->user, (array)$responsible))) // responsible has implicit right to change status
{
@ -695,14 +742,12 @@ class infolog_bo
$set_completed = !$values['info_datecompleted'] && // set date completed of finished job, only if its not already set
(in_array($values['info_status'],array('done','billed','cancelled')) || (int)$values['info_percent'] == 100);
$backup_values = $values; // to return the full values
$values = array(
'info_id' => $values['info_id'],
'info_datemodified' => $values['info_datemodified'],
);
$values = $old;
// only overwrite explicitly allowed fields
$values['info_datemodified'] = $values_in['info_datemodified'];
foreach ($this->responsible_edit as $name)
{
if (isset($backup_values[$name])) $values[$name] = $backup_values[$name];
if (isset($values_in[$name])) $values[$name] = $values_in[$name];
}
if ($set_completed)
{
@ -746,7 +791,7 @@ class infolog_bo
$status = 'done';
if (isset($values['info_type'])) {
if (isset($this->status[$values['info_type']]['done'])) {
$status = 'done';
$status = 'done';
} elseif (isset($this->status[$values['info_type']]['billed'])) {
$status = 'billed';
} elseif (isset($this->status[$values['info_type']]['cancelled'])) {
@ -766,13 +811,35 @@ class infolog_bo
{
$values['info_subject'] = $this->subject_from_des($values['info_des']);
}
// Check required custom fields
if($throw_exception) {
$custom = config::get_customfields('infolog');
foreach($custom as $c_name => $c_field)
{
if($c_field['type2']) $type2 = explode(',',$c_field['type2']);
if($c_field['needed'] && (!$c_field['type2'] || $c_field['type2'] && in_array($values['info_type'],$type2)))
{
// Required custom field
if(!$values['#'.$c_name])
{
throw new egw_exception_wrong_userinput(lang('For infolog type %1, %2 is required',lang($values['info_type']),$c_field['label']));
}
}
}
}
}
if (isset($this->group_owners[$values['info_type']]))
{
$values['info_owner'] = $this->group_owners[$values['info_type']];
if (!($this->grants[$this->group_owners[$values['info_type']]] & EGW_ACL_EDIT))
{
if (!$this->check_access($values['info_id'],EGW_ACL_EDIT)) return false; // no edit rights from the group-owner and no implicit rights (delegated and sufficient rights)
if (!$this->check_access($values['info_id'],EGW_ACL_EDIT) ||
!$values['info_id'] && !$this->check_access($values,EGW_ACL_ADD)
)
{
return false; // no edit rights from the group-owner and no implicit rights (delegated and sufficient rights)
}
}
}
elseif (!$values['info_id'] && !$values['info_owner'] || $GLOBALS['egw']->accounts->get_type($values['info_owner']) == 'g')
@ -785,8 +852,6 @@ class infolog_bo
$values['info_from'] = $this->link_id2from($values);
}
if ($status_only && !$undelete) $values = array_merge($backup_values,$values);
$to_write = $values;
if ($user2server)
{
@ -826,20 +891,22 @@ class infolog_bo
}
if ($touch_modified || !$values['info_modifier'])
{
$values['info_modifier'] = $this->so->user;
$to_write['info_modifier'] = $this->so->user;
$values['info_modifier'] = $to_write['info_modifier'] = $this->so->user;
}
// set created and creator for new entries
if (!$values['info_id'])
{
$values['info_created'] = $this->user_time_now;
$to_write['info_created'] = $this->now;
$values['info_creator'] = $to_write['info_creator'] = $this->so->user;
}
//_debug_array($values);
// error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n".array2string($values)."\n",3,'/tmp/infolog');
// we need to get the old values to update the links in customfields and for the tracking
if ($values['info_id'])
if (($info_id = $this->so->write($to_write, $check_modified, $purge_cfs)))
{
$old = $this->read($values['info_id'], false, 'server');
}
if (($info_id = $this->so->write($to_write,$check_modified)))
{
if (!isset($values['info_type']) || $status_only)
if (!isset($values['info_type']) || $status_only || empty($values['caldav_url']))
{
$values = $this->read($info_id, true, 'server');
}
@ -871,6 +938,12 @@ class infolog_bo
// create (and remove) links in custom fields
customfields_widget::update_customfield_links('infolog',$values,$old,'info_id');
// Check for restore of deleted entry, restore held links
if($old['info_status'] == 'deleted' && $values['info_status'] != 'deleted')
{
egw_link::restore('infolog', $info_id);
}
// notify the link-class about the update, as other apps may be subscribt to it
egw_link::notify_update('infolog',$info_id,$values);
@ -888,15 +961,23 @@ class infolog_bo
$values = array_merge($values,$missing_fields);
}
// Add keys missing in the $to_write array
if ($missing_fields = array_diff_key($values,$to_write))
if (($missing_fields = array_diff_key($values,$to_write)))
{
$to_write = array_merge($to_write,$missing_fields);
}
$this->tracking->track($to_write,$old,$this->user,$values['info_status'] == 'deleted' || $old['info_status'] == 'deleted',
null,$skip_notification);
}
if ($info_from_set) $values['info_from'] = '';
if ($info_from_set) $values['info_from'] = '';
// Change new values back to user time before sending them back
if($user2server)
{
$this->time2time($values);
}
// merge changes (keeping extra values from the UI)
$values_in = array_merge($values_in,$values);
}
return $info_id;
}
@ -926,7 +1007,13 @@ class infolog_bo
*/
function &search(&$query)
{
//echo "<p>boinfolog::search(".print_r($query,True).")</p>\n";
//error_log(__METHOD__.'('.array2string($query).')');
if($query['filter'] == 'bydate')
{
if (is_int($query['startdate'])) $query['col_filter'][] = 'info_startdate >= '.$GLOBALS['egw']->db->quote($query['startdate']);
if (is_int($query['enddate'])) $query['col_filter'][] = 'info_startdate <= '.$GLOBALS['egw']->db->quote($query['enddate']+(60*60*24)-1);
}
if (!isset($query['date_format']) || $query['date_format'] != 'server')
{
if (isset($query['col_filter']))
@ -983,6 +1070,31 @@ class infolog_bo
return $ret;
}
/**
* Query ctag for infolog
*
* @param array $filter=array('filter'=>'own','info_type'=>'task')
* @return string
*/
public function getctag(array $filter=array('filter'=>'own','info_type'=>'task'))
{
$filter += array(
'order' => 'info_datemodified',
'sort' => 'DESC',
'date_format' => 'server',
'start' => 0,
'num_rows' => 1,
);
$result =& $this->search($filter);
if (empty($result)) return 'EGw-empty-wGE';
$entry = array_shift($result);
return $entry['info_datemodified'];
}
/**
* imports a mail identified by uid as infolog
*
@ -1017,7 +1129,7 @@ class infolog_bo
'info_addr' => implode(', ',$email),
'info_subject' => $_subject,
'info_des' => $_message,
'info_startdate' => $_date,
'info_startdate' => egw_time::server2user($_date),
'info_status' => $status,
'info_priority' => 1,
'info_percent' => $status == 'done' ? 100 : 0,
@ -1146,15 +1258,17 @@ class infolog_bo
}
/**
* Check access to the projects file store
* Check access to the file store
*
* @param int|array $id id of entry or entry array
* @param int $check EGW_ACL_READ for read and EGW_ACL_EDIT for write or delete access
* @param string $rel_path=null currently not used in InfoLog
* @param int $user=null for which user to check, default current user
* @return boolean true if access is granted or false otherwise
*/
function file_access($id,$check,$rel_path=null)
function file_access($id,$check,$rel_path=null,$user=null)
{
return $this->check_access($id,$check);
return $this->check_access($id,$check,0,$user);
}
/**
@ -1222,6 +1336,8 @@ class infolog_bo
$title = ($do_events?common::formattime($start->format('H'),$start->format('i')).' ':'').
$info['info_subject'];
$view = egw_link::view('infolog',$info['info_id']);
$edit = egw_link::edit('infolog',$info['info_id'], $size);
$edit['size'] = $size;
$content=array();
foreach ($icons = array(
$info['info_type'] => 'infolog',
@ -1238,6 +1354,7 @@ class infolog_bo
'endtime' => ($info['info_enddate'] ? $info['info_enddate'] : $info['info_startdate']),
'title' => $title,
'view' => $view,
'edit' => $edit,
'icons' => $icons,
'content' => $content
);
@ -1424,24 +1541,27 @@ class infolog_bo
if (is_null($this->tracking) || $this->tracking->user != $user)
{
require_once(EGW_INCLUDE_ROOT.'/infolog/inc/class.infolog_tracking.inc.php');
$this->tracking = new infolog_tracking($this);
}
switch($pref)
{
case 'notify_due_responsible':
$info['prefix'] = lang('Due %1',$this->enums['type'][$info['info_type']]);
$info['message'] = lang('%1 you are responsible for is due at %2',$this->enums['type'][$info['info_type']],
$this->tracking->datetime($info['info_enddate'],false));
break;
case 'notify_due_delegated':
$info['prefix'] = lang('Due %1',$this->enums['type'][$info['info_type']]);
$info['message'] = lang('%1 you delegated is due at %2',$this->enums['type'][$info['info_type']],
$this->tracking->datetime($info['info_enddate'],false));
break;
case 'notify_start_responsible':
$info['prefix'] = lang('Starting %1',$this->enums['type'][$info['info_type']]);
$info['message'] = lang('%1 you are responsible for is starting at %2',$this->enums['type'][$info['info_type']],
$this->tracking->datetime($info['info_startdate'],null));
break;
case 'notify_start_delegated':
$info['prefix'] = lang('Starting %1',$this->enums['type'][$info['info_type']]);
$info['message'] = lang('%1 you delegated is starting at %2',$this->enums['type'][$info['info_type']],
$this->tracking->datetime($info['info_startdate'],null));
break;

View File

@ -0,0 +1,214 @@
<?php
/**
* InfoLog - Datasource for ProjektManager
*
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @package infolog
* @subpackage projectmanager
* @copyright (c) 2005-8 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
include_once(EGW_INCLUDE_ROOT.'/projectmanager/inc/class.datasource.inc.php');
/**
* DataSource for InfoLog
*
* The InfoLog datasource set's only real start- and endtimes, plus planned and used time and
* the responsible user as resources (not always the owner too!).
* The read method of the extended datasource class sets the planned start- and endtime:
* - planned start from the end of a start constrain
* - planned end from the planned time and a start-time
* - planned start and end from the "real" values
*/
class infolog_datasource extends datasource
{
/**
* Reference to infolog_bo
*
* @var infolog_bo
*/
var $infolog_bo;
/**
* Constructor
*/
function __construct()
{
parent::__construct('infolog');
$this->valid = PM_COMPLETION|PM_PLANNED_START|PM_PLANNED_END|PM_REAL_END|PM_PLANNED_TIME|PM_REPLANNED_TIME|PM_USED_TIME|PM_RESOURCES;
// we use $GLOBALS['infolog_bo'] as an already running instance might be availible there
if (!is_object($GLOBALS['infolog_bo']))
{
$GLOBALS['infolog_bo'] = new infolog_bo();
}
$this->infolog_bo =& $GLOBALS['infolog_bo'];
}
/**
* 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)
{
if (!is_array($data_id))
{
$data =& $this->infolog_bo->read((int) $data_id);
if (!is_array($data)) return false;
}
else
{
$data =& $data_id;
}
return array(
'pe_title' => $this->infolog_bo->link_title($data),
'pe_completion' => $data['info_percent'],
'pe_planned_start'=> $data['info_startdate'] ? $data['info_startdate'] : null,
'pe_planned_end' => $data['info_enddate'] ? $data['info_enddate'] : null,
'pe_real_end' => $data['info_datecompleted'] ? $data['info_datecompleted'] : null,
'pe_planned_time' => $data['info_planned_time'],
'pe_replanned_time' => $data['info_replanned_time'],
'pe_used_time' => $data['info_used_time'],
'pe_resources' => count($data['info_responsible']) ? $data['info_responsible'] : array($data['info_owner']),
'pe_details' => $data['info_des'] ? nl2br($data['info_des']) : '',
'pl_id' => $data['pl_id'],
'pe_unitprice' => $data['info_price'],
'pe_planned_quantity' => $data['info_planned_time'] / 60,
'pe_planned_budget' => $data['info_planned_time'] / 60 * $data['info_price'],
'pe_used_quantity' => $data['info_used_time'] / 60,
'pe_used_budget' => $data['info_used_time'] / 60 * $data['info_price'],
);
}
/**
* Copy the datasource of a projectelement (InfoLog entry) and re-link it with project $target
*
* @param array $element source project element representing an InfoLog entry, $element['pe_app_id'] = info_id
* @param int $target target project id
* @param array $target_data=null data of target-project, atm not used by the infolog datasource
* @return array/boolean array(info_id,link_id) on success, false otherwise
*/
function copy($element,$target,$extra=null)
{
$info =& $this->infolog_bo->read((int) $element['pe_app_id']);
if (!is_array($info)) return false;
// unsetting info_link_id and evtl. info_from
if ($info['info_link_id'])
{
$this->infolog_bo->link_id2from($info); // unsets info_from and sets info_link_target
unset($info['info_link_id']);
}
// we need to unset a view fields, to get a new entry
foreach(array('info_id','info_owner','info_modified','info_modifierer') as $key)
{
unset($info[$key]);
}
if(!($info['info_id'] = $this->infolog_bo->write($info))) return false;
// link the new infolog against the project and setting info_link_id and evtl. info_from
$info['info_link_id'] = egw_link::link('projectmanager',$target,'infolog',$info['info_id'],$element['pe_remark'],0,0,1);
if (!$info['info_from'])
{
$info['info_from'] = egw_link::title('projectmanager',$target);
}
if ($info['info_status'] == 'template')
{
$info['info_status'] = $this->infolog_bo->activate($info);
}
$this->infolog_bo->write($info);
// creating again all links, beside the one to the source-project
foreach(egw_link::get_links('infolog',$element['pe_app_id']) as $link)
{
if ($link['app'] == 'projectmanager' && $link['id'] == $element['pm_id'] || // ignoring the source project
$link['app'] == egw_link::VFS_APPNAME) // ignoring files attachments for now
{
continue;
}
egw_link::link('infolog',$info['info_id'],$link['app'],$link['id'],$link['remark']);
}
$ret = array($info['info_id'],$info['info_link_id']);
// if we have a parent set, return our callback to modify the parent id, after all entries are copied
if ($info['info_id_parent'])
{
$ret[] = array($this,'copy_callback'); // callback
$ret[] = array($info['info_id'],$info['info_id_parent']); // $param
}
return $ret;
}
/**
* Callback called after copying of all datasource, used to:
* - fix parent id's
*
* @param array $param array($info_id,$info_id_parent)
* @param array $apps_copied array('infolog' => array($old_info_id => $new_info_id))
*/
public function copy_callback(array $param, array $apps_copied)
{
//error_log(__METHOD__."(".array2string($param).', '.array2string($apps_copied).')');
list($info_id,$parent_id) = $param;
if (isset($apps_copied['infolog'][$parent_id]) && ($info = $this->infolog_bo->read($info_id)))
{
$info['info_id_parent'] = $apps_copied['infolog'][$parent_id];
$this->infolog_bo->write($info,false,true,true,true); // no default and no notification
}
}
/**
* Delete the datasource of a project element
*
* @param int $id
* @return boolean true on success, false on error
*/
function delete($id)
{
if (!is_object($GLOBALS['infolog_bo']))
{
include_once(EGW_INCLUDE_ROOT.'/infolog/inc/class.infolog_bo.inc.php');
$GLOBALS['infolog_bo'] = new infolog_bo();
}
// dont delete infolog, which are linked to other elements, but their project
if (count(egw_link::get_links('infolog',$id)) > 1)
{
return false;
}
return $this->infolog_bo->delete($id);
}
/**
* Change the status of an infolog entry according to the project status
*
* @param int $id
* @param string $status
* @return boolean true if status changed, false otherwise
*/
function change_status($id,$status)
{
//error_log("datasource_infolog::change_status($id,$status)");
if (($info = $this->infolog_bo->read($id)) && $this->infolog_bo->check_access($info,EGW_ACL_EDIT))
{
if ($status == 'active' && in_array($info['info_status'],array('template','nonactive','archive')))
{
$status = $this->infolog_bo->activate($info);
}
if($info['info_status'] != $status && isset($this->infolog_bo->status[$info['info_type']][$status]))
{
//error_log("datasource_infolog::change_status($id,$status) setting status from ".$info['info_status']);
$info['info_status'] = $status;
return $this->infolog_bo->write($info) !== false;
}
}
return false;
}
}

View File

@ -0,0 +1,157 @@
<?php
/**
* eGroupWare - Infolog - importexport
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package infolog
* @subpackage importexport
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright Nathan Gray
* @version $Id$
*/
/**
* class infolog_egw_record
*
* compability layer for iface_egw_record needet for importexport
*/
class infolog_egw_record implements importexport_iface_egw_record
{
private $identifier = '';
private $record = array();
private $bo;
// Used in conversions
static $types = array(
'select-account' => array('info_owner','info_responsible','modifier'),
'date-time' => array('info_datecompleted', 'info_datemodified','created','last_event','next_event'),
'date' => array('info_startdate', 'info_enddate'),
'select-cat' => array('info_cat', 'cat_id'),
'links' => array('info_link_id'),
);
/**
* constructor
* reads record from backend if identifier is given.
*
* @param string $_identifier
*/
public function __construct( $_identifier='' ){
$this->identifier = $_identifier;
$this->bo = new infolog_bo();
if($_identifier) {
$this->record = $this->bo->read($this->identifier);
}
}
/**
* magic method to set attributes of record
*
* @param string $_attribute_name
*/
public function __get($_attribute_name) {
return $this->record[$_attribute_name];
}
/**
* magig method to set attributes of record
*
* @param string $_attribute_name
* @param data $data
*/
public function __set($_attribute_name, $data) {
$this->record[$_attribute_name] = $data;
}
/**
* converts this object to array.
* @abstract We need such a function cause PHP5
* dosn't allow objects do define it's own casts :-(
* once PHP can deal with object casts we will change to them!
*
* @return array complete record as associative array
*/
public function get_record_array() {
return $this->record;
}
/**
* gets title of record
*
*@return string tiltle
*/
public function get_title() {
if (empty($this->record)) {
$this->get_record();
}
return $this->record['title'];
}
/**
* sets complete record from associative array
*
* @todo add some checks
* @return void
*/
public function set_record(array $_record){
$this->record = $_record;
}
/**
* gets identifier of this record
*
* @return string identifier of current record
*/
public function get_identifier() {
return $this->identifier;
}
/**
* saves record into backend
*
* @return string identifier
*/
public function save ( $_dst_identifier ) {
}
/**
* copys current record to record identified by $_dst_identifier
*
* @param string $_dst_identifier
* @return string dst_identifier
*/
public function copy ( $_dst_identifier ) {
}
/**
* moves current record to record identified by $_dst_identifier
* $this will become moved record
*
* @param string $_dst_identifier
* @return string dst_identifier
*/
public function move ( $_dst_identifier ) {
}
/**
* delets current record from backend
*
*/
public function delete () {
}
/**
* destructor
*
*/
public function __destruct() {
unset ($this->bo);
}
} // end of egw_addressbook_record
?>

View File

@ -0,0 +1,133 @@
<?php
/**
* eGroupWare
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package infolog
* @subpackage importexport
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright Nathan Gray
* @version $Id$
*/
/**
* export plugin of infolog
*/
class infolog_export_csv implements importexport_iface_export_plugin {
/**
* Exports records as defined in $_definition
*
* @param egw_record $_definition
*/
public function export( $_stream, importexport_definition $_definition) {
$options = $_definition->plugin_options;
$bo = new infolog_bo();
$selection = array();
$query = array();
$export_object = new importexport_export_csv($_stream, (array)$options);
$export_object->set_mapping($options['mapping']);
// do we need to query the cf's
foreach($options['mapping'] as $field => $map) {
if($field[0] == '#') $query['custom_fields'][] = $field;
}
if ($options['selection'] == 'search') {
$query = array_merge($GLOBALS['egw']->session->appsession('session_data','infolog'), $query);
$query['num_rows'] = -1; // all
$selection = $bo->search($query);
}
elseif ( $options['selection'] == 'all' ) {
$query['num_rows'] = -1;
$selection = $bo->search($query);
} else {
$selection = explode(',',$options['selection']);
}
foreach ($selection as $_identifier) {
if(!is_array($_identifier)) {
$record = new infolog_egw_record($_identifier);
} else {
$record = new infolog_egw_record();
$record->set_record($_identifier);
}
// Some conversion
if($options['convert']) {
importexport_export_csv::convert($record, infolog_egw_record::$types, 'infolog');
$this->convert($record);
} else {
// Implode arrays, so they don't say 'Array'
foreach($record->get_record_array() as $key => $value) {
if(is_array($value)) $record->$key = implode(',', $value);
}
}
$export_object->export_record($record);
unset($record);
}
}
/**
* returns translated name of plugin
*
* @return string name
*/
public static function get_name() {
return lang('Infolog CSV export');
}
/**
* returns translated (user) description of plugin
*
* @return string descriprion
*/
public static function get_description() {
return lang("Exports Infolog entries into a CSV File.");
}
/**
* retruns file suffix for exported file
*
* @return string suffix
*/
public static function get_filesuffix() {
return 'csv';
}
public static function get_mimetype() {
return 'text/csv';
}
/**
* return html for options.
* this way the plugin has all opertunities for options tab
*
* @return string html
*/
public function get_options_etpl() {
}
/**
* returns slectors of this plugin via xajax
*
*/
public function get_selectors_etpl() {
return array(
'name' => 'infolog.export_csv_selectors',
'content' => 'search'
);
}
/**
* Convert some internal data to something with more meaning
*
* This is for something specific to Infolog, in addition to the normal conversions.
*/
public static function convert(infolog_egw_record &$record) {
// Stub, for now
}
}

View File

@ -1,20 +1,23 @@
<?php
/**
* eGroupWare: GroupDAV access: infolog handler
* EGroupware: GroupDAV access: infolog handler
*
* @link http://www.egroupware.org
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package infolog
* @subpackage groupdav
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2007-9 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2007-12 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @version $Id$
*/
require_once EGW_SERVER_ROOT.'/phpgwapi/inc/horde/lib/core.php';
/**
* eGroupWare: GroupDAV access: infolog handler
* EGroupware: GroupDAV access: infolog handler
*
* Permanent error_log() calls should use $this->groupdav->log($str) instead, to be send to PHP error_log()
* and our request-log (prefixed with "### " after request and response, like exceptions).
*/
class infolog_groupdav extends groupdav_handler
{
@ -42,24 +45,36 @@ class infolog_groupdav extends groupdav_handler
'PRIORITY' => 'info_priority',
'LOCATION' => 'info_location',
'COMPLETED' => 'info_datecompleted',
'CREATED' => 'info_created',
);
/**
* Are we using info_id, info_uid or caldav_name for the path/url
*
* Get's set in constructor to 'caldav_name' and groupdav_handler::$path_extension = ''!
*/
static $path_attr = 'info_id';
/**
* Constructor
*
* @param string $app 'calendar', 'addressbook' or 'infolog'
* @param int $debug=null debug-level to set
* @param string $base_uri=null base url of handler
* @param string $principalURL=null pricipal url of handler
* @param groupdav $groupdav calling class
*/
function __construct($app,$debug=null,$base_uri=null,$principalURL=null)
function __construct($app, groupdav $groupdav)
{
parent::__construct($app,$debug,$base_uri,$principalURL);
parent::__construct($app, $groupdav);
$this->bo = new infolog_bo();
$this->vCalendar = new Horde_iCalendar;
}
const PATH_ATTRIBUTE = 'info_id';
// since 1.9.002 we allow clients to specify the URL when creating a new event, as specified by CalDAV
if (version_compare($GLOBALS['egw_info']['apps']['calendar']['version'], '1.9.002', '>='))
{
self::$path_attr = 'caldav_name';
groupdav_handler::$path_extension = '';
}
}
/**
* Create the path for an event
@ -67,18 +82,55 @@ class infolog_groupdav extends groupdav_handler
* @param array|int $info
* @return string
*/
static function get_path($info)
function get_path($info)
{
if (is_numeric($info) && self::PATH_ATTRIBUTE == 'info_id')
if (is_numeric($info) && self::$path_attr == 'info_id')
{
$name = $info;
}
else
{
if (!is_array($info)) $info = $this->bo->read($info);
$name = $info[self::PATH_ATTRIBUTE];
$name = $info[self::$path_attr];
}
return $name.'.ics';
return $name.groupdav_handler::$path_extension;
}
/**
* Get filter-array for infolog_bo::search used by getctag and propfind
*
* @param string $path
* @param int $user account_id
* @return array
*/
private function get_infolog_filter($path, $user)
{
if (!($infolog_types = $GLOBALS['egw_info']['user']['preferences']['groupdav']['infolog-types']))
{
$infolog_types = 'task';
}
if ($path == '/infolog/')
{
$task_filter= 'own';
}
else
{
if ($user == $GLOBALS['egw_info']['user']['account_id'])
{
$task_filter = 'own';
}
else
{
$task_filter = 'user' . $user;
}
}
$ret = array(
'filter' => $task_filter,
'info_type' => explode(',', $infolog_types),
);
//error_log(__METHOD__."('$path', $user) returning ".array2string($ret));
return $ret;
}
/**
@ -92,29 +144,8 @@ class infolog_groupdav extends groupdav_handler
*/
function propfind($path,$options,&$files,$user,$id='')
{
$myself = ($user == $GLOBALS['egw_info']['user']['account_id']);
if ($path == '/infolog/')
{
$task_filter= 'own';
}
else
{
if ($myself)
{
$task_filter = 'open';
}
else
{
$task_filter = 'open-user' . $user;
}
}
// todo add a filter to limit how far back entries from the past get synced
$filter = array(
'info_type' => 'task',
'filter' => $task_filter,
);
$filter = $this->get_infolog_filter($path, $user);
// process REPORT filters or multiget href's
if (($id || $options['root']['name'] != 'propfind') && !$this->_report_filters($options,$filter,$id))
@ -123,6 +154,11 @@ class infolog_groupdav extends groupdav_handler
// when trying to request not supported components, eg. VTODO on a calendar collection
return true;
}
// enable time-range filter for tests via propfind / autoindex
//$filter[] = $sql = $this->_time_range_filter(array('end' => '20001231T000000Z'));
if ($id) $path = dirname($path).'/'; // caldav_name get's added anyway in the callback
if ($this->debug > 1)
{
error_log(__METHOD__."($path,,,$user,$id) filter=".
@ -175,11 +211,12 @@ class infolog_groupdav extends groupdav_handler
'filter' => $task_filter,
'date_format' => 'server',
'col_filter' => $filter,
'custom_fields' => true, // otherwise custom fields get NOT loaded!
);
if (!$calendar_data)
{
$query['cols'] = array('info_id', 'info_datemodified');
$query['cols'] = array('info_id', 'info_datemodified', 'info_uid', 'caldav_name', 'info_subject');
}
if (is_array($start))
@ -200,28 +237,17 @@ class infolog_groupdav extends groupdav_handler
foreach($tasks as $task)
{
$props = array(
HTTP_WebDAV_Server::mkprop('getetag',$this->get_etag($task)),
HTTP_WebDAV_Server::mkprop('getcontenttype',$this->agent != 'kde' ?
'text/calendar; charset=utf-8; component=VTODO' : 'text/calendar'), // Konqueror (3.5) dont understand it otherwise
// getlastmodified and getcontentlength are required by WebDAV and Cadaver eg. reports 404 Not found if not set
HTTP_WebDAV_Server::mkprop('getlastmodified', $task['info_datemodified']),
HTTP_WebDAV_Server::mkprop('resourcetype',''), // DAVKit requires that attribute!
HTTP_WebDAV_Server::mkprop('getcontentlength',''),
'getcontenttype' => $this->agent != 'kde' ? 'text/calendar; charset=utf-8; component=VTODO' : 'text/calendar', // Konqueror (3.5) dont understand it otherwise
'getlastmodified' => $task['info_datemodified'],
'displayname' => $task['info_subject'],
);
if ($calendar_data)
{
$content = $handler->exportVTODO($task,'2.0','PUBLISH');
$props[] = HTTP_WebDAV_Server::mkprop('getcontentlength',bytes($content));
$content = $handler->exportVTODO($task, '2.0', null); // no METHOD:PUBLISH for CalDAV
$props['getcontentlength'] = bytes($content);
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-data',$content);
}
else
{
$props[] = HTTP_WebDAV_Server::mkprop('getcontentlength', ''); // expensive to calculate and no CalDAV client uses it
}
$files[] = array(
'path' => $path.self::get_path($task),
'props' => $props,
);
$files[] = $this->add_resource($path, $task, $props);
}
}
if ($this->debug) error_log(__METHOD__."($path) took ".(microtime(true) - $starttime).' to return '.count($files).' items');
@ -240,6 +266,8 @@ class infolog_groupdav extends groupdav_handler
{
if ($options['filters'])
{
$cal_filters_in = $cal_filters; // remember filter, to be able to reset standard open-filter, if client sets own filters
foreach($options['filters'] as $filter)
{
switch($filter['name'])
@ -276,21 +304,19 @@ class infolog_groupdav extends groupdav_handler
if ($this->debug) error_log(__METHOD__."($options[path],...) param-filter='{$filter['attrs']['name']}' not (yet) implemented!");
break;
case 'time-range':
if ($this->debug > 1) error_log(__FILE__ . __METHOD__."($options[path],...) time-range={$filter['attrs']['start']}-{$filter['attrs']['end']}");
if (!empty($filter['attrs']['start']))
{
$cal_filters[] = 'info_startdate >= ' . (int)$this->vCalendar->_parseDateTime($filter['attrs']['start']);
}
if (!empty($filter['attrs']['end']))
{
$cal_filters[] = 'info_startdate <= ' . (int)$this->vCalendar->_parseDateTime($filter['attrs']['end']);
}
$cal_filters[] = $this->_time_range_filter($filter['attrs']);
break;
default:
if ($this->debug) error_log(__METHOD__."($options[path],".array2string($options).",...) unknown filter --> ignored");
break;
}
}
// if client set an own filter, reset the open-standard filter
/* not longer necessary, as we use now own and user anyway
if ($cal_filters != $cal_filters_in)
{
$cal_filters['filter'] = str_replace(array('open', 'open-user'), array('own', 'user'), $cal_filters['filter']);
}*/
}
// multiget or propfind on a given id
//error_log(__FILE__ . __METHOD__ . "multiget of propfind:");
@ -299,14 +325,8 @@ class infolog_groupdav extends groupdav_handler
$ids = array();
if ($id)
{
if (is_numeric($id))
{
$cal_filters['info_id'] = $id;
}
else
{
$cal_filters['info_uid'] = basename($id,'.ics');
}
$cal_filters[self::$path_attr] = groupdav_handler::$path_extension ?
basename($id,groupdav_handler::$path_extension) : $id;
}
else // fetch all given url's
{
@ -315,19 +335,78 @@ class infolog_groupdav extends groupdav_handler
if ($option['name'] == 'href')
{
$parts = explode('/',$option['data']);
if (is_numeric($id = basename(array_pop($parts),'.ics'))) $ids[] = $id;
if (($id = basename(array_pop($parts))))
{
$cal_filters[self::$path_attr][] = groupdav_handler::$path_extension ?
basename($id,groupdav_handler::$path_extension) : $id;
}
}
}
if ($ids)
{
$cal_filters[] = 'info_id IN ('.implode(',',array_map(create_function('$n','return (int)$n;'),$ids)).')';
}
}
if ($this->debug > 1) error_log(__METHOD__ ."($options[path],...,$id) calendar-multiget: ids=".implode(',',$ids));
}
return true;
}
/**
* Create SQL filter from time-range filter attributes
*
* CalDAV time-range for VTODO checks DTSTART, DTEND, DUE, CREATED and allways includes tasks if none given
* @see http://tools.ietf.org/html/rfc4791#section-9.9
*
* @param array $attrs values for keys 'start' and/or 'end', at least one is required by CalDAV rfc!
* @return string with sql
*/
private function _time_range_filter(array $attrs)
{
$to_or = $to_and = array();
if (!empty($attrs['start']))
{
$start = (int)$this->vCalendar->_parseDateTime($attrs['start']);
}
if (!empty($attrs['end']))
{
$end = (int)$this->vCalendar->_parseDateTime($attrs['end']);
}
elseif (empty($attrs['start']))
{
$this->groupdav->log(__METHOD__.'('.array2string($attrs).') minimum one of start or end is required!');
return '1'; // to not give sql error, but simply not filter out anything
}
// we dont need to care for DURATION line in rfc4791#section-9.9, as we always put that in DUE/info_enddate
// we have start- and/or enddate
if (isset($start))
{
$to_and[] = "($start < info_enddate OR $start <= info_startdate)";
}
if (isset($end))
{
$to_and[] = "(info_startdate < $end OR info_enddate <= $end)";
}
$to_or[] = '('.implode(' AND ', $to_and).')';
/* either start or enddate is already included in the above, because of OR!
// only a startdate, no enddate
$to_or[] = "NOT info_enddate > 0".($start ? " AND $start <= info_startdate" : '').
($end ? " AND info_startdate < $end" : '');
// only an enddate, no startdate
$to_or[] = "NOT info_startdate > 0".($start ? " AND $start < info_enddate" : '').
($end ? " AND info_enddate <= $end" : '');*/
// no startdate AND no enddate (2. half of rfc4791#section-9.9) --> use created and due dates instead
$to_or[] = 'NOT info_startdate > 0 AND NOT info_enddate > 0 AND ('.
// we have a completed date
"info_datecompleted > 0".(isset($start) ? " AND ($start <= info_datecompleted OR $start <= info_created)" : '').
(isset($end) ? " AND (info_datecompleted <= $end OR info_created <= $end)" : '').' OR '.
// we have no completed date, but always a created date
"NOT info_datecompleted > 0". (isset($end) ? " AND info_created < $end" : '').
')';
$sql = '('.implode(' OR ', $to_or).')';
if ($this->debug > 1) error_log(__FILE__ . __METHOD__.'('.array2string($attrs).") time-range={$filter['attrs']['start']}-{$filter['attrs']['end']} --> $sql");
return $sql;
}
/**
* Handle get request for a task / infolog entry
@ -344,10 +423,10 @@ class infolog_groupdav extends groupdav_handler
return $task;
}
$handler = $this->_get_handler();
$options['data'] = $handler->exportVTODO($id,'2.0','PUBLISH');
$options['data'] = $handler->exportVTODO($task, '2.0', null); // no METHOD:PUBLISH for CalDAV
$options['mimetype'] = 'text/calendar; charset=utf-8';
header('Content-Encoding: identity');
header('ETag: '.$this->get_etag($task));
header('ETag: "'.$this->get_etag($task).'"');
return true;
}
@ -378,104 +457,31 @@ class infolog_groupdav extends groupdav_handler
$taskId = $oldTask['info_id'];
$retval = true;
}
else
else // new entry
{
// new entry?
if (($foundTasks = $handler->searchVTODO($vTodo)))
{
if (($taskId = array_shift($foundTasks)) &&
($oldTask = $this->bo->read($taskId)))
{
$retval = '301 Moved Permanently';
}
else
{
// to be safe
$taskId = 0;
$retval = '201 Created';
}
}
else
{
// new entry
$taskId = 0;
$retval = '201 Created';
}
$taskId = 0;
$retval = '201 Created';
}
if ($user)
{
if (!$prefix) // for everything in /infolog/
{
$user = null; // do NOT set current user (infolog_bo->write() set it for new entries anyway)
}
elseif($oldTask) // existing entries
{
if ($oldTask['info_owner'] != $user)
{
if ($this->debug) error_log(__METHOD__."(,$id,$user,$prefix) changing owner of existing entries is forbidden!");
return '403 Forbidden'; // changing owner of existing entries is generally forbidden
}
$user = null;
}
else // new entries in /$user/infolog
{
// ACL is checked in infolog_bo->write() called by infolog_ical->importVTODO().
// Not sure if it's a good idea to set a different owner, as GUI does NOT allow that,
// thought there's an ACL for it and backend (infolog_bo) checks it.
// More like the GUI would be to add it for current user and delegate it to $user.
}
}
if (!($infoId = $handler->importVTODO($vTodo, $taskId, false, $user)))
if (!($infoId = $handler->importVTODO($vTodo, $taskId, false, $user, null, $id)))
{
if ($this->debug) error_log(__METHOD__."(,$id) import_vtodo($options[content]) returned false");
return '403 Forbidden';
}
/*
if (strstr($option['path'], '/infolog/') === 0)
{
$task_filter= 'own';
}
else
{
if ($myself)
{
$task_filter = 'open';
}
else
{
$task_filter = 'user' . $user. '-open';
}
}
$query = array(
'order' => 'info_datemodified',
'sort' => 'DESC',
'filter' => $task_filter,
'date_format' => 'server',
'col_filter' => array('info_id' => $infoId),
);
if (!$this->bo->search($query))
{
$retval = '410 Gone';
}
else
*/
if ($infoId != $taskId)
{
$retval = '201 Created';
}
header('ETag: '.$this->get_etag($infoId));
if ($retval !== true)
// send evtl. necessary respose headers: Location, etag, ...
// but only for new entries, as X-INFOLOG-STATUS get's not updated on client, if we confirm with an etag
if ($retval !== true && (!$path_attr_is_name ||
// POST with add-member query parameter
$_SERVER['REQUEST_METHOD'] == 'POST' && isset($_GET['add-member'])))
{
$path = preg_replace('|(.*)/[^/]*|', '\1/', $options['path']);
header('Location: '.$this->base_uri.$path.self::get_path($infoId));
return $retval;
$this->put_response_headers($infoId, $options['path'], $retval, self::$path_attr == 'caldav_name');
}
return true;
return $retval;
}
/**
@ -491,32 +497,42 @@ class infolog_groupdav extends groupdav_handler
{
return $task;
}
return $this->bo->delete($id);
return $this->bo->delete($task['info_id']);
}
/**
* Read an entry
*
* @param string/id $id
* @return array/boolean array with entry, false if no read rights, null if $id does not exist
* We have to make sure to not return or even consider in read deleted infologs, as the might have
* the same UID and/or caldav_name as not deleted ones and would block access to valid entries
*
* @param string|id $id
* @return array|boolean array with entry, false if no read rights, null if $id does not exist
*/
function read($id)
{
if (is_numeric($id)) return $this->bo->read($id,false,'server');
return null;
return $this->bo->read(array(self::$path_attr => $id, "info_status!='deleted'"),false,'server');
}
/**
* Check if user has the neccessary rights on a task / infolog entry
*
* @param int $acl EGW_ACL_READ, EGW_ACL_EDIT or EGW_ACL_DELETE
* @param array/int $task task-array or id
* @param array|int $task task-array or id
* @return boolean null if entry does not exist, false if no access, true if access permitted
*/
function check_access($acl,$task)
{
if (is_null($task)) return true;
return $this->bo->check_access($task,$acl);
$access = $this->bo->check_access($task,$acl);
if (!$access && $acl == EGW_ACL_EDIT && $this->bo->is_responsible($task))
{
$access = true; // access limited to $this->bo->responsible_edit fields (handled in infolog_bo::write())
}
if ($this->debug > 1) error_log(__METHOD__."($acl, ".array2string($task).') returning '.array2string($access));
return $access;
}
/**
@ -526,48 +542,16 @@ class infolog_groupdav extends groupdav_handler
*/
public function getctag($path,$user)
{
$myself = ($user == $GLOBALS['egw_info']['user']['account_id']);
if ($path == '/infolog/')
{
$task_filter= 'own';
}
else
{
if ($myself)
{
$task_filter = 'open';
}
else
{
$task_filter = 'open-user' . $user;
}
}
$query = array(
'order' => 'info_datemodified',
'sort' => 'DESC',
'filter' => $task_filter,
'date_format' => 'server',
'col_filter' => array('info_type' => 'task'),
'start' => 0,
'num_rows' => 1,
);
$result =& $this->bo->search($query);
if (empty($result)) return 'EGw-0-wGE';
$entry = array_shift($result);
return $this->get_etag($entry);
return $this->bo->getctag($this->get_infolog_filter($path, $user));
}
/**
* Get the etag for an infolog entry
*
* @param array/int $info array with infolog entry or info_id
* @return string/boolean string with etag or false
* etag currently uses the modifcation time (info_modified), 1.9.002 adds etag column, but it's not yet used!
*
* @param array|int $info array with infolog entry or info_id
* @return string|boolean string with etag or false
*/
function get_etag($info)
{
@ -579,7 +563,7 @@ class infolog_groupdav extends groupdav_handler
{
return false;
}
return 'EGw-'.$info['info_id'].':'.$info['info_datemodified'].'-wGE';
return $info['info_id'].':'.$info['info_datemodified'];
}
/**
@ -588,30 +572,33 @@ class infolog_groupdav extends groupdav_handler
* @param array $props=array() regular props by the groupdav handler
* @param string $displayname
* @param string $base_uri=null base url of handler
* @param int $user=null account_id of owner of collection
* @return array
*/
static function extra_properties(array $props=array(), $displayname, $base_uri=null)
public function extra_properties(array $props=array(), $displayname, $base_uri=null,$user=null)
{
// calendar description
$displayname = translation::convert(lang('Tasks of') . ' ' .
$displayname,translation::charset(),'utf-8');
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-description',$displayname);
// email of the current user, see caldav-sheduling draft
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-user-address-set',array(
HTTP_WebDAV_Server::mkprop('href','MAILTO:'.$GLOBALS['egw_info']['user']['email'])));
$displayname = translation::convert(lang('Tasks of'),translation::charset(),'utf-8').' '.$displayname;
$props['calendar-description'] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-description',$displayname);
// supported components, currently only VEVENT
$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'supported-calendar-component-set',array(
// HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'comp',array('name' => 'VEVENT')),
$props['supported-calendar-component-set'] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'supported-calendar-component-set',array(
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'comp',array('name' => 'VCALENDAR')),
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'comp',array('name' => 'VTIMEZONE')),
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'comp',array('name' => 'VTODO')),
));
$props[] = HTTP_WebDAV_Server::mkprop('supported-report-set',array(
// supported reports
$props['supported-report-set'] = HTTP_WebDAV_Server::mkprop('supported-report-set',array(
HTTP_WebDAV_Server::mkprop('supported-report',array(
HTTP_WebDAV_Server::mkprop('report',array(
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-query',''))),
HTTP_WebDAV_Server::mkprop('report',array(
HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-multiget','')))))));
// get timezone of calendar
if ($this->groupdav->prop_requested('calendar-timezone'))
{
$props['calendar-timezone'] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-timezone',
calendar_timezones::user_timezone($user));
}
return $props;
}
@ -628,4 +615,38 @@ class infolog_groupdav extends groupdav_handler
return $handler;
}
}
/**
* Return appliction specific settings
*
* @param array $hook_data
* @return array of array with settings
*/
static function get_settings($hook_data)
{
if (!isset($hook_data['setup']))
{
translation::add_app('infolog');
$infolog = new infolog_bo();
$types = $infolog->enums['type'];
}
if (!isset($types))
{
$types = array(
'task' => 'Tasks',
);
}
$settings = array();
$settings['infolog-types'] = array(
'type' => 'multiselect',
'label' => 'InfoLog types to sync',
'name' => 'infolog-types',
'help' => 'Which InfoLog types should be synced with the device, default only tasks.',
'values' => $types,
'default' => 'task',
'xmlrpc' => True,
'admin' => False,
);
return $settings;
}
}

View File

@ -0,0 +1,496 @@
<?php
/**
* InfoLog - Admin-, Preferences- and SideboxMenu-Hooks
*
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @package infolog
* @copyright (c) 2003-10 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
/**
* Class containing admin, preferences and sidebox-menus (used as hooks)
*/
class infolog_hooks
{
/**
* For which groups should no group acl be used: infolog group owners
*
* @param string|array $data
* @return boolean|array true, false or array with group-account_id's
*/
static function not_enum_group_acls($data)
{
$config = config::read('infolog');
return $config['group_owners'];
}
/**
* Hook called by link-class to include infolog in the appregistry of the linkage
*
* @param array/string $location location and other parameters (not used)
* @return array with method-names
*/
static function search_link($location)
{
// register our not_enum_group_acls hook, if not already registered
// can be removed after next infolog version update after 1.6
if ($GLOBALS['egw']->hooks->single('not_enum_group_acls',$acl_app) === false)
{
include(EGW_INCLUDE_ROOT.'/infolog/setup/setup.inc.php');
$GLOBALS['egw']->hooks->register_hooks('infolog',$setup_info['infolog']['hooks']);
unset($setup_info);
}
return array(
'query' => 'infolog.infolog_bo.link_query',
'title' => 'infolog.infolog_bo.link_title',
'titles' => 'infolog.infolog_bo.link_titles',
'view' => array(
'menuaction' => 'infolog.infolog_ui.index',
'action' => 'sp'
),
'view_id' => 'action_id',
'view_list' => 'infolog.infolog_ui.index',
'add' => array(
'menuaction' => 'infolog.infolog_ui.edit',
'type' => 'task'
),
'add_app' => 'action',
'add_id' => 'action_id',
'add_popup' => '750x550',
'file_access'=> 'infolog.infolog_bo.file_access',
'edit' => array(
'menuaction' => 'infolog.infolog_ui.edit',
),
'edit_id' => 'info_id',
'edit_popup' => '750x580',
);
}
/**
* hooks to build sidebox-menu plus the admin and preferences sections
*
* @param string/array $args hook args
*/
static function all_hooks($args)
{
$appname = 'infolog';
$location = is_array($args) ? $args['location'] : $args;
//echo "<p>admin_prefs_sidebox_hooks::all_hooks(".print_r($args,True).") appname='$appname', location='$location'</p>\n";
if ($location == 'sidebox_menu')
{
$file = array(
'infolog list' => egw::link('/index.php',array(
'menuaction' => 'infolog.infolog_ui.index' )),
'Add' => "javascript:egw_openWindowCentered2('".egw::link('/index.php',array(
'menuaction' => 'infolog.infolog_ui.edit',
),false)."','_blank',750,410,'yes');",
);
display_sidebox($appname,$GLOBALS['egw_info']['apps']['infolog']['title'].' '.lang('Menu'),$file);
}
if ($GLOBALS['egw_info']['user']['apps']['preferences'] && $location != 'admin')
{
$file = array(
'Preferences' => egw::link('/index.php','menuaction=preferences.uisettings.index&appname='.$appname),
'Grant Access' => egw::link('/index.php','menuaction=preferences.uiaclprefs.index&acl_app='.$appname),
'Edit Categories' => 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' => egw::link('/index.php',array(
'menuaction' => 'infolog.infolog_ui.admin' )),
'Global Categories' => egw::link('/index.php',array(
'menuaction' => 'admin.uicategories.index',
'appname' => $appname,
'global_cats'=> True)),
'Custom fields, typ and status' => egw::link('/index.php',array(
'menuaction' => 'infolog.infolog_customfields.edit')),
'CSV-Import' => egw::link('/infolog/csv_import.php'),
);
if ($location == 'admin')
{
display_section($appname,$file);
}
else
{
display_sidebox($appname,lang('Admin'),$file);
}
}
}
/**
* populates $settings for the preferences
*
* @return array
*/
static function settings()
{
/* Setup some values to fill the array of this app's settings below */
$info = new infolog_bo(); // need some labels from
$filters = $show_home = array();
$show_home[] = lang("DON'T show InfoLog");
foreach($info->filters as $key => $label)
{
$show_home[$key] = $filters[$key] = lang($label);
}
// migrage old filter-pref 1,2 to the filter one 'own-open-today'
if (isset($GLOBALS['type']) && in_array($GLOBALS['egw']->preferences->{$GLOBALS['type']}['homeShowEvents'],array('1','2')))
{
$GLOBALS['egw']->preferences->add('infolog','homeShowEvents','own-open-today',$GLOBALS['type']);
$GLOBALS['egw']->preferences->save_repository();
}
$show_links = array(
'all' => lang('all links and attachments'),
'links' => lang('only the links'),
'attach' => lang('only the attachments'),
'none' => lang('no links or attachments'),
'no_describtion' => lang('no describtion, links or attachments'),
);
$show_details = array(
0 => lang('No'),
1 => lang('Yes'),
2 => lang('Only for details'),
);
/* Settings array for this app */
$settings = array(
'defaultFilter' => array(
'type' => 'select',
'label' => 'Default Filter for InfoLog',
'name' => 'defaultFilter',
'values' => $filters,
'help' => 'This is the filter InfoLog uses when you enter the application. Filters limit the entries to show in the actual view. There are filters to show only finished, still open or futures entries of yourself or all users.',
'xmlrpc' => True,
'admin' => False,
'default'=> 'none',
),
'homeShowEvents' => array(
'type' => 'select',
'label' => 'InfoLog filter for the main screen',
'name' => 'homeShowEvents',
'values' => $show_home,
'help' => 'Should InfoLog show up on the main screen and with which filter. Works only if you dont selected an application for the main screen (in your preferences).',
'xmlrpc' => True,
'admin' => False,
'default'=> 'responsible-open-today',
),
'listNoSubs' => array(
'type' => 'check',
'label' => 'List no Subs/Childs',
'name' => 'listNoSubs',
'help' => 'Should InfoLog show Subtasks, -calls or -notes in the normal view or not. You can always view the Subs via there parent.',
'xmlrpc' => True,
'admin' => False,
'default'=> '0', // No = List subs
),
'show_links' => array(
'type' => 'select',
'label' => 'Show in the InfoLog list',
'name' => 'show_links',
'values' => $show_links,
'help' => 'Should InfoLog show the links to other applications and/or the file-attachments in the InfoLog list (normal view when you enter InfoLog).',
'xmlrpc' => True,
'admin' => False,
'default'=> 'all',
),
'never_hide' => array(
'type' => 'check',
'label' => 'Never hide search and filters',
'name' => 'never_hide',
'help' => 'If not set, the line with search and filters is hidden for less entries then "max matches per page" (as defined in your common preferences).',
'xmlrpc' => True,
'admin' => False,
'default'=> '1',
),
'show_percent' => array(
'type' => 'select',
'label' => 'Show status and percent done separate',
'name' => 'show_percent',
'values' => $show_details,
'help' => 'Should the Infolog list show the percent done only for status ongoing or two separate icons.',
'xmlrpc' => True,
'admin' => False,
'default'=> 1, // Yes
),
'show_id' => array(
'type' => 'select',
'label' => 'Show ticket Id',
'name' => 'show_id',
'values' => $show_details,
'help' => 'Should the Infolog list show a unique numerical Id, which can be used eg. as ticket Id.',
'xmlrpc' => True,
'admin' => False,
'default'=> '1', // Yes
),
'limit_des_lines' => array(
'type' => 'input',
'size' => 5,
'label' => 'Limit number of description lines (default 5, 0 for no limit)',
'name' => 'limit_des_lines',
'help' => 'How many describtion lines should be directly visible. Further lines are available via a scrollbar.',
'xmlrpc' => True,
'admin' => False,
'default'=> 5,
),
'limit_des_width' => array(
'type' => 'input',
'size' => 5,
'label' => 'Limit width of description column ((effective only if lines limit is set), 0 for no limit)',
'name' => 'limit_des_width',
'help' => 'How wide should the description area be. This value is numeric and interpreted as em; 60 works reasonably well.',
'xmlrpc' => True,
'admin' => False,
),
'set_start' => array(
'type' => 'select',
'label' => 'Startdate for new entries',
'name' => 'set_start',
'values' => array(
'date' => lang('todays date'),
'datetime' => lang('actual date and time'),
'empty' => lang('leave it empty'),
),
'help' => 'To what should the startdate of new entries be set.',
'xmlrpc' => True,
'admin' => False,
'default'=> 'date',
),
'cal_show' => array(
'type' => 'multiselect',
'label' => 'Which types should the calendar show',
'name' => 'cal_show',
'values' => $info->enums['type'],
'help' => 'Can be used to show further InfoLog types in the calendar or limit it to show eg. only tasks.',
'xmlrpc' => True,
'admin' => False,
'default'=> 'tasks,phone',
),
'cat_add_default' => array(
'type' => 'select',
'label' => 'Default category for new Infolog entries',
'name' => 'cat_add_default',
'values' => self::all_cats(),
'help' => 'You can choose a categorie to be preselected, when you create a new Infolog entry',
'xmlrpc' => True,
'admin' => False,
),
);
// notification preferences
$settings['notify_creator'] = array(
'type' => 'check',
'label' => 'Receive notifications about own items',
'name' => 'notify_creator',
'help' => 'Do you want a notification, if items you created get updated?',
'xmlrpc' => True,
'admin' => False,
'default'=> '1', // Yes
);
$settings['notify_assigned'] = array(
'type' => 'select',
'label' => 'Receive notifications about items assigned to you',
'name' => 'notify_assigned',
'help' => 'Do you want a notification, if items get assigned to you or assigned items get updated?',
'values' => array(
'0' => lang('No'),
'1' => lang('Yes'),
'assignment' => lang('Only if I get assigned or removed'),
),
'xmlrpc' => True,
'admin' => False,
'default'=> '1', // Yes
);
// to add options for more then 3 days back or in advance, you need to update soinfolog::users_with_open_entries()!
$options = array(
'0' => lang('No'),
'-1d' => lang('one day after'),
'0d' => lang('same day'),
'1d' => lang('one day in advance'),
'2d' => lang('%1 days in advance',2),
'3d' => lang('%1 days in advance',3),
);
$settings['notify_due_delegated'] = array(
'type' => 'select',
'label' => 'Receive notifications about due entries you delegated',
'name' => 'notify_due_delegated',
'help' => 'Do you want a notification, if items you delegated are due?',
'values' => $options,
'xmlrpc' => True,
'admin' => False,
'default'=> '0', // No
);
$settings['notify_due_responsible'] = array(
'type' => 'select',
'label' => 'Receive notifications about due entries you are responsible for',
'name' => 'notify_due_responsible',
'help' => 'Do you want a notification, if items you are responsible for are due?',
'values' => $options,
'xmlrpc' => True,
'admin' => False,
'default'=> '0d', // Same day
);
$settings['notify_start_delegated'] = array(
'type' => 'select',
'label' => 'Receive notifications about starting entries you delegated',
'name' => 'notify_start_delegated',
'help' => 'Do you want a notification, if items you delegated are about to start?',
'values' => $options,
'xmlrpc' => True,
'admin' => False,
'default'=> '0', // No
);
$settings['notify_start_responsible'] = array(
'type' => 'select',
'label' => 'Receive notifications about starting entries you are responsible for',
'name' => 'notify_start_responsible',
'help' => 'Do you want a notification, if items you are responsible for are about to start?',
'values' => $options,
'xmlrpc' => True,
'admin' => False,
'default'=> '0d', // Same day
);
// Merge print
if ($GLOBALS['egw_info']['user']['apps']['filemanager'])
{
$link = egw::link('/index.php','menuaction=infolog.infolog_merge.show_replacements');
$settings['default_document'] = array(
'type' => 'input',
'size' => 60,
'label' => 'Default document to insert entries',
'name' => 'default_document',
'help' => lang('If you specify a document (full vfs path) here, infolog displays an extra document icon for each entry. That icon allows to download the specified document with the contact data inserted.').' '.
lang('The document can contain placeholder like {{info_subject}}, to be replaced with the contact data (%1full list of placeholder names%2).','<a href="'.$link.'" target="_blank">','</a>').' '.
lang('At the moment the following document-types are supported:').'*.rtf, *.txt',
'run_lang' => false,
'xmlrpc' => True,
'admin' => False,
);
$settings['document_dir'] = array(
'type' => 'input',
'size' => 60,
'label' => 'Directory with documents to insert entries',
'name' => 'document_dir',
'help' => lang('If you specify a directory (full vfs path) here, infolog displays an action for each document. That action allows to download the specified document with the infolog data inserted.').' '.
lang('The document can contain placeholder like {{info_subject}}, to be replaced with the contact data (%1full list of placeholder names%2).','<a href="'.$link.'" target="_blank">','</a>').' '.
lang('At the moment the following document-types are supported:').'*.rtf, *.txt',
'run_lang' => false,
'xmlrpc' => True,
'admin' => False,
);
}
// Import / Export for nextmatch
if ($GLOBALS['egw_info']['user']['apps']['importexport'])
{
$definitions = new importexport_definitions_bo(array(
'type' => 'export',
'application' => 'infolog'
));
$options = array();
foreach ((array)$definitions->get_definitions() as $identifier)
{
try
{
$definition = new importexport_definition($identifier);
}
catch (Exception $e)
{
// permission error
continue;
}
if ($title = $definition->get_title())
{
$options[$title] = $title;
}
unset($definition);
}
$default_def = 'export-infolog';
$settings['nextmatch-export-definition'] = array(
'type' => 'select',
'values' => $options,
'label' => 'Export definitition to use for nextmatch export',
'name' => 'nextmatch-export-definition',
'help' => lang('If you specify an export definition, it will be used when you export'),
'run_lang' => false,
'xmlrpc' => True,
'admin' => False,
'default'=> isset($options[$default_def]) ? $default_def : false,
);
}
return $settings;
}
/**
* Return InoLog Categories (used for setting )
*
* @return array
*/
private static function all_cats()
{
$categories = new categories('','infolog');
$accountId = $GLOBALS['egw_info']['user']['account_id'];
foreach((array)$categories->return_sorted_array(0,False,'','','',true) as $cat)
{
$s = str_repeat('&nbsp;',$cat['level']) . stripslashes($cat['name']);
if ($cat['app_name'] == 'phpgw' || $cat['owner'] == '-1')
{
$s .= ' &#9830;';
}
elseif ($cat['owner'] != $accountId)
{
$s .= '&lt;' . $GLOBALS['egw']->accounts->id2name($cat['owner'], 'account_fullname') . '&gt;';
}
elseif ($cat['access'] == 'private')
{
$s .= ' &#9829;';
}
$sel_options[$cat['id']] = $s; // 0.9.14 only
}
return $sel_options;
}
/**
* Verification hook called if settings / preferences get stored
*
* Installs a task to send async infolog notifications at 2h everyday
*
* @param array $data
*/
static function verify_settings($data)
{
if ($data['prefs']['notify_due_delegated'] || $data['prefs']['notify_due_responsible'] ||
$data['prefs']['notify_start_delegated'] || $data['prefs']['notify_start_responsible'])
{
$async = new asyncservice();
if (!$async->read('infolog-async-notification'))
{
$async->set_timer(array('hour' => 2),'infolog-async-notification','infolog.infolog_bo.async_notification',null);
}
}
}
}

View File

@ -1,10 +1,11 @@
<?php
/**
* InfoLog - iCalendar Parser
* EGroupware - InfoLog - iCalendar Parser
*
* @link http://www.egroupware.org
* @author Lars Kneschke <lkneschke@egroupware.org>
* @author Joerg Lehrke <jlehrke@noc.de>
* @author Ralf Becker <RalfBecker@outdoor-training.de>
* @package infolog
* @subpackage syncml
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
@ -15,7 +16,6 @@ require_once EGW_SERVER_ROOT.'/phpgwapi/inc/horde/lib/core.php';
/**
* InfoLog: Create and parse iCal's
*
*/
class infolog_ical extends infolog_bo
{
@ -157,8 +157,8 @@ class infolog_ical extends infolog_bo
$taskData['info_cat'] = $cats[0];
}
$taskData = $GLOBALS['egw']->translation->convert($taskData,
$GLOBALS['egw']->translation->charset(), $charset);
$taskData = translation::convert($taskData,
translation::charset(), $charset);
if ($this->log)
{
@ -167,39 +167,25 @@ class infolog_ical extends infolog_bo
}
$vcal = new Horde_iCalendar;
$vcal->setAttribute('PRODID','-//EGroupware//NONSGML EGroupware InfoLog '.$GLOBALS['egw_info']['apps']['infolog']['version'].'//'.
strtoupper($GLOBALS['egw_info']['user']['preferences']['common']['lang']));
$vcal->setAttribute('VERSION',$_version);
$vcal->setAttribute('METHOD',$_method);
if ($_method) $vcal->setAttribute('METHOD',$_method);
$tzid = $this->tzid;
if ($tzid && $tzid != 'UTC')
{
// check if we have vtimezone component data for tzid of event, if not default to user timezone (default to server tz)
if (!($vtimezone = calendar_timezones::tz2id($tzid,'component')))
if (!calendar_timezones::add_vtimezone($vcal, $tzid))
{
error_log(__METHOD__."() unknown TZID='$tzid', defaulting to user timezone '".egw_time::$user_timezone->getName()."'!");
$vtimezone = calendar_timezones::tz2id($tzid=egw_time::$user_timezone->getName(),'component');
calendar_timezones::add_vtimezone($vcal, $tzid=egw_time::$user_timezone->getName());
$tzid = null;
}
if (!isset(self::$tz_cache[$tzid]))
{
self::$tz_cache[$tzid] = calendar_timezones::DateTimeZone($tzid);
}
// $vtimezone is a string with a single VTIMEZONE component, afaik Horde_iCalendar can not add it directly
// --> we have to parse it and let Horde_iCalendar add it again
$horde_vtimezone = Horde_iCalendar::newComponent('VTIMEZONE',$container=false);
$horde_vtimezone->parsevCalendar($vtimezone,'VTIMEZONE');
// DTSTART must be in local time!
$standard = $horde_vtimezone->findComponent('STANDARD');
$dtstart = $standard->getAttribute('DTSTART');
$dtstart = new egw_time($dtstart, egw_time::$server_timezone);
$dtstart->setTimezone(self::$tz_cache[$tzid]);
$standard->setAttribute('DTSTART', $dtstart->format('Ymd\THis'), array(), false);
$daylight = $horde_vtimezone->findComponent('DAYLIGHT');
$dtstart = $daylight->getAttribute('DTSTART');
$dtstart = new egw_time($dtstart, egw_time::$server_timezone);
$dtstart->setTimezone(self::$tz_cache[$tzid]);
$daylight->setAttribute('DTSTART', $dtstart->format('Ymd\THis'), array(), false);
$vcal->addComponent($horde_vtimezone);
}
$vevent = Horde_iCalendar::newComponent('VTODO',$vcal);
@ -212,13 +198,13 @@ class infolog_ical extends infolog_bo
}
// set fields that may contain non-ascii chars and encode them if necessary
foreach (array(
'SUMMARY' => $taskData['info_subject'],
'DESCRIPTION' => $taskData['info_des'],
'LOCATION' => $taskData['info_location'],
'RELATED-TO' => $taskData['info_id_parent'],
'UID' => $taskData['info_uid'],
'CATEGORIES' => $taskData['info_cat'],
) as $field => $value)
'SUMMARY' => $taskData['info_subject'],
'DESCRIPTION' => $taskData['info_des'],
'LOCATION' => $taskData['info_location'],
'RELATED-TO' => $taskData['info_id_parent'],
'UID' => $taskData['info_uid'],
'CATEGORIES' => $taskData['info_cat'],
) as $field => $value)
{
if (isset($this->clientProperties[$field]['Size']))
{
@ -319,7 +305,8 @@ class infolog_ical extends infolog_bo
}
$vevent->setAttribute('DTSTAMP',time());
$vevent->setAttribute('CREATED',$GLOBALS['egw']->contenthistory->getTSforAction('infolog_task',$taskData['info_id'],'add'));
$vevent->setAttribute('CREATED', $taskData['info_created'] ? $taskData['info_created'] :
$GLOBALS['egw']->contenthistory->getTSforAction('infolog_task',$taskData['info_id'],'add'));
$vevent->setAttribute('LAST-MODIFIED', $taskData['info_datemodified'] ? $taskData['info_datemodified'] :
$GLOBALS['egw']->contenthistory->getTSforAction('infolog_task',$taskData['info_id'],'modify'));
$vevent->setAttribute('CLASS',$taskData['info_access'] == 'public' ? 'PUBLIC' : 'PRIVATE');
@ -339,6 +326,34 @@ class infolog_ical extends infolog_bo
}
$vevent->setAttribute('PRIORITY', $priority);
// for CalDAV add all X-Properties previously parsed
if ($this->productManufacturer == 'groupdav')
{
foreach($taskData as $name => $value)
{
if (substr($name, 0, 2) == '##')
{
if ($name[2] == ':')
{
if ($value[1] == ':' && ($v = unserialize($value)) !== false) $value = $v;
foreach((array)$value as $compvData)
{
$comp = Horde_iCalendar::newComponent(substr($name,3), $vevent);
$comp->parsevCalendar($compvData,substr($name,3),'utf-8');
$vevent->addComponent($comp);
}
}
elseif ($value[1] == ':' && ($attr = unserialize($value)) !== false)
{
$vevent->setAttribute(substr($name, 2), $attr['value'], $attr['params'], true, $attr['values']);
}
else
{
$vevent->setAttribute(substr($name, 2), $value);
}
}
}
}
$vcal->addComponent($vevent);
$retval = $vcal->exportvCalendar();
@ -426,12 +441,12 @@ class infolog_ical extends infolog_bo
* @param int $_taskID=-1 info_id, default -1 = new entry
* @param boolean $merge=false merge data with existing entry
* @param int $user=null delegate new task to this account_id, default null
* @param string $charset The encoding charset for $text. Defaults to
* @param string $charset=null The encoding charset for $text. Defaults to
* utf-8 for new format, iso-8859-1 for old format.
*
* @param string $caldav_name=null CalDAV URL name-part for new entries
* @return int|boolean integer info_id or false on error
*/
function importVTODO(&$_vcalData, $_taskID=-1, $merge=false, $user=null, $charset=null)
function importVTODO(&$_vcalData, $_taskID=-1, $merge=false, $user=null, $charset=null, $caldav_name=null)
{
if ($this->tzid)
@ -449,20 +464,21 @@ class infolog_ical extends infolog_bo
// keep the dates
$this->time2time($taskData, $this->tzid, false);
// we suppose that a not set status in a vtodo means that the task did not started yet
if (empty($taskData['info_status']))
{
$taskData['info_status'] = 'not-started';
}
if (empty($taskData['info_datecompleted']))
{
$taskData['info_datecompleted'] = 0;
}
if (!is_null($user))
if (!is_null($user) && $_taskID)
{
$taskData['info_owner'] = $user;
if ($this->check_access($taskData, EGW_ACL_ADD))
{
$taskData['info_owner'] = $user;
}
else
{
$taskData['info_responsible'][] = $user;
}
}
if ($this->log)
@ -471,7 +487,11 @@ class infolog_ical extends infolog_bo
array2string($taskData)."\n",3,$this->logfile);
}
return $this->write($taskData, true, true, false);
if ($caldav_name)
{
$taskData['caldav_name'] = $caldav_name;
}
return $this->write($taskData, true, true, false, false, false, 'ical');
}
/**
@ -564,13 +584,39 @@ class infolog_ical extends infolog_bo
}
$taskData = array();
$taskData['info_type'] = 'task';
if ($_taskID > 0)
{
$taskData['info_id'] = $_taskID;
}
foreach ($component->_attributes as $attribute)
// iOS reminder app only sets COMPLETED, but never STATUS nor PERCENT-COMPLETED
// if we have no STATUS, set STATUS by existence of COMPLETED and/or PERCENT-COMPLETE and X-INFOLOG-STATUS
// if we have no PERCENT-COMPLETE set it from STATUS: 0=NEEDS-ACTION, 10=IN-PROCESS, 100=COMPLETED
if (!($status = $component->getAttribute('STATUS')) || !is_scalar($status))
{
$completed = $component->getAttribute('COMPLETED');
$x_infolog_status = $component->getAttribute('X-INFOLOG-STATUS');
// check if we have a X-INFOLOG-STATUS and it's completed state is different from given COMPLETED attr
if (is_scalar($x_infolog_status) &&
($this->_status2vtodo[$x_infolog_status] === 'COMPLETED') != is_scalar($completed))
{
$percent_completed = $component->getAttribute('PERCENT-COMPLETE');
$status = $completed && is_scalar($completed) ? 'COMPLETED' :
($percent_completed && is_scalar($percent_completed) && $percent_completed > 0 ? 'IN-PROCESS' : 'NEEDS-ACTION');
$component->setAttribute('STATUS', $status);
if (!is_scalar($percent_completed))
{
$component->setAttribute('PERCENT-COMPLETE', $percent_completed = $status == 'COMPLETED' ?
100 : ($status == 'NEEDS-ACTION' ? 0 : 10));
}
if ($this->log) error_log(__METHOD__."() setting STATUS='$status' and PERCENT-COMPLETE=$percent_completed from COMPLETED and X-INFOLOG-STATUS='$x_infolog_status'\n",3,$this->logfile);
}
else
{
if ($this->log) error_log(__METHOD__."() no STATUS, X-INFOLOG-STATUS='$x_infolog_status', COMPLETED".(is_scalar($completed)?'='.$completed:' not set')." --> leaving status and percent unchanged",3,$this->logfile);
}
}
foreach ($component->getAllAttributes() as $attribute)
{
//$attribute['value'] = trim($attribute['value']);
if (!strlen($attribute['value'])) continue;
@ -608,11 +654,18 @@ class infolog_ical extends infolog_bo
$taskData['info_location'] = str_replace("\r\n", "\n", $attribute['value']);
break;
case 'DURATION':
if (!isset($taskData['info_startdate']))
{
$taskData['info_startdate'] = $component->getAttribute('DTSTART');
}
$attribute['value'] += $taskData['info_startdate'];
$taskData['##DURATION'] = $attribute['value'];
// fall throught
case 'DUE':
// eGroupWare uses date only
$parts = @getdate($attribute['value']);
$value = @mktime(0, 0, 0, $parts['mon'], $parts['mday'], $parts['year']);
$taskData['info_enddate'] = $value;
// even as EGroupware only displays the date, we can still store the full value
// unless infolog get's stored, it does NOT truncate the time
$taskData['info_enddate'] = $attribute['value'];
break;
case 'COMPLETED':
@ -643,14 +696,12 @@ class infolog_ical extends infolog_bo
}
break;
case 'X-INFOLOG-STATUS':
break;
case 'STATUS':
// check if we (still) have X-INFOLOG-STATUS set AND it would give an unchanged status (no change by the user)
foreach ($component->_attributes as $attr)
{
if ($attr['name'] == 'X-INFOLOG-STATUS') break;
}
$taskData['info_status'] = $this->vtodo2status($attribute['value'],
$attr['name'] == 'X-INFOLOG-STATUS' ? $attr['value'] : null);
($attr=$component->getAttribute('X-INFOLOG-STATUS')) && is_scalar($attr) ? $attr : null);
break;
case 'SUMMARY':
@ -679,10 +730,47 @@ class infolog_ical extends infolog_bo
case 'PERCENT-COMPLETE':
$taskData['info_percent'] = (int) $attribute['value'];
break;
// ignore all PROPS, we dont want to store like X-properties or unsupported props
case 'DTSTAMP':
case 'SEQUENCE':
case 'CREATED':
case 'LAST-MODIFIED':
//case 'ATTENDEE': // todo: add real support for it
break;
default: // X- attribute or other by EGroupware unsupported property
//error_log(__METHOD__."() $attribute[name] = ".array2string($attribute));
// for attributes with multiple values in multiple lines, merge the values
if (isset($taskData['##'.$attribute['name']]))
{
//error_log(__METHOD__."() taskData['##$attribute[name]'] = ".array2string($taskData['##'.$attribute['name']]));
$attribute['values'] = array_merge(
is_array($taskData['##'.$attribute['name']]) ? $taskData['##'.$attribute['name']]['values'] : (array)$taskData['##'.$attribute['name']],
$attribute['values']);
}
$taskData['##'.$attribute['name']] = $attribute['params'] || count($attribute['values']) > 1 ?
serialize($attribute) : $attribute['value'];
break;
}
}
break;
}
// store included, but unsupported components like valarm as x-properties
foreach($component->getComponents() as $comp)
{
$name = '##:'.strtoupper($comp->getType());
$compvData = $comp->exportvCalendar($comp,'utf-8');
if (isset($taskData[$name]))
{
$taskData[$name] = array($taskData[$name]);
$taskData[$name][] = $compvData;
}
else
{
$taskData[$name] = $compvData;
}
}
if ($this->log)
{
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."($_taskID)\n" .
@ -704,8 +792,8 @@ class infolog_ical extends infolog_bo
{
if(!($note = $this->read($_noteID, true, 'server'))) return false;
$note = $GLOBALS['egw']->translation->convert($note,
$GLOBALS['egw']->translation->charset(), $charset);
$note = translation::convert($note,
translation::charset(), $charset);
switch ($_type)
{
@ -717,11 +805,13 @@ class infolog_ical extends infolog_bo
if (!empty($note['info_cat']))
{
$cats = $this->get_categories(array($note['info_cat']));
$note['info_cat'] = $GLOBALS['egw']->translation->convert($cats[0],
$GLOBALS['egw']->translation->charset(), $charset);
$note['info_cat'] = translation::convert($cats[0],
translation::charset(), $charset);
}
$vnote = new Horde_iCalendar_vnote();
$vNote->setAttribute('VERSION', '1.1');
$vnote->setAttribute('PRODID','-//EGroupware//NONSGML EGroupware InfoLog '.$GLOBALS['egw_info']['apps']['infolog']['version'].'//'.
strtoupper($GLOBALS['egw_info']['user']['preferences']['common']['lang']));
$vnote->setAttribute('VERSION', '1.1');
foreach (array( 'SUMMARY' => $note['info_subject'],
'BODY' => $note['info_des'],
'CATEGORIES' => $note['info_cat'],
@ -863,7 +953,7 @@ class infolog_ical extends infolog_bo
case 'text/plain':
$note = array();
$note['info_type'] = 'note';
$txt = $GLOBALS['egw']->translation->convert($_data, $charset);
$txt = translation::convert($_data, $charset);
$txt = str_replace("\r\n", "\n", $txt);
if (preg_match('/([^\n]+)\n\n(.*)/ms', $txt, $match))

View File

@ -0,0 +1,455 @@
<?php
/**
* eGroupWare
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package importexport
* @link http://www.egroupware.org
* @author Cornelius Weiss <nelius@cwtech.de>
* @copyright Cornelius Weiss <nelius@cwtech.de>
* @version $Id: $
*/
/**
* class import_csv for infolog
*/
class infolog_import_infologs_csv implements importexport_iface_import_plugin {
private static $plugin_options = array(
'fieldsep', // char
'charset', // string
'contact_owner', // int
'update_cats', // string {override|add} overides record
// with cat(s) from csv OR add the cat from
// csv file to exeisting cat(s) of record
'num_header_lines', // int number of header lines
'field_conversion', // array( $csv_col_num => conversion)
'field_mapping', // array( $csv_col_num => adb_filed)
'conditions', /* => array containing condition arrays:
'type' => exists, // exists
'string' => '#kundennummer',
'true' => array(
'action' => update,
'last' => true,
),
'false' => array(
'action' => insert,
'last' => true,
),*/
);
public static $special_fields = array(
'projectmanager' => 'Link to Projectmanager, use Project-ID, Title or @project_id(id_or_title)',
'addressbook' => 'Link to Addressbook, use nlast,nfirst[,org] or contact_id from addressbook',
'link_1' => '1. link: appname:appid the entry should be linked to, eg.: addressbook:123',
'link_2' => '2. link: appname:appid the entry should be linked to, eg.: addressbook:123',
'link_3' => '3. link: appname:appid the entry should be linked to, eg.: addressbook:123',
);
/**
* actions wich could be done to data entries
*/
protected static $actions = array( 'none', 'update', 'insert', 'delete', );
/**
* conditions for actions
*
* @var array
*/
protected static $conditions = array( 'exists' );
/**
* @var definition
*/
private $definition;
/**
* @var business object
*/
private $boinfolog;
/**
* For figuring out if a record has changed
*/
protected $tracking;
/**
* @var bool
*/
private $dry_run = false;
/**
* @var int
*/
private $user = null;
/**
* List of import errors
*/
protected $errors = array();
/**
* List of actions, and how many times that action was taken
*/
protected $results = array();
/**
* imports entries according to given definition object.
* @param resource $_stream
* @param string $_charset
* @param definition $_definition
*/
public function import( $_stream, importexport_definition $_definition ) {
$import_csv = new importexport_import_csv( $_stream, array(
'fieldsep' => $_definition->plugin_options['fieldsep'],
'charset' => $_definition->plugin_options['charset'],
));
$this->definition = $_definition;
$this->user = $GLOBALS['egw_info']['user']['account_id'];
// dry run?
$this->dry_run = isset( $_definition->plugin_options['dry_run'] ) ? $_definition->plugin_options['dry_run'] : false;
// fetch the infolog bo
$this->boinfolog = new infolog_bo();
// Get the tracker for changes
$this->tracking = new infolog_tracking($this->boinfolog);
// set FieldMapping.
$import_csv->mapping = $_definition->plugin_options['field_mapping'];
// set FieldConversion
$import_csv->conversion = $_definition->plugin_options['field_conversion'];
// Add extra conversions
$import_csv->conversion_class = $this;
//check if file has a header lines
if ( isset( $_definition->plugin_options['num_header_lines'] ) && $_definition->plugin_options['num_header_lines'] > 0) {
$import_csv->skip_records($_definition->plugin_options['num_header_lines']);
} elseif(isset($_definition->plugin_options['has_header_line']) && $_definition->plugin_options['has_header_line']) {
// First method is preferred
$import_csv->skip_records(1);
}
// set Owner
$_definition->plugin_options['record_owner'] = $_definition->plugin_options['record_owner'] ?
$_definition->plugin_options['record_owner'] : $this->user;
$_definition->plugin_options['record_owner'] = $this->user;
$_lookups = array(
'info_type' => $this->boinfolog->enums['types'],
'info_status' => $this->boinfolog->status['task']
);
// Start counting successes
$count = 0;
$this->results = array();
// Failures
$this->errors = array();
while ( $record = $import_csv->get_record() ) {
$success = false;
// don't import empty records
if( count( array_unique( $record ) ) < 2 ) continue;
$lookups = $_lookups;
if($record['info_type'] && $this->boinfolog->status[$record['info_type']]) {
$lookups['info_status'] = $this->boinfolog->status[$record['info_type']];
}
importexport_import_csv::convert($record, infolog_egw_record::$types, 'infolog', $lookups);
// Set owner, unless it's supposed to come from CSV file
if($_definition->plugin_options['owner_from_csv']) {
if(!is_numeric($record['info_owner'])) {
$this->errors[$import_csv->get_current_position()] = lang(
'Invalid owner ID: %1. Might be a bad field translation. Used %2 instead.',
$record['info_owner'],
$_definition->plugin_options['record_owner']
);
$record['info_owner'] = $_definition->plugin_options['record_owner'];
}
} else {
$record['info_owner'] = $_definition->plugin_options['record_owner'];
}
// Special values
if ($record['addressbook'] && !is_numeric($record['addressbook']))
{
list($lastname,$firstname,$org_name) = explode(',',$record['addressbook']);
$record['addressbook'] = self::addr_id($lastname,$firstname,$org_name);
}
if ($record['projectmanager'] && !is_numeric($record['projectmanager']))
{
$record['projectmanager'] = self::project_id($record['projectmanager']);
}
if ( $_definition->plugin_options['conditions'] ) {
foreach ( $_definition->plugin_options['conditions'] as $condition ) {
$results = array();
switch ( $condition['type'] ) {
// exists
case 'exists' :
if($record[$condition['string']]) {
$query['col_filter'] = array( $condition['string'] => $record[$condition['string']],);
$results = $this->boinfolog->search($query);
}
if ( is_array( $results ) && count( array_keys( $results )) >= 1) {
// apply action to all records matching this exists condition
$action = $condition['true'];
foreach ( (array)$results as $contact ) {
$record['id'] = $contact['id'];
if ( $_definition->plugin_options['update_cats'] == 'add' ) {
if ( !is_array( $contact['cat_id'] ) ) $contact['cat_id'] = explode( ',', $contact['cat_id'] );
if ( !is_array( $record['cat_id'] ) ) $record['cat_id'] = explode( ',', $record['cat_id'] );
$record['cat_id'] = implode( ',', array_unique( array_merge( $record['cat_id'], $contact['cat_id'] ) ) );
}
$success = $this->action( $action['action'], $record, $import_csv->get_current_position() );
}
} else {
$action = $condition['false'];
$success = ($this->action( $action['action'], $record, $import_csv->get_current_position() ));
}
break;
// not supported action
default :
die('condition / action not supported!!!');
break;
}
if ($action['last']) break;
}
} else {
// unconditional insert
$success = $this->action( 'insert', $record, $import_csv->get_current_position() );
}
if($success) $count++;
}
return $count;
}
/**
* perform the required action
*
* @param int $_action one of $this->actions
* @param array $_data contact data for the action
* @return bool success or not
*/
private function action ( $_action, $_data, $record_num = 0 ) {
$result = true;
switch ($_action) {
case 'none' :
return true;
case 'update' :
// Only update if there are changes
$old = $this->boinfolog->read($_data['info_id']);
if(!$this->definition->plugin_options['change_owner']) {
// Don't change addressbook of an existing contact
unset($_data['owner']);
}
// Merge to deal with fields not in import record
$_data = array_merge($old, $_data);
$changed = $this->tracking->changed_fields($_data, $old);
if(count($changed) == 0 && !$this->definition->plugin_options['update_timestamp']) {
break;
}
// Fall through
case 'insert' :
if ( $this->dry_run ) {
//print_r($_data);
$this->results[$_action]++;
break;
} else {
$result = $this->boinfolog->write( $_data, true, true);
if(!$result) {
$this->errors[$record_num] = lang('Permissions error - %1 could not %2',
$GLOBALS['egw']->accounts->id2name($_data['info_owner']),
lang($_action)
);
} else {
$this->results[$_action]++;
}
break;
}
default:
throw new egw_exception('Unsupported action');
}
// Process some additional fields
if(!is_numeric($result)) {
return $result;
}
$info_link_id = false;
foreach(self::$special_fields as $field => $desc) {
if(!$_data[$field]) continue;
if(strpos('link', $field) === 0) {
list($app, $id) = explode(':', $_data[$field]);
} else {
$app = $field;
$id = $_data[$field];
}
if ($app && $app_id) {
//echo "<p>linking infolog:$id with $app:$app_id</p>\n";
$link_id = egw_link::link('infolog',$id,$app,$app_id);
if ($link_id && !$info_link_id)
{
$to_write = array(
'info_id' => $id,
'info_link_id' => $link_id,
);
$infolog_bo->write($to_write);
$info_link_id = true;
}
}
}
return $result;
}
/**
* returns translated name of plugin
*
* @return string name
*/
public static function get_name() {
return lang('Infolog CSV import');
}
/**
* returns translated (user) description of plugin
*
* @return string descriprion
*/
public static function get_description() {
return lang("Imports entries into the infolog from a CSV File. CSV means 'Comma Seperated Values'. However in the options Tab you can also choose other seperators.");
}
/**
* retruns file suffix(s) plugin can handle (e.g. csv)
*
* @return string suffix (comma seperated)
*/
public static function get_filesuffix() {
return 'csv';
}
/**
* return etemplate components for options.
* @abstract We can't deal with etemplate objects here, as an uietemplate
* objects itself are scipt orientated and not "dialog objects"
*
* @return array (
* name => string,
* content => array,
* sel_options => array,
* preserv => array,
* )
*/
public function get_options_etpl() {
// lets do it!
}
/**
* returns etemplate name for slectors of this plugin
*
* @return string etemplate name
*/
public function get_selectors_etpl() {
// lets do it!
}
/**
* Returns errors that were encountered during importing
* Maximum of one error message per record, but you can append if you need to
*
* @return Array (
* record_# => error message
* )
*/
public function get_errors() {
return $this->errors;
}
/**
* Returns a list of actions taken, and the number of records for that action.
* Actions are things like 'insert', 'update', 'delete', and may be different for each plugin.
*
* @return Array (
* action => record count
* )
*/
public function get_results() {
return $this->results;
}
// end of iface_export_plugin
// Extra conversion functions - must be static
public static function addr_id( $n_family,$n_given=null,$org_name=null ) {
// find in Addressbook, at least n_family AND (n_given OR org_name) have to match
static $contacts;
if (is_null($n_given) && is_null($org_name))
{
// Maybe all in one
list($n_family, $n_given, $org_name) = explode(',', $n_family);
}
$n_family = trim($n_family);
if(!is_null($n_given)) $n_given = trim($n_given);
if (!is_object($contacts))
{
$contacts =& CreateObject('phpgwapi.contacts');
}
if (!is_null($org_name)) // org_name given?
{
$org_name = trim($org_name);
$addrs = $contacts->read( 0,0,array('id'),'',"n_family=$n_family,n_given=$n_given,org_name=$org_name" );
if (!count($addrs))
{
$addrs = $contacts->read( 0,0,array('id'),'',"n_family=$n_family,org_name=$org_name",'','n_family,org_name');
}
}
if (!is_null($n_given) && (is_null($org_name) || !count($addrs))) // first name given and no result so far
{
$addrs = $contacts->search(array('n_family' => $n_family, 'n_given' => $n_given));
}
if (is_null($n_given) && is_null($org_name)) // just one name given, check against fn (= full name)
{
$addrs = $contacts->read( 0,0,array('id'),'',"n_fn=$n_family",'','n_fn' );
}
if (count($addrs))
{
return $addrs[0]['id'];
}
return False;
}
public static function project_id($num_or_title)
{
static $boprojects;
if (!$num_or_title) return false;
if (!is_object($boprojects))
{
$boprojects =& CreateObject('projectmanager.boprojectmanager');
}
if (($projects = $boprojects->search(array('pm_number' => $num_or_title))) ||
($projects = $boprojects->search(array('pm_title' => $num_or_title))))
{
return $projects[0]['pm_id'];
}
return false;
}
}
?>

View File

@ -0,0 +1,192 @@
<?php
/**
* Infolog - document merge
*
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @author Nathan Gray
* @package infolog
* @copyright (c) 2007-9 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright 2011 Nathan Gray
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
/**
* Infolog - document merge object
*/
class infolog_merge extends bo_merge
{
/**
* Functions that can be called via menuaction
*
* @var array
*/
var $public_functions = array(
'show_replacements' => true,
);
/**
* Business object to pull records from
*/
protected $bo = null;
/**
* Constructor
*
*/
function __construct()
{
parent::__construct();
$this->bo = new infolog_bo();
}
/**
* Get infolog replacements
*
* @param int $id id of entry
* @param string &$content=null content to create some replacements only if they are use
* @return array|boolean
*/
protected function get_replacements($id,&$content=null)
{
if (!($replacements = $this->infolog_replacements($id)))
{
return false;
}
if (!(strpos($content,'$$info_contact/') === false))
{
// Check to see if it's actually a contact, then load
if(is_array($replacements['$$info_link$$']) && $replacements['$$info_link$$']['app'] == 'addressbook')
{
$replacements += $this->contact_replacements($replacements['$$info_link$$']['id'],'info_contact');
}
if(is_array($replacements['$$info_link$$'])) unset($replacements['$$info_link$$']);
}
return $replacements;
}
/**
* Get infolog replacements
*
* @param int $id id of entry
* @param string $prefix='' prefix like eg. 'erole'
* @return array|boolean
*/
public function infolog_replacements($id,$prefix='')
{
$record = new infolog_egw_record($id);
$info = array();
// Convert to human friendly values
$types = infolog_egw_record::$types;
$_selects = $this->bo->enums + array('status' => $this->bo->status[$record->info_type]);
foreach($_selects as $name => $value)
{
$selects['info_'.$name] = $value;
$types['select'][] = 'info_'.$name;
}
importexport_export_csv::convert($record, $types, 'infolog', $selects);
if($record->info_contact)
{
$array['info_contact'] = $array['info_link']['title'];
}
// Set any missing custom fields, or the marker will stay
$array = $record->get_record_array();
foreach($this->bo->customfields as $name => $field)
{
if(!$array['#'.$name]) $array['#'.$name] = '';
}
// Add markers
foreach($array as $key => &$value)
{
if(!$value) $value = '';
$info['$$'.($prefix ? $prefix.'/':'').$key.'$$'] = $value;
}
return $info;
}
/**
* Generate table with replacements for the preferences
*
*/
public function show_replacements()
{
$GLOBALS['egw_info']['flags']['app_header'] = lang('infolog').' - '.lang('Replacements for inserting entries into documents');
$GLOBALS['egw_info']['flags']['nonavbar'] = false;
common::egw_header();
echo "<table width='90%' align='center'>\n";
echo '<tr><td colspan="4"><h3>'.lang('Infolog fields:')."</h3></td></tr>";
$n = 0;
$tracking = new infolog_tracking($this->bo);
$fields = array('info_id' => lang('Infolog ID')) + $tracking->field2label;
foreach($fields as $name => $label)
{
if (in_array($name,array('custom'))) continue; // dont show them
if (in_array($name,array('info_subject', 'info_des')) && $n&1) // main values, which should be in the first column
{
echo "</tr>\n";
$n++;
}
if (!($n&1)) echo '<tr>';
echo '<td>{{'.$name.'}}</td><td>'.$label.'</td>';
if ($n&1) echo "</tr>\n";
$n++;
}
echo '<tr><td colspan="4"><h3>'.lang('Custom fields').":</h3></td></tr>";
foreach($this->bo->customfields as $name => $field)
{
echo '<tr><td>{{#'.$name.'}}</td><td colspan="3">'.$field['label']."</td></tr>\n";
}
echo '<tr><td colspan="4"><h3>'.lang('Contact fields').':</h3></td></tr>';
$n = 0;
foreach($this->contacts->contact_fields as $name => $label)
{
if (in_array($name,array('tid','label','geo'))) continue; // dont show them, as they are not used in the UI atm.
if (in_array($name,array('email','org_name','tel_work','url')) && $n&1) // main values, which should be in the first column
{
echo "</tr>\n";
$n++;
}
if (!($n&1)) echo '<tr>';
echo '<td>{{info_contact/'.$name.'}}</td><td>'.$label.'</td>';
if ($n&1) echo "</tr>\n";
$n++;
}
echo '<tr><td colspan="4"><h3>'.lang('Custom fields').":</h3></td></tr>";
foreach($this->contacts->customfields as $name => $field)
{
echo '<tr><td>{{info_contact/#'.$name.'}}</td><td colspan="3">'.$field['label']."</td></tr>\n";
}
echo '<tr><td colspan="4"><h3>'.lang('General fields:')."</h3></td></tr>";
foreach(array(
'date' => lang('Date'),
'user/n_fn' => lang('Name of current user, all other contact fields are valid too'),
'user/account_lid' => lang('Username'),
'pagerepeat' => lang('For serial letter use this tag. Put the content, you want to repeat between two Tags.'),
'label' => lang('Use this tag for addresslabels. Put the content, you want to repeat, between two tags.'),
'labelplacement' => lang('Tag to mark positions for address labels'),
'IF fieldname' => lang('Example {{IF n_prefix~Mr~Hello Mr.~Hello Ms.}} - search the field "n_prefix", for "Mr", if found, write Hello Mr., else write Hello Ms.'),
'NELF' => lang('Example {{NELF role}} - if field role is not empty, you will get a new line with the value of field role'),
'NENVLF' => lang('Example {{NELFNV role}} - if field role is not empty, set a LF without any value of the field'),
'LETTERPREFIX' => lang('Example {{LETTERPREFIX}} - Gives a letter prefix without double spaces, if the title is emty for example'),
'LETTERPREFIXCUSTOM' => lang('Example {{LETTERPREFIXCUSTOM n_prefix title n_family}} - Example: Mr Dr. James Miller'),
) as $name => $label)
{
echo '<tr><td>{{'.$name.'}}</td><td colspan="3">'.$label."</td></tr>\n";
}
echo "</table>\n";
common::egw_footer();
}
}

View File

@ -1,11 +1,11 @@
<?php
/**
* InfoLog - Storage object
* EGroupare - InfoLog - Storage object
*
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @package infolog
* @copyright (c) 2003-9 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2003-11 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
@ -83,13 +83,16 @@ class infolog_so
* @param array $info infolog entry as array
* @return boolean
*/
function is_responsible($info)
function is_responsible($info,$user=null)
{
static $user_and_memberships;
if (is_null($user_and_memberships))
if (!$user) $user = $this->user;
static $um_cache = array();
if ($user == $this->user) $user_and_memberships =& $um_cache[$user];
if (!isset($user_and_memberships))
{
$user_and_memberships = $GLOBALS['egw']->accounts->memberships($this->user,true);
$user_and_memberships[] = $this->user;
$user_and_memberships = $GLOBALS['egw']->accounts->memberships($user,true);
$user_and_memberships[] = $user;
}
return $info['info_responsible'] && array_intersect((array)$info['info_responsible'],$user_and_memberships);
}
@ -100,10 +103,15 @@ class infolog_so
* @param array|int $info data or info_id of InfoLog entry
* @param int $required_rights EGW_ACL_xyz anded together
* @param boolean $implicit_edit=false responsible has only implicit read and add rigths, unless this is set to true
* @param array $grants=null grants to use, default (null) $this->grants
* @param int $user=null user to check, default (null) $this->user
* @return boolean True if access is granted else False
*/
function check_access( $info,$required_rights,$implicit_edit=false )
function check_access( $info,$required_rights,$implicit_edit=false,array $grants=null,$user=null )
{
if (is_null($grants)) $grants = $this->grants;
if (!$user) $user = $this->user;
if (is_array($info))
{
@ -112,7 +120,7 @@ class infolog_so
{
// dont change our own internal data,
$backup_data = $this->data;
$info = $this->read($info);
$info = $this->read(array('info_id'=>$info));
$this->data = $backup_data;
}
else
@ -124,15 +132,14 @@ class infolog_so
return False;
}
$owner = $info['info_owner'];
$access_ok = $owner == $this->user || // user has all rights
$access_ok = $owner == $user || // user has all rights
// ACL only on public entrys || $owner granted _PRIVATE
(!!($this->grants[$owner] & $required_rights) ||
$this->is_responsible($info) && // implicite rights for responsible user(s) and his memberships
(!!($grants[$owner] & $required_rights) ||
$this->is_responsible($info,$user) && // implicite rights for responsible user(s) and his memberships
($required_rights == EGW_ACL_READ || $required_rights == EGW_ACL_ADD || $implicit_edit && $required_rights == EGW_ACL_EDIT)) &&
($info['info_access'] == 'public' || !!($this->grants[$this->user] & EGW_ACL_PRIVATE));
($info['info_access'] == 'public' || !!($this->grants[$user] & EGW_ACL_PRIVATE));
//echo "<p align=right>check_access(info_id=$info_id,requited=$required_rights,implicit_edit=$implicit_edit) owner=$owner, responsible=(".implode(',',$info['info_responsible'])."): access".($access_ok?"Ok":"Denied")."</p>\n";
// error_log(__METHOD__."($info[info_id],$required_rights,$implicit_edit,".array2string($grants).",$user) returning ".array2string($access_ok));
return $access_ok;
}
@ -255,7 +262,7 @@ class infolog_so
)," AND info_responsible='0' OR ",$this->responsible_filter($f_user),')');
}
}
//echo "<p>aclFilter(filter='$filter_was',user='$user') = '$filtermethod', privat_user_list=".print_r($privat_user_list,True).", public_user_list=".print_r($public_user_list,True)."</p>\n";
//echo "<p>aclFilter(filter='$filter_was',user='$f_user') = '$filtermethod', privat_user_list=".print_r($privat_user_list,True).", public_user_list=".print_r($public_user_list,True)."</p>\n";
return $this->acl_filter[$filter.$f_user] = $filtermethod; // cache the filter
}
@ -295,7 +302,7 @@ class infolog_so
*/
function dateFilter($filter = '')
{
preg_match('/(upcoming|today|overdue|date|enddate)([-\\/.0-9]*)/',$filter,$vars);
preg_match('/(open-upcoming|upcoming|today|overdue|date|enddate)([-\\/.0-9]*)/',$filter,$vars);
$filter = $vars[1];
if (isset($vars[2]) && !empty($vars[2]) && ($date = preg_split('/[-\\/.]/',$vars[2])))
@ -310,6 +317,8 @@ class infolog_so
}
switch ($filter)
{
case 'open-upcoming':
return "AND (info_startdate >= $tomorrow OR NOT (info_status IN ('done','billed','cancelled','deleted','template','nonactive','archive')))";
case 'upcoming':
return " AND info_startdate >= $tomorrow";
case 'today':
@ -353,11 +362,12 @@ class infolog_so
*
* some cacheing is done to prevent multiple reads of the same entry
*
* @param int|string $info_id id or uid of entry
* @param array $where where clause for entry to read
* @return array|boolean the entry as array or False on error (eg. entry not found)
*/
function read($info_id) // did _not_ ensure ACL
function read(array $where) // did _not_ ensure ACL
{
//error_log(__METHOD__.'('.array2string($where).')');
if (isset($GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length']))
{
$minimum_uid_length = $GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length'];
@ -367,18 +377,16 @@ class infolog_so
$minimum_uid_length = 8;
}
//echo "<p>read($info_id) ".function_backtrace()."</p>\n";
if (!$info_id || !$this->db->select($this->info_table,'*',
$this->db->expression($this->info_table,array('info_id'=>$info_id),' OR ',array('info_uid'=>$info_id)),__LINE__,__FILE__) ||
!(($this->data = $this->db->row(true))))
if (!$where || !($this->data = $this->db->select($this->info_table,'*',$where,__LINE__,__FILE__)->fetch()))
{
$this->init( );
//error_log(__METHOD__.'('.array2string($where).') returning FALSE');
return False;
}
if (!$this->data['info_uid'] || strlen($this->data['info_uid']) < $minimum_uid_length) {
// entry without uid --> create one based on our info_id and save it
$this->data['info_uid'] = $GLOBALS['egw']->common->generate_uid('infolog', $info_id);
if (!$this->data['info_uid'] || strlen($this->data['info_uid']) < $minimum_uid_length)
{
$this->data['info_uid'] = common::generate_uid('infolog', $this->data['info_id']);
$this->db->update($this->info_table,
array('info_uid' => $this->data['info_uid']),
array('info_id' => $this->data['info_id']), __LINE__,__FILE__);
@ -387,11 +395,11 @@ class infolog_so
{
$this->data['info_responsible'] = $this->data['info_responsible'] ? explode(',',$this->data['info_responsible']) : array();
}
$this->db->select($this->extra_table,'info_extra_name,info_extra_value',array('info_id'=>$this->data['info_id']),__LINE__,__FILE__);
while ($this->db->next_record())
foreach($this->db->select($this->extra_table,'info_extra_name,info_extra_value',array('info_id'=>$this->data['info_id']),__LINE__,__FILE__) as $row)
{
$this->data['#'.$this->db->f(0)] = $this->db->f(1);
$this->data['#'.$row['info_extra_name']] = $row['info_extra_value'];
}
//error_log(__METHOD__.'('.array2string($where).') returning '.array2string($this->data));
return $this->data;
}
@ -403,8 +411,7 @@ class infolog_so
*/
function get_status($ids)
{
$this->db->select($this->info_table,'info_id,info_type,info_status,info_percent',array('info_id'=>$ids),__LINE__,__FILE__);
while (($info = $this->db->row(true)))
foreach($this->db->select($this->info_table,'info_id,info_type,info_status,info_percent',array('info_id'=>$ids),__LINE__,__FILE__) as $info)
{
switch ($info['info_type'].'-'.$info['info_status'])
{
@ -522,9 +529,10 @@ class infolog_so
*
* @param array $values with the data of the log-entry
* @param int $check_modified=0 old modification date to check before update (include in WHERE)
* @param string $purge_cfs=null null=dont, 'ical'=only iCal X-properties (cfs name starting with "#"), 'all'=all cfs
* @return int|boolean info_id, false on error or 0 if the entry has been updated in the meantime
*/
function write($values,$check_modified=0) // did _not_ ensure ACL
function write($values, $check_modified=0, $purge_cfs=null) // did _not_ ensure ACL
{
if (isset($GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length']))
{
@ -575,21 +583,34 @@ class infolog_so
$this->db->insert($this->info_table,$to_write,false,__LINE__,__FILE__);
$info_id = $this->data['info_id'] = $this->db->get_last_insert_id($this->info_table,'info_id');
}
if (!$this->data['info_uid'] || strlen($this->data['info_uid']) < $minimum_uid_length) {
// entry without uid --> create one based on our info_id and save it
$this->data['info_uid'] = $GLOBALS['egw']->common->generate_uid('infolog', $info_id);
$this->db->update($this->info_table,
array('info_uid' => $this->data['info_uid']),
$update = array();
// entry without (reasonable) uid --> create one based on our info_id and save it
if (!$this->data['info_uid'] || strlen($this->data['info_uid']) < $minimum_uid_length)
{
$update['info_uid'] = $this->data['info_uid'] = common::generate_uid('infolog', $info_id);
}
// entry without caldav_name --> generate one based on info_id plus '.ics' extension
if (empty($this->data['caldav_name']))
{
$update['caldav_name'] = $this->data['caldav_name'] = $info_id.'.ics';
}
if ($update)
{
$this->db->update($this->info_table,$update,
array('info_id' => $info_id), __LINE__,__FILE__);
}
//echo "<p>soinfolog.write values= "; _debug_array($values);
// write customfields now
if ($purge_cfs)
{
$where = array('info_id' => $info_id);
if ($purge_cfs == 'ical') $where[] = "info_extra_name LIKE '#%'";
$this->db->delete($this->extra_table,$where,__LINE__,__FILE__);
}
$to_delete = array();
foreach($values as $key => $val)
{
@ -613,7 +634,7 @@ class infolog_so
$to_delete[] = substr($key,1);
}
}
if ($to_delete)
if ($to_delete && !$purge_cfs)
{
$this->db->delete($this->extra_table,array(
'info_id' => $info_id,
@ -624,8 +645,8 @@ class infolog_so
//error_log("### soinfolog::write(".print_r($to_write,true).") where=".print_r($where,true)." returning id=".$this->data['info_id']);
// update the index
egw_index::save('infolog',$this->data['info_id'],$this->data['info_owner'],$this->data,$this->data['info_cat'],
array('info_uid','info_type','info_status','info_confirm','info_access'));
//egw_index::save('infolog',$this->data['info_id'],$this->data['info_owner'],$this->data,$this->data['info_cat'],
// array('info_uid','info_type','info_status','info_confirm','info_access'));
return $this->data['info_id'];
}
@ -645,7 +666,10 @@ class infolog_so
if ((int)$info_id <= 0) return 0;
}
$counts = array();
foreach($this->db->select($this->info_table,'info_id_parent,COUNT(*) AS info_anz_subs',array('info_id_parent' => $info_id),__LINE__,__FILE__,
foreach($this->db->select($this->info_table,'info_id_parent,COUNT(*) AS info_anz_subs',array(
'info_id_parent' => $info_id,
"info_status != 'deleted'", // dont count deleted subs as subs, as they are not shown by default
),__LINE__,__FILE__,
false,'GROUP BY info_id_parent','infolog') as $row)
{
$counts[$row['info_id_parent']] = (int)$row['info_anz_subs'];
@ -673,7 +697,7 @@ class infolog_so
*/
function search(&$query)
{
//echo "<p>soinfolog.search(".print_r($query,True).")</p>\n";
//error_log(__METHOD__.'('.array2string($query).')');
$action2app = array(
'addr' => 'addressbook',
'proj' => 'projects',
@ -682,7 +706,9 @@ class infolog_so
$action = isset($action2app[$query['action']]) ? $action2app[$query['action']] : $query['action'];
if ($action != '')
{
$links = solink::get_links($action=='sp'?'infolog':$action,explode(',',$query['action_id']),'infolog');
$links = solink::get_links($action=='sp'?'infolog':$action,
is_array($query['action_id']) ? $query['action_id'] : explode(',',$query['action_id']),'infolog');
if (count($links))
{
$links = call_user_func_array('array_merge',$links); // flatten the array
@ -735,7 +761,7 @@ class infolog_so
$filtermethod .= ' AND '.$data;
continue;
}
if (substr($col,0,5) != 'info_' && substr($col,0,1)!='#') $col = 'info_'.$col;
if ($col[0] != '#' && substr($col,0,5) != 'info_' && isset($table_def['fd']['info_'.$col])) $col = 'info_'.$col;
if (!empty($data) && preg_match('/^[a-z_0-9]+$/i',$col))
{
switch ($col)
@ -768,17 +794,16 @@ class infolog_so
// Multi-select - any entry with the filter value selected matches
$filtermethod .= $this->db->expression($this->extra_table, array(
'info_extra_name' => substr($col,1),
"CONCAT(',',info_extra_value,',') LIKE '%,$data,%'"
$this->db->concat("','",'info_extra_value',"','").' '.$this->db->capabilities[egw_db::CAPABILITY_CASE_INSENSITIV_LIKE].' '.$this->db->quote('%,'.$data.',%'),
)).')';
}
else
{
$filtermethod .= $this->db->expression($this->extra_table,array(
'info_extra_name' => substr($col,1),
'info_extra_value' => $data,
)).')';
'info_extra_name' => substr($col,1),
'info_extra_value' => $data,
)).')';
}
$cfcolfilter++;
}
}
@ -818,14 +843,15 @@ class infolog_so
// at the moment MaxDB 7.5 cant cast nor search text columns, it's suppost to change in 7.6
if ($this->db->capabilities['like_on_text']) $columns[] = 'info_des';
$search = so_sql::search2criteria($query['search'], $wildcard, $op, null, $columns);
$sql_query = 'AND ('.(is_numeric($query['search']) ? 'main.info_id='.(int)$query['search'].' OR ' : '').
implode($pattern.' OR ',$columns).$pattern.') ';
implode($op, $search) .')';
$join = ($cfcolfilter>0 ? '':'LEFT')." JOIN $this->extra_table ON main.info_id=$this->extra_table.info_id ";
// mssql and others cant use DISTICT if text columns (info_des) are involved
$distinct = $this->db->capabilities['distinct_on_text'] ? 'DISTINCT' : '';
}
$pid = 'AND info_id_parent='.($action == 'sp' ? $query['action_id'] : 0);
$pid = 'AND ' . $this->db->expression($this->info_table,array('info_id_parent' => ($action == 'sp' ?$query['action_id'] : 0)));
if (!$GLOBALS['egw_info']['user']['preferences']['infolog']['listNoSubs'] &&
$action != 'sp' || isset($query['subs']) && $query['subs'])
@ -883,11 +909,19 @@ class infolog_so
$ids[$info['info_id']] = $info;
}
static $index_load_cfs;
if (is_null($index_load_cfs) && $query['col_filter']['info_type'])
{
$config_data = config::read('infolog');
$index_load_cfs = (array)$config_data['index_load_cfs'];
}
// if no specific custom field is selected, show/query all custom fields
if ($ids && ($query['custom_fields'] || $query['csv_export']))
if ($ids && ($query['custom_fields'] || $query['csv_export'] ||
$index_load_cfs && $query['col_filter']['info_type'] && in_array($query['col_filter']['info_type'],$index_load_cfs)))
{
$where = array('info_id' => array_keys($ids));
if (!($query['csv_export'] || strchr($query['selectcols'],'#') === false))
if (!($query['csv_export'] || strchr($query['selectcols'],'#') === false ||
$index_load_cfs && $query['col_filter']['info_type'] && in_array($query['col_filter']['info_type'],$index_load_cfs)))
{
$where['info_extra_name'] = array();
foreach(explode(',',$query['selectcols']) as $col)

View File

@ -56,11 +56,11 @@ class infolog_tracking extends bo_tracking
'info_status' => 'St',
'info_percent' => 'Pe',
'info_datecompleted' => 'Co',
'info_datemodified' => 'Mo',
'info_location' => 'Lo',
'info_startdate' => 'st',
'info_enddate' => 'En',
'info_responsible' => 'Re',
'info_cc' => 'cc',
'info_subject' => 'Su',
'info_des' => 'De',
'info_location' => 'Lo',
@ -75,11 +75,14 @@ class infolog_tracking extends bo_tracking
/**
* Translate field-names to labels
*
* @note The order of these fields is used to determine the order for CSV export
* @var array
*/
var $field2label = array(
'info_type' => 'Type',
'info_from' => 'Contact',
'info_subject' => 'Subject',
'info_des' => 'Description',
'info_addr' => 'Phone/Email',
'info_link_id' => 'primary link',
'info_cat' => 'Category',
@ -94,8 +97,7 @@ class infolog_tracking extends bo_tracking
'info_startdate' => 'Startdate',
'info_enddate' => 'Enddate',
'info_responsible' => 'Responsible',
'info_subject' => 'Subject',
'info_des' => 'Description',
'info_cc' => 'Cc',
// PM fields
'info_planned_time' => 'planned time',
'info_used_time' => 'used time',
@ -136,7 +138,11 @@ class infolog_tracking extends bo_tracking
*/
function get_subject($data,$old)
{
if (!$old || $old['info_status'] == 'deleted')
if ($data['prefix'])
{
$prefix = $data['prefix']; // async notification
}
elseif (!$old || $old['info_status'] == 'deleted')
{
$prefix = lang('New %1',lang($this->infolog->enums['type'][$data['info_type']]));
}
@ -245,7 +251,7 @@ class infolog_tracking extends bo_tracking
}
$details['#'.$name] = array(
'label' => $field['label'],
'value' => $data['#'.$name],
'value' => (is_array($field['values']) && !empty($field['values']) && array_key_exists($data['#'.$name],$field['values']))?$field['values'][$data['#'.$name]] : $data['#'.$name],
);
}
}
@ -254,7 +260,7 @@ class infolog_tracking extends bo_tracking
/**
* Track changes
*
*
* Overrides parent to log the modified date in the history, but not to send a notification
*
* @param array $data current entry
@ -317,4 +323,31 @@ class infolog_tracking extends bo_tracking
return parent::save_history($data,$old);
}
/**
* Get a notification-config value
*
* @param string $what
* - 'copy' array of email addresses notifications should be copied too, can depend on $data
* - 'lang' string lang code for copy mail
* - 'sender' string send email address
* @param array $data current entry
* @param array $old=null old/last state of the entry or null for a new entry
* @return mixed
*/
function get_config($name,$data,$old=null)
{
$config = array();
switch($name)
{
case 'copy': // include the info_cc addresses
if ($data['info_access'] == 'private') return array(); // no copies for private entries
if ($data['info_cc'])
{
$config = array_merge($config,preg_split('/, ?/',$data['info_cc']));
}
break;
}
return $config;
}
}

View File

@ -5,7 +5,7 @@
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @package infolog
* @copyright (c) 2003-10 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2003-12 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
@ -1055,6 +1055,7 @@ class infolog_ui
$parent = $this->bo->so->data;
$content['info_id'] = $info_id = 0;
$content['info_uid'] = ''; // ensure that we have our own UID
$content['caldav_name'] = ''; // ensure that we have our own caldav_name
$content['info_owner'] = $this->user;
$content['info_id_parent'] = $parent['info_id'];
/*
@ -1220,13 +1221,17 @@ class infolog_ui
}
}
$preserv = $content;
// for implizit edit of responsible user make all fields readonly, but status and percent
if ($info_id && !$this->bo->check_access($info_id,EGW_ACL_EDIT) && $this->bo->is_responsible($content) && !$undelete)
// for no edit rights or implizit edit of responsible user make all fields readonly, but status and percent
if ($info_id && !$this->bo->check_access($info_id,EGW_ACL_EDIT) && !$undelete)
{
$content['status_only'] = !in_array('link_to',$this->bo->responsible_edit);
foreach(array_diff(array_merge(array_keys($content),array('pm_id')),$this->bo->responsible_edit) as $name)
{
$readonlys[$name] = true;
foreach($this->bo->responsible_edit as $name)
{
$readonlys[$name] = false;
}
$readonlys['button[edit]'] = $readonlys['button[save]'] = $readonlys['button[apply]'] = $readonlys['no_notifications'] = false;
}
unset($readonlys[$tabs]);
// need to set all customfields extra, as they are not set if empty

View File

@ -0,0 +1,29 @@
<?php
/**
* eGroupWare - Wizard for Infolog CSV export
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package infolog
* @subpackage importexport
* @link http://www.egroupware.org
* @author Nathan Gray
* @version $Id$
*/
class infolog_wizard_export_csv extends importexport_wizard_basic_export_csv
{
public function __construct() {
parent::__construct();
// Field mapping
$bo = new infolog_tracking();
$this->export_fields = array('info_id' => 'Infolog ID') + $bo->field2label;
// Custom fields
unset($this->export_fields['custom']); // Heading, not a real field
$custom = config::get_customfields('infolog', true);
foreach($custom as $name => $data) {
$this->export_fields['#'.$name] = $data['label'];
}
}
}

View File

@ -0,0 +1,120 @@
<?php
/**
* eGroupWare - Wizard for Infolog CSV import
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package addressbook
* @link http://www.egroupware.org
* @author Nathan Gray
* @version $Id: $
*/
class infolog_wizard_import_infologs_csv extends importexport_wizard_basic_import_csv
{
/**
* constructor
*/
function __construct()
{
parent::__construct();
$this->steps += array(
'wizard_step50' => lang('Manage mapping'),
# This doesn't work with infolog very well
#'wizard_step60' => lang('Choose owner of imported data'),
);
// Field mapping
$tracking = new infolog_tracking();
$this->mapping_fields = array('info_id' => 'Infolog ID') + $tracking->field2label + infolog_import_infologs_csv::$special_fields;
// List each custom field
unset($this->mapping_fields['custom']);
$custom = config::get_customfields('infolog');
foreach($custom as $name => $data) {
$this->mapping_fields['#'.$name] = $data['label'];
}
// Actions
$this->actions = array(
'none' => lang('none'),
'update' => lang('update'),
'insert' => lang('insert'),
'delete' => lang('delete'),
);
// Conditions
$this->conditions = array(
'exists' => lang('exists'),
);
}
function wizard_step50(&$content, &$sel_options, &$readonlys, &$preserv)
{
$result = parent::wizard_step50($content, $sel_options, $readonlys, $preserv);
return $result;
}
# Skipped for now (or forever)
function wizard_step60(&$content, &$sel_options, &$readonlys, &$preserv)
{
if($this->debug) error_log(__METHOD__.'->$content '.print_r($content,true));
unset($content['no_owner_map']);
// Check that record owner has access
$access = true;
if($content['record_owner'])
{
$bo = new infolog_bo();
$access = $bo->check_access(0,EGW_ACL_EDIT, $content['record_owner']);
}
// return from step60
if ($content['step'] == 'wizard_step60')
{
if(!$access) {
$step = $content['step'];
unset($content['step']);
return $step;
}
switch (array_search('pressed', $content['button']))
{
case 'next':
return $GLOBALS['egw']->importexport_definitions_ui->get_step($content['step'],1);
case 'previous' :
return $GLOBALS['egw']->importexport_definitions_ui->get_step($content['step'],-1);
case 'finish':
return 'wizard_finish';
default :
return $this->wizard_step60($content,$sel_options,$readonlys,$preserv);
}
}
// init step60
else
{
$content['msg'] = $this->steps['wizard_step60'];
if(!$access) {
$content['msg'] .= "\n* " . lang('Owner does not have edit rights');
}
$content['step'] = 'wizard_step60';
if(!array_key_exists($content['record_owner']) && $content['plugin_options']) {
$content['record_owner'] = $content['plugin_options']['record_owner'];
}
if(!array_key_exists($content['owner_from_csv']) && $content['plugin_options']) {
$content['owner_from_csv'] = $content['plugin_options']['owner_from_csv'];
}
if(!array_key_exists($content['change_owner']) && $content['plugin_options']) {
$content['change_owner'] = $content['plugin_options']['change_owner'];
}
if(!in_array('info_owner', $content['field_mapping'])) {
$content['no_owner_map'] = true;
}
$preserv = $content;
unset ($preserv['button']);
return 'infolog.importexport_wizard_chooseowner';
}
}
}

28
infolog/js/edit.js Normal file
View File

@ -0,0 +1,28 @@
/**
* Javascript used on the infolog edit popup
*/
function add_email_from_ab(ab_id,info_cc)
{
var ab = document.getElementById(ab_id);
if (!ab || !ab.value)
{
set_style_by_class('tr','hiddenRow','display','block');
}
else
{
var cc = document.getElementById(info_cc);
for(var i=0; i < ab.options.length && ab.options[i].value != ab.value; ++i) ;
if (i < ab.options.length)
{
cc.value += (cc.value?', ':'')+ab.options[i].text.replace(/^.* <(.*)>$/,'$1');
ab.value = '';
ab.onchange();
set_style_by_class('tr','hiddenRow','display','none');
}
}
return false;
}

30
infolog/js/index.js Normal file
View File

@ -0,0 +1,30 @@
/**
* Javascript used on the infolog index page
*/
/**
* Javascript handling for multiple entry actions
*/
function do_infolog_action(selbox) {
if(selbox.value == "") return;
var prefix = selbox.id.substring(0,selbox.id.indexOf('['));
var popup = document.getElementById(prefix + '[' + selbox.value + '_popup]');
if(popup) {
popup.style.display = 'block';
return;
}
selbox.form.submit();
selbox.value = "";
}
/**
* Hide popup and clear values
*/
function hide_popup(element, div_id) {
var prefix = element.id.substring(0,element.id.indexOf('['));
var popup = document.getElementById(prefix+'['+div_id+']');
if(popup) {
popup.style.display = 'none';
}
}

363
infolog/lang/egw_bg.lang Normal file
View File

@ -0,0 +1,363 @@
%1 days in advance infolog bg %1 дена предварително
%1 deleted infolog bg %1 е изтрит
%1 deleted by %2 at %3 infolog bg %1 е изтрит от %2 в %3
%1 modified infolog bg %1 е променен
%1 modified by %2 at %3 infolog bg %1 е променен от %2 в %3
%1 records imported infolog bg %1 записа са импортирани
%1 records read (not yet imported, you may go %2back%3 and uncheck test import) infolog bg %1 записа прочетени (все още не импортирани, може да се върнете %2обратно%3 и да махнете отметката Тестов Импорт)
%1 you are responsible for is due at %2 infolog bg %1, за което отговаряте, приключва на %2
%1 you are responsible for is starting at %2 infolog bg %1, за което отговаряте, започва на %2
%1 you delegated is due at %2 infolog bg %1, делегирано от Вас приключва на %2
%1 you delegated is starting at %2 infolog bg %1, делегирано от Вас започва на %2
- subprojects from infolog bg - Подпроекти от
0% infolog bg 0%
10% infolog bg 10%
100% infolog bg 100%
20% infolog bg 20%
30% infolog bg 30%
40% infolog bg 40%
50% infolog bg 50%
60% infolog bg 60%
70% infolog bg 70%
80% infolog bg 80%
90% infolog bg 90%
a short subject for the entry infolog bg тема на записа накратко
abort without deleting infolog bg Отказ без изтриване
accept infolog bg приемане
action infolog bg Действие
actual date and time infolog bg действителни дата и час
add infolog bg Добавяне
add a file infolog bg Добави файл
add a new entry infolog bg Добави нов запис
add a new note infolog bg Добави нова бележка
add a new phonecall infolog bg Добави ново телефонно обаждане
add a new sub-task, -note, -call to this entry infolog bg Добави нова под-задача, -бележка, -обаждане към този запис
add a new todo infolog bg Добави Предстоящо
add file infolog bg Добави файл
add sub infolog bg Добави Под-
add timesheet entry infolog bg Добави запис в Графика
add: infolog bg Добавяне:
all infolog bg Всички
all links and attachments infolog bg всички връзки и приложения
allows to set the status of an entry, eg. set a todo to done if it's finished (values depend on entry-typ) infolog bg позволява установяване статуса на запис, напр. Предстоящо като Изпълнено, ако е приключило (стойностите зависят от типа на записа)
apply the changes infolog bg Приложи промените
archive infolog bg архив
are you shure you want to delete this entry ? infolog bg Желаете ли да изтриете този запис ?
attach a file infolog bg Прилагане на файл
attach file infolog bg Приложи файл
attention: no contact with address %1 found. infolog bg Внимание! Не е намерен контакт с адрес %1.
back to main list infolog bg Обратно към основния списък
billed infolog bg таксуван
both infolog bg и двете
call infolog bg обаждане
can be used to show further infolog types in the calendar or limit it to show eg. only tasks. infolog bg Може да се използва за нови типове елементи в Дневника и Календара, или за филтър, напр. само Задачи
cancel infolog bg Отмени
cancelled infolog bg отменен
categories infolog bg Категории
category infolog bg Категория
change the status of an entry, eg. close it infolog bg Променя статуса на запис, напр. приключва го
charset of file infolog bg Символен набор (Charset) на файла
check to set startday infolog bg отбележете за начална дата
check to specify custom contact infolog bg отбележете за използване на контакт по избор
click here to create the link infolog bg кликнете тук за създаване на връзката
click here to start the search infolog bg кликнете тук за начало на търсенето
close infolog bg Затвори
comment infolog bg Коментар
completed infolog bg Приключен
configuration infolog bg Конфигурация
confirm infolog bg Потвърди
contact infolog bg Контакт
copy your changes to the clipboard, %1reload the entry%2 and merge them. infolog bg Копирайте промените в клипборда, %1презаредете записа%2 и ги съберете.
create new links infolog bg Създаване на нови връзки
creates a new field infolog bg създава ново поле
creates a new status with the given values infolog bg създава нов статус съз зададените стойности
creates a new typ with the given name infolog bg създава нов тип със зададеното име
creation infolog bg Създаване
csv-fieldname infolog bg Име на полето в CSV
csv-filename infolog bg Име на CSV файла
csv-import common bg Импорт на CSV
custom infolog bg По избор
custom contact-address, leave empty to use information from most recent link infolog bg Адрес на контакт по избор, ако не се попълни използва последната връзка
custom contact-information, leave emtpy to use information from most recent link infolog bg Информация за контакт по избор, ако не се попълни използва последната връзка
custom fields infolog bg Полета по избор
custom fields, typ and status common bg По избор - полета, тип и статус
custom from infolog bg Форма по избор
custom regarding infolog bg По избор - относно
custom status for typ infolog bg По избор - статус за тип
customfields infolog bg Полета по избор
date completed infolog bg Дата на завършване
date completed (leave it empty to have it automatic set if status is done or billed) infolog bg Дата на завършване (непопълнена се установява автоматично при статус Изпълнен или Таксуван)
datecreated infolog bg дата на създаване
dates, status, access infolog bg Дати, Статус, Достъп
days infolog bg дни
default category for new infolog entries infolog bg Категория по подразбиране за нови записи в Дневника
default filter for infolog infolog bg Филтър по подразбиране в Дневника
default status for a new log entry infolog bg Сатус по подразбиране на новите записи
delegated infolog bg делегиран
delegated open infolog bg делегиран - отворен
delegated overdue infolog bg делегиран - просрочен
delegated upcomming infolog bg делегиран - предстоящ
delegation infolog bg Делегиране
delete infolog bg Изтриване
delete one record by passing its id. infolog bg Изтриване на един запис чрез предаване на ИД №
delete the entry infolog bg Изтриване записа
delete this entry infolog bg изтриване на този запис
delete this entry and all listed sub-entries infolog bg Изтриване на този запис и всички показани под-записи
deleted infolog bg изтрит
deletes the selected typ infolog bg изтрива избрания тип
deletes this field infolog bg изтрива това поле
deletes this status infolog bg изтрива този статус
description infolog bg Описание
determines the order the fields are displayed infolog bg определя реда за показване на полетата
disables a status without deleting it infolog bg забранява даден статус, без да го изтрива
do you want a confirmation of the responsible on: accepting, finishing the task or both infolog bg Изисква потвърждение от отговорния при: приемане на задачата, приключването й или и двете
do you want a notification, if items get assigned to you or assigned items get updated? infolog bg Желаете ли известие при назначаване задачи или при актуализация на вече назначените?
do you want a notification, if items you are responsible for are about to start? infolog bg Желаете ли известие преди началото на задачи, за които отговаряте?
do you want a notification, if items you are responsible for are due? infolog bg Желаете ли известие при достигане на крайния срок за задачи, за които отговаряте?
do you want a notification, if items you created get updated? infolog bg Желаете ли известие при актуализация на създадени от вас записи?
do you want a notification, if items you delegated are about to start? infolog bg Желаете ли известие преди началото на задачи, които сте делефирали?
do you want a notification, if items you delegated are due? infolog bg Желаете ли известие при достигане на крайния срок за задачи, които сте делегирали?
do you want to receive notifications as html-mails or plain text? infolog bg Известията да бъдат във вид на HTML или текстови съобщения?
don't show infolog infolog bg НЕ показвай Дневника
done infolog bg приключен
download infolog bg Изтегляне
duration infolog bg Продължителност
each value is a line like <id>[=<label>] infolog bg всяка стойност е ред във формат <id>[=<label>]
edit infolog bg Редактиране
edit or create categories for ingolog infolog bg Редактиране или създаване на категории за Дневника
edit rights (full edit rights incl. making someone else responsible!) infolog bg право за редакция (пълна редакция включва и промяна на отговорника!)
edit status infolog bg Редактиране на статуса
edit the entry infolog bg Редактиране на записа
edit this entry infolog bg Редактиране на записа
empty for all infolog bg непопълнен за всички
enddate infolog bg Краен срок
enddate can not be before startdate infolog bg Крайния срок не може да бъде преди началната дата
enter a custom contact, leave empty if linked entry should be used infolog bg Въведете контакт по избор, или ще се използва контакт от връзка
enter a custom phone/email, leave empty if linked entry should be used infolog bg Въведете телефон/E-Mail, или ще се използват от връзка
enter a textual description of the log-entry infolog bg въведете текстово описание за Log-записа
enter the query pattern infolog bg Въведете низ за запитване
entry and all files infolog bg Записа и всички файлове
error: saving the entry infolog bg Грешка при запис
error: the entry has been updated since you opened it for editing! infolog bg Грешка: записът е бил обновен, след като сте го отворили за редакция!
existing links infolog bg Съществуващи връзки
fax infolog bg Факс
field must not be empty !!! infolog bg Полето не може да бъде празно !!!
fieldseparator infolog bg Разделител на полетата
finish infolog bg край
for which types should this field be used infolog bg за кои типове да се използва полето
from infolog bg От
general infolog bg Общи
group owner for infolog bg Група-собственик на
high infolog bg висок
history logging infolog bg Запис на история
history logging and deleting of items infolog bg Запис на история и изтриване на елементи
id infolog bg No.
id# infolog bg No.
if a type has a group owner, all entries of that type will be owned by the given group and not the user who created it! infolog bg Ако за даден тип собственика е група, всички записи от този тип ще бъдат собственост на групата, НЕ на потребителя, който ги е създал!
if not set, the line with search and filters is hidden for less entries then "max matches per page" (as defined in your common preferences). infolog bg Ако не е зададен, редът за търсене и филтър е скрит за брой елементи, по-малък от "Максимален брой резултати на страница", дефиниран в настройките
import infolog bg Импорт
import next set infolog bg импорт на следващ набор
info log common bg Дневник
infolog common bg Дневник
infolog - delete infolog bg Дневник - Изтриване
infolog - edit infolog bg Дневник - Редактиране
infolog - import csv-file infolog bg Дневник - Импорт на CSV-файл
infolog - new infolog bg Дневник - Нов
infolog - new subproject infolog bg Дневник - Нов подпроект
infolog - subprojects from infolog bg Дневник - Подпроект от
infolog entry deleted infolog bg Записът е изтрит от Дневника
infolog entry saved infolog bg Записът е запазен в Дневника
infolog filter for the main screen infolog bg Филтър за главния екран на Дневника
infolog list infolog bg Дневник - списък
infolog preferences common bg Дневник - настройки
infolog-fieldname infolog bg Дневник - име на поле
invalid filename infolog bg Невалидно име на файл
label<br>helptext infolog bg Етикет<br>Помощен текст
last changed infolog bg Последна промяна
last modified infolog bg Последно изменение
leave it empty infolog bg оставете непопълнено
leave without saveing the entry infolog bg изход без запис
leaves without saveing infolog bg изход без запис
length<br>rows infolog bg Дължина<br>Редове
link infolog bg Връзка
links infolog bg Връзки
links of this entry infolog bg Връзки на записа
list all categories infolog bg Покажи всички категории
list no subs/childs infolog bg Покажи без Под-
location infolog bg Местонахождение
longer textual description infolog bg по-дълго текстово описание
low infolog bg нисък
max length of the input [, length of the inputfield (optional)] infolog bg макс. дължина на входа[, дължнина на вх.поле (опция)]
name must not be empty !!! infolog bg Името трябва да бъде попълнено!!!
name of new type to create infolog bg име на новия тип
never hide search and filters infolog bg Не скривай търсенето и филтрите
new %1 infolog bg Нов %1
new %1 created by %2 at %3 infolog bg Нов %1, създаден от %2 в %3
new name infolog bg ново име
new search infolog bg Ново търсене
no - cancel infolog bg Не - Отменя
no describtion, links or attachments infolog bg няма описание, връзки или приложения
no details infolog bg без детайли
no entries found, try again ... infolog bg няма намерени записи, опитайте отново
no filter infolog bg няма филтър
no links or attachments infolog bg няма връзки или приложения
nonactive infolog bg неактивен
none infolog bg Няма
normal infolog bg нормален
not infolog bg не е
not assigned infolog bg не назначен
not-started infolog bg не започнат
note infolog bg Бележка
number of records to read (%1) infolog bg Брой записи за четене (%1)
number of row for a multiline inputfield or line of a multi-select-box infolog bg Брой редове за многоредово текстово поле или множествен избор
offer infolog bg оферта
one day after infolog bg един ден по-късно
one day in advance infolog bg един ден предварително
ongoing infolog bg състоящ се
only for details infolog bg Само за детайлите
only if i get assigned or removed infolog bg Само ако бъда включен или изключен
only the attachments infolog bg само приложенията
only the links infolog bg само връзките
open infolog bg отвори
optional note to the link infolog bg бележка към връзката
order infolog bg Ред
overdue infolog bg просрочен
own infolog bg собствен
own open infolog bg отворен от собственика
own overdue infolog bg просрочен от собственика
own upcoming infolog bg предстоящ за собственика
parent infolog bg Родителски
path on (web-)serverside<br>eg. /var/samba/share infolog bg път от страна на (web-)сървъра<br>напр. /var/samba/Share
path to user and group files has to be outside of the webservers document-root!!! infolog bg Пътят до файловете на потребителите и групите ТРЯБВА ДА БЪДЕ ИЗВЪН document-root на WEB сървъра!!!
pattern for search in addressbook infolog bg низ за търсене в Адр. указател
pattern for search in projects infolog bg низ за търсене в Проектите
percent completed infolog bg Степен на изпълнение
permission denied infolog bg Достъпът отказан
phone infolog bg Телефонен разговор
phone/email infolog bg Телефон/E-Mail
phonecall infolog bg Телефонен разговор
planned infolog bg планиран
planned time infolog bg планирано време
price infolog bg Цена
pricelist infolog bg Ценова листа
primary link infolog bg основна връзка
priority infolog bg Приоритет
private infolog bg Личен
project infolog bg Проект
project settings: price, times infolog bg Параметри на проекта: цена, срокове
re: infolog bg Отг:
read one record by passing its id. infolog bg Прочита един запис чрез предаване на ИД №
read rights (default) infolog bg право за четене (по подразбиране)
receive notifications about due entries you are responsible for infolog bg Известяване за срока на задачи, за които сте отговорни
receive notifications about due entries you delegated infolog bg Известяване за срока на задачи, които сте делегирали
receive notifications about items assigned to you infolog bg Известяване за срока на задачи, назначени на Вас
receive notifications about own items infolog bg Известяване за срока на собствени задачи
receive notifications about starting entries you are responsible for infolog bg Известяване за началото на задачи, за които сте отговорни
receive notifications about starting entries you delegated infolog bg Известяване за началото на задачи, които сте делегирали
receive notifications as html-mails infolog bg Получаване на известия във вид на HTML E-mail-и
remark infolog bg Забележка
remove this link (not the entry itself) infolog bg Премахване на връзката (не и на записа)
responsible infolog bg отговорен
responsible open infolog bg отворен от отговорника
responsible overdue infolog bg просрочен от отговорника
responsible upcoming infolog bg предстоящ за отговорника
responsible user, priority infolog bg отговорен потребител, приоритет
returns a list / search for records. infolog bg Връща списък / търси записи.
rights for the responsible infolog bg Права на отговорника
same day infolog bg същия ден
save infolog bg запис
saves the changes made and leaves infolog bg запазва направените промени и излиза
saves this entry infolog bg Запазва записа
search infolog bg Търсене
search for: infolog bg Търсене за:
select infolog bg Избор
select a category for this entry infolog bg избор на категория за този запис
select a price infolog bg Избор на цена
select a priority for this task infolog bg приоритет на задачата
select a project infolog bg Изберете проект
select a responsible user: a person you want to delegate this task infolog bg избор на отговорник: потребител, на който да делегирате тази задача
select a typ to edit it's status-values or delete it infolog bg изберете тип за да редактирате статуса или да го изтриете
select an app to search in infolog bg Изберете приложение, в което да се търси
select an entry to link with infolog bg Изберете запис, с който да се свърже
select to filter by owner infolog bg филтър по собственик
select to filter by responsible infolog bg филтър по отговорни
sets the status of this entry and its subs to done infolog bg Маркира записа заедно с под-зап. като Изпълнени
should infolog show subtasks, -calls or -notes in the normal view or not. you can always view the subs via there parent. infolog bg Дневникът да показва ли Под-задачите, -обажданията -бележките в стандартния изглед или не. Тези елементи винаги могат да бъдат показани чрез "родителите" им.
should infolog show the links to other applications and/or the file-attachments in the infolog list (normal view when you enter infolog). infolog bg Дневникът да показва ли връзките към други приложения и/или приложените файлове в "Дневник-списък" (стандартния изглед при влизане в Дневник)?
should infolog show up on the main screen and with which filter. works only if you dont selected an application for the main screen (in your preferences). infolog bg Да се показва ли Дневникът на главния екран и с какъв филтър. Работи само, ако не сте избрали приложение за главния екран (в настройките)?
should infolog use full names (surname and familyname) or just the loginnames. infolog bg Да се използват ли пълните имена (име, презиме и фамилия) или само потребителските имена?
should the infolog list show a unique numerical id, which can be used eg. as ticket id. infolog bg В списъка на Дневника да се показва ли уникалния цифров идентификатор, който може да се използва примерно като No. на етикет?
should the infolog list show the column "last modified". infolog bg Да се показва ли в списъка на Дневника колоната "последно изменен"?
should the infolog list show the percent done only for status ongoing or two separate icons. infolog bg Да показва ли в списъка на Дневника степента на изпълнение само за състоящите се в момента, или две отделни икони?
should this entry only be visible to you and people you grant privat access via the acl infolog bg да бъде ли този запис видим само за Вас и хората, на които сте дали достъп до Лични чрез ACL-списъка (за контрол на достъпа)
show a column for used and planned times in the list. infolog bg Показва колона за използваното и планираното време в списъка.
show full usernames infolog bg Показва пълните имена на потребители
show in the infolog list infolog bg Показва списък на Дневника
show last modified infolog bg Последно променени
show status and percent done separate infolog bg Статус и степен на изпълнение отделно
show ticket id infolog bg Показва No. на етикет
show times infolog bg Показва времената
small view infolog bg умален изглед
start a new search, cancel this link infolog bg Започва ново търсене, отказва тази връзка
startdate infolog bg Начална дата
startdate enddate infolog bg Начална дата дата за прикл.
startdate for new entries infolog bg Начална дата за новите записи
startrecord infolog bg Начален запис
status infolog bg Статус
status ... infolog bg Статус...
sub infolog bg Под-
sub-entries become subs of the parent or main entries, if there's no parent infolog bg Под-записите са такива за "родителските" или главните записи, ако няма "родителски"
subject infolog bg Тема
task infolog bg Предстоящо
template infolog bg Шаблон
test import (show importable records <u>only</u> in browser) infolog bg Тестов импорт (показва импортируемите записи <u>само</u> в браузъра)
the name used internaly (<= 10 chars), changeing it makes existing data unavailible infolog bg името, използвано в системата (до 10 символа) Промяната му прави съществуващите данни недостъпни
the name used internaly (<= 20 chars), changeing it makes existing data unavailible infolog bg името, използвано в системата (до 20 символа) Промяната му прави съществуващите данни недостъпни
the text displayed to the user infolog bg текстът, показван на потребителя
this is the filter infolog uses when you enter the application. filters limit the entries to show in the actual view. there are filters to show only finished, still open or futures entries of yourself or all users. infolog bg Филтърът, който Дневникът използва, когато влезете в него. Филтрите ограничават записите, които се показват. Има филтри за показване само на приключените, неприключените или предстоящи елементи за Вас или всички потребители.
til when should the todo or phonecall be finished infolog bg до кога да приключи Предстоящо или Тел. обаждане
times infolog bg Времена
to many might exceed your execution-time-limit infolog bg твърде много могат да надвишат максималното време за изпълнение
to what should the startdate of new entries be set. infolog bg Каква да бъде началната дата за новите записи.
today infolog bg Днес
todays date infolog bg днешна дата
todo infolog bg Предстоящо
translation infolog bg Превод
typ infolog bg Тип
typ '%1' already exists !!! infolog bg Тип '%1' вече съществува !!!
type infolog bg Тип
type ... infolog bg Тип ...
type of customfield infolog bg Тип на полето по избор
type of the log-entry: note, phonecall or todo infolog bg Тип на записа: Бележка, Тел.обаждане или Предстоящо
unlink infolog bg Премахва връзка
upcoming infolog bg предстоящ
urgency infolog bg спешност
urgent infolog bg спешно
used time infolog bg използвано време
valid path on clientside<br>eg. \servershare or e: infolog bg валиден път от страна на клиента<br>напр. \\Server\Share или e:\
valid path on clientside<br>eg. servershare or e: infolog bg валиден път от страна на клиента<br>напр. \\Server\Share или e:\
values for selectbox infolog bg Стойности за избор
view all subs of this entry infolog bg Показва всички под- на записа
view other subs infolog bg показва другите Под-
view parent infolog bg Показва "Родителя"
view subs infolog bg показва Под-
view the parent of this entry and all his subs infolog bg Показва Родителя на записа и всичките му под-
view this linked entry in its application infolog bg показва свързания запис в съотв. приложение
which additional fields should the responsible be allowed to edit without having edit rights?<br />status, percent and date completed are always allowed. infolog bg Кои допълнителни полета да бъдат достъпни за редакция от отговорника, без да има съответните права?<br />Статус, степен на изпълнение и датата на приключване винаги са достъпни.
which implicit acl rights should the responsible get? infolog bg Какви права от Списъка за контрол на достъпа (ACL) да получава отговорника при назначаването си?
which types should the calendar show infolog bg Кои типове да се показват в Календара
whole query infolog bg всички резултати
will-call infolog bg ще телефонира
write (add or update) a record by passing its fields. infolog bg Запис (добавяне или актуализация) на запис чрез подаване на полетата му
yes - delete infolog bg Да - Изтриване
yes - delete including sub-entries infolog bg Да - Изтриване с под-записите
yes, noone can purge deleted items infolog bg Да, никой не може да заличава изтрити записи
yes, only admins can purge deleted items infolog bg Да, само администраторите могат да заличават изтрити записи
yes, with larger fontsize infolog bg Да, с по-голям размер на шрифта
yes, with purging of deleted items possible infolog bg Да, с възможност за заличаване на изтрити записи
you can choose a categorie to be preselected, when you create a new infolog entry infolog bg Може да настроите предварителен избор на категория при създаване на нов запис
you have entered an invalid ending date infolog bg Въвели сте невалидна дата на приключване
you have entered an invalid starting date infolog bg Въвели сте невалидна начална дата
you have to enter a name, to create a new typ!!! infolog bg За създаване на нов тип е необходимо да въведете име!!!
you must enter a subject or a description infolog bg Необходимо е да въведете тема или описание
your database is not up to date (%1 vs. %2), please run %3setup%4 to update your database. infolog bg Базата данни не е обновена (%1 вм. %2). Моля, използвайте %3setup%4 за обновяване.

325
infolog/lang/egw_ca.lang Normal file
View File

@ -0,0 +1,325 @@
%1 modified by %2 at %3 infolog ca %1 modificat per %2 a %3
%1 records imported infolog ca %1 registres importats
%1 records read (not yet imported, you may go %2back%3 and uncheck test import) infolog ca %1 registres llegits (encara sense importar, podeu %2tornar%3 i desmarcar Provar d'Importació)
- subprojects from infolog ca - Subprojectes de
0% infolog ca 0%
10% infolog ca 10%
100% infolog ca 100%
20% infolog ca 20%
30% infolog ca 30%
40% infolog ca 40%
50% infolog ca 50%
60% infolog ca 60%
70% infolog ca 70%
80% infolog ca 80%
90% infolog ca 90%
<b>file-attachments via symlinks</b> instead of uploads and retrieval via file:/path for direct lan-clients infolog ca <b>fitxers adjunts via enllaços simbòlics</b> en lloc d'enviaments i recuperacions via fitxer:/ruta per clients de la xarxa local
a short subject for the entry infolog ca descripció curta per l'entrada
abort without deleting infolog ca Cancel·lar sense esborrar
accept infolog ca acceptar
action infolog ca Acció
actual date and time infolog ca Data i hora actual
add infolog ca Afegir
add a file infolog ca Afegir arxiu
add a new entry infolog ca Afegir una nova entrada
add a new note infolog ca Afegir una nova nota
add a new phonecall infolog ca Afegir una nova trucada
add a new sub-task, -note, -call to this entry infolog ca Afegir nova subtasca, -nota-, -trucada a aquesta entrada
add a new todo infolog ca Afegir nova tasca pendent
add file infolog ca Afegir arxiu
add sub infolog ca afegir Sub
add timesheet entry infolog ca Afegeix entrada d'horari
add: infolog ca Afegir:
all infolog ca Tots
all links and attachments infolog ca tots els enllaços i arxius adjunts
allows to set the status of an entry, eg. set a todo to done if it's finished (values depend on entry-typ) infolog ca permet establir l'estat d'una entrada, p.e. posar una tasca pendent com a finalitzada si està acabada (els valors depenen del tipus d'entrada)
apply the changes infolog ca Aplica els canvis
are you shure you want to delete this entry ? infolog ca Esteu segurs d'esborrar aquesta entrada?
attach a file infolog ca Adjunta un arxiu
attach file infolog ca Adjunta arxiu
attension: no contact with address %1 found. infolog ca Atenció: No s'ha trobat el contacte amb l'adreça %1
attention: no contact with address %1 found. infolog ca Atenció: No s'ha trobat el contacte amb l'adreça %1
back to main list infolog ca Tornar a la llista principal
billed infolog ca facturat
both infolog ca ambdós
call infolog ca truca
cancel infolog ca Cancel·la
cancelled infolog ca cancel·lat
categories infolog ca Categories
category infolog ca Categoria
change the status of an entry, eg. close it infolog ca Canviar l'estat d'una entrada, ex. tancar-la
charset of file infolog ca Joc de caràcters de l'arxiu
check to set startday infolog ca marca per establir data d'inici
check to specify custom contact infolog ca Comproveu per especificat el contacte personalitzat
click here to create the link infolog ca premeu aquí per crear un enllaç
click here to start the search infolog ca premeu aquí per començar la recerca
close infolog ca Tancar
comment infolog ca Comentari
completed infolog ca Complet
configuration infolog ca Configuració
confirm infolog ca confirmar
contact infolog ca Contacte
copy your changes to the clipboard, %1reload the entry%2 and merge them. infolog ca Copia els teus canvis al portapapers, %1recarrega l'entrada%2 i ajunta-ho.
create new links infolog ca Crear nous enllaços
creates a new field infolog ca crea un camp nou
creates a new status with the given values infolog ca crea un nou estat amb els valors donats
creates a new typ with the given name infolog ca crea un nou tipus amb el nom donat
creation infolog ca Creació
csv-fieldname infolog ca CSV-Nom de Camp
csv-filename infolog ca CSV-Nom d'Arxiu
csv-import common ca CSV-Importar
custom infolog ca Personalitzat
custom contact-address, leave empty to use information from most recent link infolog ca Adreça de contacte personalitzada. Deixar en blanc per usar la informació de l'enllaç més recent
custom contact-information, leave emtpy to use information from most recent link infolog ca Informació de contacte personalitzada. Deixar en blanc per usar la informació de l'enllaç més recent
custom fields infolog ca Camps personalitzats
custom fields, typ and status common ca Camps personalitzats, tipus i estat
custom regarding infolog ca Records personals
custom status for typ infolog ca Estat personalitzat per al tipus
customfields infolog ca Camps personalitzats
date completed infolog ca Data complerta
date completed (leave it empty to have it automatic set if status is done or billed) infolog ca Data complerta (deixeu-la buida per
datecreated infolog ca data de creació
dates, status, access infolog ca Dates, Estat, Accés
days infolog ca dies
default filter for infolog infolog ca Filtre predeterminat per Tasques
default status for a new log entry infolog ca Estat predeterminat per a una nova entrada de registre
delegation infolog ca Delegació
delete infolog ca Esborrar
delete one record by passing its id. infolog ca Suprimeix un registre indicant el seu ID.
delete the entry infolog ca Esborrar l'entrada
delete this entry infolog ca esborrar aquesta entrada
delete this entry and all listed sub-entries infolog ca Esborra aquesta entrada i totes les subentrades llistades
deletes the selected typ infolog ca esborra el tipus seleccionat
deletes this field infolog ca esborra aquest camp
deletes this status infolog ca esborra aquest estat
description infolog ca Descripció
determines the order the fields are displayed infolog ca determina l'ordre en que es mostren els camps
disables a status without deleting it infolog ca desactiva un estat sense esborrar-lo
do you want a confirmation of the responsible on: accepting, finishing the task or both infolog ca Voleu una confirmació del responsable en acceptar, finalitzar la tasca, o ambdues coses?
do you want a notification mail, if items you created get updated? infolog ca Voleu un e-mail de notificació si els elements que heu creat són actualitzats?
do you want a notification mails, if items get assigned to you or assigned items get updated? infolog ca Voleu un e-mail de notificació si us assignen elements o si els elements assignats són actualitzats?
do you want to receive notifications as html-mails or plain text? infolog ca Voleu rebre notificacions com e-mails html o text simple?
do you want to see custom infolog types in the calendar? infolog ca Voleu veure tipus personalitzats de Tasques i Notes al calendari?
don't show infolog infolog ca NO mostris Tasques i Notes
done infolog ca fet
download infolog ca Descarregar
duration infolog ca Duració
each value is a line like <id>[=<label>] infolog ca cada valor és una línia com <id>[=<etiqueta>]
edit infolog ca Editar
edit or create categories for ingolog infolog ca Editar o crear categories per les Tasques
edit rights (full edit rights incl. making someone else responsible!) infolog ca drets d'edició (drets totals d'edició inclouen fer algú responsable)
edit status infolog ca Editar Estat
edit the entry infolog ca Editar l'entrada
edit this entry infolog ca Editar aquesta entrada
empty for all infolog ca buidar tot
enddate infolog ca Data de finalització
enddate can not be before startdate infolog ca La data de finalització no pot ser anterior a la d'inici
enter a custom contact, leave empty if linked entry should be used infolog ca entreu un contacte personalitzat, o deixeu-lo buit si preferiu usar un enllaç
enter a custom phone/email, leave empty if linked entry should be used infolog ca entreu un telèfon o correu electrònic personalitzat, o deixeu-lo buit si preferiu usar un enllaç
enter a textual description of the log-entry infolog ca entreu un text descriptiu de l'entrada
enter the query pattern infolog ca entreu el patró de recerca
entry and all files infolog ca Entrada i tots els arxius
error: saving the entry infolog ca Error guardant l'entrada
error: the entry has been updated since you opened it for editing! infolog ca Error: l'entrada ha estat actualitzada des de que l'has obert per editar-la!
existing links infolog ca Enllaços existents
fax infolog ca Fax
field must not be empty !!! infolog ca El camp no ha d'estar buit !!!
fieldseparator infolog ca Separador de camps
finish infolog ca finalitzar
for which types should this field be used infolog ca per quins tipus s'utilitza aquest camp
from infolog ca De
general infolog ca General
group owner for infolog ca Propietari de grup per
high infolog ca alta
id infolog ca id
if a type has a group owner, all entries of that type will be owned by the given group and not the user who created it! infolog ca Si un tipus té un propietari de grup, totes les entrades del tipus seran del grup donat i no de l'usuari que l'ha creat!
if not set, the line with search and filters is hidden for less entries then "max matches per page" (as defined in your common preferences). infolog ca Si no se posa, la línia amb cerca i filtres s'amaga per menys entrades de "max coincidències per pàgina" (definit a les teves preferències comuns).
import infolog ca Importar
import next set infolog ca importar grup següent
info log common ca Tasques i Notes
infolog common ca Tasques i Notes
infolog - delete infolog ca Tasques i Notes - Esborrar
infolog - edit infolog ca Tasques i Notes - Editar
infolog - import csv-file infolog ca Tasques i Notes - Importar arxiu CSV
infolog - new infolog ca Tasques i Notes - Nou
infolog - new subproject infolog ca Tasques i Notes - Nou Subprojecte
infolog - subprojects from infolog ca Tasques i Notes- Subprojectes de
infolog entry deleted infolog ca Entrada de Tasques i Notes esborrada
infolog entry saved infolog ca Entrada de Tasques i notes desada
infolog filter for the main screen infolog ca Filtre de Tasques i Notes per la pàgina d'inici
infolog list infolog ca Llista de Tasques i Notes
infolog preferences common ca Preferències de Tasques i Notes
infolog-fieldname infolog ca Tasques i Notes - Nom de Camp
invalid filename infolog ca El nom d'arxiu no és vàlid
label<br>helptext infolog ca Marca<br>Text d'ajuda
last changed infolog ca Darrer canvi
last modified infolog ca Darrera modificació
leave it empty infolog ca deixa-ho buit
leave without saveing the entry infolog ca Sortir sense desar l'entrada
leaves without saveing infolog ca Sortir sense desar
length<br>rows infolog ca Longitud<br>Files
link infolog ca Enllaç
links infolog ca Enllaços
links of this entry infolog ca Enllaços de l'entrada
list all categories infolog ca Llista totes les categories
list no subs/childs infolog ca No llistar Subs/Fills
location infolog ca Ubicació
longer textual description infolog ca descripció llarga de l'entrada
low infolog ca baix
max length of the input [, length of the inputfield (optional)] infolog ca longitud màxima de l'entrada [, longitud del camp d'entrada (opcional)]
name must not be empty !!! infolog ca El nom no pot estar buit !!!
name of new type to create infolog ca Nom del nou tipus a crear
never hide search and filters infolog ca Mai amaga cerca i filtres
new %1 created by %2 at %3 infolog ca Nou %1 creat per %2 a %3
new name infolog ca nou nom
new search infolog ca Nova recerca
no - cancel infolog ca No - Cancel·lar
no describtion, links or attachments infolog ca sense descripció, links o adjunts
no details infolog ca sense detalls
no entries found, try again ... infolog ca no s'han trobat registres, intenteu de nou ...
no filter infolog ca sense filtre
no links or attachments infolog ca sense enllaços o adjunts
none infolog ca Cap
normal infolog ca normal
not infolog ca no
not assigned infolog ca sense assignar
not-started infolog ca sense començar
note infolog ca Nota
number of records to read (%1) infolog ca Nombre de registres a llegir (%1)
number of row for a multiline inputfield or line of a multi-select-box infolog ca Número de fila per a un quadre de text multilínia o llista desplegable multiselecció
offer infolog ca suggerit
ongoing infolog ca en curs
only for details infolog ca Només per detalls
only the attachments infolog ca només adjunts
only the links infolog ca només enllaços
open infolog ca obert
optional note to the link infolog ca nota opcional per a l'enllaç
order infolog ca Ordre
overdue infolog ca vençut
own infolog ca propi
own open infolog ca propi obert
own overdue infolog ca propi vençut
own upcoming infolog ca propi en camí
path on (web-)serverside<br>eg. /var/samba/share infolog ca Ruta al servidor (web) <br>ex. /var/samba/Share
path to user and group files has to be outside of the webservers document-root!!! infolog ca La ruta als arxius d'usuari i grups HA D'ESTAR FORA de l'estructura de directoris del servidor web!!
pattern for search in addressbook infolog ca patró a cercar a la Llibreta d'Adreces
pattern for search in projects infolog ca patró a cercar dins Projectes
percent completed infolog ca Percentatge completat
permission denied infolog ca Permís denegat
phone infolog ca Trucada Telefònica
phone/email infolog ca Telèfon/Correu electrònic
phonecall infolog ca Trucada Telefònica
planned infolog ca panificat
planned time infolog ca temps planificat
price infolog ca Preu
priority infolog ca Prioritat
private infolog ca Privat
project infolog ca Projecte
project settings: price, times infolog ca Configuració del projecte: preu, horaris
re: infolog ca Re:
read one record by passing its id. infolog ca Llegeix un registre introduïnt el seu ID.
read rights (default) infolog ca llegiu els drets (per defecte)
receive notifications about items assigned to you infolog ca Rebeu notificacions sobre els vostres elements assignats
receive notifications about own items infolog ca Rebeu notificacions sobre els vostres propis elements
receive notifications as html-mails infolog ca Rebeu notificacions com a e-mails html
reg. expr. for local ip's<br>eg. ^192\.168\.1\. infolog ca expressió regular per IPs locals <br>ex. ^192\.168\.1\.
remark infolog ca Comentari
remove this link (not the entry itself) infolog ca Eliminar aquest enllaç (no l'entrada)
responsible infolog ca Responsable
responsible open infolog ca responsable obrir
responsible overdue infolog ca responsable restràs
responsible upcoming infolog ca responsable pròxim
responsible user, priority infolog ca usuari responsable, prioritat
returns a list / search for records. infolog ca Retorna una llista / cerca registres.
rights for the responsible infolog ca Drets del responsable
save infolog ca Desar
saves the changes made and leaves infolog ca Desar els canvis fets i sortir
saves this entry infolog ca Desar aquesta entrada
search infolog ca Cercar
search for: infolog ca Cercar per:
select infolog ca Seleccionar
select a category for this entry infolog ca seleccionar una categoria per aquesta entrada
select a price infolog ca Selecciona un preu
select a priority for this task infolog ca seleccionar una prioritat per aquesta tasca
select a project infolog ca Selecciona un projecte
select a responsible user: a person you want to delegate this task infolog ca seleccionar un responsable: una persona en la que es vol delegar la tasca
select a typ to edit it's status-values or delete it infolog ca seleccionar un tipus per editar els seus valors d'estat o esborrar-lo
select an app to search in infolog ca Seleccionar una aplicació on cercar
select an entry to link with infolog ca Seleccionar una entrada per enllaçar
select to filter by owner infolog ca selecciona filtrar per propietari
select to filter by responsible infolog ca selecciona filtrar per responsable
sets the status of this entry and its subs to done infolog ca Estableix l'estat d'aquesta entrada i les seves subtasques com a fetes.
should infolog show subtasks, -calls or -notes in the normal view or not. you can always view the subs via there parent. infolog ca S'han de mostrar les Subtasques, (subtrucades o subnotes) a la visualització normal? Podeu veure sempre els subtipus a través del seu tipus pare.
should infolog show the links to other applications and/or the file-attachments in the infolog list (normal view when you enter infolog). infolog ca Tasques i Notes hauria de mostrar els enllaços a altres aplicacions i/o els fitxers adjunts dins la llista de Tasques i Notes (vista normal quan entres a Tasques i Notes).
should infolog show up on the main screen and with which filter. works only if you dont selected an application for the main screen (in your preferences). infolog ca Tasques i Notes hauria de mostrar-se a la pàgina d'inici i amb quin filtre. Funciona només si no seleccioneu una aplicació per la pantalla d'inici (en les vostres preferències).
should infolog use full names (surname and familyname) or just the loginnames. infolog ca Fem servir noms complets (cognom i nom de pila), o només els noms d'usuari de l'aplicació?
should the calendar show custom types too infolog ca El calendari ha de mostrar tipus personalitzats també
should the infolog list show a unique numerical id, which can be used eg. as ticket id. infolog ca La llista de Tasques i Notes ha de mostrar un únic Id numèric, que pot ser utilitzat p.e. com ticket Id.
should the infolog list show the column "last modified". infolog ca La llista de Tasques i Notes ha de mostrar la columna "darrera modificació".
should the infolog list show the percent done only for status ongoing or two separate icons. infolog ca La llista de Tasques i Notes ha de mostrar el percentatge fet només per estat iniciat o per dues icones separades.
should this entry only be visible to you and people you grant privat access via the acl infolog ca Aquesta entrada ha de ser visible només per vos i a qui atorgueu accés privat mitjançant la llista LCA?
show a column for used and planned times in the list. infolog ca Mostra una columna per a horaris utilitzats i planificats de la llista.
show full usernames infolog ca Mostrar noms d'usuari complets
show in the infolog list infolog ca Mostrar la llista de Tasques i Notes
show last modified infolog ca Mostra la darrera modificació
show status and percent done separate infolog ca Mostra per separat l'estat i el percentatge completat
show ticket id infolog ca Mostra ticket Id
show times infolog ca Mostra horaris
small view infolog ca vista reduïda
start a new search, cancel this link infolog ca iniciar nova recerca, cancel·lar aquest enllaç
startdate infolog ca Data d'inici
startdate enddate infolog ca Dates d'inici i fi
startdate for new entries infolog ca Data d'inici per a noves entrades
startrecord infolog ca Començar gravació de registre
status infolog ca Estat
status ... infolog ca Estat...
sub infolog ca Sub
sub-entries become subs of the parent or main entries, if there's no parent infolog ca Les subentrades se converteixen en filles de l'entrada pare o de l'entrada principal si no té pare.
subject infolog ca Assumpte
task infolog ca Tasca
test import (show importable records <u>only</u> in browser) infolog ca Prova d'Importació (mostrar registres importables <u>només</u> al navegador)
the name used internaly (<= 10 chars), changeing it makes existing data unavailible infolog ca nom utilitzat internament (<= 10 caràcters). Si es canvia algunes dades seran inaccessibles
the name used internaly (<= 20 chars), changeing it makes existing data unavailible infolog ca nom utilitzat internament (<= 20 caràcters). Si es canvia algunes dades seran inaccessibles
the text displayed to the user infolog ca el text mostrat a l'usuari
this is the filter infolog uses when you enter the application. filters limit the entries to show in the actual view. there are filters to show only finished, still open or futures entries of yourself or all users. infolog ca Aquest és el filtre que Tasques i Notes usa quan entreu a l'aplicació. Els filtres limiten les entrades que es mostren a la vista actual. Hi ha filtres per mostrar només les acabades, encara obertes o entrades futures vostres o de tots els usuaris.
til when should the todo or phonecall be finished infolog ca Quan s'ha de finalitzar la tasca o trucada telefònica?
times infolog ca Hores
to many might exceed your execution-time-limit infolog ca Quant es pot excedir el límit de temps d'execució?
to what should the startdate of new entries be set. infolog ca En relació a què s'ha d'establir la data d'inici de les noves entrades.
today infolog ca Avui
todays date infolog ca data d'avui
todo infolog ca Tasques Pendents
translation infolog ca Traducció
typ infolog ca Tipus
typ '%1' already exists !!! infolog ca El tipus '%1' ja existeix!
type infolog ca Tipus
type ... infolog ca Tipus...
type of customfield infolog ca Tipus de camp personalitzat
type of the log-entry: note, phonecall or todo infolog ca Tipus d'entrada: Nota, Trucada Telefònica o Tasca Pendent
unlink infolog ca Desenllaçar
upcoming infolog ca propera
urgency infolog ca Urgència
urgent infolog ca urgent
used time infolog ca temps usat
valid path on clientside<br>eg. \\server\share or e:\ infolog ca Ruta vàlida en el client<br>ex. \\servidor\recurs o e:\
values for selectbox infolog ca Valors per a llista desplegable
view all subs of this entry infolog ca Veure tots els subs d'aquesta entrada
view other subs infolog ca veure altres subs
view parent infolog ca Veure pare
view subs infolog ca Veure subs
view the parent of this entry and all his subs infolog ca Veure el pare d'aquesta entrada i tots els seus subs
view this linked entry in its application infolog ca Veure aquesta entrada vinculada a la seva aplicació
when should the todo or phonecall be started, it shows up from that date in the filter open or own open (startpage) infolog ca Quan s'han d'iniciar les Tasques Pendents o les Trucades Telefòniques. Es mostra a la data del filtre o en obrir manualment a la plana d'inici
which additional fields should the responsible be allowed to edit without having edit rights?<br />status, percent and date completed are always allowed. infolog ca Quins camps adicionals pot editar el responsable sense tenir drets per editar?<br />,Estat, percentatge i data complerta sempre estan permesos.
which implicit acl rights should the responsible get? infolog ca Quins drets ACL implícits ha de tenir el responsable?
whole query infolog ca consulta sencera
will-call infolog ca trucarà
write (add or update) a record by passing its fields. infolog ca Escriu (afegeix o actualitza) un registre, introduïnt els seus camps.
yes - delete infolog ca Sí - Esborrar
yes - delete including sub-entries infolog ca Sí - Esborrar incloent subentrades
you can't delete one of the stock types !!! infolog ca No podeu esborrar un dels tipus de cotitzacions !!
you have entered an invalid ending date infolog ca Heu entrat una data incorrecta de finalització
you have entered an invalid starting date infolog ca Heu entrat una data incorrecta d'inici
you have to enter a name, to create a new typ!!! infolog ca Heu d'entrar un nom per crear un tipus nou!!!
you must enter a subject or a description infolog ca Heu d'entrar un assumpte o descripció
your database is not up to date (%1 vs. %2), please run %3setup%4 to update your database. infolog ca La teva base de dades NO està al dia (%1 vs. %2), per favor, executa %3setup%4 per actualitzar la teva base de dades.

View File

@ -75,7 +75,7 @@ completed infolog cs Ukončeno
configuration infolog cs Konfigurace
confirm infolog cs Potvrdit
contact infolog cs Kontakt
copy of: infolog cs Kopie (čeho):
copy of: infolog cs Kopie z:
copy your changes to the clipboard, %1reload the entry%2 and merge them. infolog cs Zkopírujte Vaše úpravy do clipboardu, %1načtěte znovu záznam%2 a slučte je.
create new links infolog cs Vytvořit nové odkazy
creates a new field infolog cs vytvoří novou položku
@ -104,6 +104,7 @@ default filter for infolog infolog cs Výchozí filtr pro InfoLog
default status for a new log entry infolog cs výchozí stav pro nový záznam
delegated infolog cs delegované
delegated open infolog cs delegované otevřené
delegated open and upcoming infolog cs delegované otevřené a nadcházející
delegated overdue infolog cs delegované po termínu
delegated upcomming infolog cs delegované nadcházející
delegation infolog cs Pověření
@ -130,6 +131,7 @@ do you want to receive notifications as html-mails or plain text? infolog cs Chc
don't show infolog infolog cs NEzobrazovat InfoLog
done infolog cs hotovo
download infolog cs Stáhnout
due %1 infolog cs Do %1
duration infolog cs Trvání
e-mail: infolog cs E-mail:
each value is a line like <id>[=<label>] infolog cs každá hodnota je řádkem jako <id>[=<label>]
@ -179,7 +181,7 @@ infolog - import csv-file infolog cs InfoLog - Importovat CSV soubor
infolog - new infolog cs InfoLog - Nový
infolog - new subproject infolog cs InfoLog - Nový podprojekt
infolog - subprojects from infolog cs InfoLog - Podprojekty (čeho)
infolog copied - the copy can now be edited infolog cs InfoLog zkopírován - kopii lze nyní editovat
infolog copied - the copy can now be edited infolog cs Infolog byl zkopírován a kopii lze nyní editovat
infolog entry deleted infolog cs Záznam InfoLogu smazán
infolog entry saved infolog cs Záznam InfoLogu uložen
infolog filter for the main screen infolog cs Filtr InfoLogu pro hlavní obrazovku
@ -205,7 +207,6 @@ location infolog cs Umístění
longer textual description infolog cs delší textový popis
low infolog cs Nízká
max length of the input [, length of the inputfield (optional)] infolog cs maximální délka vstupu (volitelné)
modifierer infolog cs Modifikátor
name must not be empty !!! infolog cs Název nemůže být prázdný !!!
name of new type to create infolog cs název nově vytvářeného typu
never hide search and filters infolog cs Nikdy neskrývat hledání a filtry
@ -238,12 +239,14 @@ only if i get assigned or removed infolog cs Jen pokud budu přidělen nebo odeb
only the attachments infolog cs jen přílohy
only the links infolog cs jen odkazy
open infolog cs otevřít
open and upcoming infolog cs otevřené a nadcházející
optional note to the link infolog cs volitelná poznámka k odkazu
order infolog cs Pořadí
organization infolog cs Organizace
overdue infolog cs po termínu
own infolog cs vlastní
own open infolog cs vlastní otevřené
own open and upcoming infolog cs vlastní otevřené a nadcházející
own overdue infolog cs vlastní po termínu
own upcoming infolog cs vlastní nadcházející
parent infolog cs Nadřazený
@ -258,7 +261,7 @@ phone infolog cs Telefonní hovor
phone/email infolog cs Telefon/E-mail
phonecall infolog cs Telefonní hovor
planned infolog cs plánované
planned time infolog cs plánovaný čas
planned time infolog cs Plánovaný čas
price infolog cs Cena
pricelist infolog cs Ceník
primary link infolog cs hlavní odkaz
@ -284,6 +287,7 @@ remark infolog cs Poznámka
remove this link (not the entry itself) infolog cs Odstranit tento odkaz (ne celý záznam)
responsible infolog cs odpovědný
responsible open infolog cs odpovědný - otevřené
responsible open and upcoming infolog cs odpovědný - otevřené a nadcházející
responsible overdue infolog cs odpovědný - po termínu
responsible upcoming infolog cs odpovědný - nastávající
responsible user, priority infolog cs odpovědný uživatel, priorita
@ -307,8 +311,8 @@ select an entry to link with infolog cs Vybrat záznam, na který se má odkazov
select to filter by owner infolog cs vybrat pro filtrování dle vlastníka
select to filter by responsible infolog cs vybrat pro filtrování dle odpovědné osoby
sender infolog cs Odesílatel
set status to done infolog cs Změnit stav na hotovo
set status to done for all entries infolog cs Změnit stav na hotovo u všech záznamů
set status to done infolog cs Nastavit stav na hotovo
set status to done for all entries infolog cs Nastavit stav na hotovo pro všechny záznamy
sets the status of this entry and its subs to done infolog cs Nastavit stav záznamu a jeho podpoložek na hotový
should infolog show subtasks, -calls or -notes in the normal view or not. you can always view the subs via there parent. infolog cs Má InfoLog zobrazovat Pod-úkoly, -hovory nebo -poznámky v normálním zobrazení nebo ne? Podpoložky můžete vždy zobrazit přes jejich rodiče.
should infolog show the links to other applications and/or the file-attachments in the infolog list (normal view when you enter infolog). infolog cs Má InfoLog zobrazovat odkazy na ostatní aplikace nebo připojené soubory v seznamu InfoLogu nebo ne (normální zobrazení při vstupu do InfoLogu)?
@ -330,6 +334,7 @@ start a new search, cancel this link infolog cs začít nové hledání, zrušit
startdate infolog cs Počáteční datum
startdate enddate infolog cs Počáteční datum Datum dokončení
startdate for new entries infolog cs Počáteční datum pro nové záznamy
starting %1 infolog cs Začíná %1
startrecord infolog cs První záznam
status infolog cs Stav
status ... infolog cs Stav ...
@ -338,6 +343,7 @@ sub-entries become subs of the parent or main entries, if there's no parent info
subject infolog cs Předmět
sum infolog cs Celkem
task infolog cs Úkol
tasks of infolog cs Úkoly
template infolog cs Šablona
test import (show importable records <u>only</u> in browser) infolog cs Testovat import (zobrazit importovatelné záznamy <u>jen</u> v prohlížeči)
the name used internaly (<= 10 chars), changeing it makes existing data unavailible infolog cs název používaný interně (<= 10 znaků), jeho změna způsobí nedostupnost stávajících dat
@ -376,6 +382,7 @@ when should the todo or phonecall be started, it shows up from that date in the
which additional fields should the responsible be allowed to edit without having edit rights?<br />status, percent and date completed are always allowed. infolog cs Které dodatečné položky má mít odpovědná osoba možnost editovat bez toho, že by měla práva k editaci?<br>Stav, procento a datum dokončení může vždy.
which implicit acl rights should the responsible get? infolog cs Jaká implicitní ACL práva má odpovědná osoba dostat?
which types should the calendar show infolog cs Které typy má kalendář zobrazovat
whole query infolog cs celý dotaz
will-call infolog cs zavolá
write (add or update) a record by passing its fields. infolog cs Zapsat (přidat nebo smazat) záznam zadáním jeho položek.
yes - close infolog cs Ano - uzavřít

488
infolog/lang/egw_de.lang Normal file
View File

@ -0,0 +1,488 @@
%1 days in advance infolog de %1 Tage im Voraus
%1 deleted infolog de %1 gelöscht
%1 deleted by %2 at %3 infolog de %1 wurde von %2 am %3 gelöscht
%1 entries %2 infolog de %1 Einträge %2
%1 entries %2, %3 failed because of insufficent rights !!! infolog de %1 Einträge %2, %3 wegen fehlender Rechte!!!
%1 modified infolog de %1 geändert
%1 modified by %2 at %3 infolog de %1 wurde von %2 am %3 geändert
%1 records imported infolog de %1 Datensätze importiert
%1 records read (not yet imported, you may go %2back%3 and uncheck test import) infolog de %1 Datensätze gelesen (noch nicht importiert, sie können %2zurück%3 gehen und Test Import ausschalten)
%1 you are responsible for is due at %2 infolog de %1 für die Sie verantwortlich sind ist am %2 fällig
%1 you are responsible for is starting at %2 infolog de %1 für die Sie verantwortlich sind startet am %2
%1 you delegated is due at %2 infolog de %1 die Sie delegierten ist am %2 fällig
%1 you delegated is starting at %2 infolog de %1 die Sie delegierten startet am %2
- subprojects from infolog de - Untereinträge von
0% infolog de 0%
10% infolog de 10%
100% infolog de 100%
20% infolog de 20%
30% infolog de 30%
40% infolog de 40%
50% infolog de 50%
60% infolog de 60%
70% infolog de 70%
80% infolog de 80%
90% infolog de 90%
<b>file-attachments via symlinks</b> instead of uploads and retrieval via file:/path for direct lan-clients infolog de <b>Dateianhänge über Symlinks</b> anstatt sie hochzuladen und Zugriff mit file:/pfad für lokale Benutzer
a short subject for the entry infolog de einen kurzen Titel für diesen Eintrag
abort without deleting infolog de Abbruch ohne zu Löschen
accept infolog de bei Annahme
action infolog de Befehle
actions... infolog de Befehle...
actual date and time infolog de aktuelles Datum und Uhrzeit
add infolog de Hinzufügen
add / remove link infolog de Hinzufügen / Entfernen von Verknüpfungen
add a file infolog de Datei anhängen
add a new entry infolog de einen neuen Eintrag anlegen
add a new note infolog de eine neue Notiz anlegen
add a new phonecall infolog de einen neuen Telefonanruf anlegen
add a new sub-task, -note, -call to this entry infolog de einen neuen Unterauftrag, -notiz, -anruf zu diesem Eintrag anlegen
add a new todo infolog de einen neuen Auftrag anlegen
add file infolog de Datei hinzufügen
add or delete links infolog de Verknüpfungen hinzufügen oder löschen
add sub infolog de neuen Untereintrag anlegen
add timesheet entry infolog de Stundenzettel Eintrag hinzufügen
add: infolog de Hinzufügen:
added infolog de Hinzugefügt
all infolog de alle
all links and attachments infolog de alle Verknüpfungen und Anhänge
all projects infolog de Alle Projekte
allows to set the status of an entry, eg. set a todo to done if it's finished (values depend on entry-typ) infolog de erlaubt den Status eines Eintrags zu setzen, zB. eine Aufgabe auf erledigt wenn sie beendet ist (Werte hängen vom Type des Eintrags ab)
alternatives infolog de Alternativen
apply the action on the whole query, not only the shown entries!!! infolog de Wendet den Befehl auf die gesamte Abfrage an, NICHT nur auf die angezeigten Datensätze !!
apply the changes infolog de Übernimmt die Änderungen
archive infolog de Archiviert
are you shure you want to close this entry ? infolog de Wollen Sie wirklich den Infolog schliessen?
are you shure you want to delete this entry ? infolog de Sind Sie sicher, dass Sie diesen Eintrag löschen wollen?
at the moment the following document-types are supported: infolog de Zur Zeit werden die folgenden Dokument Typen unterstützt:
attach a file infolog de Datei anhängen
attach file infolog de Datei anhängen
attention: no contact with address %1 found. infolog de Achtung: Kein Kontakt mit der Adresse %1 gefunden!
back to main list infolog de Zurück zur Gesamtliste
billed infolog de abgerechnet
both infolog de Annahme+erledigt
call infolog de anrufen
can be used to show further infolog types in the calendar or limit it to show eg. only tasks. infolog de Kann dazu benutzt werden um weitere InfoLog Typen im Kalender anzuzeigen oder zu begrenzen dass z.B. nur noch Aufgaben angezeigt werden.
cancel infolog de Abbruch
cancelled infolog de abgesagt
categories infolog de Kategorien
category infolog de Kategorie
change category infolog de Kategorie ändern
change completed infolog de Änderungen duchgeführt
change completion infolog de Bearbeitungsstatus ändern
change history infolog de Änderungsverlauf
change owner when updating infolog de Ändert den Benutzer bei bei diesem Änderungsvorgang
change responsible infolog de Zuständigkeit ändern
change status: infolog de Status ändern
change the status of an entry, eg. close it infolog de Status eines Eintrags ändern, z.B. Ihn als erledigt markieren
change type: infolog de Infolog Typ ändern
changed category to %1 infolog de Kategorie geändert zu %1
changed completion to %1% infolog de Bearbeitungsstatus geändert zu %1
changed responsible infolog de Zuständigkeit ändern
changed status to %1 infolog de Typ geändert zu %1
changed type infolog de Typ ändern
charset of file infolog de Zeichensatz der Datei
check all infolog de Alle marktieren
check to set startday infolog de ankreuzen um Startdatum zu setzen
check to specify custom contact infolog de Ankreuzen um einen eigenen Kontakt anzugeben
choose owner of imported data infolog de Wählen Sie den Eigentümer der Importierten Daten
click here to create the link infolog de hier klicken um die Verknüpfung zu erzeugen
click here to start the search infolog de hier klicken um die Suche zu starten
close infolog de Schließen
close all infolog de Alle schließen
close this entry and all listed sub-entries infolog de Diesen Eintrag und alle Untereinträge schliessen
closed infolog de geschlossen
colon (:) separated list of field names to use if value is empty or to sum up infolog de Spalte (:) Liste von Feldnamen, die für eine Summierung verwendet werden können
comment infolog de Kommentar
compare infolog de vergleichen
completed infolog de Erledigt
configuration infolog de Konfiguration
confirm infolog de Bestätigung
contact infolog de Kontakt
contact cf infolog de Kontakt CF
contact fields infolog de Kontaktfelder
contactfield infolog de Kontaktfelder
copy of: infolog de Kopie von:
copy your changes to the clipboard, %1reload the entry%2 and merge them. infolog de Kopieren Sie ihre Änderungen in die Zwischenablage, %1laden den Eintrag neu%2 und fügen diese wieder ein.
create new links infolog de Neue Verknüpfung erzeugen
creates a new field infolog de erstellt ein neues Feld
creates a new status with the given values infolog de erstellt einen neuen Status mit den angegebenen Werten
creates a new typ with the given name infolog de erstellt einen neuen Typ mit dem eingegebenen Namen
creation infolog de Erstellung
csv-fieldname infolog de CSV-Feldname
csv-filename infolog de CSV-Dateiname
csv-import common de CSV-Import
custom infolog de Benutzerdefiniert
custom contact-address, leave empty to use information from most recent link infolog de benutzerdefinierte Kontaktadresse, leer lassen um die Daten der Verknüpfung zu verwenden
custom contact-information, leave emtpy to use information from most recent link infolog de benutzerdefinierte Kontaktinformationen, leer lassen um die Daten der Verknüpfung zu verwenden
custom fields infolog de Benutzerdefinierte Felder
custom fields, typ and status common de Benutzerdefinierte Felder, Typen und Status
custom from infolog de Benutzerdefinierter Kontakt
custom regarding infolog de Benutzerdefinierter Bezug
custom status for typ infolog de Benutzerdefinierter Status für Typ
customfields infolog de Benutzerdefinierte Felder
date completed infolog de Erledigt am
date completed (leave it empty to have it automatic set if status is done or billed) infolog de Fertigstellungsdatum (leer lassen um es automatisch zu setzen wenn der Status erledigt oder abgerechnet ist)
datecreated infolog de Erstellt am
dates, status, access infolog de Daten, Status, Zugriff
days infolog de Tage
default category for new infolog entries infolog de Vorgabe Kategorie für neue InfoLog Einträge
default document to insert entries infolog de Standarddokument zum Einfügen von Infologs
default filter for infolog infolog de Standard-Filter für InfoLog
default status for a new log entry infolog de Vorgabe für den Status eines neuen Eintrags
delegated infolog de delegiert
delegated open infolog de delegiert offen
delegated open and upcoming infolog de delegiert offene und zukünftige
delegated overdue infolog de delegiert überfällig
delegated upcomming infolog de delegiert zukünftig
delegation infolog de Delegation
delete infolog de Löschen
delete one record by passing its id. infolog de Einen Datensatz spezifiziert durch seine id löschen.
delete selected entries? infolog de Sollen die ausgewählen Datensätze gelöscht werden?
delete the entry infolog de Eintrag löschen
delete this entry infolog de diesen Eintrag löschen
delete this entry and all listed sub-entries infolog de Diesen Eintrag und all aufgelisteten Untereinträge löschen
deleted infolog de gelöscht
deletes the selected typ infolog de löscht den ausgewählten Typ
deletes this field infolog de löscht dieses Feld
deletes this status infolog de löscht diesen Status
description infolog de Beschreibung
determines the order the fields are displayed infolog de legt die Reihenfolge fest in der die Felder angezeigt werden
directory with documents to insert entries infolog de Verzeichnis mit Dokumenten zum Einfügen von Infologs
disables a status without deleting it infolog de deaktiviert einen Status ohne ihn zu löschen
do not notify of these changes infolog de Keine Benachrichtigung senden
do you want a confirmation of the responsible on: accepting, finishing the task or both infolog de Wollen Sie eine Bestätigung des Verantwortlichen bei: Annahme, Beendigung der Aufgabe oder bei beidem
do you want a notification, if items get assigned to you or assigned items get updated? infolog de Wollen Sie eine Benachrichtigung, wenn Einträge Ihnen zugewiesen werden oder zugewiesene Einträge geändert werden?
do you want a notification, if items you are responsible for are about to start? infolog de Wollen Sie eine Benachrichtigung, wenn Einträge für die Sie verantwortlich sind beginnen sollen?
do you want a notification, if items you are responsible for are due? infolog de Wollen Sie eine Benachrichtigung, wenn Einträge für die Sie verantwortlich sind fällig werden?
do you want a notification, if items you created get updated? infolog de Wollen Sie eine Benachrichtigung, wenn Einträge die Sie angelegt haben aktualisiert werden?
do you want a notification, if items you delegated are about to start? infolog de Wollen Sie eine Benachrichtigung, wenn Einträge die Sie delegiert haben beginnen sollen?
do you want a notification, if items you delegated are due? infolog de Wollen Sie eine Benachrichtigung, wenn Einträge die Sie delegiert haben fällig werden?
do you want to receive notifications as html-mails or plain text? infolog de Wollen Sie die Benachrichtigungen als HTML Mail oder reinen Text empfangen?
document '%1' does not exist or is not readable for you! infolog de Dokument '%1' existiert nicht, oder ist nicht lesbar für Sie!
don't show infolog infolog de InfoLog NICHT anzeigen
done common de erledigt
download infolog de Datei laden
due %1 infolog de %1 fällig
duration infolog de Dauer
e-mail: infolog de E-Mail
each value is a line like <id>[=<label>] infolog de jeder Wert ist eine Zeile im Format id=[angezeigter Wert]
edit infolog de Bearbeiten
edit or create categories for ingolog infolog de Kategorien für InfoLog bearbeiten oder neu anlegen
edit rights (full edit rights incl. making someone else responsible!) infolog de Bearbeitungsrechte (komplettes Bearbeiten einschl. jemand anderen dafür verantwortlich machen!)
edit status infolog de Status ändern
edit the entry infolog de Eintrag bearbeiten
edit this entry infolog de diesen Eintrag bearbeiten
empty for all infolog de leer für alle
end infolog de Ende
enddate infolog de Fällig am
enddate can not be before startdate infolog de Das Fälligkeitsdatum kann nicht vor dem Startdatum liegen
enter a custom contact, leave empty if linked entry should be used infolog de benutzerdefinierter Kontakt, leer lassen um die Daten der Verknüpfung zu verwenden
enter a custom phone/email, leave empty if linked entry should be used infolog de benutzerdefinierte Telefonnumer/E-Mail-Adresse, leer lassen um die Daten der Verknüpfung zu verwenden
enter a textual description of the log-entry infolog de geben Sie eine textliche Beschreibung des Eintrags ein
enter the query pattern infolog de geben Sie ein Suchmuster ein
entry and all files infolog de Eintrag und alle Dateien
error: saving the entry infolog de Fehler: beim Speichern des Eintrags
error: the entry has been updated since you opened it for editing! infolog de Fehler: der Eintrag wurde geändert seit Sie ihn zum Bearbeiten geöffnet haben!
example {{if n_prefix~mr~hello mr.~hello ms.}} - search the field "n_prefix", for "mr", if found, write hello mr., else write hello ms. infolog de Beispiel: "{{IF n_prefix~Herr~Sehr geehrter~Sehr geehrte}}" - suche in dem Feld "n_prefix" nach "Herr", wenn gefunden, schreibe "Sehr geehrter", wenn nicht gefunden schreibe "Sehr geehrte". Es ist auch möglich anstatt fixer Werte, den Wert eines andren Feldes zu übernehmen. Beispiel (Land wird nur dann angezeigt, denn es nicht DEUTSCHLAND ist: }
example {{letterprefixcustom n_prefix title n_family}} - example: mr dr. james miller infolog de Beispiel für {{LETTERPREFIXCUSTOM n_prefix title n_family}} - Beispiel: Herr Dr. James Miller
example {{nelf role}} - if field role is not empty, you will get a new line with the value of field role infolog de Beispiel {{NELF role}} - Erzeugt einen Zeilenumbruch, wenn das Feld role nicht leer ist. Der Wert des Feldes role (Funktion) wird nach dem Zeilenumbruch ausgegeben.
example {{nelfnv role}} - if field role is not empty, set a lf without any value of the field infolog de Beispiel {{NELFNV role}} -Erzeugt einen Zeilenumbruch, wenn das Feld role (Funktion) einen Wert besitzt. Der Wert role (Funktion) wird auch bei Vorhandensein des Feldes role nicht ausgegeben.
execute a further action for this entry infolog de Eine weitere Aktion für diesen Eintrag ausführen
existing links infolog de Bestehende Verknüpfungen
exists infolog de Besteht
export definitition to use for nextmatch export infolog de Export Profil der Listenansicht (Disketten Symbol)
exports infolog entries into a csv file. infolog de Exportiert Infolog Einträge in einen CSV Datei.
fax infolog de Fax
field must not be empty !!! infolog de Feld darf nicht leer sein !!!
fieldseparator infolog de Feldbegrenzer
finish infolog de wenn erledigt
first argument for preg_replace infolog de Ersten Argument für pre_replace
for serial letter use this tag. put the content, you want to repeat between two tags. infolog de Für Serienbriefe benutzen Sie folgenden Platzhalter. Fügen die den Inhalt zwischen diese beiden Platzhalter ein.
for which types should this field be used infolog de für welche Typen soll dieses Feld benutzt werden
from infolog de Von
general infolog de Allgemein
general fields: infolog de Allgemeine Felder
global categories infolog de Globale Kategorien
group owner for infolog de Gruppeneigentümer für
high infolog de hoch
history infolog de Historie
history logging infolog de Protokollierung der Historie
history logging and deleting of items infolog de Protokollierung der Historie und löschen von Einträgen
how many describtion lines should be directly visible. further lines are available via a scrollbar. infolog de Wie viele Zeilen der Beschreibung sollen direkt sichtbar sein. Zusätzliche Zeilen können über einen Rollbalken (scrollbar) erreicht werden.
how wide should the description area be. this value is numeric and interpreted as em; 60 works reasonably well. infolog de Wie breit darf das Beschreibungsfeld sein. Sie können hier einen nummerischen Wert eintragen.
id infolog de Id
id# infolog de Id#
if a type has a group owner, all entries of that type will be owned by the given group and not the user who created it! infolog de Wenn ein Typ einen Gruppeneigentümer hat, gehören alle Einträge dieses Typs der angegebenen Gruppe und NICHT dem Benutzer der sie angelegt hat!
if not set, the line with search and filters is hidden for less entries then "max matches per page" (as defined in your common preferences). infolog de Falls nicht gesetzt, wird die Suche und die Filter ausgeblendet für weniger Einträge als "maximale Treffer pro Seite" (in ihren allgemeinen Einstellungen definiert).
if you specify a directory (full vfs path) here, infolog displays an action for each document. that action allows to download the specified document with the infolog data inserted. infolog de Wenn Sie hier ein Verzeichnis angeben (kompletter Pfad des VFS), erstellt das Infolog eine Aktion zum Einfügen der Infolog Daten für jeden Dokument.
if you specify a document (full vfs path) here, infolog displays an extra document icon for each entry. that icon allows to download the specified document with the contact data inserted. infolog de Wenn Sie hier ein Verzeichnis angeben (kompletter Pfad des VFS), erstellt das Infolog eine Aktion zum Einfügen der Infolog Daten für jeden Dokument.
if you specify an export definition, it will be used when you export infolog de Wählen Sie eine Export Definition für den Export.
import infolog de Import
import next set infolog de Nächsten Satz importieren
importance infolog de Wichtigkeit
imports entries into the infolog from a csv file. csv means 'comma seperated values'. however in the options tab you can also choose other seperators. infolog de Importiert Einträge in das Infolog Modul aus einer CSV Datei (Komma getrennte Werte).
info log common de InfoLog
infolog common de InfoLog
infolog - delete infolog de InfoLog - Löschen
infolog - edit infolog de InfoLog - Bearbeiten
infolog - import csv-file infolog de InfoLog - Import CSV-Datei
infolog - new infolog de InfoLog - Anlegen
infolog - new subproject infolog de InfoLog - Anlegen Teilprojekt
infolog - subprojects from infolog de InfoLog - Teilprojekte von
infolog copied - the copy can now be edited infolog de Infolog Kopie - Diese Kopie kann jetzt bearbeitet werden
infolog csv export infolog de Infolog CSV Export
infolog csv import infolog de Infolog CSV Import
infolog entry deleted infolog de InfoLog Eintrag gelöscht
infolog entry saved infolog de InfoLog Eintrag gespeichert
infolog fields: infolog de Infolog Felder
infolog filter for the main screen infolog de InfoLog Filter für Startseite
infolog id infolog de Infolog ID
infolog list infolog de InfoLog Liste
infolog preferences common de InfoLog Einstellungen
infolog-fieldname infolog de InfoLog-Feldname
insert infolog de einfügen
insert in document infolog de In ein Dokument einfügen
invalid filename infolog de Ungültiger Dateiname
invalid owner id: %1. might be a bad field translation. used %2 instead. infolog de Ungültige Eigentümer ID: %1. Ist möglicher Weise eine falsche Feld Übersetzung. Es wird %2 anstatt dem ursprünglichen Wert verwendet.
invalid status for entry type %1. infolog de Ungültiger Status für den eingegebenen Typ %1
label<br>helptext infolog de Beschriftung<br>Hilfetext
last changed infolog de letzte Änderung
last modified infolog de zuletzt geändert
leave blank to get the used time calculated by timesheet entries infolog de Leer lassen um die Zeit nach den Stundenzetteln zu bekommen
leave it empty infolog de leer lassen
leave it empty for a full week infolog de leer lassen, für eine gesamte Woche
leave without saveing the entry infolog de Abbruch ohne den Eintrag zu speichern
leaves without saveing infolog de Abbruch ohne speichern
length<br>rows infolog de Länge<br />Zeilen
limit number of description lines (default 5, 0 for no limit) infolog de Begrenze Anzahl Beschreibungszeilen (Vorgabe 5, 0 für keine Grenze)
limit width of description column ((effective only if lines limit is set), 0 for no limit) infolog de Beschränkt die Breite der Spalte Beschreibung. Setzten Sie den Wert 0, für kein Limit.
link infolog de Verknüpfung
linked to %1 infolog de Verknüpft mit %1
links infolog de Verknüpfungen
links of this entry infolog de Verknüpfungen dieses Eintrags
list all categories infolog de Alle Kategorien anzeigen
list no subs/childs infolog de Untereinträge nicht anzeigen
location infolog de Ort
longer textual description infolog de längere textliche Beschreibung
low infolog de niedrig
manage mapping infolog de Verwaltung der Zuordnung
max length of the input [, length of the inputfield (optional)] infolog de max. Länge der Eingabe [, Länge des Eingabefeldes (optional)]
modifier infolog de Geändert von
modifierer infolog de Geändert von
name must not be empty !!! infolog de Name darf nicht leer sein !!!
name of current user, all other contact fields are valid too infolog de Name des aktiven Benutzers, all anderen Kontakt Felder sind weiterhin gültig
name of new type to create infolog de Name des neu anzulegenden Typs
never hide search and filters infolog de Suche und Filter niemals ausblenden
new %1 infolog de Neue %1
new %1 created by %2 at %3 infolog de Neue %1 wurde von %2 am %3 angelegt
new name infolog de neuer Name
new search infolog de Neue Suche
no - cancel infolog de Nein - Abbruch
no describtion, links or attachments infolog de Keine Beschreibung, Verknüpfungen oder Anhänge
no details infolog de Keine Details
no entries found, try again ... infolog de Kein Einträge gefunden, nochmal versuchen ...
no filter infolog de kein Filter
no links or attachments infolog de keine Verknüpfungen oder Anhänge
no project infolog de Kein Projekt
nonactive infolog de Nicht aktiv
none infolog de keine
normal infolog de normal
not infolog de nicht
not assigned infolog de nicht zugewiesen
not-started infolog de nicht gestartet
note infolog de Notiz
number of records to read (%1) infolog de Anzahl Datensätze lesen (%1)
number of row for a multiline inputfield or line of a multi-select-box infolog de Anzahl Zeilen für ein mehrzeiliges Eingabefeld oder eines mehrfachen Auswahlfeldes
offer infolog de Angebot
one day after infolog de am nächsten Tag
one day in advance infolog de am Vortag
ongoing infolog de in Arbeit
only for details infolog de Nur bei Details
only if i get assigned or removed infolog de Nur wenn ich zugewiesen oder entfernt werde
only the attachments infolog de nur die Anhänge
only the links infolog de nur die Verknüpfungen
open infolog de offen
open and upcoming infolog de zukünftige und offene
optional note to the link infolog de zusätzliche Notiz zur Verknüpfung
order infolog de Reihenfolge
organization infolog de Organisation
overdue infolog de überfällig
own infolog de eigene
own open infolog de eigene offen
own open and upcoming infolog de eigene offene und zukünftige
own overdue infolog de eigene überfällig
own upcoming infolog de eigene zukünftig
owner does not have edit rights infolog de Der Besitzer dieses Datensatzes benötigt Bearbeitungsrechte
parent infolog de Elterneintrag
parent infolog infolog de Übergeordneter Infolog
path on (web-)serverside<br>eg. /var/samba/share infolog de Pfad auf (Web-)Server<br>zB. /var/samba/Share
path to user and group files has to be outside of the webservers document-root!!! infolog de Pfad zu Benutzer- und Gruppen-Dateien MUSS AUSSERHALB des Wurzelverzeichnisses des Webservers (document-root) liegen!!!
pattern for search in addressbook infolog de Muster für Suche im Adressbuch
pattern for search in projects infolog de Muster für Suche des Projekts
percent completed infolog de Prozent erledigt
permission denied infolog de Zugriff verweigert
permissions error - %1 could not %2 infolog de Fehler in den Zugriffsberechtigungen - %1 could not %2
phone infolog de Anruf
phone/email infolog de Telefon/E-Mail
phonecall infolog de Telefonanruf
planned infolog de geplant
planned time infolog de geplante Zeit
price infolog de Preis
pricelist infolog de Preisliste
primary link infolog de Primäre Verknüpfung
priority infolog de Priorität
private infolog de Privat
project infolog de Projekt
project settings: price, times infolog de Einstellungen zum Projekt: Preis, Zeiten
projectmanager infolog de Projektmanager
re-planned infolog de Umgeplant
re-planned time infolog de Umgeplante Zeit
re: infolog de Re:
read one record by passing its id. infolog de Einen Datensatz spezifiziert durch seine id lesen.
read rights (default) infolog de Leserechte (Vorgabe)
receive notifications about due entries you are responsible for infolog de Benachrichtigungen über fällige Einträge für die Sie verantwortlich sind
receive notifications about due entries you delegated infolog de Benachrichtigungen über fällige Einträge die Sie delegiert haben
receive notifications about items assigned to you infolog de Benachrichtigungen über Einträge für die Sie verantwortlich sind
receive notifications about own items infolog de Benachrichtigungen über eigene Einträge
receive notifications about starting entries you are responsible for infolog de Benachrichtigungen über zu startende Einträge für die Sie verantwortlich sind
receive notifications about starting entries you delegated infolog de Benachrichtigungen über zu startende Einträge die Sie delegiert haben
receive notifications as html-mails infolog de Benachrichtigungen als HTML Mails
regular expression infolog de Reguläre Ausdrücke
remark infolog de Bemerkung
remove this link (not the entry itself) infolog de Diese Verknüpfung lösen (nicht den Eintrag selbst)
removed infolog de Entfernt
replacement infolog de Platzhalter
replacements for inserting entries into documents infolog de Platzhalter für das Einfügen in Dokumente
responsible infolog de verantwortlich
responsible open infolog de verantwortlich offen
responsible open and upcoming infolog de verantwortlich offene und zukünftige
responsible overdue infolog de verantwortlich überfällig
responsible upcoming infolog de verantwortlich zukünftig
responsible user, priority infolog de Verantwortlicher, Priorität
returns a list / search for records. infolog de Liefert eine Liste von / sucht nach Datensätzen.
rights for the responsible infolog de Rechte für den Verantwortlichen
same day infolog de gleichen Tag
save infolog de Speichern
saves the changes made and leaves infolog de speichert die Änderungen und beendet
saves this entry infolog de diesen Eintrag speichern
search infolog de Suchen
search for: infolog de Suchen nach:
second parameter for preg_replace infolog de 2. Paramter für preg_replace
select infolog de Auswählen
select a category for this entry infolog de eine Kategorie für diesen Eintrag auswählen
select a price infolog de Preis auswählen
select a priority for this task infolog de eine Priorität für diesen Eintrag auswählen
select a project infolog de Projekt auswählen
select a responsible user: a person you want to delegate this task infolog de Verantwortlichen auswählen: Person(en) der Sie diese Aufgabe delegieren wollen
select a typ to edit it's status-values or delete it infolog de einen Type auswählen um seine Statuswerte zu ändern oder ihn zu löschen
select an action infolog de Befehl oder Aktion auswählen
select an action... infolog de Befehl oder Aktion auswählen...
select an app to search in infolog de eine Anwendung zum Durchsuchen auswählen
select an entry to link with infolog de einen Eintrag zum Verknüpfen auswählen
select multiple contacts for a further action infolog de Wählen Sie mehrere Konatkte für eine weitere Aktion aus.
select new category infolog de Wählen Sie eine Kategory
select to filter by owner infolog de Besitzer zum Filtern auswählen
select to filter by responsible infolog de Verantwortlichen zum Filtern auswählen
select users or groups infolog de Wählen Sie Benutzer oder Gruppen aus
selection cf infolog de Auswahl für CF
sender infolog de Absender
set status to done infolog de Status auf erledigt setzen
set status to done for all entries infolog de Status für alle Einträge auf erledigt setzen
sets the status of this entry and its subs to done infolog de Setzt den Status dieses Eintrags und seiner Untereinträge auf erledigt
sets the status of this entry to done infolog de Setzt den Status für diesen Infolog auf erledigt
should infolog show subtasks, -calls or -notes in the normal view or not. you can always view the subs via there parent. infolog de Soll InfoLog Untereinträge in der normalen Ansicht anzeigen oder nicht. Sie können die Untereinträge immer über deren Haupteintrag anzeigen.
should infolog show the links to other applications and/or the file-attachments in the infolog list (normal view when you enter infolog). infolog de Soll InfoLog die Verknüpfungen zu anderen Anwendungen und/oder die Datei-Anhänge in der InfoLog Liste (normale Ansicht wenn InfoLog aufgerufen wird) anzeigen.
should infolog show up on the main screen and with which filter. works only if you dont selected an application for the main screen (in your preferences). infolog de Soll InfoLog auf der Startseite angezeigt werden und mit welchem Filter. Funktioniert nur, wenn Sie keine (einzelne) Anwendung für die Startseite ausgewählt haben (in Ihren Einstellungen).
should infolog use full names (surname and familyname) or just the loginnames. infolog de Soll InfoLog den vollen Namen (Vor- und Familienname) oder nur die Benutzerkennung verwenden.
should the infolog list show a unique numerical id, which can be used eg. as ticket id. infolog de Soll die InfoLog Liste eine eindeutige Nummer anzeigen, die zB. als Ticketnummer verwendet werden kann.
should the infolog list show the column "last modified". infolog de Soll die InfoLog Liste die Spalte "Zuletzt geändert" anzeigen.
should the infolog list show the percent done only for status ongoing or two separate icons. infolog de Soll die InfoLog Liste Prozent erledigt nur für den Status "in Arbeit" anzeigen oder zwei separate Icons.
should this entry only be visible to you and people you grant privat access via the acl infolog de soll dieser Eintrag nur sichtbar sein für Sie und Personen denen Sie privaten Zugriff über die ACL erlaubt haben
show a column for used and planned times in the list. infolog de Zeige eine Spalte für benutzte und geplante Zeiten in der List.
show a x if content equals this compare value, otherwise show nothing infolog de zeigt ein X an, wenn der Inhalt mit dem Vergleichswert übereinstimmt, ansonsten erfolgt keine Anzeige.
show full usernames infolog de Kompletten Benutzernamen anzeigen
show in the infolog list infolog de In der InfoLog Liste anzeigen
show last modified infolog de Zuletzt geändert anzeigen
show status and percent done separate infolog de Status und Prozent erledigt separat anzeigen
show ticket id infolog de Ticketnummer anzeigen
show times infolog de Zeiten anzeigen
small view infolog de schmale Ansicht
start infolog de Start
start a new search, cancel this link infolog de eine neue Suche starten, diese Verknüpfung abbrechen
startdate infolog de Startdatum
startdate enddate infolog de Startdatum Fälligkeitsdatum
startdate for new entries infolog de Startdatum für neue Einträge
starting %1 infolog de %1 startet
startrecord infolog de Startdatensatz
status infolog de Status
status ... infolog de Status ...
sub infolog de Unter-<br />einträge
sub-entries become subs of the parent or main entries, if there's no parent infolog de Untereinträge gehören dann zum übergeordneten Eintrag oder werden Haupteinträge wenn es keinen übergeordneten gibt.
sub-entries will not be closed infolog de Untereinträge werden nicht geschlossen
subject infolog de Titel
sum infolog de Summe
tag to mark positions for address labels infolog de Platzhalter, um die Position der Adresslabels festzulegen
task infolog de Aufgabe
tasks of infolog de Aufgaben von
template infolog de Vorlage
test import (show importable records <u>only</u> in browser) infolog de Test Import (zeige importierbare Datensätze <u>nur</u> im Browser)
the name used internaly (<= 10 chars), changeing it makes existing data unavailible infolog de der Name wird intern benutzt (<= 10 Zeichen), wenn er geändert wird, werden existierende Daten unzugänglich
the name used internaly (<= 20 chars), changeing it makes existing data unavailible infolog de der Name wird intern benutzt (<= 20 Zeichen), wenn er geändert wird, werden existierende Daten unzugänglich
the text displayed to the user infolog de der Text der dem Benutzer angezeigt wird
this is the filter infolog uses when you enter the application. filters limit the entries to show in the actual view. there are filters to show only finished, still open or futures entries of yourself or all users. infolog de Das ist der Filter, den InfoLog benutzt wenn es das erste mal aufgerufen wird. Filter beschränken die aktuelle Anzeige. Es gibt Filter um nur beendete, offene oder zukünftige Einträge von Ihnen oder allen Benutzern anzuzeigen.
til when should the todo or phonecall be finished infolog de bis wann soll der Auftrag oder Anruf erledigt sein
times infolog de Zeiten
to many might exceed your execution-time-limit infolog de zu viel können Ihre Laufzeitbeschränkung überschreiten
to what should the startdate of new entries be set. infolog de Auf was soll das Startdatum von neuen Einträgen gesetzt werden.
today infolog de Heute
todays date infolog de heutiges Datum
todo infolog de Aufgabe
translation infolog de Translation
typ infolog de Typ
typ '%1' already exists !!! infolog de Typ '%1' existiert bereits !!!
type infolog de Typ
type ... infolog de Typ ...
type of customfield infolog de Typ des benutzerdefinierten Feldes
type of the log-entry: note, phonecall or todo infolog de Typ des Eintrags: Notiz, Anruf oder Auftrag
unlink infolog de Verknüpfung lösen
unlinked from %1 infolog de Verknüpfung entfernt von %1
upcoming infolog de zukünftig
urgency infolog de Priorität
urgent infolog de Dringend
use all infolog de Alle Einträge
use field from csv if possible infolog de Verwendet die Felder vom CSV, wenn möglich
use search results infolog de Verwendet die Sucherbegnisse
use this tag for addresslabels. put the content, you want to repeat, between two tags. infolog de Benutzern Sie diesen Platzhalter für Adressetiketten. Fügen Sie den Inhalt, den Sie wiederholen möchten zwischen zwei Platzhalter ein.
used time infolog de benötigte Zeit
values for selectbox infolog de Werte für die Auswahlbox
view all subs of this entry infolog de alle Untereinträge dieses Eintrag anzeigen
view other subs infolog de andere Untereinträge anzeigen
view parent infolog de übergeordneter Eintrag anzeigen
view subs infolog de Untereinträge anzeigen
view the parent of this entry and all his subs infolog de übergeordneter Eintrag mit allen seinen Untereinträgen anzeigen
view this linked entry in its application infolog de diesen verknüpfen Eintrag in seiner Anwendung anzeigen
when should the todo or phonecall be started, it shows up from that date in the filter open or own open (startpage) infolog de wann soll mit dem Auftrag oder Anruf begonnen werden, ab diesem Datum wird er beim Filter offen oder eigene offen angezeigt (Startseite)
which additional fields should the responsible be allowed to edit without having edit rights?<br />status, percent and date completed are always allowed. infolog de Welche zusätzlichen Felder soll der Verantwortliche bearbeiten können ohne Bearbeitungsrechte zu haben?<br />Status, Prozent und Datum erledigt sind immer erlaubt.
which implicit acl rights should the responsible get? infolog de Welche impliziten Zugriffsrechte soll der Verantwortliche bekommen?
which types should the calendar show infolog de Welche Typen soll der Kalender anzeigen
whole query infolog de gesamte Abfrage
will-call infolog de ruft zurück
write (add or update) a record by passing its fields. infolog de Schreiben (zufügen oder aktualisieren) eines Datensatzes durch Angabe seiner Felder.
yes - close infolog de Ja schließen
yes - close including sub-entries infolog de Ja- schließen inklusive Untereinträge
yes - delete infolog de Ja - Löschen
yes - delete including sub-entries infolog de Ja - Löschen einschließlich Untereinträge
yes, noone can purge deleted items infolog de Ja, niemand darf gelöschte Einträge bereinigen
yes, only admins can purge deleted items infolog de Ja, nur Admins dürfen gelöschte Einträge bereinigen
yes, with larger fontsize infolog de Ja, mit einer größeren Schrift
yes, with purging of deleted items possible infolog de Ja, jeder darf gelöschte Einträge bereinigen
you can choose a categorie to be preselected, when you create a new infolog entry infolog de Sie können eine Kategorie festlegen die vorausgewählt wird, wenn Sie neue InfoLog Einträge anlegen.
you can't delete one of the stock types !!! infolog de Sie können keinen der Standardtypen löschen!!!
you have entered an invalid ending date infolog de Sie haben ein ungültiges Fälligkeitsdatum eingegeben
you have entered an invalid starting date infolog de Sie haben ein ungültiges Startdatum eingegeben
you have to enter a name, to create a new typ!!! infolog de Sie müssen einen Namen angeben, um einen neuen Typ zu erzeugen!!!
you must enter a subject or a description infolog de Sie müssen einen Titel oder eine Beschreibung eingeben
you need to select an entry for linking. infolog de Sie müssen einen Datensatz auswählen, um eine Verknüpfung zu erstellen.
you need to select some entries first infolog de Bitte wählen Sie zuerst die Datensätze aus, die Sie bearbeiten wollen.
your database is not up to date (%1 vs. %2), please run %3setup%4 to update your database. infolog de Ihre Datenbank ist NICHT aktuell (%1 gegenüber %2), bitte rufen Sie %3setup%4 auf um ihre Datenbank zu aktualisieren.

348
infolog/lang/egw_el.lang Normal file
View File

@ -0,0 +1,348 @@
%1 days in advance infolog el %1 ημέρες πριν
%1 deleted infolog el %1 διαγράφηκαν
%1 deleted by %2 at %3 infolog el %1 διαγράφηκαν από %2 στις %3
%1 modified infolog el %1 επεξεργάστηκαν
%1 modified by %2 at %3 infolog el %1 επεξεργάστηκαν από %2 στις %3
%1 records imported infolog el %1 εγγραφές εισήχθησαν
%1 records read (not yet imported, you may go %2back%3 and uncheck test import) infolog el Έχουν διαβαστεί %1 εγγραφές (δεν έχουν εισαχθεί ακόμη, μπορείτε να πάτε %2 πίσω %3 και να ξετσεκάρετε το Τεστ Εισαγωγής)
- subprojects from infolog el - Υποκατηγορίες σχεδίων από
0% infolog el 0%
10% infolog el 10%
100% infolog el 100%
20% infolog el 20%
30% infolog el 30%
40% infolog el 40%
50% infolog el 50%
60% infolog el 60%
70% infolog el 70%
80% infolog el 80%
90% infolog el 90%
a short subject for the entry infolog el ένα μικρό θέμα για την καταχώρηση
abort without deleting infolog el Έξοδος χωρίς διαγραφή
accept infolog el αποδοχή
action infolog el Ενέργεια
actual date and time infolog el πραγματική ημερομηνία και ώρα
add infolog el Προσθήκη
add a file infolog el Προσθήκη αρχείου
add a new entry infolog el Προσθήκη νέας καταχώρησης
add a new note infolog el Προσθήκη νέας σημείωσης
add a new phonecall infolog el Προσθήκη νέας τηλεφωνικής κλήσης
add a new sub-task, -note, -call to this entry infolog el Προσθήκη νεόυ υπο-καθήκοντος,-σημείωσης,-κλήσης σε αυτή την καταχώρηση
add a new todo infolog el Προσθήκη νέας εκκρεμότητας
add file infolog el Προσθήκη αρχείου
add sub infolog el προσθήκη υπό
add: infolog el Προσθήκη:
all infolog el Όλα
all links and attachments infolog el όλοι οι σύνδεσμοι και οι επισυνάψεις
allows to set the status of an entry, eg. set a todo to done if it's finished (values depend on entry-typ) infolog el επιτρέπει τον καθορισμό της κατάστασης μιας καταχώρησης,π.χ. καθορισμός μιας εκκρεμότητας σε ολοκληρωμένη εάν τελείωσε (αξίες εξαρτώνται από τον τύπο της καταχώρησης)
apply the changes infolog el Εφαρμογή των αλλαγών
are you shure you want to delete this entry ? infolog el Είστε σίγουρος ότι θέλετε να διαγράψετε αυτή την καταχώρηση;
attach a file infolog el Επισύναψη αρχείου
attach file infolog el Επισύναψη αρχείου
attention: no contact with address %1 found. infolog el Προσοχή: Καμία Επαφή με τη διεύθυνση %1 δεν βρέθηκε.
back to main list infolog el Πίσω στη βασική λίστα
billed infolog el χρεώθηκε
both infolog el και οι δύο
call infolog el κλήση
cancel infolog el Ακύρωση
cancelled infolog el ακυρώθηκε
categories infolog el Κατηγορίες
category infolog el Κατηγορία
change the status of an entry, eg. close it infolog el Αλλαγή της κατάστασης μιας καταχώρησης, π.χ. κλείσιμο
charset of file infolog el Κωδικοσελίδα αρχείου
check to set startday infolog el Έλεγχος για ορισμό ημέρας έναρξης
check to specify custom contact infolog el Έλεγχος για καθορισμό επαφής πελάτη
click here to create the link infolog el κάντε κλικ εδώ για τη δημιουργία συνδέσμου
click here to start the search infolog el κάντε κλικ εδώ για να αρχίσετε την αναζήτηση
close infolog el Κλείσιμο
comment infolog el Σχόλιο
completed infolog el Ολοκληρώθηκε
configuration infolog el Τροποποίηση
confirm infolog el Επιβεβαίωση
contact infolog el Επαφή
create new links infolog el Δημιουργία νέων συνδέσμων
creates a new field infolog el δημιουργεί νέο πεδίο
creates a new status with the given values infolog el δημιουργεί μια νέα κατάσταση με τις δοσμένες αξίες
creates a new typ with the given name infolog el δημιουργεί ένα νέο τύπο με το δοσμένο όνομα
creation infolog el Δημιουργία
csv-fieldname infolog el CSV-Όνομα πεδίου
csv-filename infolog el CSV-Όνομα αρχείου
csv-import common el CSV-Εισαγωγή
custom infolog el Προσαρμογή
custom contact-address, leave empty to use information from most recent link infolog el Προσαρμοσμένη διεύθυνση επαφής, να μένει κενό για τη χρησιμοποίηση των πιο πρόσφατων συνδέσμων
custom contact-information, leave emtpy to use information from most recent link infolog el Προσαρμοσμένες πληροφορίες επαφής, να μένει κενό για τη χρησιμοποίηση των πιο πρόσφατων συνδέσμων
custom fields infolog el Προσαρμοσμένα πεδία
custom fields, typ and status common el Προσαρμοσμένα πεδία,τύπος και κατάσταση
custom regarding infolog el Προσαρμοσμένη αναφορά
custom status for typ infolog el Προσαρμοσμένη κατάσταση για τον τύπο
customfields infolog el Προσαρμοσμένα πεδία
date completed infolog el Ημερομηνία και ώρα ολοκλήρωσης
date completed (leave it empty to have it automatic set if status is done or billed) infolog el Συνάντηση ολοκληρώθηκε(να μείνει κενό για να οριστεί αυτόματα αν η κατάσταση εχει τελειώσει ή χρεωθεί
datecreated infolog el ορίστηκε συνάντηση
dates, status, access infolog el Συναντήσεις, Κατάσταση,Είσοδος
days infolog el ημέρες
default category for new infolog entries infolog el Προκαθορισμένη κατηγορία για νέες καταχωρήσεις στο InfoLog
default filter for infolog infolog el Προκαθορισμένο Φίλτρο για το InfoLog
default status for a new log entry infolog el προκαθορισμένη κατάσταση για νέες καταχωρήσεις
delegated infolog el εξουσιοδοτήθηκε
delegated open infolog el εξουσιοδοτημένα ανοικτά
delegated overdue infolog el εξουσιοδοτημένα καθυστερημένα
delegated upcomming infolog el εξουσιοδοτημένα επερχόμενα
delegation infolog el Εξουσιοδότηση
delete infolog el Διαγραφή
delete one record by passing its id. infolog el Διαγραφή μιας εγγραφής διαβαίνοντας απο το id της.
delete the entry infolog el Διαγραφή αυτής της καταχώρησης
delete this entry infolog el δαγραφή αυτής της καταχώρησης
delete this entry and all listed sub-entries infolog el Διαγραφή αυτής της καταχώρησης και όλων των υπο-καταχωρήσεων στη λίστα
deleted infolog el διαγράφηκαν
deletes the selected typ infolog el διαγραφή του επιλεγμένου τύπου
deletes this field infolog el διαγραφή αυτού του πεδίου
deletes this status infolog el διαγραφή αυτής της κατάστασης
description infolog el Περιγραφή
determines the order the fields are displayed infolog el καθορίζει τη σειρά με την οποία εμφανίζονται τα πεδία
disables a status without deleting it infolog el ακυρώνει μια κατάσταση χωρίς να τη διαγράφει
do you want a confirmation of the responsible on: accepting, finishing the task or both infolog el επιθυμείτε επιβεβαίωση του υπεύθυνου για:αποδοχή,ολοκλήρωση του έργου ή και τα δύο
do you want a notification, if items get assigned to you or assigned items get updated? infolog el Επιθυμείτε ειδοποίηση, αν αντικείμενα ανατίθενται σε εσάς ή αντικείμενα τα οποία έχουν ανατεθεί ενημερώνονται;
do you want a notification, if items you are responsible for are about to start? infolog el Επιθυμείτε ειδοποίηση, αν αντικείμενα για τα οποία είστε υπεύθυνος πρόκειται να ξεκινήσουν;
do you want a notification, if items you are responsible for are due? infolog el Επιθυμείτε ειδοποίηση, αν αντικείμενα για τα οποία είστε υπεύθυνος έχουν καθυστερήσει;
do you want a notification, if items you created get updated? infolog el Επιθυμείτε ειδοποίηση, αν αντικείμενα που έχετε δημιουργήσει ενημερώνονται;
do you want a notification, if items you delegated are about to start? infolog el Επιθυμείτε ειδοποίηση, αν αντικείμενα για τα οποία είστε εξουσιοδοτημένος πρόκειται να ξεκινήσουν;
do you want a notification, if items you delegated are due? infolog el Επιθυμείτε ειδοποίηση, αν αντικείμενα για τα οποία είστε εξουσιοδοτημένος έχουν καθυστερήσει;
do you want to receive notifications as html-mails or plain text? infolog el Επιθυμείτε να λαμβάνετε ειδοποιήσεις ως html-mails ή απλό κείμενο;
don't show infolog infolog el ΜΗΝ εμφανίζετε το InfoLog
done infolog el ολοκληρώθηκε
download infolog el Download
duration infolog el Διάρκεια
each value is a line like <id>[=<label>] infolog el κάθε αξία είναι μία γραμμή όπως <id>[=<label>]
edit infolog el Προσαρμογή
edit or create categories for ingolog infolog el Επιμέλεια ή δημιουργία κατηγοριών για το InfoLog
edit rights (full edit rights incl. making someone else responsible!) infolog el προσαρμογή δικαιωμάτων(ολοκληρωμένη προσαρμογή δικαιωμάτων συμπεριλαμβανομένου τον καθορισμό κάποιου άλλου ως υπεύθυνο!)
edit status infolog el Επιμέλεια κατάστασης
edit the entry infolog el Επιμέλεια της καταχώρησης
edit this entry infolog el Επιμέλεια αυτής της καταχώρησης
empty for all infolog el κενό για όλα
enddate infolog el Προθεσμία λήξης
enddate can not be before startdate infolog el Η προθεσμία λήξης δεν μπορεί να είναι προηγούμενη της ημερομηνίας έναρξης
enter a custom contact, leave empty if linked entry should be used infolog el είσοδος σε μία προσαρμοσμένη επαφή, να μείνει κενή αν πρέπει να χρησιμοποιηθεί συνδεδεμένη καταχώρηση
enter a custom phone/email, leave empty if linked entry should be used infolog el είσοδος σε ένα προσαρμοσμένο τηλέφωνο/email, να μένει κενό αν πρέπει να χρησιμοποιηθεί συνδεδεμένη καταχώρηση
enter a textual description of the log-entry infolog el Πληκτρολογήστε μία περιγραφή κειμένου για την καταχώρηση
enter the query pattern infolog el Enter the query pattern
error: saving the entry infolog el Σφάλμα: κατά την αποθήκευση της καταχώρησης
error: the entry has been updated since you opened it for editing! infolog el Σφάλμα:η καταχώρηση έχει ενημερωθεί από όταν την ανοίξατε για προσαρμογή!
existing links infolog el Υπάρχοντες σύνδεσμοι
fax infolog el Fax
field must not be empty !!! infolog el Το πεδίο δεν πρέπει να μείνει κενό !!!
fieldseparator infolog el Διαχωριστής πεδίων
finish infolog el τέλος
for which types should this field be used infolog el για ποιους τύπους να χρησιμοποιηθεί αυτό το πεδίο
from infolog el Από
general infolog el Γενικά
high infolog el υψηλή
history logging infolog el Ιστορικό εισόδων
history logging and deleting of items infolog el Ιστορικό εισόδων και διαγραφών αντικειμένων
id infolog el Id
if a type has a group owner, all entries of that type will be owned by the given group and not the user who created it! infolog el Αν ο τύπος έχει έναν ιδιοκτήτη ομάδας, όλες οι καταχωρήσεις αυτού του τύπου θα ανήκουν στη συγκεριμένη ομάδα και ΟΧΙ στο χρήστη που το δημιούργησε!
import infolog el Εισαγωγή
import next set infolog el εισαγωγή επόμενου σετ
info log common el Infolog
infolog common el Infolog
infolog - delete infolog el Infolog - Διαγραφή
infolog - edit infolog el Infolog - Επιμέλεια
infolog - import csv-file infolog el Infolog - Εισαγωγή CSV αρχείου
infolog - new infolog el Infolog - Νέο
infolog - new subproject infolog el Infolog - Νέα υποκατηγορία σχεδίων
infolog - subprojects from infolog el Infolog - Υποκατηγορίες σχεδίων από
infolog entry deleted infolog el Infolog καταχώρηση διεγράφη
infolog entry saved infolog el Infolog καταχώρηση αποθηκεύτηκε
infolog filter for the main screen infolog el Infolog φίλτρο για τη βασική οθόνη
infolog list infolog el Infolog λίστα
infolog preferences common el Infolog προτιμήσεις
infolog-fieldname infolog el Infolog - Όνομα πεδίου
invalid filename infolog el Άκυρο όνομα αρχείου
label<br>helptext infolog el Ετικέτα<br>Κείμενο βοήθειας
last changed infolog el Τελευταία αλλαγή
last modified infolog el Τελευταία προσαρμογή
leave it empty infolog el να μείνει κενό
leave without saveing the entry infolog el αποχώρηση χωρίς αποθήκευση της καταχώρησης
leaves without saveing infolog el αποχωρεί χωρίς αποθήκευση
length<br>rows infolog el Μήκος<br>Γραμμών
link infolog el Σύνδεσμος
links infolog el Σύνδεσμοι
links of this entry infolog el Σύνδεσμοι αυτής της καταχώρησης
list all categories infolog el Να μπουν σε λίστα όλες οι κατηγορίες
list no subs/childs infolog el Να μην εμφανίζονται οι υπο- καταχωρήσεις
location infolog el Τοποθεσία
longer textual description infolog el εκτενέστερη περιγραφή κειμένου
low infolog el χαμηλή
max length of the input [, length of the inputfield (optional)] infolog el μέγιστο μήκος εισροής [μήκος πεδίου εισροών (κατ'επιλογή)]
name must not be empty !!! infolog el Το όνομα δεν πρέπει να μένει κενό
name of new type to create infolog el όνομα νέου τύπου προς δημιουργία
never hide search and filters infolog el Να μην αποκρύπτεται ποτέ η αναζήτηση και τα φίλτρα
new %1 infolog el Νέο %1
new %1 created by %2 at %3 infolog el Νέο %1 δημιουργήθηκε από %2 στις %3
new name infolog el νέο όνομα
new search infolog el Νέα αναζήτηση
no - cancel infolog el Όχι-Άκυρο
no describtion, links or attachments infolog el καμία περιγραφή, σύνδεσμος ή επισύναψη
no details infolog el συνοπτικά
no entries found, try again ... infolog el δεν βρέθηκαν καταχωρήσεις, προσπαθήστε ξανά
no filter infolog el κανένα Φίλτρο
no links or attachments infolog el κανένα φίλτρο ή επισυνάψεις
nonactive infolog el ανενεργό
none infolog el Κανένα
normal infolog el κανονικό
not infolog el δεν
not assigned infolog el δεν έχει ανατεθεί
not-started infolog el δεν έχει αρχίσει
note infolog el Σημείωση
number of records to read (%1) infolog el Αριθμός εγγραφών προς ανάγνωση (%1)
number of row for a multiline inputfield or line of a multi-select-box infolog el αριθμός σειράς για ένα πολυγραμμικό πεδίο εισροών ή γραμμή ενός κουτιού πολλών επιλογών
offer infolog el προσφορά
one day after infolog el μία μέρα μετά
one day in advance infolog el Μία μέρα νωρίτερα
ongoing infolog el τρέχων
only for details infolog el Μόνο για λεπτομέρειες
only if i get assigned or removed infolog el Μόνο εάν μου ανατεθεί ή αφαιρεθεί
only the attachments infolog el μόνο οι επισυνάψεις
only the links infolog el μόνο οι σύνδεσμοι
open infolog el ανοικτά
optional note to the link infolog el προαιρετική σημείωση στο σύνδεσμο
order infolog el Παραγγελία
overdue infolog el καθυστερημένα
own infolog el δικά μου
own open infolog el δικά μου ανοικτά
own overdue infolog el δικά μου καθυστερημένα
own upcoming infolog el δικά μου επερχόμενα
parent infolog el Μητρική
path to user and group files has to be outside of the webservers document-root!!! infolog el Το μονοπάτι προς τα αρχεία του χρήστη και των ομάδων ΠΡΕΠΕΙ ΝΑ ΕΙΝΑΙ ΕΚΤΟΣ της πορείας των εγγράφων των webservers!!!
pattern for search in addressbook infolog el πρότυπο αναζήτησης στο βιβλίο διευθύνσεων
pattern for search in projects infolog el πρότυπο αναζήτησης στα σχέδια
percent completed infolog el ποσοστό που έχει ολοκληρωθεί
permission denied infolog el Άδεια απορρίπτεται
phone infolog el Κλήση τηλεφώνου
phone/email infolog el Τηλέφωνο/Email
phonecall infolog el Κλήση τηλεφώνου
planned infolog el προγραμματίστηκε
planned time infolog el ώρα σχεδίου
price infolog el Τιμή
pricelist infolog el Τιμοκατάλογος
primary link infolog el κύριος σύνδεσμος
priority infolog el Προτεραιότητα
private infolog el Προσωπικό
project infolog el Σχέδιο
project settings: price, times infolog el Ρυθμίσεις σχεδίων:τιμή, ώρες
re: infolog el Αφορά:
read one record by passing its id. infolog el Διαβάστε μία εγγραφή διαβαίνοντας από το ID της
read rights (default) infolog el ανάγνωση δικαιωμάτων (προκαθορισμένη)
receive notifications about due entries you are responsible for infolog el Λήψη ειδοποιήσεων για καταχωρήσεις των οποίων είστε υπεύθυνος και που έχουν καθυστερήσει
receive notifications about due entries you delegated infolog el Λήψη ειδοποιήσεων για καταχωρήσεις των οποίων είστε εξουσιοδοτημένος και που έχουν καθυστερήσει
receive notifications about items assigned to you infolog el Λήψη ειδοποιήσεων για αντικείμενα τα οποία σας έχουν ανατεθεί
receive notifications about own items infolog el Λήψη ειδοποιήσεων για τα δικά σας αντικείμενα
receive notifications about starting entries you are responsible for infolog el Λήψη ειδοποιήσεων για καταχωρήσεις που ξεκινούν και για τις οποίες είστε υπεύθυνος
receive notifications about starting entries you delegated infolog el Λήψη ειδοποιήσεων για καταχωρήσεις που ξεκινούν και για τις οποίες είστε εξουσιοδοτημένος
receive notifications as html-mails infolog el Λήψη ειδοποιήσεων ως html-mails
remark infolog el Ξαναμαρκάρετε
remove this link (not the entry itself) infolog el Αφαιρέστε το σύνδεσμο (όχι την ίδια την καταχώρηση)
responsible infolog el υπεύθυνος
responsible open infolog el υπεύθυνος ανοικτά
responsible overdue infolog el υπεύθυνος καθυστερημένα
responsible upcoming infolog el υπεύθυνος επερχόμενα
responsible user, priority infolog el υπεύθυνος χρήστης, προτεραιότητα
returns a list / search for records. infolog el Επιστρέφει μια λίστα/αναζήτηση για εγγραφές
rights for the responsible infolog el Δικαιώματα για τον υπεύθυνο
same day infolog el ίδια ημέρα
save infolog el Αποθήκευση
saves the changes made and leaves infolog el αποθηκεύει τις αλλαγές που έγιναν και αποχωρεί
saves this entry infolog el Αποθηκεύει την καταχώρηση
search infolog el Αναζήτηση
search for: infolog el Αναζήτηση για:
select infolog el Επιλέξτε
select a category for this entry infolog el επιλέξτε μια κατηγορία για αυτήν την καταχώρηση
select a price infolog el Επιλέξτε μια τιμή
select a priority for this task infolog el επιλέξτε προτεραιότητα για αυτό το έργο
select a project infolog el Επιλέξτε ένα έργο
select a responsible user: a person you want to delegate this task infolog el Επιλέξτε τον υπεύθυνο χρήστη: ένα άτομο στο οποίο θέλετε να αναθέσετε αυτό το έργο
select a typ to edit it's status-values or delete it infolog el επιλέξτε έναν τύπο για να επιμεληθείτε την κατάστασή του
select an app to search in infolog el Επελέξτε μία εφαρμογή στην οποία θα γίνει αναζήτηση
select an entry to link with infolog el Επελέξτε μία καταχώρηση προς σύνδεση
select to filter by owner infolog el Επιλογή φίλτρου σύμφωνα με τον ιδιοκτήτη
select to filter by responsible infolog el Επιλογή φίλτρου σύμφωνα με τον υπεύθυνο
sets the status of this entry and its subs to done infolog el Ορίζει την κατάσταση αυτής της καταχώρησης και των υποκατηγοριών της σε ολοκληρωμένη
should infolog show subtasks, -calls or -notes in the normal view or not. you can always view the subs via there parent. infolog el Θα έπρεπε το InfoLog να εμφανίζει τις υπο-εργασίες, -κλήσεις ή -σημειώσεις στην κανονική εμφάνιση ή όχι. Μπορείτε πάντα να βλέπετε τις υποκατηγορίες μέσω των μητρικών τους.
should infolog show the links to other applications and/or the file-attachments in the infolog list (normal view when you enter infolog). infolog el Θα έπρεπε το InfoLog να εμφανίζει τους συνδέσμους με άλλες εφαρμογές και/ή τις προσκολλήσεις αρχείου στην InfoLog λίστα (κανονική εμφάνιση όταν εισέρχεστε στο InfoLog).
should infolog show up on the main screen and with which filter. works only if you dont selected an application for the main screen (in your preferences). infolog el Θα έπρεπε το InfoLog να εμφανίζεται στη βασική οθόνη και με ποιο φίλτρο.Λειτουργεί μόνο αν δεν επιλέξατε μία εφαρμογή για την βασική οθόνη (στις προτιμήσεις σας).
should infolog use full names (surname and familyname) or just the loginnames. infolog el Θα έπρεπε το InfoLog να χρησιμοποιεί ολόκληρα ονόματα (επώνυμα και οικογενειακά ονόματα) ή μόνο τα ονόματα εισόδου.
should the infolog list show a unique numerical id, which can be used eg. as ticket id. infolog el Θα έπρεπε η InfoLog λίστα να εμφανίζει ένα μοναδικό νούμερο Id, το οποίο να μπορεί να χρησιμοποιείται π.χ. ως Id εισιτηρίου.
should the infolog list show the column "last modified". infolog el Θα έπρεπε η InfoLog λίστα να εμφανίζει τη στήλη "τελευταία τροποποίηση".
should the infolog list show the percent done only for status ongoing or two separate icons. infolog el Θα έπρεπε η InfoLog λίστα να εμφανίζει το ποσοστό ολοκλήρωσης μόνο για την κατάσταση τρέχων ή δύο ξεχωριστά εικονίδια.
should this entry only be visible to you and people you grant privat access via the acl infolog el θέλετε αυτή η καταχώρηση να είναι ορατή σε εσάς και στα άτομα που δίνεται προσωπική άδεια μέσω του ACL
show a column for used and planned times in the list. infolog el Παρουσιάστε μία στήλη για χρησιμοποιημένες και προγραμματισμένες ώρες στη λίστα
show full usernames infolog el Παρουσίαση ολόκληρων των ονομάτων χρήστη
show in the infolog list infolog el Εμφάνιση στην InfoLog λίστα
show last modified infolog el Εμφάνιση τελευταίας μετατροπής
show status and percent done separate infolog el Παρουσίαση της κατάστασης και του ποσοστού ολοκλήρωσης ξεχωριστά
show ticket id infolog el Εμφάνιση Id εισιτηρίου
show times infolog el Εμφάνιση των ωρών
small view infolog el μικρή εμφάνιση
start a new search, cancel this link infolog el έναρξη νέας αναζήτησης, ακύρωση αυτού του συνδέσμου
startdate infolog el Ημερομηνία έναρξης
startdate enddate infolog el Ημερομηνία έναρξης ημερομηνία εξόφλησης
startdate for new entries infolog el Ημερομηνία έναρξης για νέες καταχωρήσεις
startrecord infolog el Έναρξη εγγραφής
status infolog el Κατάσταση
status ... infolog el Κατάσταση
sub infolog el Υπό
sub-entries become subs of the parent or main entries, if there's no parent infolog el Υπό-καταχωρήσεις γίνονται υποκατηγορίες των μητρικών ή βασικών καταχωρήσεων, αν δεν υπάρχει μητρική
subject infolog el Θέμα
sum infolog el Σύνολο
task infolog el Εργασίες
template infolog el Πρότυπο
test import (show importable records <u>only</u> in browser) infolog el Έλεγχος Εισαγωγής (εμφάνιση εισαγώγιμων εγγραφών <u>only</> στο browser)
the text displayed to the user infolog el το κείμενο εμφανίζεται στο χρήστη
this is the filter infolog uses when you enter the application. filters limit the entries to show in the actual view. there are filters to show only finished, still open or futures entries of yourself or all users. infolog el Αυτές είναι οι χρήσεις του InfoLog φίλτρου όταν εισέρχεστε στην εφαρμογή.Τα φίλτρα περιορίζουν τις καταχωρήσεις να εμφανίζονται στην πραγματική παρουσίαση.Υπάρχουν φίλτρα που παρουσιάζουν μόνο τα ολοκληρωμένα, τα τρέχοντα ή τις δικές σας μελλοντικές καταχωρήσεις ή και όλων των χρηστών.
til when should the todo or phonecall be finished infolog el μέχρι πότε πρέπει το θέμα προς διεκπαιρέωση ή η τηλεφωνική κλήση να έχει τελείωσει
times infolog el Ώρες
to many might exceed your execution-time-limit infolog el Πάρα πολλοί. Ενδεχόμενο υπέρβασης ορίου χρόνου εκτέλεσης
to what should the startdate of new entries be set. infolog el Σε τι πρέπει η ημερομηνία έναρξης νέων καταχωρήσεων να οριστεί.
today infolog el Σήμερα
todays date infolog el σημερινή ημερομηνία
todo infolog el Εργασίες
translation infolog el Μετάφραση
typ infolog el Τύπος
typ '%1' already exists !!! infolog el Ο τύπος '%1' υπάρχει ήδη
type infolog el Τύπος
type ... infolog el Τύπος ...
type of customfield infolog el Τύπος προσαρμοσμένου πεδίου
type of the log-entry: note, phonecall or todo infolog el Τύπος της ειδόδου-καταχώρησης: Σημείωση, Κλήση τηλεφώνου ή Εργασίες
unlink infolog el Αποσύνδεση
upcoming infolog el επερχόμενο
urgency infolog el επείγον
urgent infolog el επείγον
used time infolog el χρησιμοποιημένος χρόνος
valid path on clientside<br>eg. \servershare or e: infolog el έγκυρο μονοπάτι προς την πλευρά του πελάτη<br>π.χ. \\Server\Share ή e:\
valid path on clientside<br>eg. servershare or e: infolog el έγκυρο μονοπάτι προς την πλευρά του πελάτη<br>π.χ. \\Server\Share ή e:\
values for selectbox infolog el Αξίες του επιλεγμένου κουτιού
view all subs of this entry infolog el Εμφάνιση όλων των υποκατηγοριών αυτής της καταχώρησης
view other subs infolog el εμφάνιση άλλων υποκατηγοριών
view parent infolog el Εμφάνιση μητρικής
view subs infolog el εμφάνιση υποκατηγοριών
view the parent of this entry and all his subs infolog el Εμφάνιση της μητρικής αυτής της καταχώρησης και όλων των υποκατηγοριών της
view this linked entry in its application infolog el εμφάνιση αυτής της συνδεδεμένης καταχώρησης στην αίτησή της
when should the todo or phonecall be started, it shows up from that date in the filter open or own open (startpage) infolog el πότε θα έπρεπε οι Εργασίες ή Κλήση τηλεφώνου να αρχίζουν,εμφανίζεται από εκείνη την ημερομηνία στο άνοιγμα φίλτρου ή δικά μου άνοιγμα (αρχική σελίδα)
which additional fields should the responsible be allowed to edit without having edit rights?<br />status, percent and date completed are always allowed. infolog el Ποια επιπλέον πεδία πρέπει ο υπεύθυνος να έχει άδεια να επιμεληθεί χωρίς να έχει δικαιώματα επιμέλειας;<br/>Κατάσταση, ποσοστό και ημερομηνία ολοκλήρωσης είναι πάντοτε επιτρεπτά.
which implicit acl rights should the responsible get? infolog el Ποια αυτονόητα ACL δικαιώματα πρέπει ο υπεύθυνος να έχει;
whole query infolog el ολόκληρη απορία
will-call infolog el θα τηλεφωνήσει
write (add or update) a record by passing its fields. infolog el Καταγραφή (προσθήκη ή ενημέρωση) μιας εγγραφής διαβαίνοντας από τα πεδία της.
yes - delete infolog el Ναι- Διαγραφή
yes - delete including sub-entries infolog el Ναι-Διαγραφή συμπεριλαμβανομένων και των υπο-καταχωρήσεων
yes, noone can purge deleted items infolog el Νια, κανείς δεν μπορεί να καθαρίσει τα αντικείμενα που έχουν διαγραφεί
yes, only admins can purge deleted items infolog el Ναι, μόνο ο διαχειριστής μπορεί να καθαρίσει τα αντικείμενα που έχουν διαγραφεί
yes, with larger fontsize infolog el Ναι, με μεγαλύτερο μέγεθος φόντου
yes, with purging of deleted items possible infolog el Ναι, με τον καθαρισμό των διαγραμένων αντικειμένων να είναι δυνατός
you can't delete one of the stock types !!! infolog el Δεν μπορείτε να διαγράψετε έναν από τους προκαθορισμένους τύπους !!!
you have entered an invalid ending date infolog el Έχετε πληκτρολογήσει μια άκυρη ημερομηνία οφειλής
you have entered an invalid starting date infolog el Έχετε πληκτρολογήσει μια άκυρη ημερομηνία έναρξης
you have to enter a name, to create a new typ!!! infolog el Πρέπει να πληκτρολογήσετε ένα όνομα, για τη δημιουργία νέου τύπου!!!
you must enter a subject or a description infolog el Πρέπει να πληκτρολογήσετε ένα θέμα ή μια περιγραφή
your database is not up to date (%1 vs. %2), please run %3setup%4 to update your database. infolog el Το ηλεκτρονικό σας αρχείο ΔΕΝ είναι ενημερωμένο (%1 vs. %2), παρακαλώ τρέξτε %3setup%4 για να ενημερώσετε το ηλεκτρονικό σας αρχείο.

490
infolog/lang/egw_en.lang Normal file
View File

@ -0,0 +1,490 @@
%1 days in advance infolog en %1 days in advance
%1 deleted infolog en %1 deleted
%1 deleted by %2 at %3 infolog en %1 deleted by %2 at %3
%1 entries %2 infolog en %1 entries %2
%1 entries %2, %3 failed because of insufficent rights !!! infolog en %1 entries %2, %3 failed because of insufficent rights !!!
%1 modified infolog en %1 modified
%1 modified by %2 at %3 infolog en %1 modified by %2 at %3
%1 records imported infolog en %1 records imported
%1 records read (not yet imported, you may go %2back%3 and uncheck test import) infolog en %1 records read (not yet imported, you may go %2back%3 and uncheck Test Import)
%1 you are responsible for is due at %2 infolog en %1 you are responsible for is due at %2
%1 you are responsible for is starting at %2 infolog en %1 you are responsible for is starting at %2
%1 you delegated is due at %2 infolog en %1 you delegated is due at %2
%1 you delegated is starting at %2 infolog en %1 you delegated is starting at %2
- subprojects from infolog en - Subprojects from
0% infolog en 0%
10% infolog en 10%
100% infolog en 100%
20% infolog en 20%
30% infolog en 30%
40% infolog en 40%
50% infolog en 50%
60% infolog en 60%
70% infolog en 70%
80% infolog en 80%
90% infolog en 90%
<b>file-attachments via symlinks</b> instead of uploads and retrieval via file:/path for direct lan-clients infolog en <b>file-attachments via symlinks</b> instead of uploads and retrieval via file:/path for direct lan-clients
a short subject for the entry infolog en a short subject for the entry
abort without deleting infolog en Abort without deleting
accept infolog en accept
action infolog en Action
actions... infolog en Actions...
actual date and time infolog en actual date and time
add infolog en Add
add / remove link infolog en Add / Remove link
add a file infolog en Add a file
add a new entry infolog en Add a new Entry
add a new note infolog en Add a new Note
add a new phonecall infolog en Add a new Phone Call
add a new sub-task, -note, -call to this entry infolog en Add a new sub-task, -note, -call to this entry
add a new todo infolog en Add a new ToDo
add file infolog en Add file
add or delete links infolog en Add or delete links
add sub infolog en add Sub
add timesheet entry infolog en Add timesheet entry
add: infolog en Add:
added infolog en added
all infolog en All
all links and attachments infolog en all links and attachments
all projects infolog en All projects
allows to set the status of an entry, eg. set a todo to done if it's finished (values depend on entry-typ) infolog en Allows to set the status of an entry, eg. set a ToDo to done if it's finished (values depend on entry-type)
alternatives infolog en Alternatives
apply the action on the whole query, not only the shown entries!!! infolog en Apply the action on the whole query, NOT only the shown entries!!!
apply the changes infolog en Apply the changes
archive infolog en archive
are you shure you want to close this entry ? infolog en Are you shure you want to close this entry ?
are you shure you want to delete this entry ? infolog en Are you sure you want to delete this entry ?
at the moment the following document-types are supported: infolog en At the moment the following document-types are supported:
attach a file infolog en Attach a file
attach file infolog en Attach file
attention: no contact with address %1 found. infolog en Attention: No Contact with address %1 found.
back to main list infolog en Back to main list
billed infolog en billed
both infolog en both
call infolog en call
can be used to show further infolog types in the calendar or limit it to show eg. only tasks. infolog en Can be used to show further InfoLog types in the calendar or limit it to show e.g. only tasks.
cancel infolog en Cancel
cancelled infolog en cancelled
categories infolog en Categories
category infolog en Category
change category infolog en Change category
change completed infolog en Change completed
change completion infolog en Change completion
change history infolog en Change history
change owner when updating infolog en Change owner when updating
change responsible infolog en Change responsible
change status: infolog en Change status:
change the status of an entry, eg. close it infolog en Change the status of an entry, eg. close it
change type: infolog en Change type:
changed category to %1 infolog en changed category to %1
changed completion to %1% infolog en changed completion to %1%
changed responsible infolog en changed responsible
changed status to %1 infolog en changed status to %1
changed type infolog en changed type
charset of file infolog en Charset of file
check all infolog en Check all
check to set startday infolog en check to set startday
check to specify custom contact infolog en Check to specify custom contact
choose owner of imported data infolog en Choose owner of imported data
click here to create the link infolog en click here to create the Link
click here to start the search infolog en click here to start the search
close infolog en Close
close all infolog en Close all
close this entry and all listed sub-entries infolog en Close this entry and all listed sub-entries
closed infolog en closed
colon (:) separated list of field names to use if value is empty or to sum up infolog en colon (:) separated list of field names to use if value is empty or to sum up
comment infolog en Comment
compare infolog en Compare
completed infolog en Completed
configuration infolog en Configuration
confirm infolog en Confirm
contact infolog en Contact
contact cf infolog en contact CF
contact fields infolog en Contact fields
contactfield infolog en Contactfield
copy of: infolog en Copy of:
copy your changes to the clipboard, %1reload the entry%2 and merge them. infolog en Copy your changes to the clipboard, %1reload the entry%2 and merge them.
create new links infolog en Create new links
creates a new field infolog en creates a new field
creates a new status with the given values infolog en creates a new status with the given values
creates a new typ with the given name infolog en creates a new type with the given name
creation infolog en Creation
csv-fieldname infolog en CSV-Fieldname
csv-filename infolog en CSV-Filename
csv-import common en CSV-Import
custom infolog en Custom
custom contact-address, leave empty to use information from most recent link infolog en Custom contact-address, leave empty to use information from most recent link
custom contact-information, leave emtpy to use information from most recent link infolog en Custom contact-information, leave empty to use information from most recent link
custom fields infolog en Custom Fields
custom fields, typ and status common en Custom fields, type and status
custom from infolog en Custom from
custom regarding infolog en Custom regarding
custom status for typ infolog en Custom status for type
customfields infolog en Customfields
date completed infolog en Date completed
date completed (leave it empty to have it automatic set if status is done or billed) infolog en Date completed (leave it empty to have it automatically set if status is done or billed)
datecreated infolog en date created
dates, status, access infolog en Dates, Status, Access
days infolog en days
default category for new infolog entries infolog en Default category for new Infolog entries
default document to insert entries infolog en Default document to insert entries
default filter for infolog infolog en Default Filter for InfoLog
default status for a new log entry infolog en default status for a new log entry
delegated infolog en delegated
delegated open infolog en delegated open
delegated open and upcoming infolog en delegated open and upcoming
delegated overdue infolog en delegated overdue
delegated upcomming infolog en delegated upcomming
delegation infolog en Delegation
delete infolog en Delete
delete one record by passing its id. infolog en Delete one record by passing its id.
delete selected entries? infolog en Delete selected entries?
delete the entry infolog en Delete the entry
delete this entry infolog en delete this entry
delete this entry and all listed sub-entries infolog en Delete this entry and all listed sub-entries
deleted infolog en deleted
deletes the selected typ infolog en deletes the selected type
deletes this field infolog en deletes this field
deletes this status infolog en deletes this status
description infolog en Description
determines the order the fields are displayed infolog en determines the order the fields are displayed
directory with documents to insert entries infolog en Directory with documents to insert entries
disables a status without deleting it infolog en disables a status without deleting it
do not notify of these changes infolog en Do not notify of these changes
do you want a confirmation of the responsible on: accepting, finishing the task or both infolog en Do you want a confirmation of the responsible on: accepting, finishing the task or both
do you want a notification, if items get assigned to you or assigned items get updated? infolog en Do you want a notification, if items get assigned to you or assigned items get updated?
do you want a notification, if items you are responsible for are about to start? infolog en Do you want a notification, if items you are responsible for are about to start?
do you want a notification, if items you are responsible for are due? infolog en Do you want a notification, if items you are responsible for are due?
do you want a notification, if items you created get updated? infolog en Do you want a notification, if items you created get updated?
do you want a notification, if items you delegated are about to start? infolog en Do you want a notification, if items you delegated are about to start?
do you want a notification, if items you delegated are due? infolog en Do you want a notification, if items you delegated are due?
do you want to receive notifications as html-mails or plain text? infolog en Do you want to receive notifications as html-mails or plain text?
document '%1' does not exist or is not readable for you! infolog en Document '%1' does not exist or is not readable for you!
don't show infolog infolog en DON'T show InfoLog
done common en done
download infolog en Download
due %1 infolog en Due %1
duration infolog en Duration
e-mail: infolog en E-mail:
each value is a line like <id>[=<label>] infolog en Each value is a line like <id>[=<label>]
edit infolog en Edit
edit or create categories for ingolog infolog en Edit or create categories for IngoLog
edit rights (full edit rights incl. making someone else responsible!) infolog en Edit rights (full edit rights incl. making someone else responsible!)
edit status infolog en Edit status
edit the entry infolog en Edit the entry
edit this entry infolog en Edit this entry
empty for all infolog en empty for all
end infolog en End
enddate infolog en Due date
enddate can not be before startdate infolog en Due date can not be before start date
enter a custom contact, leave empty if linked entry should be used infolog en Enter a custom contact, leave empty if linked entry should be used
enter a custom phone/email, leave empty if linked entry should be used infolog en Enter a custom phone/email, leave empty if linked entry should be used
enter a textual description of the log-entry infolog en enter a textual description of the log-entry
enter the query pattern infolog en Enter the query pattern
entry and all files infolog en Entry and all files
error: saving the entry infolog en Error: saving the entry
error: the entry has been updated since you opened it for editing! infolog en Error: the entry has been updated since you opened it for editing!
example {{if n_prefix~mr~hello mr.~hello ms.}} - search the field "n_prefix", for "mr", if found, write hello mr., else write hello ms. infolog en Example {{IF n_prefix~Mr~Hello Mr.~Hello Ms.}} - search the field "n_prefix", for "Mr", if found, write Hello Mr., else write Hello Ms.
example {{letterprefixcustom n_prefix title n_family}} - example: mr dr. james miller infolog en Example {{LETTERPREFIXCUSTOM n_prefix title n_family}} - Example: Mr Dr. James Miller
example {{nelf role}} - if field role is not empty, you will get a new line with the value of field role infolog en Example {{NELF role}} - if field role is not empty, you will get a new line with the value of field role
example {{nelfnv role}} - if field role is not empty, set a lf without any value of the field infolog en Example {{NELFNV role}} - if field role is not empty, set a LF without any value of the field
execute a further action for this entry infolog en Execute a further action for this entry
existing links infolog en Existing links
exists infolog en exists
export definitition to use for nextmatch export infolog en Export definitition to use for nextmatch export
exports infolog entries into a csv file. infolog en Exports Infolog entries into a CSV File.
fax infolog en Fax
field must not be empty !!! infolog en Field must not be empty !!!
fieldseparator infolog en Fieldseparator
finish infolog en finish
first argument for preg_replace infolog en first argument for preg_replace
for serial letter use this tag. put the content, you want to repeat between two tags. infolog en For serial letter use this tag. Put the content, you want to repeat between two Tags.
for which types should this field be used infolog en for which types should this field be used
from infolog en From
general infolog en General
general fields: infolog en General fields:
global categories infolog en Global Categories
group owner for infolog en Group owner for
high infolog en high
history infolog en History
history logging infolog en History logging
history logging and deleting of items infolog en History logging and deleting of items
how many describtion lines should be directly visible. further lines are available via a scrollbar. infolog en How many description lines should be directly visible. Further lines are available via a scroll bar.
how wide should the description area be. this value is numeric and interpreted as em; 60 works reasonably well. infolog en How wide should the description area be. This value is numeric and interpreted as em; 60 works reasonably well.
id infolog en Id
id# infolog en Id#
if a type has a group owner, all entries of that type will be owned by the given group and not the user who created it! infolog en If a type has a group owner, all entries of that type will be owned by the given group and NOT the user who created it!
if not set, the line with search and filters is hidden for less entries then "max matches per page" (as defined in your common preferences). infolog en If not set, the line with search and filters is hidden for less entries then "max matches per page" (as defined in your common preferences).
if you specify a directory (full vfs path) here, infolog displays an action for each document. that action allows to download the specified document with the infolog data inserted. infolog en If you specify a directory (full vfs path) here, infolog displays an action for each document. That action allows to download the specified document with the infolog data inserted.
if you specify a document (full vfs path) here, infolog displays an extra document icon for each entry. that icon allows to download the specified document with the contact data inserted. infolog en If you specify a document (full vfs path) here, infolog displays an extra document icon for each entry. That icon allows to download the specified document with the contact data inserted.
if you specify an export definition, it will be used when you export infolog en If you specify an export definition, it will be used when you export
import infolog en Import
import next set infolog en import next set
importance infolog en Importance
imports entries into the infolog from a csv file. csv means 'comma seperated values'. however in the options tab you can also choose other seperators. infolog en Imports entries into the infolog from a CSV File. CSV means 'Comma Seperated Values'. However in the options Tab you can also choose other seperators.
info log common en InfoLog
infolog common en InfoLog
infolog - delete infolog en Info Log - Delete
infolog - edit infolog en InfoLog - Edit
infolog - import csv-file infolog en InfoLog - Import CSV-File
infolog - new infolog en InfoLog - New
infolog - new subproject infolog en InfoLog - New Subproject
infolog - subprojects from infolog en InfoLog - Subprojects from
infolog copied - the copy can now be edited infolog en Infolog copied - the copy can now be edited
infolog csv export infolog en Infolog CSV export
infolog csv import infolog en Infolog CSV import
infolog entry deleted infolog en InfoLog entry deleted
infolog entry saved infolog en InfoLog entry saved
infolog fields: infolog en Infolog fields:
infolog filter for the main screen infolog en InfoLog filter for the main screen
infolog id infolog en Infolog ID
infolog list infolog en InfoLog list
infolog preferences common en InfoLog preferences
infolog-fieldname infolog en Info Log-Fieldname
insert infolog en insert
insert in document infolog en Insert in document
invalid filename infolog en Invalid filename
invalid owner id: %1. might be a bad field translation. used %2 instead. infolog en Invalid owner ID: %1. Might be a bad field translation. Used %2 instead.
invalid status for entry type %1. infolog en Invalid status for entry type %1.
label<br>helptext infolog en Label<br>Helptext
last changed infolog en Last changed
last modified infolog en Last modified
leave blank to get the used time calculated by timesheet entries infolog en Leave blank to get the used time calculated by time sheet entries
leave it empty infolog en leave it empty
leave it empty for a full week infolog en Leave it empty for a full week
leave without saveing the entry infolog en leave without saveing the entry
leaves without saveing infolog en leaves without saveing
length<br>rows infolog en Length<br>Rows
limit number of description lines (default 5, 0 for no limit) infolog en Limit number of description lines (default 5, 0 for no limit)
limit width of description column ((effective only if lines limit is set), 0 for no limit) infolog en Limit width of description column ((effective only if lines limit is set), 0 for no limit)
link infolog en Link
linked to %1 infolog en linked to %1
links infolog en Links
links of this entry infolog en Links of this entry
list all categories infolog en List all categories
list no subs/childs infolog en List no Subs/Childs
location infolog en Location
longer textual description infolog en longer textual description
low infolog en low
manage mapping infolog en Manage mapping
max length of the input [, length of the inputfield (optional)] infolog en max length of the input [, length of the input field (optional)]
modifier infolog en Modifier
modifierer infolog en Modifierer
name must not be empty !!! infolog en Name must not be empty !!!
name of current user, all other contact fields are valid too infolog en Name of current user, all other contact fields are valid too
name of new type to create infolog en name of new type to create
never hide search and filters infolog en Never hide search and filters
new %1 infolog en New %1
new %1 created by %2 at %3 infolog en New %1 created by %2 at %3
new name infolog en new name
new search infolog en New search
no - cancel infolog en No - Cancel
no describtion, links or attachments infolog en no describtion, links or attachments
no details infolog en no details
no entries found, try again ... infolog en no entries found, try again ...
no filter infolog en no Filter
no links or attachments infolog en no links or attachments
no project infolog en No project
nonactive infolog en nonactive
none infolog en None
normal infolog en normal
not infolog en not
not assigned infolog en not assigned
not-started infolog en not started
note infolog en Note
number of records to read (%1) infolog en Number of records to read (%1)
number of row for a multiline inputfield or line of a multi-select-box infolog en number of row for a multi-line input field or line of a multi-select-box
offer infolog en offer
one day after infolog en one day after
one day in advance infolog en one day in advance
ongoing infolog en ongoing
only for details infolog en Only for details
only if i get assigned or removed infolog en Only if I get assigned or removed
only the attachments infolog en only the attachments
only the links infolog en only the links
open infolog en open
open and upcoming infolog en open and upcoming
optional note to the link infolog en optional note to the Link
order infolog en Order
organization infolog en Organization
overdue infolog en overdue
own infolog en own
own open infolog en own open
own open and upcoming infolog en own open and upcoming
own overdue infolog en own overdue
own upcoming infolog en own upcoming
owner does not have edit rights infolog en Owner does not have edit rights
parent infolog en Parent
parent infolog infolog en Parent Infolog
path on (web-)serverside<br>eg. /var/samba/share infolog en path on (web-)serverside<br>eg. /var/samba/Share
path to user and group files has to be outside of the webservers document-root!!! infolog en Path to user and group files HAS TO BE OUTSIDE of the webservers document-root!!!
pattern for search in addressbook infolog en pattern for search in Addressbook
pattern for search in projects infolog en pattern for search in Projects
percent completed infolog en Percent completed
permission denied infolog en Permission denied
permissions error - %1 could not %2 infolog en Permissions error - %1 could not %2
phone infolog en Phone Call
phone/email infolog en Phone/Email
phonecall infolog en Phone Call
planned infolog en planned
planned time infolog en planned time
price infolog en Price
pricelist infolog en Pricelist
primary link infolog en primary link
priority infolog en Priority
private infolog en Private
project infolog en Project
project settings: price, times infolog en Project settings: price, times
projectmanager infolog en Projectmanager
re-planned infolog en Re-planned
re-planned time infolog en Re-planned time
re: infolog en Re:
read one record by passing its id. infolog en Read one record by passing its id.
read rights (default) infolog en read rights (default)
receive notifications about due entries you are responsible for infolog en Receive notifications about due entries you are responsible for
receive notifications about due entries you delegated infolog en Receive notifications about due entries you delegated
receive notifications about items assigned to you infolog en Receive notifications about items assigned to you
receive notifications about own items infolog en Receive notifications about own items
receive notifications about starting entries you are responsible for infolog en Receive notifications about starting entries you are responsible for
receive notifications about starting entries you delegated infolog en Receive notifications about starting entries you delegated
receive notifications as html-mails infolog en Receive notifications as html-mails
reg. expr. for local ip's<br>eg. ^192\.168\.1\. infolog en reg. expr. for local IP's<br>eg. ^192\.168\.1\.
regular expression infolog en Regular expression
remark infolog en Remark
remove this link (not the entry itself) infolog en Remove this link (not the entry itself)
removed infolog en removed
replacement infolog en Replacement
replacements for inserting entries into documents infolog en Replacements for inserting entries into documents
responsible infolog en responsible
responsible open infolog en responsible open
responsible open and upcoming infolog en responsible open and upcoming
responsible overdue infolog en responsible overdue
responsible upcoming infolog en responsible upcoming
responsible user, priority infolog en responsible user, priority
returns a list / search for records. infolog en Returns a list / search for records.
rights for the responsible infolog en Rights for the responsible
same day infolog en same day
save infolog en Save
saves the changes made and leaves infolog en saves the changes made and leaves
saves this entry infolog en Saves this entry
search infolog en Search
search for: infolog en Search for:
second parameter for preg_replace infolog en second parameter for preg_replace
select infolog en Select
select a category for this entry infolog en select a category for this entry
select a price infolog en Select a price
select a priority for this task infolog en select a priority for this task
select a project infolog en Select a project
select a responsible user: a person you want to delegate this task infolog en Select a responsible user: a person you want to delegate this task
select a typ to edit it's status-values or delete it infolog en Select a type to edit it's status-values or delete it
select an action infolog en Select an action
select an action... infolog en Select an action...
select an app to search in infolog en Select an App to search in
select an entry to link with infolog en Select an entry to link with
select multiple contacts for a further action infolog en Select multiple contacts for a further action
select new category infolog en Select new category
select to filter by owner infolog en select to filter by owner
select to filter by responsible infolog en select to filter by responsible
select users or groups infolog en Select users or groups
selection cf infolog en Selection CF
sender infolog en Sender
set status to done infolog en Set status to done
set status to done for all entries infolog en Set status to done for all entries
sets the status of this entry and its subs to done infolog en Sets the status of this entry and its subs to done
sets the status of this entry to done infolog en Sets the status of this entry to done
should infolog show subtasks, -calls or -notes in the normal view or not. you can always view the subs via there parent. infolog en Should InfoLog show Subtasks, -calls or -notes in the normal view or not. You can always view the subs via their parent.
should infolog show the links to other applications and/or the file-attachments in the infolog list (normal view when you enter infolog). infolog en Should InfoLog show the links to other applications and/or the file-attachments in the InfoLog list (normal view when you enter InfoLog).
should infolog show up on the main screen and with which filter. works only if you dont selected an application for the main screen (in your preferences). infolog en Should InfoLog show up on the main screen and with which filter. Works only if you didn't select an application for the main screen (in your preferences).
should infolog use full names (surname and familyname) or just the loginnames. infolog en Should InfoLog use full names (sure name and family name) or just the login names.
should the infolog list show a unique numerical id, which can be used eg. as ticket id. infolog en Should the InfoLog list show a unique numerical Id, which can be used eg. as ticket Id.
should the infolog list show the column "last modified". infolog en Should the InfoLog list show the column "last modified".
should the infolog list show the percent done only for status ongoing or two separate icons. infolog en Should the InfoLog list show the percent done only for status ongoing or two separate icons.
should this entry only be visible to you and people you grant privat access via the acl infolog en should this entry only be visible to you and people you grant private access via the ACL
show a column for used and planned times in the list. infolog en Show a column for used and planned times in the list.
show a x if content equals this compare value, otherwise show nothing infolog en show a X if content equals this compare value, otherwise show nothing
show full usernames infolog en Show full usernames
show in the infolog list infolog en Show in the InfoLog list
show last modified infolog en Show last modified
show status and percent done separate infolog en Show status and percent done separate
show ticket id infolog en Show ticket Id
show times infolog en Show times
small view infolog en small view
start infolog en Start
start a new search, cancel this link infolog en start a new search, cancel this link
startdate infolog en Start Date
startdate enddate infolog en Start Date Due Date
startdate for new entries infolog en Startdate for new entries
starting %1 infolog en Starting %1
startrecord infolog en Startrecord
status infolog en Status
status ... infolog en Status ...
sub infolog en Sub
sub-entries become subs of the parent or main entries, if there's no parent infolog en Sub-entries become subs of the parent or main entries, if there's no parent
sub-entries will not be closed infolog en Sub-entries will not be closed
subject infolog en Subject
sum infolog en Sum
tag to mark positions for address labels infolog en Tag to mark positions for address labels
task infolog en ToDo
tasks of infolog en Tasks of
template infolog en Template
test import (show importable records <u>only</u> in browser) infolog en Test Import (show importable records <u>only</u> in browser)
the name used internaly (<= 10 chars), changeing it makes existing data unavailible infolog en the name used internally (<= 10 chars), changing it makes existing data unavailable
the name used internaly (<= 20 chars), changeing it makes existing data unavailible infolog en the name used internally (<= 20 chars), changing it makes existing data unavailable
the text displayed to the user infolog en the text displayed to the user
this is the filter infolog uses when you enter the application. filters limit the entries to show in the actual view. there are filters to show only finished, still open or futures entries of yourself or all users. infolog en This is the filter InfoLog uses when you enter the application. Filters limit the entries to show in the actual view. There are filters to show only finished, still open or futures entries of yourself or all users.
til when should the todo or phonecall be finished infolog en til when should the ToDo or Phone call be finished
times infolog en Times
to many might exceed your execution-time-limit infolog en to many might exceed your execution-time-limit
to what should the startdate of new entries be set. infolog en To what should the start date of new entries be set.
today infolog en Today
todays date infolog en todays date
todo infolog en ToDo
translation infolog en Translation
typ infolog en Type
typ '%1' already exists !!! infolog en Type '%1' already exists !!!
type infolog en Type
type ... infolog en Type ...
type of customfield infolog en Type of customfield
type of the log-entry: note, phonecall or todo infolog en Type of the log-entry: Note, Phone call or ToDo
unlink infolog en Unlink
unlinked from %1 infolog en unlinked from %1
upcoming infolog en upcoming
urgency infolog en urgency
urgent infolog en urgent
use all infolog en Use all
use field from csv if possible infolog en Use field from CSV if possible
use search results infolog en Use search results
use this tag for addresslabels. put the content, you want to repeat, between two tags. infolog en Use this tag for addresslabels. Put the content, you want to repeat, between two tags.
used time infolog en used time
valid path on clientside<br>eg. \\server\share or e:\ infolog en valid path on clientside<br>eg. \\Server\Share or e:\
values for selectbox infolog en Values for selectbox
view all subs of this entry infolog en View all subs of this entry
view other subs infolog en view other Subs
view parent infolog en View parent
view subs infolog en view Subs
view the parent of this entry and all his subs infolog en View the parent of this entry and all his subs
view this linked entry in its application infolog en view this linked entry in its application
when should the todo or phonecall be started, it shows up from that date in the filter open or own open (startpage) infolog en when should the ToDo or Phone call be started, it shows up from that date in the filter open or own open (start page)
which additional fields should the responsible be allowed to edit without having edit rights?<br />status, percent and date completed are always allowed. infolog en Which additional fields should the responsible be allowed to edit without having edit rights?<br />Status, percent and date completed are always allowed.
which implicit acl rights should the responsible get? infolog en Which implicit ACL rights should the responsible get?
which types should the calendar show infolog en Which types should the calendar show
whole query infolog en whole query
will-call infolog en will call
write (add or update) a record by passing its fields. infolog en Write (add or update) a record by passing its fields.
yes - close infolog en Yes - Close
yes - close including sub-entries infolog en Yes - Close including sub-entries
yes - delete infolog en Yes - Delete
yes - delete including sub-entries infolog en Yes - Delete including sub-entries
yes, noone can purge deleted items infolog en Yes, noone can purge deleted items
yes, only admins can purge deleted items infolog en Yes, only admins can purge deleted items
yes, with larger fontsize infolog en Yes, with larger fontsize
yes, with purging of deleted items possible infolog en Yes, with purging of deleted items possible
you can choose a categorie to be preselected, when you create a new infolog entry infolog en You can choose a category to be preselected, when you create a new InfoLog entry
you can't delete one of the stock types !!! infolog en You can't delete one of the stock types !!!
you have entered an invalid ending date infolog en You have entered an invalid due date
you have entered an invalid starting date infolog en You have entered an invalid starting date
you have to enter a name, to create a new typ!!! infolog en You have to enter a name, to create a new type!!!
you must enter a subject or a description infolog en You must enter a subject or a description
you need to select an entry for linking. infolog en You need to select an entry for linking.
you need to select some entries first infolog en You need to select some entries first
your database is not up to date (%1 vs. %2), please run %3setup%4 to update your database. infolog en Your database is NOT up to date (%1 vs. %2), please run %3setup%4 to update your database.

View File

@ -26,7 +26,6 @@ a short subject for the entry infolog es-es descripción corta para la entrada
abort without deleting infolog es-es Cancelar sin eliminar
accept infolog es-es aceptar
action infolog es-es Acción
actions... infolog es-es Acciones...
actual date and time infolog es-es Fecha y hora actuales
add infolog es-es Añadir
add a file infolog es-es Añadir archivo
@ -75,7 +74,6 @@ completed infolog es-es Completado
configuration infolog es-es Configuración
confirm infolog es-es Confirmar
contact infolog es-es Contacto
copy of: infolog es-es Copia de:
copy your changes to the clipboard, %1reload the entry%2 and merge them. infolog es-es Copiar los cambios al portapapeles, %1recargar la entrada%2 e incluirlos.
create new links infolog es-es Crear nuevos enlaces
creates a new field infolog es-es crea un campo nuevo
@ -179,7 +177,6 @@ infolog - import csv-file infolog es-es Registro - Importar archivo CSV
infolog - new infolog es-es Registro - Nuevo
infolog - new subproject infolog es-es Registro - Nuevo subproyecto
infolog - subprojects from infolog es-es Registro - Subproyectos de
infolog copied - the copy can now be edited infolog es-es Registro copiado - ahora la copia se puede editar
infolog entry deleted infolog es-es Se ha borrado la entrada del registro
infolog entry saved infolog es-es Se ha guardado la entrada en el registro
infolog filter for the main screen infolog es-es Filtro para el registro en la pantalla principal
@ -279,7 +276,6 @@ receive notifications about own items infolog es-es Recibir notificaciones acerc
receive notifications about starting entries you are responsible for infolog es-es Recibir notificaciones acerca de las entradas que comienzan de las que usted es responsable
receive notifications about starting entries you delegated infolog es-es Recibir notificaciones acerca de las entradas que comienzan que usted ha delegado
receive notifications as html-mails infolog es-es Recibir notificaciones como correo html
reg. expr. for local ip's<br>eg. ^192.168.1. infolog es-es expresión regular para IPs locales<br>ej. ^192\.168\.1\.
reg. expr. for local ip's<br>eg. ^192\.168\.1\. infolog es-es expresión regular para IPs locales<br>ej. ^192\.168\.1\.
remark infolog es-es Comentario
remove this link (not the entry itself) infolog es-es Eliminar este enlace (no la entrada en sí)
@ -308,8 +304,6 @@ select an entry to link with infolog es-es Seleccionar una entrada a la que apun
select to filter by owner infolog es-es seleccionar para filtrar por propietario
select to filter by responsible infolog es-es seleccionar para filtrar por responsable
sender infolog es-es Remitente
set status to done infolog es-es Establecer estado a terminado
set status to done for all entries infolog es-es Establecer el estado a terminado para todas las entradas
sets the status of this entry and its subs to done infolog es-es Pone el estado de esta entrada y las inferiores a Hecho
should infolog show subtasks, -calls or -notes in the normal view or not. you can always view the subs via there parent. infolog es-es ¿Mostrar las Subtareas (llamadas o notas) en la vista normal? Siempre puede ver estos subtipos a través de su tipo superior.
should infolog show the links to other applications and/or the file-attachments in the infolog list (normal view when you enter infolog). infolog es-es ¿Deben mostrarse los enlaces a otras aplicaciones y/o los ficheros adjuntos en la lista del registro (la vista normal al entrar en el registro)?
@ -365,7 +359,6 @@ urgency infolog es-es Urgencia
urgent infolog es-es urgente
used time infolog es-es tiempo utilizado
valid path on clientside<br>eg. \\server\share or e:\ infolog es-es ruta válida en el cliente<br>p.ej. \\servidor\recurso o e:
valid path on clientside<br>eg. \servershare or e: infolog es-es ruta válida en el cliente<br>p.ej. \\servidor\recurso o e:
values for selectbox infolog es-es Valores para lista desplegable
view all subs of this entry infolog es-es Ver todos los subs de esta entrada
view other subs infolog es-es ver otros subs
@ -377,6 +370,7 @@ when should the todo or phonecall be started, it shows up from that date in the
which additional fields should the responsible be allowed to edit without having edit rights?<br />status, percent and date completed are always allowed. infolog es-es ¿Qué campos adicionales puede editar el responsable sin tener derechos de edición?<br />El estado, porcentaje y fecha de finalización se permiten siempre.
which implicit acl rights should the responsible get? infolog es-es ¿Qué derechos implícitos de ACL debe tener el responsable?
which types should the calendar show infolog es-es Qué tipos debe mostrar el calendario
whole query infolog es-es la consulta completa
will-call infolog es-es va a llamar
write (add or update) a record by passing its fields. infolog es-es Escribir (añadir o actualizar) un registro pasando sus campos.
yes - close infolog es-es Sí, cerrar

398
infolog/lang/egw_fi.lang Normal file
View File

@ -0,0 +1,398 @@
%1 days in advance infolog fi %1 päivää etukäteen
%1 deleted infolog fi %1 poistettu
%1 deleted by %2 at %3 infolog fi %1 poistanut: %2, klo %3
%1 modified infolog fi %1 muokattu
%1 modified by %2 at %3 infolog fi %1 muokannut: %2, klo %3
%1 records imported infolog fi %1 tietue(tta) tuotu
%1 records read (not yet imported, you may go %2back%3 and uncheck test import) infolog fi %1 tietue(tta) luettu (Ei vielä tuotu, mene %2takaisin%3 ja poista valinta Testi Tuonti)
%1 you are responsible for is due at %2 infolog fi %1, jossa olet vastuuhenkilönä, erääntyy
%1 you are responsible for is starting at %2 infolog fi %1, jossa olet vastuuhenkilönä, alkaa
%1 you delegated is due at %2 infolog fi %1, jonka delegoit, erääntyy
%1 you delegated is starting at %2 infolog fi %1, jonka delegoit, alkaa
- subprojects from infolog fi - Aliprojektit
0% infolog fi 0%
10% infolog fi 10%
100% infolog fi 100%
20% infolog fi 20%
30% infolog fi 30%
40% infolog fi 40%
50% infolog fi 50%
60% infolog fi 60%
70% infolog fi 70%
80% infolog fi 80%
90% infolog fi 90%
<b>file-attachments via symlinks</b> instead of uploads and retrieval via file:/path for direct lan-clients infolog fi <b>Liitetiedostot symbolisina linkkeinä</b>Sen sijaan että lataisit ja etsisit tiedostoja: /polku suoraan lähiverkon koneille
a short subject for the entry infolog fi Lyhyt aihe merkinnälle
abort without deleting infolog fi Keskeytä ilman poistamista
accept infolog fi Hyväksy
action infolog fi Toiminnot
actions... infolog fi Toiminnot...
actual date and time infolog fi Tämänhetkinen päivä ja aika
add infolog fi Lisää
add a file infolog fi Lisää tiedosto
add a new entry infolog fi Lisää uusi merkintä
add a new note infolog fi Lisää uusi muistiinpano
add a new phonecall infolog fi Lisää uusi puhelinsoitto
add a new sub-task, -note, -call to this entry infolog fi Lisää uusi ali-tehtävä, -huomautus, -soitto merkinnälle
add a new todo infolog fi Lisää uusi tehtävä
add file infolog fi Lisää tiedosto
add sub infolog fi Lisää Sub
add timesheet entry infolog fi Lisää ajanseurannan merkintä
add: infolog fi Lisää:
all infolog fi Kaikki
all links and attachments infolog fi Kaikki linkit ja liitteet
all projects infolog fi Kaikki projektit
allows to set the status of an entry, eg. set a todo to done if it's finished (values depend on entry-typ) infolog fi Salli tilan muokkaaminen, esim. tehtävän tehdyksi asettaminen, jos se on päättynyt (arvot riippuvat sisällön muodosta).
alternatives infolog fi Vaihtoehdot
apply the changes infolog fi Hyväksy muutokset
archive infolog fi Arkisto
are you shure you want to close this entry ? infolog fi Haluatko varmasti sulkea tämän ikkunan?
are you shure you want to delete this entry ? infolog fi Halutako varmasti poistaa tämän merkinnän?
attach a file infolog fi Liitä tiedosto
attach file infolog fi Liitä tiedosto
attention: no contact with address %1 found. infolog fi Huomio: Yhteytietoja/Kontaktia %1 ei löytynyt
back to main list infolog fi Takaisin päälistaan
billed infolog fi Laskutettu
both infolog fi Molemmat
call infolog fi Puhelinsoitto
can be used to show further infolog types in the calendar or limit it to show eg. only tasks. infolog fi Valitse InfoLog tyypit joiden haluat näkyvän kalenterissa. Voit myös rajoittaa kalenterissa näkyvien IngoLogien määrän vain esim. Tehtäviin.
cancel infolog fi Peruuta
cancelled infolog fi Peruutettu
categories infolog fi Kategoriat
category infolog fi Kategoria
change history infolog fi Muuta historiaa
change the status of an entry, eg. close it infolog fi Vaihda merkinnän tilaa, esim. "Valmis"
charset of file infolog fi Tiedoston merkistö
check to set startday infolog fi Tarkista aloituspäivä
check to specify custom contact infolog fi Valitse tietty yhteystieto
click here to create the link infolog fi Luo linkki
click here to start the search infolog fi Aloita haku
close infolog fi Sulje
close all infolog fi Sulje kaikki
close this entry and all listed sub-entries infolog fi Sulje tämä merkintä ja kaikki sen alimerkinnät
comment infolog fi Kommentti
completed infolog fi Valmis
configuration infolog fi Asetukset
confirm infolog fi Vahvista
contact infolog fi Yhteystieto
copy of: infolog fi Kopio:
copy your changes to the clipboard, %1reload the entry%2 and merge them. infolog fi Kopioi tekemäsi muutokset leikepöydälle, %1 lataa merkintä %2 ja sulauta ne.
create new links infolog fi Luo uusi linkki
creates a new field infolog fi Luo uusi kenttä
creates a new status with the given values infolog fi Luo uusi tila annetuilla arvoilla
creates a new typ with the given name infolog fi Luo uusi tyyppi annetuilla nimillä
creation infolog fi Luo uusi
csv-fieldname infolog fi CSV -kentän nimi
csv-filename infolog fi CSV -tiedoston nimi
csv-import common fi CSV -Tuonti
custom infolog fi Muokkaa
custom fields infolog fi Lisäkentät
custom fields, typ and status common fi Lisäkentät, tyyppi ja tila
custom status for typ infolog fi Muokattava tila InfoLog-tyypille
customfields infolog fi Lisäkentät
date completed infolog fi Valmistumispäivä
date completed (leave it empty to have it automatic set if status is done or billed) infolog fi Valmistumispäivä (jätä tyhjäksi automaattista asetusta varten, jos tilana on "Valmis" tai "Laskutettu")
datecreated infolog fi Luotu
dates, status, access infolog fi Päivämäärä, tila, ACL-oikeudet
days infolog fi päivää
default category for new infolog entries infolog fi Oletuskategoria uusille InfoLog -merkinnöille
default filter for infolog infolog fi InfoLogin oletussuodatin
default status for a new log entry infolog fi Uuden merkinnän oletustila
delegated infolog fi Delegoidut
delegated open infolog fi Delegoidut avoimet
delegated open and upcoming infolog fi Delegoidut avoimet ja tulevat
delegated overdue infolog fi Delegoidut myöhästyneet
delegated upcomming infolog fi Delegoidut tulevat
delegation infolog fi Delegointi
delete infolog fi Poista
delete one record by passing its id. infolog fi Poista yksi tietue ohittaen sen id.
delete the entry infolog fi Poista merkintä
delete this entry infolog fi Poista tämä merkintä
delete this entry and all listed sub-entries infolog fi Poista tämä merkintä ja kaikki sen alimerkinnät
deleted infolog fi Poistettu
deletes the selected typ infolog fi Poista valittu tyyppi
deletes this field infolog fi Poista tämä kenttä
deletes this status infolog fi Poista tämä tila
description infolog fi Kuvaus
determines the order the fields are displayed infolog fi Määrittelee näkyvissä olevien kenttien järjestyksen
disables a status without deleting it infolog fi Ota tila pois käytöstä poistamatta sitä
do you want a confirmation of the responsible on: accepting, finishing the task or both infolog fi Haluatko vahvistuksen vastuuhenkilöltä: tehtävän hyväksymisestä, tehtävän valmistumisesta vai molemmista?
do you want a notification, if items get assigned to you or assigned items get updated? infolog fi Haluatko muistutusviestin, jos sinulle delegoidaan tai delegoimaasi merkintää/tehtävää päivitetään?
do you want a notification, if items you are responsible for are about to start? infolog fi Haluatko muistutusviestin, kun merkinnän/tehtävän alkamispäivä lähestyy, jos olet vastuuhenkilönä?
do you want a notification, if items you are responsible for are due? infolog fi Haluatko muistutusviestin, kun merkinnän/tehtävän eräpäivä lähestyy, jos olet vastuuhenkilönä?
do you want a notification, if items you created get updated? infolog fi Haluatko muistutusviestin, jos luomiasi merkintöjä/tehtäviä päivitetään?
do you want a notification, if items you delegated are about to start? infolog fi Haluatko muistutusviestin, kun delegoimasi merkinnän/tehtävän alkamispäivä lähestyy?
do you want a notification, if items you delegated are due? infolog fi Haluatko muistutusviestin, kun delegoimasi merkinnän/tehtävän eräpäivä lähestyy?
do you want to receive notifications as html-mails or plain text? infolog fi Haluatko muistutusviestiesi olevan HTML vai pelkkänä tekstinä?
don't show infolog infolog fi Älä näytä InfoLogia
done infolog fi Valmis
download infolog fi Lataa
due %1 infolog fi Erääntyy %1
duration infolog fi Kesto
e-mail: infolog fi Sähköposti:
each value is a line like <id>[=<label>] infolog fi Jokainen arvo on rivillä kuten <id>[=<label>]
edit infolog fi Muokkaa
edit or create categories for ingolog infolog fi Muokkaa tai luo kategoria InfoLogiin
edit rights (full edit rights incl. making someone else responsible!) infolog fi Muokkausoikeudet (täydet muokkausoikeudet, mukaanlukien delegointi!)
edit status infolog fi Muokkaa tilaa
edit the entry infolog fi Muokkaa merkintää
edit this entry infolog fi Muokkaa tätä merkintää
empty for all infolog fi Tyhjä kaikille
enddate infolog fi Erääntymispäivä
enddate can not be before startdate infolog fi Erääntymispäivä ei voi olla ennen alkamispäivää
enter a custom contact, leave empty if linked entry should be used infolog fi Syötä kontakti, jätä tyhjäsi jos linkitettyä kontaktia käytetään
enter a custom phone/email, leave empty if linked entry should be used infolog fi Syötä puhelinnumero/sähköposti, jätä tyhjäsi jos linkitettyä yhteystietoa käytetään
enter a textual description of the log-entry infolog fi Syötä tekstimuotoinen kuvaus merkintään
enter the query pattern infolog fi Anna tiedustelun malli
entry and all files infolog fi Merkintä ja kaikki tiedostot
error: saving the entry infolog fi Virhe tallentaessa merkintää
error: the entry has been updated since you opened it for editing! infolog fi Virhe: Merkintää on päivitetty sen jälkeen kun avasit sen muokattavaksi!
existing links infolog fi Olemassa olevat linkit
fax infolog fi Faksi
field must not be empty !!! infolog fi Kenttää ei voi jättää tyhjäksi!!
fieldseparator infolog fi Kenttäerotin
finish infolog fi Lopeta
for which types should this field be used infolog fi Mitkä Infolog-tyypit käyttävät tätä kenttää?
from infolog fi Lähettäjä:
general infolog fi Yleinen
global categories infolog fi Yleiset kategoriat
group owner for infolog fi Ryhmän omistaja
high infolog fi Korkea
history infolog fi Historia
history logging infolog fi Merkintöjen historia
history logging and deleting of items infolog fi Historia ja poistettujen merkintöjen lopullinen poistaminen. Kenellä on oikeus tarkastella ja poistaa lopullisesti (deletoituja) merkintöjä?
how many describtion lines should be directly visible. further lines are available via a scrollbar. infolog fi Kuinka monta riviä kuvaustekstiä on näkyvillä. Loput rivit ovat näkyvillä scroll barissa.
id infolog fi Id
id# infolog fi Id#
if a type has a group owner, all entries of that type will be owned by the given group and not the user who created it! infolog fi Jos tyypin omistaa ryhmä, kaikki tapahtumat/merkinnät omistaa ryhmä, EI käyttäjä joka loi sen!
import infolog fi Tuonti
import next set infolog fi Tuo seuraava osa
importance infolog fi Tärkeys
info log common fi InfoLog
infolog common fi InfoLog
infolog - delete infolog fi InfoLog - Poista
infolog - edit infolog fi InfoLog - Muokkaa
infolog - import csv-file infolog fi InfoLog - Tuo CSV-tiedosto
infolog - new infolog fi InfoLog - Uusi
infolog - new subproject infolog fi InfoLog - Uusi aliprojekti
infolog - subprojects from infolog fi InfoLog - Aliprojektit kohteesta
infolog copied - the copy can now be edited infolog fi InfoLog kopioitu - kopiota voidaan nyt muokata
infolog entry deleted infolog fi InfoLog merkintä poistettu
infolog entry saved infolog fi InfoLog merkintä tallennettu
infolog filter for the main screen infolog fi InfoLog suodatin etusivulla
infolog list infolog fi InfoLog lista
infolog preferences common fi InfoLog asetukset
infolog-fieldname infolog fi InfoLog kentän nimi
invalid filename infolog fi Tiedostonimi ei kelpaa
label<br>helptext infolog fi Otsikko<br>Vihjeteksti
last changed infolog fi Viimeksi muutettu
last modified infolog fi Viimeksi muokattu
leave blank to get the used time calculated by timesheet entries infolog fi Jätä tyhjäksi, jos haluat (kulutetun ajan) summan tulevan Ajanhallinnan merkinnöistä.
leave it empty infolog fi Jätä tyhjäksi
leave without saveing the entry infolog fi Poistu tallentamatta merkintää
leaves without saveing infolog fi Poistu tallentamatta
length<br>rows infolog fi Pituus<br>Rivit
limit number of description lines (default 5, 0 for no limit) infolog fi Rajoita kuvauskentän rivien määrää (Oletus on 5, merkitse 0, jos et halua rajoitusta).
link infolog fi Linkki
links infolog fi Linkit
links of this entry infolog fi Tämän merkinnän linkit
list all categories infolog fi Listaa kaikki kategoriat
list no subs/childs infolog fi Älä listaa alimerkintöjä
location infolog fi Sijainti
longer textual description infolog fi Pitempi tekstikuvaus
low infolog fi Matala
max length of the input [, length of the inputfield (optional)] infolog fi Kentän tekstin maksimipituus [, kentän maksimi pituus(vaihtoehtoinen)]
name must not be empty !!! infolog fi Nimikenttä ei saa olla tyhjä !!!
name of new type to create infolog fi Luotavan tyypin nimi
never hide search and filters infolog fi Hakua ja suodattimia ei piiloiteta koskaan
new %1 infolog fi Uusi %1
new %1 created by %2 at %3 infolog fi Uusi %1, luonut %1, %3
new name infolog fi Uusi nimi
new search infolog fi Uusi haku
no - cancel infolog fi Ei - Peruuta
no describtion, links or attachments infolog fi Ei kuvausta, linkkejä tai liitetiedostoja
no details infolog fi Ei lisätietoja
no entries found, try again ... infolog fi Merkintöjä ei löytynyt, yritä uudelleen ...
no filter infolog fi Ei suodatinta
no links or attachments infolog fi Ei linkkejä tai liitteitä
no project infolog fi Ei projektia
nonactive infolog fi Aktivoimaton
none infolog fi Ei mitään
normal infolog fi Normaali
not infolog fi ei
not assigned infolog fi Kohdistamattomat
not-started infolog fi Aloittamatta olevat
note infolog fi Muistiinpano
number of records to read (%1) infolog fi (%1) tietuetta luettu
number of row for a multiline inputfield or line of a multi-select-box infolog fi Rivien määrä monenrivisessä tekstikentässä tai monivalinta-laatikosta
offer infolog fi Tarjous
one day after infolog fi Päivää myöhemmin
one day in advance infolog fi Päivä etukäteen
ongoing infolog fi Meneillään oleva
only for details infolog fi Vain lisätiedot
only if i get assigned or removed infolog fi Vain jos minulle delegoidaan tai minut poistetaan vastuusta
only the attachments infolog fi Vain liitteet
only the links infolog fi Vain linkit
open infolog fi Avoimet
open and upcoming infolog fi Avoimet ja tulevat
optional note to the link infolog fi Vapaaehtoinen muistiinpano linkkiin
order infolog fi Järjestä
organization infolog fi Organisaatio
overdue infolog fi Myöhästynyt
own infolog fi Omat
own open infolog fi Omat avoimet
own open and upcoming infolog fi Omat avoimet ja tulevat
own overdue infolog fi Omat myöhästyneet
own upcoming infolog fi Omat tulevat
parent infolog fi Päämerkintä
parent infolog infolog fi Päämerkintä
path on (web-)serverside<br>eg. /var/samba/share infolog fi Polku (web-)palvelimella<br>esim. /var/samba/Share
path to user and group files has to be outside of the webservers document-root!!! infolog fi Polku käyttäjän ja ryhmän tiedostoihin pitää olla palvelimen tiedostojuuren ULKOPUOLELLA!!!
pattern for search in addressbook infolog fi Merkkijonohaku Yhteystiedoista
pattern for search in projects infolog fi Merkkijonohaku Projekteista
percent completed infolog fi Prosenttia valmistunut
permission denied infolog fi Pääsy estetty
phone infolog fi Puhelinsoitto
phone/email infolog fi Puhelin/E-mail
phonecall infolog fi Puhelinsoitto
planned infolog fi Suunniteltu
planned time infolog fi Suunniteltu aikataulu
price infolog fi Hinta
pricelist infolog fi Hinnasto
primary link infolog fi Ensisijainen linkki
priority infolog fi Tärkeys
private infolog fi Yksityinen
project infolog fi Projekti
project settings: price, times infolog fi Projektin asetukset: hinta, aika
projectmanager infolog fi Projektinhallinta
re-planned infolog fi Uudelleen suunniteltu
re-planned time infolog fi Uudelleen suunniteltu aikataulu
re: infolog fi Re:
read one record by passing its id. infolog fi Lue yksi tietue ohittaen sen id.
read rights (default) infolog fi Lukuoikeudet (oletus)
receive notifications about due entries you are responsible for infolog fi Vastaanota muistutusviestejä eräpäivää lähestyvistä merkinnöistä/tehtävistä, joissa olet vastuuhenkilönä.
receive notifications about due entries you delegated infolog fi Vastaanota muistutusviestejä delegoimistasi eräpäivää lähestyvistä merkinnöistä/tehtävistä.
receive notifications about items assigned to you infolog fi Vastaanota muistutusviestejä sinulle delegoiduista merkinnöistä/tehtävistä.
receive notifications about own items infolog fi Vastaanota muistutusviestejä omista merkinnöistä/tehtävistä.
receive notifications about starting entries you are responsible for infolog fi Vastaanota muistutusviestejä alkamassa olevista merkinnöistä/tehtävistä, joissa olet vastuuhenkilönä.
receive notifications about starting entries you delegated infolog fi Vastaanota muistutusviestejä delegoimistasi alkamassa olevista merkinnöistä/tehtävistä.
receive notifications as html-mails infolog fi Vastaanota muistutusviestit HTMLnä
reg. expr. for local ip's<br>eg. ^192.168.1. infolog fi reg. expr. paikallisille IP-osoitteille<br>esim. ^192\.168\.1\.
reg. expr. for local ip's<br>eg. ^192\.168\.1\. infolog fi reg. expr. paikallisille IP-osoitteille<br>esim. ^192\.168\.1\.
remark infolog fi Merkitse uudelleen
remove this link (not the entry itself) infolog fi Poista tämä linkki (ei poista itse merkintää)
responsible infolog fi Vastuuhenkilö
responsible open infolog fi Vastuuhenkilö, avoimet
responsible open and upcoming infolog fi Vastuuhenkilö, avoimet ja tulevat
responsible overdue infolog fi Vastuuhenkilö, myöhästyneet
responsible upcoming infolog fi Vastuuhenkilö, tulevat
responsible user, priority infolog fi Vastuuhenkilö, tärkeysaste
returns a list / search for records. infolog fi Palauttaa listan / haun arvoista
rights for the responsible infolog fi Vastuuhenkilön oikeudet
same day infolog fi Samana päivänä
save infolog fi Tallenna
saves the changes made and leaves infolog fi Tallenna muutokset ja poistu
saves this entry infolog fi Tallenna tämä merkintä
search infolog fi Etsi
search for: infolog fi Etsi:
select infolog fi Valitse
select a category for this entry infolog fi Valitse kategoria merkinnälle
select a price infolog fi Valitse hinta
select a priority for this task infolog fi Valitse tehtävän tärkeys
select a project infolog fi Valitse projekti
select a responsible user: a person you want to delegate this task infolog fi Valitse vastuuhenkilö, jolle haluat siirtää tehtävän
select a typ to edit it's status-values or delete it infolog fi Valitse tyyppi muokataksesi sen tilan arvoa tai poistaaksesi sen.
select an app to search in infolog fi Valitse haettava sovellus
select an entry to link with infolog fi Valitse merkintä linkitettäväksi
select to filter by owner infolog fi Suodata omistajan mukaan
select to filter by responsible infolog fi Suodata vastuuhenkilön mukaan
sender infolog fi Lähettäjä
set status to done infolog fi Aseta tilaksi "Valmis"
set status to done for all entries infolog fi Aseta kaikkien merkintöjen tilaksi "Valmis"
sets the status of this entry and its subs to done infolog fi Aseta tälle merkinnälle ja sen alimerkinnöille tilaksi "Valmis".
should infolog show subtasks, -calls or -notes in the normal view or not. you can always view the subs via there parent. infolog fi Näyttääkö InfoLog alitehtävät, -soitot tai -muistiot normaalinäkymässä? Voit aina nähdä alimerkinnät päämerkinnän alta?
should infolog show the links to other applications and/or the file-attachments in the infolog list (normal view when you enter infolog). infolog fi Näyttääkö InfoLog linkkejä muihin sovelluksiin ja / tai liitetiedostoihin InfoLog listalla (normaalinäkymässä kun avaat InfoLogin).
should infolog show up on the main screen and with which filter. works only if you dont selected an application for the main screen (in your preferences). infolog fi Näyttetääkö InfoLog näkymä etusivulla ja millä suotimilla. Toimii vain jos et ole valinnut mitään sovellusta etusivulle omissa (henkilökohtaisissa) asetuksissasi?
should infolog use full names (surname and familyname) or just the loginnames. infolog fi Käyttääkö InfoLog koko nimeä (etunimi, sukunimi) vai ainoastaan kirjautumisnimeä.
should the infolog list show a unique numerical id, which can be used eg. as ticket id. infolog fi Näyttääkö InfoLog lista yksilöllisen numerotunnisteen, jota voidaan käyttää esimerkiksi merkinnän tunnisteena?
should the infolog list show the column "last modified". infolog fi Näyttääkö InfoLog listaussarakkeen "viimeksi muokattu?
should the infolog list show the percent done only for status ongoing or two separate icons. infolog fi Näyttääkö InfoLog listauksessa montako prosenttia tehtävästä on tehty vai näytetäänkö kaksi erillistä kuvaketta?
should this entry only be visible to you and people you grant privat access via the acl infolog fi pitäisikö tämän merkinnän näkyä vain sinulle ja niille joille olet antanut oikeudet ACL:ssä?
show a column for used and planned times in the list. infolog fi Näytä listalla sarakkeet käytetystä ja suunnitellusta ajankäytöstä.
show full usernames infolog fi Näytä koko käyttäjänimet
show in the infolog list infolog fi Näytä InfoLogin listassa
show last modified infolog fi Näytä viimeksi muokattu
show status and percent done separate infolog fi Näytä tila ja prosenttia tehty, erillisinä kohtina.
show ticket id infolog fi Näytä merkinnän id
show times infolog fi Näytä ajat
small view infolog fi Pieni näkymä
start a new search, cancel this link infolog fi Aloita uusi haku
startdate infolog fi Aloituspäivä
startdate enddate infolog fi Aloituspäivä, Erääntymispäivä
startdate for new entries infolog fi Aloituspäivä uusille merkinnöille
starting %1 infolog fi Alkaa %1
startrecord infolog fi Startrecord
status infolog fi Tila
status ... infolog fi Tila...
sub infolog fi Sub
sub-entries become subs of the parent or main entries, if there's no parent infolog fi Alimerkinnät (sub entries) tulevat päämerkinnän (parent) alle tai päämerkinnöiksi, ellei niille aseteta päämerkintää.
subject infolog fi Aihe
sum infolog fi Summa
task infolog fi Tehtävä
tasks of infolog fi Tehtävät:
template infolog fi Mallipohja
test import (show importable records <u>only</u> in browser) infolog fi Testi Tuonti (näytä tuotavat tietueet <u>vain</u> selaimessa)
the name used internaly (<= 10 chars), changeing it makes existing data unavailible infolog fi Nimi on sisäisessä käytössä (<= 10 merkkiä), sen muuttaminen aiheuttaa nykyisen tiedon menettämisen.
the name used internaly (<= 20 chars), changeing it makes existing data unavailible infolog fi Nimi on sisäisessä käytössä (<= 10 merkkiä), sen muuttaminen aiheuttaa nykyisen tiedon menettämisen.
the text displayed to the user infolog fi Käyttäjälle näytettävä teksti
this is the filter infolog uses when you enter the application. filters limit the entries to show in the actual view. there are filters to show only finished, still open or futures entries of yourself or all users. infolog fi Käytä valitsemaasi suodatinta kun kirjaudut InfoLogiin. Suodatin rajoittaa kerralla näkyvien merkintöjen määrää. (Suodata avoimet, tulevat tai myöhästyneet, jne... merkinnät ensinäkymäksi)
til when should the todo or phonecall be finished infolog fi Milloin tehtävä tai puhelinsoitto on valmistunut
times infolog fi Ajat
to many might exceed your execution-time-limit infolog fi Liian moni saattaa ylittää suoritusajan ylärajan.
to what should the startdate of new entries be set. infolog fi Mikä alkamispäivä uusille merkinnöille asetetaan.
today infolog fi Tänään
todays date infolog fi Päivämäärä
todo infolog fi Tehtävä
translation infolog fi Käännös
typ infolog fi Tyyppi
typ '%1' already exists !!! infolog fi Tyyppi %1 on jo olemassa
type infolog fi Tyyppi
type ... infolog fi Tyyppi...
type of customfield infolog fi Lisäkentän tyyppi
type of the log-entry: note, phonecall or todo infolog fi InfoLog -merkinnän tyyppi: Muistio, Soitto, Tehtävä
unlink infolog fi Poista linkki
upcoming infolog fi Tuleva
urgency infolog fi Tärkeys
urgent infolog fi Tärkeä
used time infolog fi Käytetty aika
valid path on clientside<br>eg. \\server\share or e:\ infolog fi polku asiakkaan puolella<br>eg. \\Server\Share tai e:\
valid path on clientside<br>eg. \servershare or e: infolog fi polku asiakkaan puolella<br>eg. \\Server\Share tai e:\
valid path on clientside<br>eg. servershare or e: infolog fi polku asiakkaan puolella<br>eg. \\Server\Share tai e:\
values for selectbox infolog fi Valintalaatikon arvot
view all subs of this entry infolog fi Näytä kaikki tämän merkinnän alimerkinnät
view other subs infolog fi Näytä muut alimerkinnät
view parent infolog fi Näytä isäntä
view subs infolog fi Näytä alimerkinnät
view the parent of this entry and all his subs infolog fi Näytä tämän merkinnän päämerkintä ja kaikki sen alimerkinnät
view this linked entry in its application infolog fi Näytä tämä linkitetty merkintä sovelluksessa
when should the todo or phonecall be started, it shows up from that date in the filter open or own open (startpage) infolog fi Millon tehtävän tai puhelinsoiton pitäisi alkaa, se näkyy sinä päivä avoimissa tai omissa avoimissa tehtävissä (aloitussivulla)?
which additional fields should the responsible be allowed to edit without having edit rights?<br />status, percent and date completed are always allowed. infolog fi Mitä muita kenttiä vastuuhenkilö saa muokata, ilman että hänellä on muokkas oikeuksia? <br/>Tila, prosentit, valmistumispäivä ovat aina sallittuja.
which implicit acl rights should the responsible get? infolog fi Mitä ACL -oikeuksia vastuuhenkilö saa?
which types should the calendar show infolog fi Mitkä InfoLog tyypit kalenterin tulisi näyttää
whole query infolog fi Koko kysely
will-call infolog fi soittaa
yes - close infolog fi Kyllä - Sulje
yes - close including sub-entries infolog fi Kyllä - Sulje myös alimerkinnät
yes - delete infolog fi Kyllä - Poista
yes - delete including sub-entries infolog fi Kyllä - Poista myös alimerkinnät
yes, noone can purge deleted items infolog fi Kyllä, kukaan ei voi lopullisesti poistaa poistettuja merkintöjä
yes, only admins can purge deleted items infolog fi Kyllä, ainoastaan ylläpitäjät voivat lopullisesti poistaa poistettuja merkintöjä
yes, with larger fontsize infolog fi Kyllä, Isompi fonttikoko
yes, with purging of deleted items possible infolog fi Kyllä, Poistettujen merkintöjen lopullinen poistaminen on mahdollista
you can choose a categorie to be preselected, when you create a new infolog entry infolog fi Minkä kategorian pitäisi olla oletuksena, kun luot uutta InfoLog merkintää?
you can't delete one of the stock types !!! infolog fi Et voi poistaa mitään tyyppien ryhmästä !!!
you have entered an invalid ending date infolog fi Virheellinen erääntymispäivä
you have entered an invalid starting date infolog fi Virheellinen alkamispäivä
you have to enter a name, to create a new typ!!! infolog fi Kirjoita nimi luodaksesi uuden tyypin !!!
you must enter a subject or a description infolog fi Anna aihe tai kuvaus
your database is not up to date (%1 vs. %2), please run %3setup%4 to update your database. infolog fi Tietokantasi EI ole ajantasalla (%1 vs. %2), suorita %3asetukset%4 päivittääksesi tietokantasi.

318
infolog/lang/egw_fr.lang Normal file
View File

@ -0,0 +1,318 @@
%1 records imported infolog fr %1 enregistrements importés
%1 records read (not yet imported, you may go %2back%3 and uncheck test import) infolog fr %1 enregistrements lus (pas encore importés, vous pourriez %2revenir%3 et désélectionner Test d'Importation)
- subprojects from infolog fr - Sous-projets de
0% infolog fr 0%
10% infolog fr 10%
100% infolog fr 100%
20% infolog fr 20%
30% infolog fr 30%
40% infolog fr 40%
50% infolog fr 50%
60% infolog fr 60%
70% infolog fr 70%
80% infolog fr 80%
90% infolog fr 90%
<b>file-attachments via symlinks</b> instead of uploads and retrieval via file:/path for direct lan-clients infolog fr <b>Attachement de fichiers via des liens symboliques</b> à la place du dépôt de fichiers et récupération via fichier:/chemin pour les clients avec accès réseau direct
a short subject for the entry infolog fr Un court sujet pour l'entrée
abort without deleting infolog fr Annuler sans supprimer
accept infolog fr Accepte
action infolog fr Action
actual date and time infolog fr date et heure actuelles
add infolog fr Ajouter
add a file infolog fr Ajouter un fichier
add a new entry infolog fr Ajouter uen nouvelle entrée
add a new note infolog fr Ajouter une nouvelle Note
add a new phonecall infolog fr Ajouter un nouvel appel téléphonique
add a new sub-task, -note, -call to this entry infolog fr Ajouter un(e) nouvelle sous-tâche, -note, -appel à cette entrée
add a new todo infolog fr Ajouter un nouveau A-faire
add file infolog fr Ajouter un fichier
add sub infolog fr Ajouter enfant
add timesheet entry infolog fr Ajouter une entrée feuille de temps
add: infolog fr Ajouter:
all infolog fr Tous
all links and attachments infolog fr Tous les liens et attachements
allows to set the status of an entry, eg. set a todo to done if it's finished (values depend on entry-typ) infolog fr permet de changer le statut d´une entrée, c'est-à-dire de spécifier un A-Faire à fait s'il est terminé (les valeurs dépendent du type d´entrée).
apply the changes infolog fr Appliquer les changements
are you shure you want to delete this entry ? infolog fr Etes-vous sûr de vouloir supprimer cette entrée ?
attach a file infolog fr Attacher un fichier
attach file infolog fr Attacher un fichier
attension: no contact with address %1 found. infolog fr Attention: Aucun contact avec adresse %1 trouvé.
back to main list infolog fr Retour à la liste principale
billed infolog fr Facturé
both infolog fr Les deux
call infolog fr Appel
cancel infolog fr Annuler
cancelled infolog fr Annulé
categories infolog fr Catégories
category infolog fr Catégorie
change the status of an entry, eg. close it infolog fr Change le statut d´une entrée, c'est-à-dire la ferme
charset of file infolog fr Jeu de caractères du fichier
check to set startday infolog fr Vérifier pour mettre le jour de départ
check to specify custom contact infolog fr Vérifiez pour spécifier un contact personnalisé
click here to create the link infolog fr Cliquer ici pour créer le lien
click here to start the search infolog fr Cliquer ici pour démarrer la recherche
close infolog fr Fermer
comment infolog fr Commentaire
completed infolog fr Terminé
configuration infolog fr Configuration
confirm infolog fr Confirmer
contact infolog fr Contact
copy your changes to the clipboard, %1reload the entry%2 and merge them. infolog fr Copie vos changements vers le presse-papiers, %1recharger l'entrée%2 et les fusionne.
create new links infolog fr Créer de nouveaux liens
creates a new field infolog fr crée un nouveau champ
creates a new status with the given values infolog fr crée un nouveau statut avec la valeur spécifiée
creates a new typ with the given name infolog fr créée un nouveau type avec le nom spécifié
creation infolog fr Création
csv-fieldname infolog fr CSV-Nomdechamp
csv-filename infolog fr CSV-Nomdefichier
csv-import common fr CSV-Importer
custom infolog fr Personnalisé
custom contact-address, leave empty to use information from most recent link infolog fr Adresse de contact personnalisée, laissez vide pour utiliser l'information du lien le plus récent
custom contact-information, leave emtpy to use information from most recent link infolog fr Information de contact personnalisée, laissez vide pour utiliser l'information du lien le plus récent
custom fields infolog fr Champs personnalisés
custom fields, typ and status common fr Champs, types et statuts personnalisés
custom regarding infolog fr Personnalisé concernant
custom status for typ infolog fr Statuts personnalisés pour le type
customfields infolog fr Champs personnalisés
date completed infolog fr Date de clôture
date completed (leave it empty to have it automatic set if status is done or billed) infolog fr Date de clôture (laisser vide pour que ce soit rempli automatiquement si le statut est Fait ou Rempli)
datecreated infolog fr Date de création
dates, status, access infolog fr Dates, Statut, Accès
days infolog fr Jours
default filter for infolog infolog fr Filtre par défaut pour InfoLog
default status for a new log entry infolog fr statut par défaut pour une nouvelle entrée de log
delegation infolog fr Délégation
delete infolog fr Supprimer
delete one record by passing its id. infolog fr Effacer un enregistrement en passant son id.
delete the entry infolog fr supprimer l´entrée
delete this entry infolog fr supprimer cette entrée
delete this entry and all listed sub-entries infolog fr Détruire cette entrée et toutes les sous-entrées listées
deletes the selected typ infolog fr supprime le type sélectionné
deletes this field infolog fr supprime ce champ
deletes this status infolog fr supprime ce statut
description infolog fr Description
determines the order the fields are displayed infolog fr détermine l´ordre d´affichage des champs
disables a status without deleting it infolog fr désactive un statut sans le supprimer
do you want a confirmation of the responsible on: accepting, finishing the task or both infolog fr Voulez-vous une confirmation du responsable sur: accepter, finir la tâche, ou les deux
do you want to see custom infolog types in the calendar? infolog fr Voulez-vous voir les types d'InfoLog personnalisé dans le calendrier?
don't show infolog infolog fr NE PAS afficher InfoLog
done infolog fr Fait
download infolog fr Télécharger
duration infolog fr Durée
each value is a line like <id>[=<label>] infolog fr chaque valeur est une ligne telle que <id>[=<label>]
edit infolog fr Modifier
edit or create categories for ingolog infolog fr Modifier ou créer des catégories pour InfoLog
edit rights (full edit rights incl. making someone else responsible!) infolog fr modifier les droits (édition complète des droits, pouvant rendre quelqu'un d'autre responsable!)
edit status infolog fr Modifier Statut
edit the entry infolog fr Modifier l'entrée
edit this entry infolog fr Modifier cette entrée
empty for all infolog fr vide pour tous
enddate infolog fr Date de fin
enddate can not be before startdate infolog fr La date de fin ne peut pas être avant la date de début
enter a custom contact, leave empty if linked entry should be used infolog fr Entrez un contact personnalisé, laissez vide si l'entrée liée devrait être utilisée
enter a custom phone/email, leave empty if linked entry should be used infolog fr Entrez un EMail/numéro de téléphone personnalisé, laissez vide si l'entrée liée devait être utilisée
enter a textual description of the log-entry infolog fr Entrez une description textuelle de l'entrée de Log
enter the query pattern infolog fr Entrez le champ de requête
entry and all files infolog fr Entrée et tous les fichiers
error: saving the entry infolog fr Erreur: lors de l'enregistrement de l'entrée
error: the entry has been updated since you opened it for editing! infolog fr Erreur: cette entrée a été mise à jour pendant que vous l'aviez ouverte pour la modifier!
existing links infolog fr Liens existants
fax infolog fr Fax
fieldseparator infolog fr Séparateur de champ
finish infolog fr Terminer
for which types should this field be used infolog fr pour quels types ce champ devrait être utilisé
from infolog fr De
general infolog fr Général
group owner for infolog fr Propriétaire de Groupe pour
high infolog fr Haut
id infolog fr Id
if a type has a group owner, all entries of that type will be owned by the given group and not the user who created it! infolog fr Si un type a un propriétaire de groupe, toutes les entrées de ce type appartiendront à ce groupe, et NON à l'utilisateur qui l'a créé!
if not set, the line with search and filters is hidden for less entries then "max matches per page" (as defined in your common preferences). infolog fr Si non renseigné, la ligne contenant la recherche et les filtres est cachée lorsqu'il y a plus d'entrées que le "Nombre max d'occurences correspondantes par page" (défini dans les préférences).
import infolog fr Importer
import next set infolog fr importer l´élément suivant
info log common fr InfoLog
infolog common fr InfoLog
infolog - delete infolog fr InfoLog - Supprimer
infolog - edit infolog fr InfoLog - Modifier
infolog - import csv-file infolog fr InfoLog - Importer fichier CSV
infolog - new infolog fr InfoLog - Nouveau
infolog - new subproject infolog fr InfoLog - Nouveau sous-projet
infolog - subprojects from infolog fr InfoLog - Sous-projets de
infolog entry deleted infolog fr InfoLog entrée supprimée
infolog entry saved infolog fr InfoLog entrée enregistrée
infolog filter for the main screen infolog fr InfoLog filtre pour l'écran principal
infolog list infolog fr Liste InfoLog
infolog preferences common fr Préférences InfoLog
infolog-fieldname infolog fr InfoLog - Nom du champ
invalid filename infolog fr Nom de fichier invalide
label<br>helptext infolog fr Label<br>Texte d´aide
last changed infolog fr Dernière modification
last modified infolog fr Dernière modification
leave it empty infolog fr laisser vide
leave without saveing the entry infolog fr quitter sans enregistrer l'entrée
leaves without saveing infolog fr quitte sans enregistrer
length<br>rows infolog fr Longueur<br>Lignes
link infolog fr Lien
links infolog fr Liens
links of this entry infolog fr Liens de cette entrée
list all categories infolog fr Liste toutes les catégories
list no subs/childs infolog fr Ne pas lister les Sous/Enfants
location infolog fr Emplacement
longer textual description infolog fr Description textuelle plus longue
low infolog fr Bas
max length of the input [, length of the inputfield (optional)] infolog fr taille max de l´entrée [, taille du champ d´entrée (optionnel)]
name must not be empty !!! infolog fr Le nom ne peut pas être vide !!!
name of new type to create infolog fr nom du nouveau type à créer
never hide search and filters infolog fr Ne jamais cacher la recherche et les filtres
new name infolog fr nouveau nom
new search infolog fr Nouvelle recherche
no - cancel infolog fr Non - Abandonner
no describtion, links or attachments infolog fr Pas de description, de liens ou d'attachements
no details infolog fr Pas de détails
no entries found, try again ... infolog fr Aucune occurrence trouvée, essayez à nouveau ...
no filter infolog fr Aucun filtre
no links or attachments infolog fr Aucun lien ou attachement
none infolog fr Aucun
normal infolog fr Normal
not infolog fr pas
not assigned infolog fr Pas attribué
not-started infolog fr Non démarré
note infolog fr Note
number of records to read (%1) infolog fr Nombre d'enregistrements à lire (%1)
number of row for a multiline inputfield or line of a multi-select-box infolog fr nombre de lignes pour un champ d´entrées multilignes ou pour une boîte de sélection multiple
offer infolog fr Offre
ongoing infolog fr Entrant
only for details infolog fr Seulement pour les détails
only the attachments infolog fr Seulement les attachements
only the links infolog fr Seulement les liens
open infolog fr Ouvrir
optional note to the link infolog fr note optionnelle vers le lien
order infolog fr Tri
overdue infolog fr Tardif
own infolog fr Propre
own open infolog fr Propre ouvert
own overdue infolog fr Propre tardif
own upcoming infolog fr Propre arrivant
path on (web-)serverside<br>eg. /var/samba/share infolog fr Chemin côté (web)server<br>p.ex. /var/samba/Share
path to user and group files has to be outside of the webservers document-root!!! infolog fr Le chemin vers les fichiers utilisateurs et groupes DOIT FIGURER EN DEHORS du répertoire racine des serveurs Web!!!
pattern for search in addressbook infolog fr Chaîne de caractères à rechercher dans le carnet d'adresses
pattern for search in projects infolog fr Chaîne de caractères à rechercher dans les projets
percent completed infolog fr Pourcentage complété
permission denied infolog fr Permission refusée
phone infolog fr Appel téléphonique
phone/email infolog fr Téléphone/EMail
phonecall infolog fr Appel téléphonique
planned infolog fr planifié
planned time infolog fr Temps prévu
price infolog fr Prix
priority infolog fr Priorité
private infolog fr Privé
project infolog fr Projet
project settings: price, times infolog fr Réglages projet: prix, heures
re: infolog fr Re:
read one record by passing its id. infolog fr Lire un enregistrement en passant son id.
read rights (default) infolog fr droits de lecture (défaut)
reg. expr. for local ip's<br>eg. ^192.168.1. infolog fr Expr. reg. pour les IPs locales<br>p.e. ^192\.168\.1\.
reg. expr. for local ip's<br>eg. ^192\.168\.1\. infolog fr Expr. reg. pour les IPs locales<br>p.e. ^192\.168\.1\.
remark infolog fr Remarque
remove this link (not the entry itself) infolog fr Enlever ce lien (pas l'entrée elle-même)
responsible infolog fr Responsable
responsible open infolog fr Responsable ouverture
responsible overdue infolog fr Responsable tardif
responsible upcoming infolog fr Responsable à venir
responsible user, priority infolog fr Responsable utilisateur, priorité
returns a list / search for records. infolog fr Retourne une liste / Recherche d´enregistrements.
rights for the responsible infolog fr Droits pour le responsable
save infolog fr Enregistrer
saves the changes made and leaves infolog fr enregistre les modifications et quitte
saves this entry infolog fr Enregistre cette entrée
search infolog fr Rechercher
search for: infolog fr Rechercher ceci:
select infolog fr Choisir
select a category for this entry infolog fr Choisissez une catégorie pour cette entrée
select a price infolog fr Sélectionnez un prix
select a priority for this task infolog fr Choisissez une priorité pour cette tâche
select a project infolog fr Sélectionnez un projet
select a responsible user: a person you want to delegate this task infolog fr Choisissez un utilisateur responsable: une personne à qui déléguer cette tâche
select a typ to edit it's status-values or delete it infolog fr sélectionnez un type dont la valeur est à éditer ou effacez-le
select an app to search in infolog fr Choisissez une App dans laquelle rechercher
select an entry to link with infolog fr Choisissez une entrée à lier
select to filter by owner infolog fr Filtrer par propriétaire
select to filter by responsible infolog fr Filtrer par responsable
sets the status of this entry and its subs to done infolog fr Fixer le statut de cette entrée et ses sous-entrées à Fait
should infolog show subtasks, -calls or -notes in the normal view or not. you can always view the subs via there parent. infolog fr InfoLog doit-il afficher les sous-tâches, -appels ou -notes dans la vue normale ou pas. Vous pouvez toujours voir les sous- via leurs parents.
should infolog show the links to other applications and/or the file-attachments in the infolog list (normal view when you enter infolog). infolog fr InfoLog doit-il afficher les liens vers d'autres applications et/ou les attachements de fichiers dans la liste InfoLog (vue normale quand vous entrez dans InfoLog)
should infolog show up on the main screen and with which filter. works only if you dont selected an application for the main screen (in your preferences). infolog fr InfoLog doit-il s'afficher sur l'écran principal et avec quel filtre. Ne fonctionne que si vous n'avez pas sélectionné une application pour l'écran principal (dans vos préférences)
should infolog use full names (surname and familyname) or just the loginnames. infolog fr InfoLog doit-il utiliser les noms complets (surnom et nom de famille) ou juste les noms de connexion.
should the calendar show custom types too infolog fr Le calendrier doit-il afficher aussi les types personnalisés
should the infolog list show a unique numerical id, which can be used eg. as ticket id. infolog fr InfoLog doit-il afficher un Id numérique unique qui peut être utilisé p.ex. comme Id de ticket.
should the infolog list show the column "last modified". infolog fr La liste InfoLog doit-elle afficher la colonne "dernière modification".
should the infolog list show the percent done only for status ongoing or two separate icons. infolog fr La liste InfoLog doit-elle afficher le pourcentage de réalisation seulement pour le statut en cours ou deux icônes séparées.
should this entry only be visible to you and people you grant privat access via the acl infolog fr Cette entrée ne doit-elle être visible que par vous et les personnes à qui vous avez donné l'accès privé via ACL
show a column for used and planned times in the list. infolog fr Afficher une colonne dans la liste pour les temps utilisés et planifiés.
show full usernames infolog fr Montrer les noms complets d'utilisateurs
show in the infolog list infolog fr Montrer dans la liste InfoLog
show last modified infolog fr Montrer les derniers modifiés
show status and percent done separate infolog fr Montrer les statuts et pourcentage réalisés séparément
show ticket id infolog fr Montrer l'Id de ticket
show times infolog fr Montrer les heures
small view infolog fr vue réduite
start a new search, cancel this link infolog fr Démarre une nouvelle recherche, annule ce lien
startdate infolog fr Date de départ
startdate enddate infolog fr Date de départ date de fin
startdate for new entries infolog fr Date de début pour les nouvelles entrées
startrecord infolog fr Enregistrement de départ
status infolog fr Statut
status ... infolog fr Statut ...
sub infolog fr Enfant
sub-entries become subs of the parent or main entries, if there's no parent infolog fr Les sous-entrées deviennent les enfants des parents des entrées principales s'il n'y a pas de parent
subject infolog fr Sujet
task infolog fr Tâche
test import (show importable records <u>only</u> in browser) infolog fr Tester l'importation (montrer les enregistrements importables <u>seulement</u> dans le navigateur)
the name used internaly (<= 10 chars), changeing it makes existing data unavailible infolog fr le nom utilisé en interne (<= 10 caractères), le modifier rend les données indisponible
the name used internaly (<= 20 chars), changeing it makes existing data unavailible infolog fr le nom est utilisé en interne (<= 20 caractères), si vous le modifiez vous rendez les données indisponibles
the text displayed to the user infolog fr le texte affiché pour l´utilisateur
this is the filter infolog uses when you enter the application. filters limit the entries to show in the actual view. there are filters to show only finished, still open or futures entries of yourself or all users. infolog fr Ceci est le filtre qu'InfoLog utilise quand vous entrez dans l'application. Les filtres limitent les entrées à montrer dans la vue actuelle. Il y a des filtres pour montrer seulement ceux temrminés, encore ouverts ou les futures entrées de vous-même ou de tous les utilisateurs.
til when should the todo or phonecall be finished infolog fr jusqu'à quand le A-faire ou l'appel téléphonique devrait être terminé
times infolog fr Heures
to many might exceed your execution-time-limit infolog fr trop peut excéder votre temps limite d´exécution.
to what should the startdate of new entries be set. infolog fr A quoi devrait être fixée la date de départ de la nouvelle entrée.
today infolog fr Aujourd'hui
todays date infolog fr date du jour
todo infolog fr A-faire
translation infolog fr Traduction
typ infolog fr Type
typ '%1' already exists !!! infolog fr Type '%1' existe déjà !!!
type infolog fr Type
type ... infolog fr Type ...
type of customfield infolog fr Type de champ personnalisé
type of the log-entry: note, phonecall or todo infolog fr Type de l'entrée Log: Note, Appel téléphonique ou A-faire
unlink infolog fr Délier
upcoming infolog fr Arrivant
urgency infolog fr Priorité
urgent infolog fr Urgent
used time infolog fr Temps utilisé
valid path on clientside<br>eg. \\server\share or e:\ infolog fr Chemin valide côté client<br>p.e. \\Partage\Serveur ou e:\
valid path on clientside<br>eg. \servershare or e: infolog fr Chemin valide côté client<br>p.e. \\Partage\Serveur ou e:\
valid path on clientside<br>eg. servershare or e: infolog fr Chemin valide côté client<br>p.e. \\Partage\Serveur ou e:\
values for selectbox infolog fr Valeurs des boîtes de sélection
view all subs of this entry infolog fr Voir tous les Enfants de cette entrée
view other subs infolog fr Voir les autres Enfants
view parent infolog fr Voir le parent
view subs infolog fr Voir les Enfants
view the parent of this entry and all his subs infolog fr Voir le parent de cette entrée et tous ses Enfants
view this linked entry in its application infolog fr Voir cette entrée liée dans son application
when should the todo or phonecall be started, it shows up from that date in the filter open or own open (startpage) infolog fr Quand le A-faire ou l'appel téléphonique devrait être démarré, il s'affiche depuis cette date dans le filtre ouvert ou propre ouvert (page de démarrage)
which additional fields should the responsible be allowed to edit without having edit rights?<br />status, percent and date completed are always allowed. infolog fr Quels champs supplémentaires le responsable devrait être autorisé à modifier sans avoir à modifier les droits d'édition??<br />Statuts, pourcentage et date de fin sont toujours permis.
which implicit acl rights should the responsible get? infolog fr Quels droits ACL implicites le responsable devrait obtenir?
whole query infolog fr requête entière
will-call infolog fr Va appeler
write (add or update) a record by passing its fields. infolog fr Ecrit (ajoute ou met à jour) un enregistrement en passant ses champs.
yes - delete infolog fr Oui - Supprimer
yes - delete including sub-entries infolog fr Oui - Supprimer y-compris les sous-entrées
you can't delete one of the stock types !!! infolog fr Vous ne pouvez pas effacer un des types de stock !!!
you have entered an invalid ending date infolog fr Vous avez entré une date de fin invalide
you have entered an invalid starting date infolog fr Vous avez entré une date de début invalide
you have to enter a name, to create a new typ!!! infolog fr Vous devez entrer un nom pour créer un nouveau type!!!
you must enter a subject or a description infolog fr Vous devez entrer un sujet ou une description
your database is not up to date (%1 vs. %2), please run %3setup%4 to update your database. infolog fr Votre base de données n'est PAS à jour (%1 au lieu de %2), veuillez exécuter %3setup%4 pour la mettre à jour.

393
infolog/lang/egw_hu.lang Normal file
View File

@ -0,0 +1,393 @@
%1 days in advance infolog hu %1 napot előre
%1 deleted infolog hu %1 törölve
%1 deleted by %2 at %3 infolog hu %1 törölve %2 által, ekkor: %3
%1 modified infolog hu %1 módosítva
%1 modified by %2 at %3 infolog hu %1 módosítva %2 által, ekkor: %3
%1 records imported infolog hu %1 bejegyzés importálva
%1 records read (not yet imported, you may go %2back%3 and uncheck test import) infolog hu %1 bejegyzés beolvasva (importálás nem történt, %2visszatérés%3 után a Test Import kapcsolót ki kell kapcsolni a tényleges beolvasáshoz)
%1 you are responsible for is due at %2 infolog hu %1 felelőseként a határidő %2
%1 you are responsible for is starting at %2 infolog hu %1 felelőseként a kezdés ideje %2
%1 you delegated is due at %2 infolog hu %1 megbízottjaként a határidő %2
%1 you delegated is starting at %2 infolog hu %1 megbízottjaként a kezdés ideje %2
- subprojects from infolog hu - Alprojektek innen
0% infolog hu 0%
10% infolog hu 10%
100% infolog hu 100%
20% infolog hu 20%
30% infolog hu 30%
40% infolog hu 40%
50% infolog hu 50%
60% infolog hu 60%
70% infolog hu 70%
80% infolog hu 80%
90% infolog hu 90%
<b>file-attachments via symlinks</b> instead of uploads and retrieval via file:/path for direct lan-clients infolog hu <b>állomány csatolás szimbolikus link használatával</b> állományok feltöltése és letöltése helyett file:/útvonal megadásával közvetlen helyi hálózati kliensek esetében
a short subject for the entry infolog hu egy rövid tárgy a bejegyzésnek
abort without deleting infolog hu Megszakítás törlés nélkül
accept infolog hu elfogad
action infolog hu Művelet
actual date and time infolog hu aktuális dátum és idő
add infolog hu Hozzáad
add a file infolog hu Állomány hozzáadása
add a new entry infolog hu Új bejegyzés hozzáadása
add a new note infolog hu Új megjegyzés hozzáadása
add a new phonecall infolog hu Új telefonhívás hozzáadása
add a new sub-task, -note, -call to this entry infolog hu Új alfeladat, -jegyzet, -hívás hozzáadása a bejegyzéshez
add a new todo infolog hu Új tennivaló hozzáadása
add file infolog hu Állomány hozzáadása
add sub infolog hu Albejegyzés hozzáadása
add timesheet entry infolog hu Regiszter bejegyzés hozzáadása
add: infolog hu Hozzáadás:
all infolog hu Összes
all links and attachments infolog hu Minden hivatkozás és melléklet
all projects infolog hu Minden projekt
allows to set the status of an entry, eg. set a todo to done if it's finished (values depend on entry-typ) infolog hu engedélyezi a bejegyzés állapotának állíthatóságát; pl. egy ToDo-nál a Kész állapotot kell beállítani, ha megvalósult a feladat (az értékek bejegyzéstípusonként eltérhetnek)
alternatives infolog hu Alternatívák
apply the changes infolog hu Változások végrehajtása
archive infolog hu archív
are you shure you want to close this entry ? infolog hu Valóban le akarod zárni ezt a bejegyzést?
are you shure you want to delete this entry ? infolog hu Valóban törölni kívánod ezt a bejegyzést?
attach a file infolog hu Állomány csatolása
attach file infolog hu Állomány csatolása
attention: no contact with address %1 found. infolog hu Figyelem: %1 címmel Kapcsolat nem található.
back to main list infolog hu Vissza a fő listára
billed infolog hu számlázva
both infolog hu mindkettő
call infolog hu hívás
can be used to show further infolog types in the calendar or limit it to show eg. only tasks. infolog hu Alkalmazható további InfoLog típusok megjelenítésére a naptárban vagy korlátozható pl. csak a teendők megjelenítésére.
cancel infolog hu Mégsem
cancelled infolog hu megszakítva
categories infolog hu Kategóriák
category infolog hu Kategória
change history infolog hu Történet módosítása
change the status of an entry, eg. close it infolog hu Bejegyzés státusza megváltozott, pl. lezárták
charset of file infolog hu Állomány karakterkészlete
check to set startday infolog hu Kezdőnap beállításhoz ikszelje be
check to specify custom contact infolog hu Kapcsolja be az egyéni kapcsolat megadásához
click here to create the link infolog hu hivatkozás létrehozásához kattintson ide
click here to start the search infolog hu keresés elindításához kattintson ide
close infolog hu Lezár
close all infolog hu Mindet lezár
close this entry and all listed sub-entries infolog hu Bejegyzés lezárása minden el bejegyzéssel együtt
comment infolog hu Megjegyzés
completed infolog hu Teljesítve
configuration infolog hu Konfiguráció
confirm infolog hu Megerősít
contact infolog hu Kapcsolat
copy your changes to the clipboard, %1reload the entry%2 and merge them. infolog hu A változásokat másolja a Vágóasztalra, %1újra olvassa be a bejegyzést%2 és fűzze össze őket
create new links infolog hu Új hivatkozás létrehozása
creates a new field infolog hu Új mező létrehozása
creates a new status with the given values infolog hu Új állapot létrehozása az adott értékkel
creates a new typ with the given name infolog hu Új típus létrehozása az adott néven
creation infolog hu Létrehozás
csv-fieldname infolog hu CSV-Mezőnév
csv-filename infolog hu CSV-Fájlnév
csv-import common hu CSV-Import
custom infolog hu Egyedi
custom contact-address, leave empty to use information from most recent link infolog hu Egyedi kapcsolat cím, hagyja üresen annak az információnak a használatához ami a legújabb hivatkozásban található
custom contact-information, leave emtpy to use information from most recent link infolog hu Egyedi kapcsolat információ, hagyja üresen annak az információnak a használatához ami a legújabb hivatkozásban található
custom fields infolog hu Egyedi mezők
custom fields, typ and status common hu Egyedi mezők, típus és státusz
custom from infolog hu Egyedi innen
custom regarding infolog hu Egyedi vonatkozás
custom status for typ infolog hu Típus egyedi státusza
customfields infolog hu Egyedi mezők
date completed infolog hu Teljesítés dátuma
date completed (leave it empty to have it automatic set if status is done or billed) infolog hu Teljesítés dátuma (ha üresen hagyja, akkor készrejelentés vagy számlázás esetén az a dátum kerül beállításra)
datecreated infolog hu létrehozás dátuma
dates, status, access infolog hu Dátumok, Státusz, Elérés
days infolog hu napok
default category for new infolog entries infolog hu Alapértelmezett kategória új Infolog bejegyzéshez
default filter for infolog infolog hu Infolog alapértelmezett szűrője
default status for a new log entry infolog hu Alapértelmezett státusz egy új napló bejegyzésnek
delegated infolog hu megbízva
delegated open infolog hu nyitott megbízatás
delegated overdue infolog hu lejárt megbízatás
delegated upcomming infolog hu közelgő megbízatás
delegation infolog hu Feladat delegálás
delete infolog hu Töröl
delete one record by passing its id. infolog hu Egy rekord törlése az azonosítója alapján
delete the entry infolog hu Bejegyzés törlése
delete this entry infolog hu Aktuális bejegyzés törlése
delete this entry and all listed sub-entries infolog hu Aktuális és az összes listázott albejegyzés törlése
deleted infolog hu törölve
deletes the selected typ infolog hu kiválasztott típus törlése
deletes this field infolog hu Aktuális mező törlése
deletes this status infolog hu Aktuális státusz törlése
description infolog hu Leírás
determines the order the fields are displayed infolog hu megjelenített mezők rendezésének meghatározása
disables a status without deleting it infolog hu státusz tiltása annak törlése nélkül
do you want a confirmation of the responsible on: accepting, finishing the task or both infolog hu A felelős jóváhagyása szükséges a feladat elfogadásához / befejezéséhez vagy mindkettőhöz.
do you want a notification, if items get assigned to you or assigned items get updated? infolog hu Kér értesítést, ha egy bejegyzést önhöz rendeltek, vagy egy megbízatást frissítettek?
do you want a notification, if items you are responsible for are about to start? infolog hu Kér értesítést, ha egy saját feladatát rövidesen el kell kezdenie?
do you want a notification, if items you are responsible for are due? infolog hu Kér értesítést, ha egy saját feladatának a határideje rövidesen lejár?
do you want a notification, if items you created get updated? infolog hu Kér értesítést, ha egy ön által létrehozott bejegyzést frissítettek?
do you want a notification, if items you delegated are about to start? infolog hu Kér értesítést, ha egy megbízásos feladatot rövidesen el kell kezdenie?
do you want a notification, if items you delegated are due? infolog hu Kér értesítést, ha egy megbízásos feladatának határideje rövidesen elérkezik?
do you want to receive notifications as html-mails or plain text? infolog hu Az értesítést html vagy normál szöveges formában kéri?
don't show infolog infolog hu Infolog bejegyzés elrejtése
done infolog hu kész
download infolog hu Letölt
duration infolog hu Időtartam
e-mail: infolog hu E-mail:
each value is a line like <id>[=<label>] infolog hu minden érték egy sorban, formája: <id>[=<label>]
edit infolog hu Szerkeszt
edit or create categories for ingolog infolog hu Szerkesztés vagy kategória létrehozása az Infolognak
edit rights (full edit rights incl. making someone else responsible!) infolog hu jogok szerkesztése (teljes mértékű szerkesztés, beleértve másvalakit felelőssé megtenni!)
edit status infolog hu Állapot szerkesztése
edit the entry infolog hu Bejegyzés szerkesztése
edit this entry infolog hu Aktuális bejegyzés szerkesztése
empty for all infolog hu összes törlése
enddate infolog hu Lejárat napja
enddate can not be before startdate infolog hu A lejárat dátuma nem lehet korábbi mint a kezdés dátuma
enter a custom contact, leave empty if linked entry should be used infolog hu egyedi kapcsolat megadása, üresen hagyva a hivatkozott bejegyzést használja
enter a custom phone/email, leave empty if linked entry should be used infolog hu egyedi telefon/email, üresen hagyva a hivatkozott bejegyzést használja
enter a textual description of the log-entry infolog hu bejegyzés szöveges leírásának megadása
enter the query pattern infolog hu Lekérdező minta megadása
entry and all files infolog hu Bejegyzés és minden állomány
error: saving the entry infolog hu Hiba a bejegyzés mentésekor
error: the entry has been updated since you opened it for editing! infolog hu Hiba: a bejegyzés megváltozott azóta, hogy megnyitotta szerkesztésre! Másvalaki frissítette.
existing links infolog hu Létező hivatkozások
fax infolog hu Fax
field must not be empty !!! infolog hu A mezőt ki kell tölteni!!!
fieldseparator infolog hu Mező elválasztó
finish infolog hu befejez
for which types should this field be used infolog hu használható típusok a mezőben
from infolog hu Feladó
general infolog hu Általános
global categories infolog hu Globális kategóriák
group owner for infolog hu Csoport tulajdonos
high infolog hu magas
history infolog hu Történet
history logging infolog hu Történet követése (log)
history logging and deleting of items infolog hu Történet követése (log) és bejegyzések törlése
how many describtion lines should be directly visible. further lines are available via a scrollbar. infolog hu Mennyi leírás sor legyen látható? További sorokat a csúszka használatával lehet megtekinteni.
id infolog hu Azonosító
id# infolog hu Azonosító#
if a type has a group owner, all entries of that type will be owned by the given group and not the user who created it! infolog hu Ha egy típusnak van csoport tulajdonosa, akkor az ezzel a típussal létrehozott bejegyzések tulajdonosa a megadott CSOPORT és nem az a felhasználó, aki létrehozta!
if not set, the line with search and filters is hidden for less entries then "max matches per page" (as defined in your common preferences). infolog hu Ha nincs beállítva, a kereső és szűrő sor rejtve marad abban az esetben, ha a "maximális egyezés egy lapon" limittől kevesebb találat volt (ahogy az Általános beállítások alatt be van állítva)
import infolog hu Importál
import next set infolog hu Következő széria importálása
importance infolog hu Fontosság
info log common hu InfoLog
infolog common hu InfoLog
infolog - delete infolog hu InfoLog - Törlés
infolog - edit infolog hu InfoLog - Szerkesztés
infolog - import csv-file infolog hu InfoLog - CSV-állomány import
infolog - new infolog hu InfoLog - Új
infolog - new subproject infolog hu InfoLog - Új alprojekt
infolog - subprojects from infolog hu InfoLog - Alprojekt innen
infolog entry deleted infolog hu InfoLog bejegyzés törölve
infolog entry saved infolog hu InfoLog bejegyzés elmentve
infolog filter for the main screen infolog hu InfoLog szűrő a fő képernyőn
infolog list infolog hu InfoLog lista
infolog preferences common hu InfoLog tulajdonságok
infolog-fieldname infolog hu InfoLog - Mezőnév
invalid filename infolog hu Érvénytelen állománynév
label<br>helptext infolog hu Címke<br>Segítség szöveg
last changed infolog hu Utolsó változás
last modified infolog hu Utolsó módosítás
leave blank to get the used time calculated by timesheet entries infolog hu Hagyd üresen, hogy a kalkulált időt az időtábla bejegyzéseiből lehessen kalkulálni.
leave it empty infolog hu hagyja üresen
leave without saveing the entry infolog hu kilépés mentés nélkül
leaves without saveing infolog hu kilépés mentés nélkül
length<br>rows infolog hu Sorok<br>Hossza
limit number of description lines (default 5, 0 for no limit) infolog hu A leírás sorainak felső határa (5 alapértelmezésben, 0 korlátlan számú sorhoz)
link infolog hu Hivatkozás
links infolog hu Hivatkozások
links of this entry infolog hu Aktuális bejegyzés hivatkozásai
list all categories infolog hu Összes kategória listázása
list no subs/childs infolog hu Albejegyzések elrejtése
location infolog hu Hely
longer textual description infolog hu hosszabb szöveges leírás
low infolog hu alacsony
max length of the input [, length of the inputfield (optional)] infolog hu beviteli mező maximális mérete (opcionális)
modifierer infolog hu Módosította
name must not be empty !!! infolog hu Név nem lehet üres!!!
name of new type to create infolog hu új típusnév létrehozása
never hide search and filters infolog hu Soha ne rejtse el a keresést és szűrést
new %1 infolog hu Új %1
new %1 created by %2 at %3 infolog hu %1 új létrehozva %2 által, ekkor: %3
new name infolog hu új név
new search infolog hu Új keresés
no - cancel infolog hu Nem - Mégsem
no describtion, links or attachments infolog hu nincsenek leírások, hivatkozások, mellékletek
no details infolog hu Részletek nélkül
no entries found, try again ... infolog hu nem találtam bejegyzést, próbálja újra ...
no filter infolog hu Szűrő nélkül
no links or attachments infolog hu nincs hivatkozás vagy melléklet
no project infolog hu Nincs projekt
nonactive infolog hu nem aktív
none infolog hu Semmi
normal infolog hu Normál
not infolog hu nem
not assigned infolog hu Nincs hozzárendelve senkihez
not-started infolog hu Még nem indult el
note infolog hu Feljegyzés
number of records to read (%1) infolog hu Bejegyzések száma olvasáshoz (%1)
number of row for a multiline inputfield or line of a multi-select-box infolog hu többsoros beviteli mező vagy többszörös kiválasztó doboz sorainak száma
offer infolog hu ajánlat
one day after infolog hu egy nappal ezután
one day in advance infolog hu egy nappal előre
ongoing infolog hu folyamatban levő
only for details infolog hu Csak a részletekhez
only if i get assigned or removed infolog hu Csak ha hozzám rendelték vagy törölték
only the attachments infolog hu csupán a mellékletek
only the links infolog hu csupán a hivatkozások
open infolog hu nyitott
optional note to the link infolog hu tetszőleges megjegyzés a hivatkozáshoz
order infolog hu Rendezés
organization infolog hu Szervezet
overdue infolog hu lejárt
own infolog hu saját
own open infolog hu saját nyitott
own overdue infolog hu saját lejárt
own upcoming infolog hu saját közelgő
parent infolog hu Szülő
parent infolog infolog hu Szülő Infolog bejegyzés
path on (web-)serverside<br>eg. /var/samba/share infolog hu út a (web-)szerveroldalon<br>pl. /var/samba/Share
path to user and group files has to be outside of the webservers document-root!!! infolog hu A felhasználók/csoportok állományainak útvonala KÍVÜL KELL ESSEN a webszerver dokumentum könyvtárán!!!
pattern for search in addressbook infolog hu keresési minta a címjegyzékben
pattern for search in projects infolog hu keresési minta a projektekben
percent completed infolog hu Teljesítés százalékban
permission denied infolog hu Hozzáférés megtagadva
phone infolog hu Telefonhívás
phone/email infolog hu Telefon/Email
phonecall infolog hu Telefonhívás
planned infolog hu tervezett
planned time infolog hu tervezett idő
price infolog hu Ár
pricelist infolog hu Árlista
primary link infolog hu elsődleges kapcsolat
priority infolog hu Prioritás
private infolog hu Magán
project infolog hu Projekt
project settings: price, times infolog hu Projekt beállítások: ár, idő
projectmanager infolog hu Projektmanager
re-planned infolog hu újratervezett
re-planned time infolog hu újratervezés ideje
re: infolog hu Válasz:
read one record by passing its id. infolog hu Egy bejegyzés olvasása az azonosítója alapján.
read rights (default) infolog hu olvasási jogok (alapbeállítás)
receive notifications about due entries you are responsible for infolog hu Értesítés a felelősségembe tartozó bejegyzés határidejének lejártáról
receive notifications about due entries you delegated infolog hu Értesítés a hozzám rendelt bejegyzés határidejének lejártáról
receive notifications about items assigned to you infolog hu Értesítés bejegyzés hozzám rendeléséről
receive notifications about own items infolog hu Értesítés saját bejegyzésekről
receive notifications about starting entries you are responsible for infolog hu Értesítés a felelősségembe tartozó bejegyzés kezdéséről
receive notifications about starting entries you delegated infolog hu Értesítés a hozzám rendelt bejegyzés kezdéséről
receive notifications as html-mails infolog hu Értesítés mint html email
reg. expr. for local ip's<br>eg. ^192.168.1. infolog hu reguláris kifejezés a helyi IP címhez <b>pl. ^192\.168\.1\.
reg. expr. for local ip's<br>eg. ^192\.168\.1\. infolog hu reguláris kifejezés a helyi IP címhez <b>pl. ^192\.168\.1\.
remark infolog hu Megjegyzés
remove this link (not the entry itself) infolog hu Hivatkozás eltávolítása (nem a bejegyzést magát)
responsible infolog hu Felelős
responsible open infolog hu Felelős nyitott
responsible overdue infolog hu Felelős lejárt
responsible upcoming infolog hu Felelős közelgő
responsible user, priority infolog hu Felelős felhasználó, prioritás
returns a list / search for records. infolog hu Lista visszaadása / bejegyzés keresése
rights for the responsible infolog hu Felelős jogai
same day infolog hu azonos napon
save infolog hu Mentés
saves the changes made and leaves infolog hu Változás mentése és kilépés
saves this entry infolog hu Bejegyzés mentése
search infolog hu Keresés
search for: infolog hu Keresés:
select infolog hu Kiválaszt
select a category for this entry infolog hu Kategória választása a bejegyzés számára
select a price infolog hu Ár kiválasztása
select a priority for this task infolog hu Feladat prioritásának kiválasztása
select a project infolog hu Projekt kiválasztása
select a responsible user: a person you want to delegate this task infolog hu Felelős kiválasztása: az a személy akit megbíz a feladattal
select a typ to edit it's status-values or delete it infolog hu válassz egy tipust szerkeszteni annak a státusz értékét vagy törölni azt
select an app to search in infolog hu Alkalmazásban keresés
select an entry to link with infolog hu Bejegyzés választása hivatkozás készítéshez
select to filter by owner infolog hu szűrés tulajdonosra
select to filter by responsible infolog hu szűrés felelősre
sender infolog hu Feladó
sets the status of this entry and its subs to done infolog hu Bejegyzés és hozzátartozó albejegyzések állapotának megváltoztatása készre
should infolog show subtasks, -calls or -notes in the normal view or not. you can always view the subs via there parent. infolog hu InfoLog megjelenítse-e az alfeladatokat, -hívásokat vagy -feljegyzéseket a normál nézetben. Az albejegyzéseket mindig el lehet érni a szülőn keresztül.
should infolog show the links to other applications and/or the file-attachments in the infolog list (normal view when you enter infolog). infolog hu Infolog megjelenítse-e a hivatkozásokat más alkalmazáskhoz és/vagy állomány mellékleteket a normál nézetben.
should infolog show up on the main screen and with which filter. works only if you dont selected an application for the main screen (in your preferences). infolog hu Az InfoLog megjelenik a Kezdőlapon egy szűrő alkalmazásával, listázva a feladatait stb. Akkor működik, ha nem választott ki egy alkalmazást Főképernyőnek (a beállításai között). Ha nem tudja, ez miről szól, akkor valószínűleg nem állította át és működni fog.
should infolog use full names (surname and familyname) or just the loginnames. infolog hu Az InfoLog használja-e a teljes nevet (családi és keresztnév), vagy csak a bejelentkező nevet.
should the infolog list show a unique numerical id, which can be used eg. as ticket id. infolog hu Az InfoLog lista megjelenítse-e az egyedi azonosítót, ami egy szám. Ez a szám alkalmas például jegy azonosító használatára (egyértelműbb, mint a megnevezés, tehát van értelme).
should the infolog list show the column "last modified". infolog hu Az InfoLog lista megjelenítse-e az "utolsó módosítás" oszlopot is.
should the infolog list show the percent done only for status ongoing or two separate icons. infolog hu Az InfoLog lista megjelenítse-e a %-os készültséget a folyamatban lévő feladatokra, vagy használjon két külön ikont? Javaslat: két ikon.
should this entry only be visible to you and people you grant privat access via the acl infolog hu A bejegyzés csak saját magának látható és azoknak a felhasználóknak, akiknek hozzáférést biztosít az ACL-en keresztül.
show a column for used and planned times in the list. infolog hu Tervezett és felhasznált idők megjelenítése a listában. Erősen javasolt!!! E nélkül ugyanis nem lehet Regiszter bejegyzést hozzáadni az InfoLog bejegyzéshez.
show full usernames infolog hu Teljes felhasználónév mutatása
show in the infolog list infolog hu InfoLog listában mutassa a következőket
show last modified infolog hu Mutassa az utolsónak módosítottat
show status and percent done separate infolog hu Mutassa a státuszt és százalékos készültséged külön-külön
show ticket id infolog hu Mutassa a jegy azonosítóját
show times infolog hu Időtartamok megjelenítése
small view infolog hu kis nézet
start a new search, cancel this link infolog hu új keresés indítása, hivatkozás elhagyása
startdate infolog hu Kezdés dátuma
startdate enddate infolog hu Kezdés dátuma Befejezés dátuma
startdate for new entries infolog hu Új bejegyzés kezdési időpontja
startrecord infolog hu Kezdő bejegyzés
status infolog hu Állapot
status ... infolog hu Állapot ...
sub infolog hu Albejegyzés
sub-entries become subs of the parent or main entries, if there's no parent infolog hu Az albejegyzések a struktúrában felettük álló kategóriának lesznek gyermekei, ha nincs szülő.
subject infolog hu Tárgy
sum infolog hu Összesen
task infolog hu Tennivaló
template infolog hu Sablon
test import (show importable records <u>only</u> in browser) infolog hu Test Import (<u>csupán</u> az importálható bejegyzések mutatása a böngészöben)
the name used internaly (<= 10 chars), changeing it makes existing data unavailible infolog hu Belső használatú név (<= 10 karakter), megváltoztatása esetén a meglévő adatok elérhetetlenek lesznek!!
the name used internaly (<= 20 chars), changeing it makes existing data unavailible infolog hu Belső használatú név (<= 20 karakter), megváltoztatása esetén a meglévő adatok elérhetetlenek lesznek!!
the text displayed to the user infolog hu a szöveg megjelenítve a felhasználónak
this is the filter infolog uses when you enter the application. filters limit the entries to show in the actual view. there are filters to show only finished, still open or futures entries of yourself or all users. infolog hu Az InfoLog alkalmazásba belépéskor ezt a szűrőt használja az alkalmazás. A szűrők korlátozzák a megjelenített bejegyzéseket az aktuális nézetben. Léteznek szűrők a csak befejezett, nyitott, vagy jövőbeli bejegyzésekhez saját magunk, vagy az összes felhasználó számára is.
til when should the todo or phonecall be finished infolog hu amíg a Tennivalók vagy Telefonhívások befejeződnek
times infolog hu Időigény
to many might exceed your execution-time-limit infolog hu Futtatási időkorlát túllépés
to what should the startdate of new entries be set. infolog hu Új bejegyzés esetén az alapértelmezett kezdési időpont.
today infolog hu Ma
todays date infolog hu Mai dátum
todo infolog hu Tennivalók
translation infolog hu Fordítás
typ infolog hu Típus
typ '%1' already exists !!! infolog hu '%1' típus már létezik!!!
type infolog hu Típus
type ... infolog hu Típus ...
type of customfield infolog hu Egyedi mező típusa
type of the log-entry: note, phonecall or todo infolog hu Napló bejegyzés típusa: Jegyzet, Telefon hívás vagy Tennivaló
unlink infolog hu Hivatkozás törlése
upcoming infolog hu közelgő
urgency infolog hu Sürgősség
urgent infolog hu sürgős
used time infolog hu felhasznált idő
valid path on clientside<br>eg. \\server\share or e:\ infolog hu Kliensoldali érvényes útvonal<br>pl. \\Server\Share vagy e:\
valid path on clientside<br>eg. \servershare or e: infolog hu Kliensoldali érvényes útvonal<br>pl. \\Server\Share vagy e:\
valid path on clientside<br>eg. servershare or e: infolog hu Kliensoldali érvényes útvonal<br>pl. \\Server\Share vagy e:\
values for selectbox infolog hu Éktékek a kiválasztódoboznak
view all subs of this entry infolog hu Bejegyzés összes albejegyzésének a megtekintése
view other subs infolog hu egyéb albejegyzések megtekintése
view parent infolog hu Szülő megtekintése
view subs infolog hu Albejegyzések megtekintése
view the parent of this entry and all his subs infolog hu Szülő bejegyzés és összes albejegyzésének megtekintése
view this linked entry in its application infolog hu hivatkozott bejegyzés megtekintése a hozzá tartozó alkalmazással
when should the todo or phonecall be started, it shows up from that date in the filter open or own open (startpage) infolog hu Ha egy tennivaló vagy telefonhívás elindult, a nyitott (vagy saját nyitott) szűrőbe bekerül a dátuma (ezt is ki kell találni, hogy hol szerepel)
which additional fields should the responsible be allowed to edit without having edit rights?<br />status, percent and date completed are always allowed. infolog hu A bejegyzés felelőse mely mezőket szerkesztheti anélkül, hogy egyébként szerkesztési jogosultságai lennének?<br/>A Státusz, százalékos készültség és dátum mindig szerkeszthető!<br/>(például ha az alapértelmezett csoportjogosultság nem tenné lehetővé, hogy a leírást módosítsa a felelős).
which implicit acl rights should the responsible get? infolog hu Mely értelemszerűen felmerülő ACL jogosultságokat kapjon a felelős?
which types should the calendar show infolog hu Mely típust mutasson a naptár
whole query infolog hu teljes lekérdezés
will-call infolog hu hívni fog
write (add or update) a record by passing its fields. infolog hu Kiír (hozzáad vagy módosít) egy bejegyzést a mezők átadásával.
yes - close infolog hu Igen - lezárni
yes - close including sub-entries infolog hu Igen - lezárni az albejegyzésekkel együtt
yes - delete infolog hu Igen - Töröl
yes - delete including sub-entries infolog hu Igen - Törlés az összes albejegyzéssel együtt
yes, noone can purge deleted items infolog hu Igen, senki nem távolíthatja el a törölt elemeket
yes, only admins can purge deleted items infolog hu Igen, csak az adminisztrátorok távolíthatják el a törölt elemeket
yes, with larger fontsize infolog hu Igen, nagyobb karaktermérettel
yes, with purging of deleted items possible infolog hu Igen, az összes törölt elem eltávolítható
you can choose a categorie to be preselected, when you create a new infolog entry infolog hu Választhatsz alapértelmezett kategóriát új InfoLog bejegyzések létrehozásához.
you can't delete one of the stock types !!! infolog hu Gyári típust nem lehet törölni !!!
you have entered an invalid ending date infolog hu Helytelen befejezési dátum
you have entered an invalid starting date infolog hu Helytelen kezdési dátum
you have to enter a name, to create a new typ!!! infolog hu Új típus létrehozásához meg kell adni egy nevet!
you must enter a subject or a description infolog hu Meg kell adni a tárgyat vagy a leírást
your database is not up to date (%1 vs. %2), please run %3setup%4 to update your database. infolog hu Az adatbázis frissítésre szorul (%1 <-> %2), kérem futtassa a %3beállításokat%4 az adatbázis frissítéséhez.

267
infolog/lang/egw_id.lang Normal file
View File

@ -0,0 +1,267 @@
%1 days in advance infolog id %1 hari dimuka
%1 deleted infolog id %1 dihapus
%1 deleted by %2 at %3 infolog id %1 dihapus oleh %2 pada %3
%1 modified infolog id %1 diubah
%1 modified by %2 at %3 infolog id %1 diubah oleh %2 pada %3
%1 records imported infolog id %1 rekaman diimpor
%1 you are responsible for is due at %2 infolog id %1 dimana anda bertanggungjawab akan berakhir pada %2
%1 you are responsible for is starting at %2 infolog id %1 dimana anda bertanggungjawab akan mulai pada %2
%1 you delegated is due at %2 infolog id %1 dimana anda didelegasikan akan berakhir pada %2
%1 you delegated is starting at %2 infolog id %1 dimana anda didelegasikan akan mulai pada %2
- subprojects from infolog id - Sub-proyek dari
0% infolog id 0%
10% infolog id 10%
100% infolog id 100%
20% infolog id 20%
30% infolog id 30%
40% infolog id 40%
50% infolog id 50%
60% infolog id 60%
70% infolog id 70%
80% infolog id 80%
90% infolog id 90%
abort without deleting infolog id Batal tanpa menghapus
accept infolog id diterima
action infolog id Tindakan
actions... infolog id Tindakan...
actual date and time infolog id Tanggal dan waktu sesungguhnya
add infolog id Tambah
add a file infolog id Tambah berkas
add a new entry infolog id Tambah Entri baru
add a new note infolog id Tambah Catatan baru
add a new phonecall infolog id Tambah Panggilan telepon baru
add a new sub-task, -note, -call to this entry infolog id Tambah sub-task, -catatan, -panggilan ke entri ini
add a new todo infolog id Tambah ToDo baru
add file infolog id Tambah berkas
add sub infolog id tambah Sub
add timesheet entry infolog id Tamba entri lembarwaktu
add: infolog id Tambah:
all infolog id Semua
all links and attachments infolog id semua tautan dan lampiran
all projects infolog id Semua Proyek
alternatives infolog id Pengganti
apply the changes infolog id Terapkan perubahan
archive infolog id arsip
are you shure you want to close this entry ? infolog id Apakah anda yakin hendak menutup entri ini?
are you shure you want to delete this entry ? infolog id Apakah anda yakin hendak menghapus entri ini?
attach a file infolog id Lampirkan berkas
attach file infolog id Lampirkan berkas
attention: no contact with address %1 found. infolog id Perhatian: Tidak ditemukan Kontak dengan alamat %1.
back to main list infolog id Kembali ke daftar utama
billed infolog id ditagihkan
both infolog id both
call infolog id panggilan
cancel infolog id Batal
cancelled infolog id dibatalkan
categories infolog id Kategori
category infolog id Kategori
change history infolog id Riwayat pengubahan
click here to create the link infolog id klik disini untuk membuat tautan
click here to start the search infolog id Klik disini untuk memulai pencarian
close infolog id Tutup
close all infolog id Tutup semua
close this entry and all listed sub-entries infolog id Tutup entri ini dan semua sub-entri yang tercantum
comment infolog id Komentar
completed infolog id Lengkap
configuration infolog id Konfigurasi
confirm infolog id konfirmasi
contact infolog id Kontak
copy of: infolog id Salinan dari:
create new links infolog id Bikin tautan baru
creates a new field infolog id bikin field baru
creation infolog id Creation
csv-fieldname infolog id CSV-Fieldname
csv-filename infolog id CSV-Filename
csv-import common id CSV-Import
custom infolog id Custom
custom fields infolog id Custom Fields
custom fields, typ and status common id Custom fields, tipe dan status
custom from infolog id Custom from
custom regarding infolog id Custom regarding
customfields infolog id Customfields
date completed infolog id Tanggal selesai
datecreated infolog id tanggal dibuat
dates, status, access infolog id Tanggal, Status, Akses
days infolog id hari
default category for new infolog entries infolog id Kategori bawaan untuk entri baru InfoLog
default filter for infolog infolog id Saringan bawaan untuk InfoLog
default status for a new log entry infolog id Status bawaan untuk entri baru InfoLog
delegated infolog id didelegasikan
delegated open infolog id delegated open
delegated overdue infolog id delegated overdue
delegated upcomming infolog id delegated upcomming
delegation infolog id Delegasi
delete infolog id Hapus
delete the entry infolog id Hapus entri
delete this entry infolog id Hapus entri ini
delete this entry and all listed sub-entries infolog id Hapus entri ini dan semua sub-entri tercamtum
deleted infolog id terhapus
deletes this field infolog id hapus field ini
deletes this status infolog id hapus status ini
description infolog id Uraian
don't show infolog infolog id JANGAN tampilkan InfoLog
done infolog id selesai
download infolog id Unduh
due %1 infolog id Berakhir %1
duration infolog id Durasi
e-mail: infolog id E-mail:
edit infolog id Edit
edit or create categories for ingolog infolog id Edit atau bikin kategori untuk InfoLog
edit status infolog id Edit status
edit the entry infolog id Edit entri
edit this entry infolog id Edit entri ini
enddate infolog id Tanggal jatuh-tempo
enddate can not be before startdate infolog id Tanggal jatuh-tempo tidak dapat sebelum tanggal memulai
existing links infolog id Tautan yang ada
fax infolog id Fax
fieldseparator infolog id Pemisah field
finish infolog id rampung
from infolog id Dari
general infolog id Umum
global categories infolog id Kategori Global
high infolog id tinggi
history infolog id Riwayat
history logging infolog id Catatan riwayat
history logging and deleting of items infolog id Riwayat catatan dan penghapusan item
id infolog id ID
id# infolog id ID#
import infolog id Impor
import next set infolog id Impor kelompok berikutnya
importance infolog id Importance
info log common id InfoLog
infolog common id InfoLog
infolog - delete infolog id InfoLog - Hapus
infolog - edit infolog id InfoLog - Edit
infolog - import csv-file infolog id InfoLog - Impor berkas-CSV
infolog - new infolog id InfoLog - Baru
infolog - new subproject infolog id InfoLog - Subproyek Baru
infolog - subprojects from infolog id InfoLog - Subproyek dari
infolog list infolog id Daftar InfoLog
infolog preferences common id Kesukaan InfoLog
infolog-fieldname infolog id Nama field-InfoLog
invalid filename infolog id Nama berkas keliru
label<br>helptext infolog id Label<br>Helptext
last changed infolog id Pengubahan terakhir
last modified infolog id Pengubahan terakhir
leave it empty infolog id biarkan kosong
length<br>rows infolog id Length<br>Rows
link infolog id Tautan
links infolog id Tautan
list all categories infolog id Daftar semua kategori
location infolog id Lokasi
low infolog id rendah
new %1 infolog id Baru %1
new %1 created by %2 at %3 infolog id Baru %1 dibikin oleh %2 pada %3
new name infolog id nama baru
new search infolog id Pencarian baru
no - cancel infolog id Tidak - Batal
no describtion, links or attachments infolog id tiada uraian, tautan atau lampiran
no details infolog id tiada kejelasan
no entries found, try again ... infolog id tiada entri ditemukan, coba lagi ...
no filter infolog id tiada Saringan
no links or attachments infolog id tiada tautan dan lampiran
no project infolog id Tiada proyek
nonactive infolog id tidak aktif
none infolog id Tiada
normal infolog id normal
not infolog id not
not assigned infolog id not assigned
not-started infolog id belum dimulai
note infolog id Catatan
offer infolog id penawaran
one day after infolog id sehari setelah
one day in advance infolog id sehari dimuka
ongoing infolog id berlangsung
open infolog id buka
order infolog id Urutan
organization infolog id Organisasi
overdue infolog id lewat waktu
own infolog id own
own open infolog id own open
own overdue infolog id own overdue
own upcoming infolog id own upcoming
parent infolog id Leluhur
parent infolog infolog id InfoLog Leluhur
pattern for search in addressbook infolog id pola pencarian pada Buku Alamat
pattern for search in projects infolog id pola pencarian pada Proyek
percent completed infolog id Persentasi rampung
permission denied infolog id Ijin Ditolak
phone infolog id Panggilan Telepon
phone/email infolog id Telepon/Email
phonecall infolog id Panggilan Telepon
planned infolog id direncanakan
planned time infolog id waktu yang direncanakan
price infolog id Harga
pricelist infolog id Daftar harga
primary link infolog id tautan utama
priority infolog id Prioritas
private infolog id Privat
project infolog id Proyek
project settings: price, times infolog id Pengaturan Proyek: harga, waktu
projectmanager infolog id Manajer Proyek
re-planned infolog id Perencanaan ulang
re-planned time infolog id Waktu Perencanaan ulang
re: infolog id Re:
remark infolog id Remark
responsible infolog id penanggungjawab
responsible open infolog id responsible open
responsible overdue infolog id responsible overdue
responsible upcoming infolog id responsible upcoming
same day infolog id hari yang sama
save infolog id Simpan
saves the changes made and leaves infolog id simpan perubahan dan tinggalkan
saves this entry infolog id Simpan entri ini
search infolog id Cari
search for: infolog id Mencari:
select infolog id Pilih
select a category for this entry infolog id pilih kategori untuk entri ini
select a price infolog id Pilih harga
select a priority for this task infolog id pilih prioritas untuk tugas ini
select a project infolog id Pilih proyek
sender infolog id Pengirim
set status to done infolog id Tetapkan status menjadi selesai
set status to done for all entries infolog id Tetapkan status menjadi selesai untuk semua entri
sets the status of this entry and its subs to done infolog id Tetapkan status entri ini dan sub-entrinya menjadi selesai
show full usernames infolog id Tampilkan Nama lengkap pengguna
show in the infolog list infolog id Tampilkan pada daftar InfoLog
show last modified infolog id Tampilkan pengubahan terakhir
show status and percent done separate infolog id Tampilkan terpisah status dan persentasi selesai
show ticket id infolog id Tampilkan ID Tiket
show times infolog id Tampilkan waktu
small view infolog id tampilan kecil
start a new search, cancel this link infolog id memulai pencarian baru, batalkan tautan ini
startdate infolog id Tanggal memulai
startdate enddate infolog id Tanggal memulai Tanggal berakhir
startdate for new entries infolog id Tanggal memulai untuk entri baru
starting %1 infolog id Memulai %1
startrecord infolog id Rekaman awal
status infolog id Status
status ... infolog id Status ...
sub infolog id Sub
subject infolog id Subyek
sum infolog id Jml
task infolog id ToDo
template infolog id Templat
times infolog id Waktu
today infolog id Hari ini
todays date infolog id Tanggal sekarang
todo infolog id ToDo
translation infolog id Terjemahan
typ infolog id Tipe
typ '%1' already exists !!! infolog id Tipe '%1' telah ada !!!
type infolog id Tipe
type ... infolog id Tipe ...
type of the log-entry: note, phonecall or todo infolog id Tipe entri log: Catatan, Panggilan telepon atau ToDo
unlink infolog id Batalkan tautan
upcoming infolog id mendatang
urgency infolog id urgency
urgent infolog id mendesak
used time infolog id waktu yang dihabiskan
view parent infolog id Lihat leluhur
view subs infolog id view Subs
whole query infolog id seluruh query
will-call infolog id will call
yes - close infolog id Ya - Tutup
yes - close including sub-entries infolog id Ya - Tutup berikut sub-entrinya
yes - delete infolog id Ya - Hapus
yes - delete including sub-entries infolog id Ya - Hapus berikut sub-entrinya

377
infolog/lang/egw_nl.lang Normal file
View File

@ -0,0 +1,377 @@
%1 days in advance infolog nl %1 dagen tevoren
%1 deleted infolog nl %1 verwijderd
%1 deleted by %2 at %3 infolog nl %1 verwijderd door %2 op %3
%1 modified infolog nl %1 gewijzigd
%1 modified by %2 at %3 infolog nl %1 gewijzigd door %2 op %3
%1 records imported infolog nl %1 records geïmporteerd
%1 records read (not yet imported, you may go %2back%3 and uncheck test import) infolog nl %1 records gelezen (nog niet geïmporteerd, u kunt terug gaan naar %2back%3 en 'Test Import' uitvinken)
%1 you are responsible for is due at %2 infolog nl %1 waar jij verantwoordelijk voor bent vervalt op %2
%1 you are responsible for is starting at %2 infolog nl %1 waar jij verantwoordelijk voor bent start op %2
%1 you delegated is due at %2 infolog nl %1 die jij gedelegeerd hebt vervalt op %2
%1 you delegated is starting at %2 infolog nl %1 die jij gedelegeerd hebt start op %2
- subprojects from infolog nl - Subprojecten van
0% infolog nl 0%
10% infolog nl 10%
100% infolog nl 100%
20% infolog nl 20%
30% infolog nl 30%
40% infolog nl 40%
50% infolog nl 50%
60% infolog nl 60%
70% infolog nl 70%
80% infolog nl 80%
90% infolog nl 90%
<b>file-attachments via symlinks</b> instead of uploads and retrieval via file:/path for direct lan-clients infolog nl <b>bestandsbijlagen via symbolische links</b> in plaats van uploads en ontvangen via file:/pad voor directe LAN-clients
a short subject for the entry infolog nl een kort onderwerp voor een nieuw infolog
abort without deleting infolog nl Afbreken zonder te verwijderen
accept infolog nl Accepteren
action infolog nl Actie
actual date and time infolog nl werkelijke datum en tijd
add infolog nl Toevoegen
add a file infolog nl Een bestand toevoegen
add a new entry infolog nl Een nieuw infolog toevoegen
add a new note infolog nl Een nieuwe notitie toevoegen
add a new phonecall infolog nl Een nieuw telefoongesprek toevoegen
add a new sub-task, -note, -call to this entry infolog nl Een nieuwe subtaak, subnotitie, subtelefoongesprek aan dit infolog toevoegen
add a new todo infolog nl Een nieuwe taak toevoegen
add file infolog nl Bestand toevoegen
add sub infolog nl Sub toevoegen
add timesheet entry infolog nl Urenregistratie toevoegen
add: infolog nl Toevoegen:
all infolog nl Alles
all links and attachments infolog nl alle links en bijlagen
allows to set the status of an entry, eg. set a todo to done if it's finished (values depend on entry-typ) infolog nl Toestaan de status van een infolog te wijzigen, bijv. een taak op status klaar zetten (waarden zijn onafhankelijk van infolog-type)
applies the changes infolog nl wijzigingen doorvoeren
apply the changes infolog nl De wijzigingen doorvoeren
archive infolog nl archief
are you shure you want to delete this entry ? infolog nl Weet u zeker dat deze infolog wilt verwijderen
attach a file infolog nl Een bestand toevoegen
attach file infolog nl Een bestand toevoegen
attention: no contact with address %1 found. infolog nl Attentie: Geen contact met adres %1 gevonden.
back to main list infolog nl Terug naar hoofdlijst
billed infolog nl gefactureerd
both infolog nl beiden
call infolog nl bellen
can be used to show further infolog types in the calendar or limit it to show eg. only tasks. infolog nl Kan gebruikt worden om meer InfoLog types in de kalender te tonen of de weergave te beperken door bijvoorbeeld alleen maar taken te tonen.
cancel infolog nl Annuleren
cancelled infolog nl geannuleerd
categories infolog nl Categorieën
category infolog nl Categorie
change history infolog nl Historie van wijzigingen
change the status of an entry, eg. close it infolog nl Wijzig de status van een item, bijvoorbeeld afsluiten
charset of file infolog nl Karakterset van bestand
check to set startday infolog nl Markeer om startdag aan te geven
check to specify custom contact infolog nl Markeer om aangepaste contact aan te geven
click here to create the link infolog nl klik hier om een link aan te maken
click here to start the search infolog nl klik hier om te starten met zoeken
close infolog nl Sluiten
comment infolog nl Opmerking
completed infolog nl Voltooid
configuration infolog nl Configuratie
confirm infolog nl Bevestigen
contact infolog nl Contact
copy your changes to the clipboard, %1reload the entry%2 and merge them. infolog nl Kopieer uw wijzigingen naar het klembord, %1laad het record opnieuw%2 voeg de wijziging ermee samen.
create new links infolog nl Nieuwe links aanmaken
creates a new field infolog nl Maakt nieuw veld aan
creates a new status with the given values infolog nl Maakt nieuwe status aan met de ingevoerde waarden
creates a new typ with the given name infolog nl Maakt nieuw type aan met de ingevoerde naam
creation infolog nl Aanmaak
csv-fieldname infolog nl CSV-Veldnaam
csv-filename infolog nl CSV-Bestandsnaam
csv-import common nl CSV-Import
custom infolog nl Aangepast
custom contact-address, leave empty to use information from most recent link infolog nl Aangepast contactadres, laat dit leeg om de informatie te gebruiken van de meest recente link
custom contact-information, leave emtpy to use information from most recent link infolog nl Aangepaste contactinformatie, laat dit leeg om de informatie te gebruiken van de meest recente link
custom fields infolog nl Aangepast velden
custom fields, typ and status common nl Aangepaste velden, type en status
custom from infolog nl Aangepast van
custom regarding infolog nl Aangepast m.b.t.
custom status for typ infolog nl Aangepaste status voor type
customfields infolog nl Aanpaste velden
date completed infolog nl Datum voltooid
date completed (leave it empty to have it automatic set if status is done or billed) infolog nl Datum voltooid (leeg laten als je het automatisch wilt laten vullen indien de status op gedaan of gefactureerd wordt gezet)
datecreated infolog nl datum aangemaakt
dates, status, access infolog nl Data, Status, Toegang
days infolog nl dagen
default category for new infolog entries infolog nl Standaard categorie voor nieuwe InfoLog invoer
default filter for infolog infolog nl Standaard filter voor InfoLog
default status for a new log entry infolog nl Standaard status voor een nieuwe infolog
delegated infolog nl gedelegeerd
delegated open infolog nl gedelegeerd openstaand
delegated overdue infolog nl gedelegeerd over tijd
delegated upcomming infolog nl gedelegeerd toekomstig
delegation infolog nl Delegatie
delete infolog nl Verwijderen
delete one record by passing its id. infolog nl Verwijder één record door het id mee te geven.
delete the entry infolog nl Infolog verwijderen
delete this entry infolog nl dit infolog verwijderen
delete this entry and all listed sub-entries infolog nl Deze infolog en alle getoonde onderliggende infologs verwijderen
deleted infolog nl verwijderd
deletes the selected typ infolog nl verwijderd het geselecteerde type
deletes this field infolog nl verwijderd dit veld
deletes this status infolog nl verwijderd deze status
description infolog nl Beschrijving
determines the order the fields are displayed infolog nl bepaald hoe de volgorde van de velden wordt weergegeven
disables a status without deleting it infolog nl deactiveerd een status zonder het te verwijderen
do you want a confirmation of the responsible on: accepting, finishing the task or both infolog nl Wilt een een bevestiging van de verantwoordelijke voor accepteren, voor beëindigen, of voor beide
do you want a notification, if items get assigned to you or assigned items get updated? infolog nl Wilt u een notificatie ontvangen indien items aan u toegewezen worden of toegewezen items gewijzigd worden?
do you want a notification, if items you are responsible for are about to start? infolog nl Wilt u een notificatie ontvangen indien items waarvoor u verantwoordelijk bent op het punt staat te starten?
do you want a notification, if items you are responsible for are due? infolog nl Wilt u een notificatie ontvangen indien item, waarvoor u verantwoordelijk bent, op het punt staan te vervallen?
do you want a notification, if items you created get updated? infolog nl Wilt u een notificatie ontvangen indien items, die u heeft aangemaakt, bijgewerkt worden?
do you want a notification, if items you delegated are about to start? infolog nl Wilt u een notificatie ontvangen indien items, die u heeft gedelegeerd, op het punt staan te starten?
do you want a notification, if items you delegated are due? infolog nl Wilt u een notificatie ontvangen indien items, die u heeft gedelegeerd, op het punt staan te vervallen?
do you want to receive notifications as html-mails or plain text? infolog nl Wilt u notificaties ontvangen als html-emails of als platte tekst?
don't show infolog infolog nl InfoLog NIET weergeven
done infolog nl gereed
download infolog nl Downloaden
duration infolog nl Duur
each value is a line like <id>[=<label>] infolog nl elke waarde is een regel zoals <id>[=<label>]
edit infolog nl Wijzigen
edit or create categories for ingolog infolog nl Categorieën voor InfoLog aanmaken of wijzigen
edit rights (full edit rights incl. making someone else responsible!) infolog nl bewerk rechten (volledige rechten voor bewerking, inclusief anderen hiervoor verantwoordelijk maken!)
edit status infolog nl Wijzig status
edit the entry infolog nl Wijzig de infolog
edit this entry infolog nl Wijzig dit infolog
empty for all infolog nl leeg voor alle
enddate infolog nl Einddatum
enddate can not be before startdate infolog nl Einddatum kan vroeger zijn dan begindatum
enter a custom contact, leave empty if linked entry should be used infolog nl Voer een nieuw contact in, laat dit leeg als u de gelinkte wilt gebruiken
enter a custom phone/email, leave empty if linked entry should be used infolog nl voer een aangepaste telefoon/email in, laat dit leeg als u de gelinkte wilt gebruiken
enter a textual description of the log-entry infolog nl Voer een textuele beschrijving in van de infolog
enter the query pattern infolog nl Geef een zoek patroon
entry and all files infolog nl Infolog en alle bestanden
error: saving the entry infolog nl Fout: bij opslaan van de infolog
error: the entry has been updated since you opened it for editing! infolog nl Fout: de infolog is gewijzigd sinds dat u het opende voor bewerking!
existing links infolog nl Bestaande links
fax infolog nl Fax
field must not be empty !!! infolog nl Veld mag niet leeg zijn !!!
fieldseparator infolog nl Veldonderbreker
finish infolog nl Einde
for which types should this field be used infolog nl Voor welke type wordt dit veld gebruikt
from infolog nl Van
general infolog nl Algemeen
group owner for infolog nl Groepseigenaar
high infolog nl hoog
history infolog nl Historie
history logging infolog nl Historie vastleggen
history logging and deleting of items infolog nl Historie vastleggen en verwijdering van items
id infolog nl ID
id# infolog nl ID#
if a type has a group owner, all entries of that type will be owned by the given group and not the user who created it! infolog nl Indien een type een groepseigenaar heeft wordt alle invoer van dat type eigendom van de betreffende groep en NIET de gebruiker die ze heeft aangemaakt!
if not set, the line with search and filters is hidden for less entries then "max matches per page" (as defined in your common preferences). infolog nl Indien niet ingesteld wordt de regel met zoeken en filters verborgen in het geval van minder entries dan "maximum aantal matches per pagina" (zoals bepaald in uw algemene voorkeuren).
import infolog nl Importeren
import next set infolog nl Volgende set imporeten
info log common nl InfoLog
infolog common nl InfoLog
infolog - delete infolog nl InfoLog - Verwijderen
infolog - edit infolog nl InfoLog - Wijzigen
infolog - import csv-file infolog nl InfoLog - Importeer CSV-bestand
infolog - new infolog nl InfoLog - Nieuw
infolog - new subproject infolog nl InfoLog - Nieuw subproject
infolog - subprojects from infolog nl InfoLog - Subprojecten van
infolog entry deleted infolog nl InfoLog verwijderd
infolog entry saved infolog nl InfoLog opgeslagen
infolog filter for the main screen infolog nl InfoLog filter tbv de voorpagina
infolog list infolog nl InfoLog lijst
infolog preferences common nl InfoLog voorkeuren
infolog-fieldname infolog nl InfoLog-veldnaam
invalid filename infolog nl Ongeldige bestandsnaam
label<br>helptext infolog nl Label<br/>Helptekst
last changed infolog nl Laatst gewijzigd
last modified infolog nl Laatst gewijzigd
leave blank to get the used time calculated by timesheet entries infolog nl Laat het leeg om de gebruikte tijd uit de urenregistratie te verkrijgen
leave it empty infolog nl leeg laten
leave without saveing the entry infolog nl weggaan zonder op te slaan
leaves without saveing infolog nl weggaan zonder op te slaan
length<br>rows infolog nl Lengte<br/>Regels
link infolog nl Link
links infolog nl Links
links of this entry infolog nl Links van deze infolog
list all categories infolog nl Toon alle categorieën
list no subs/childs infolog nl Sub's niet weergeven
location infolog nl Locatie
longer textual description infolog nl langere tekstuele beschrijving
low infolog nl laag
max length of the input [, length of the inputfield (optional)] infolog nl maximale lengte van de invoer [lengte van het invoerveld (optioneel)]
modifierer infolog nl Gewijzigd door
name must not be empty !!! infolog nl Naam mag niet leeg zijn !!!
name of new type to create infolog nl Naam van het nieuw te creëren type
never hide search and filters infolog nl Zoeken en filters niet verbergen
new %1 infolog nl Nieuw %1
new %1 created by %2 at %3 infolog nl Nieuw %1 aangemaakt door %2 op %3
new name infolog nl nieuwe naam
new search infolog nl Nieuwe zoekopdracht
no - cancel infolog nl Nee - Annuleren
no describtion, links or attachments infolog nl geen beschrijving, links of bijlagen
no details infolog nl geen details
no entries found, try again ... infolog nl Geen items gevonden, probeer het nogmaals ...
no filter infolog nl Geen filter
no links or attachments infolog nl geen links of bijlagen
nonactive infolog nl inactief
none infolog nl Geen
normal infolog nl normaal
not infolog nl niet
not assigned infolog nl Niet toegewezen
not-started infolog nl niet gestart
note infolog nl Notitie
number of records to read (%1) infolog nl Aantal records om te importen (%1)
number of row for a multiline inputfield or line of a multi-select-box infolog nl Aantal regels voor een een invoertekstvak of van een menu-box
offer infolog nl offerte
one day after infolog nl één dag na
one day in advance infolog nl één dag voor
ongoing infolog nl lopende
only for details infolog nl Alleen voor details
only if i get assigned or removed infolog nl Alleen indien ik wordt toegewezen of verwijderd
only the attachments infolog nl alleen de bijlagen
only the links infolog nl alleen de links
open infolog nl open
optional note to the link infolog nl optionele notitie bij de link
order infolog nl Order
overdue infolog nl te laat
own infolog nl eigen
own open infolog nl eigen - openstaande
own overdue infolog nl eigen - te laat
own upcoming infolog nl eigen - toekomstige
parent infolog nl Ouder
path on (web-)serverside<br>eg. /var/samba/share infolog nl pad aan de serverkant <br/> bijv. /var/samba/Share
path to user and group files has to be outside of the webservers document-root!!! infolog nl Pad naar gebruiker- en groepsbestanden MOETEN BUITEN de documentendirectory zijn van de webserver's
pattern for search in addressbook infolog nl Patroon voor zoekopdracht in Adressenboek
pattern for search in projects infolog nl Patroon voor zoekopdracht in Projecten
percent completed infolog nl Percentage voltooid
permission denied infolog nl Toestemming geweigerd
phone infolog nl Telefoongesprek
phone/email infolog nl Telefoon/Email
phonecall infolog nl Telefoongesprek
planned infolog nl gepland
planned time infolog nl geplande tijd
price infolog nl Prijs
pricelist infolog nl Prijslijst
primary link infolog nl primaire link
priority infolog nl Prioriteit
private infolog nl Privé
project infolog nl Project
project settings: price, times infolog nl Project instellingen: prijs, tijden
projectmanager infolog nl Projectmanager
re-planned infolog nl Hergepland
re-planned time infolog nl Hergeplande tijd
re: infolog nl Re:
read one record by passing its id. infolog nl Lees één record door het id mee te geven.
read rights (default) infolog nl leesrechten (standaard)
receive notifications about due entries you are responsible for infolog nl Ontvang notificaties over vervallende items waarvoor u verantwoordelijk bent
receive notifications about due entries you delegated infolog nl Ontvang notificaties over vervallende items waaraan u bent toegewezen
receive notifications about items assigned to you infolog nl Ontvang notificaties over items die aan u zijn toegewezen
receive notifications about own items infolog nl Ontvang notificaties over uw eigen items
receive notifications about starting entries you are responsible for infolog nl Ontvang notificaties over startende items waarvoor u verantwoordelijk bent
receive notifications about starting entries you delegated infolog nl Ontvang notificaties over startende items die aan u zijn toegewezen
receive notifications as html-mails infolog nl Ontvang notificaties als html-emails
reg. expr. for local ip's<br>eg. ^192.168.1. infolog nl reg. expr. for local IP's<br>eg. ^192\.168\.1\.
reg. expr. for local ip's<br>eg. ^192\.168\.1\. infolog nl reg. expr. for local IP's<br>eg. ^192\.168\.1\.
remark infolog nl aandachtspunt
remove this link (not the entry itself) infolog nl Verwijder deze link (niet het record zeld)
responsible infolog nl verantwoordelijk
responsible open infolog nl verantwoordelijk open
responsible overdue infolog nl verantwoordelijk te laat
responsible upcoming infolog nl verantwoordelijk toekomstige
responsible user, priority infolog nl verantwoordelijke gebruiker, prioriteit
returns a list / search for records. infolog nl Toont een lijst / zoek records
rights for the responsible infolog nl Rechten voor de verantwoordelijke
same day infolog nl dezelfde dag
save infolog nl Opslaan
saves the changes made and leaves infolog nl Slaat wijzigingen op en verlaat het scherm
saves this entry infolog nl Slaat deze infolog op
search infolog nl Zoeken
search for: infolog nl Zoeken naar:
select infolog nl Selecteren
select a category for this entry infolog nl Selecteer een categorie voor deze infolog
select a price infolog nl Selecteer een prijs
select a priority for this task infolog nl Selecteer een prioriteit voor deze taak
select a project infolog nl Selecteer een project
select a responsible user: a person you want to delegate this task infolog nl Selecteer een verantwoordelijke gebruiker: een persoon aan wie u deze taak wilt delegeren
select a typ to edit it's status-values or delete it infolog nl Selecteer een type waarvan uw de statuswaarden wilt wijzigen of om te verwijderen
select an app to search in infolog nl Selecteer een applicatie om in te zoeken
select an entry to link with infolog nl Selecteer een infolog om mee te linken
select to filter by owner infolog nl selecteer om te filteren op eigenaar
select to filter by responsible infolog nl selecteer om te filteren bij verantwoordelijke
sets the status of this entry and its subs to done infolog nl Zet de status van dit item en de onderliggende op gedaan
should infolog show subtasks, -calls or -notes in the normal view or not. you can always view the subs via there parent. infolog nl Moet InfoLog subtaken, -telefoongesprekken of -notities standaard weergeven, ja of nee. U kunt altijd sub's weergeven via de bovenliggende infolog ?
should infolog show the links to other applications and/or the file-attachments in the infolog list (normal view when you enter infolog). infolog nl Moet InfoLog de links naar andere toepassingen en/of bestanden in de InfoLog lijst (normale weergave als u InfoLog start).
should infolog show up on the main screen and with which filter. works only if you dont selected an application for the main screen (in your preferences). infolog nl Moet InfoLog op de voorpagina getoond worden en met welk filter. Werkt alleen indien u geen toepassing voor de voorpagina heeft gekozen (in uw voorkeuren).
should infolog use full names (surname and familyname) or just the loginnames. infolog nl Moet InfoLog volledige namen (voor- en achternaam) gebruiken of alleen gebruikersnamen?
should the infolog list show a unique numerical id, which can be used eg. as ticket id. infolog nl Moet de InfoLog lijst een uniek nummer tonen dat bijvoorbeeld als ticket ID gebruik kan worden?
should the infolog list show the column "last modified". infolog nl Moet de InfoLog lijst de kolom "laatst gewijzigd" tonen?
should the infolog list show the percent done only for status ongoing or two separate icons. infolog nl Moet de InfoLog lijst uitsluitend het percentage voltooid tonen bij status in behandeling of moet het twee separate ikonen tonen.
should this entry only be visible to you and people you grant privat access via the acl infolog nl Moet deze infolog alleen zochtbaar zijn voor u en de personen die toegang hebben tot uw persoonlijke informatie d.m.v. het ingestelde toegangsbeheer
show a column for used and planned times in the list. infolog nl Toont een kolom voor gebruikte en geplande tijden in de lijst.
show full usernames infolog nl Volledige gebruikersnamen weergeven
show in the infolog list infolog nl In de InfoLoglijst weergeven
show last modified infolog nl Laatst gewijzigd weergeven
show status and percent done separate infolog nl Status en percentage voltooid separaat weergeven
show ticket id infolog nl Ticket ID weergeven
show times infolog nl Toon tijden
small view infolog nl kleine weergave
start a new search, cancel this link infolog nl Start een nieuwe zoekopdracht, deze link annuleren
startdate infolog nl Startdatum
startdate enddate infolog nl Startdatum Einddatum
startdate for new entries infolog nl Startdatum voor nieuwe items
startrecord infolog nl Startregel
status infolog nl Status
status ... infolog nl Status ...
sub infolog nl Onderliggende
sub-entries become subs of the parent or main entries, if there's no parent infolog nl Sub infologs worden subs van de bovenliggende of hoofd infologs als er geen boven liggende infolog is
subject infolog nl Onderwerp
sum infolog nl Som
task infolog nl Taak
template infolog nl Template
test import (show importable records <u>only</u> in browser) infolog nl Test Import (te importeren record weergeven in uw browser)
the name used internaly (<= 10 chars), changeing it makes existing data unavailible infolog nl de naam die intern gebruikt wordt (<= 10 tekens), als je dit wijzigt worden de huidige gegevens onbereikbaar
the name used internaly (<= 20 chars), changeing it makes existing data unavailible infolog nl de naam die intern gebruikt wordt (<= 20 tekens), als je dit wijzigt worden de huidige gegevens onbereikbaar
the text displayed to the user infolog nl De tekst die wordt weergegeven aan de gebruiker
this is the filter infolog uses when you enter the application. filters limit the entries to show in the actual view. there are filters to show only finished, still open or futures entries of yourself or all users. infolog nl Dit is het filter dat InfoLog standaard gebruikt wanneer u InfoLog opent. Filters geven een selectie weer van het totale aantal infologs. Er zijn filters voor AFGEWERKTE, OPENSTAANDE of TOEKOMSTIGE infologs voor U of ALLE gebruikers.
til when should the todo or phonecall be finished infolog nl Wanneer moet de taak of het telefoongesprek afgehandeld zijn?
times infolog nl Tijden
to many might exceed your execution-time-limit infolog nl Te veel kan mogelijk de uitvoertijd van dit programme overschrijden.
to what should the startdate of new entries be set. infolog nl Waarop moet de startdatum van nieuwe items ingesteld worden.
today infolog nl Vandaag
todays date infolog nl datum van vandaag
todo infolog nl taak
translation infolog nl Vertaling
typ infolog nl Type
typ '%1' already exists !!! infolog nl Type '%1' bestaat al !!!
type infolog nl Type
type ... infolog nl Type ...
type of customfield infolog nl Type van aangepaste veld
type of the log-entry: note, phonecall or todo infolog nl Infologtype: Taak, Telefoongesprek of Notitie
unlink infolog nl Maak link ongedaan
upcoming infolog nl aankomende
urgency infolog nl Prioriteit
urgent infolog nl dringend
used time infolog nl gebruikte tijd
valid path on clientside<br>eg. \\server\share or e:\ infolog nl geldig pad op de computer van de cliënt<br/>. Bijv. \\Server\Share of D:\
valid path on clientside<br>eg. \servershare or e: infolog nl geldig pad op de computer van de cliënt<br/>. Bijv. \\Server\Share of D:\
values for selectbox infolog nl Waarden voor selectbox
view all subs of this entry infolog nl Alle sub's van deze infolog weergeven
view other subs infolog nl overige sub's weergeven
view parent infolog nl bovenliggende weergeven
view subs infolog nl Sub's weergeven
view the parent of this entry and all his subs infolog nl Van deze infolog de bovenliggende en alle bijbehorende zijn sub's weergeven
view this linked entry in its application infolog nl Gelinkt record in bijbehorende applicatie bekijken
when should the todo or phonecall be started, it shows up from that date in the filter open or own open (startpage) infolog nl Wanneer moet de Taak of het Telefoongesprek worden gestart? If zal vanaf die datum worden weergeven in het filter OPEN of EIGEN OPENSTAANDE
which additional fields should the responsible be allowed to edit without having edit rights?<br />status, percent and date completed are always allowed. infolog nl Welke additionele velden moet de verantwoordelijke kunnen bewerken zonder daarvoor speciale bewerk rechten te moeten krijgen?<br />Status, percentage en datum voltooid zijn altijd toegestaan.
which implicit acl rights should the responsible get? infolog nl Welke impliciete ACL rechten moet de verantwoordelijke krijgen?
which types should the calendar show infolog nl Welke types moet de kalender weergeven
whole query infolog nl gehele query
will-call infolog nl zal bellen
write (add or update) a record by passing its fields. infolog nl Schrijf (toevoegen of bijwerken) een record door de velden mee te geven.
yes - delete infolog nl Ja - Verwijderen
yes - delete including sub-entries infolog nl Ja - Verwijderen inclusief de onderliggende infologs
yes, noone can purge deleted items infolog nl Ja, niemand kan gewiste items definitief verwijderen
yes, only admins can purge deleted items infolog nl Ja, alleen beheerders kunnen gewiste items definitief verwijderen
yes, with larger fontsize infolog nl Ja, met groter lettertype
yes, with purging of deleted items possible infolog nl Ja, met definitieve verwijdering van gewiste items als mogelijk
you can choose a categorie to be preselected, when you create a new infolog entry infolog nl U kunt een categorie kiezen als voorgeselecteerd voor het geval u een nieuw InfoLog item aanmaakt
you can't delete one of the stock types !!! infolog nl U kunt de standaard types niet verwijderen
you have entered an invalid ending date infolog nl U heeft een ongeldige einddatum ingevoerd
you have entered an invalid starting date infolog nl U heeft een ongeldige startdatum ingevoerd
you have to enter a name, to create a new typ!!! infolog nl U moet een naam invoeren om een nieuw type aan te maken!
you must enter a subject or a description infolog nl U moet een onderwerp of een beschrijving invoeren
your database is not up to date (%1 vs. %2), please run %3setup%4 to update your database. infolog nl Uw database is NIET actueel (%1 vs. %2), voer s.v.p. %3setup%4 uit om uw database te actualiseren.

View File

@ -1,14 +1,5 @@
%1 days in advance infolog pl %1 dni w przód
%1 deleted infolog pl %1 usunięto
%1 deleted by %2 at %3 infolog pl %1 usunięto przez %2 , %3
%1 modified infolog pl %1 zmodyfikowano
%1 modified by %2 at %3 infolog pl %1 zmodyfikowano przez %2, %3
%1 records imported infolog pl Zaimportowano %1 rekordów
%1 records read (not yet imported, you may go %2back%3 and uncheck test import) infolog pl %1 rekordów wczytano (jeszcze nie zaimportowano, możesz %2wrócić%3 i odznaczyć Test Import)
%1 you are responsible for is due at %2 infolog pl %1 za które jesteś odpowiedzialny ma termin zakończenia %2
%1 you are responsible for is starting at %2 infolog pl %1 za które jesteś odpowiedzialny zaczynia się %2
%1 you delegated is due at %2 infolog pl %1 które oddelegowałeś ma termin zakończenia %2
%1 you delegated is starting at %2 infolog pl %1 które oddelegowałeś zaczyna się %2
- subprojects from infolog pl - Projekty podrzędne od
0% infolog pl 0%
10% infolog pl 10%
@ -26,7 +17,6 @@ a short subject for the entry infolog pl krótki temat tego wpisu
abort without deleting infolog pl Anuluj bez kasowania
accept infolog pl Akceptuj
action infolog pl Czynność
actions... infolog pl Czynności...
actual date and time infolog pl aktualna data i czas
add infolog pl Dodaj
add a file infolog pl Dodaj plik
@ -41,26 +31,20 @@ add timesheet entry infolog pl Dodaj wpis czasu pracy
add: infolog pl Dodaj:
all infolog pl Wszystko
all links and attachments infolog pl wszystkie linki i załączniki
all projects infolog pl Wszystkie projekty
allows to set the status of an entry, eg. set a todo to done if it's finished (values depend on entry-typ) infolog pl pozwala zmienić status wpisu, np. z Do Zrobienia na Wykonane (wartości zależą od typu wpisu)
alternatives infolog pl Alternatywy
apply the changes infolog pl Zastosuj zmiany
archive infolog pl archiwum
are you shure you want to close this entry ? infolog pl Czy na pewno zamknąć ten wpis?
are you shure you want to delete this entry ? infolog pl Na pewno usunąć ten wpis?
attach a file infolog pl Dołącz plik
attach file infolog pl Dołącz plik
attention: no contact with address %1 found. infolog pl Uwaga: Brak kontaktu z adresem %1.
attension: no contact with address %1 found. infolog pl Uwaga: nie znaleziono kontaktu z adresem %1.
back to main list infolog pl Wróć do głównej listy
billed infolog pl rozliczony
both infolog pl Oba
call infolog pl rozmowa
can be used to show further infolog types in the calendar or limit it to show eg. only tasks. infolog pl Może być użyte w celu wyświetlenia większej ilość typów wpisów Dzennika CRM w kalendarzu lub ograniczyć ich wyświetlanie np tylko do zadań.
cancel infolog pl Anuluj
cancelled infolog pl anulowany
categories infolog pl Kategorie
category infolog pl Kategoria
change history infolog pl Zmień historię
change the status of an entry, eg. close it infolog pl Zmień status wpisu, np. zakończ go
charset of file infolog pl Kodowanie pliku (charset)
check to set startday infolog pl check to set startday
@ -68,14 +52,11 @@ check to specify custom contact infolog pl Zaznacz, aby określić niestandardow
click here to create the link infolog pl kliknij tutaj aby stworzyć odnośnik
click here to start the search infolog pl kliknij tutaj aby rozpocząć szukanie
close infolog pl Zamknij
close all infolog pl Zamknij wszystko
close this entry and all listed sub-entries infolog pl Zamknij ten wpis i wszystkie wpisy podrzędne
comment infolog pl Komentarz
completed infolog pl Wykonany
configuration infolog pl Konfiguracja
confirm infolog pl Potwierdzenie
contact infolog pl Kontakt
copy of: infolog pl Kopia z:
copy your changes to the clipboard, %1reload the entry%2 and merge them. infolog pl Kopiuje Twoje zmiany do schowka, %1odświeża zawartość wpisu%2 i łączy je ze sobą.
create new links infolog pl Tworzy nowe linki
creates a new field infolog pl stwórz nowe pole
@ -90,7 +71,6 @@ custom contact-address, leave empty to use information from most recent link inf
custom contact-information, leave emtpy to use information from most recent link infolog pl Własna informacja dla kontaktu, zostaw puste aby użyć informacji z dodawanego linku
custom fields infolog pl Pola własne
custom fields, typ and status common pl Typ i status pola własnego
custom from infolog pl Władny formularz
custom regarding infolog pl Custom regarding
custom status for typ infolog pl Własny status dla typu
customfields infolog pl Polawłasne
@ -99,20 +79,14 @@ date completed (leave it empty to have it automatic set if status is done or bil
datecreated infolog pl Data utworzenia
dates, status, access infolog pl daty, statusy i dostęp
days infolog pl dni
default category for new infolog entries infolog pl Domyślna kategoria dla nowych wpisów Dziennika CRM
default filter for infolog infolog pl Domyślny filtr dziennika
default status for a new log entry infolog pl domyślny status dla nowego wpisu do logu
delegated infolog pl oddelegowane
delegated open infolog pl oddelegowane otwarte
delegated overdue infolog pl oddelegowane przeterminowane
delegated upcomming infolog pl oddelegowane nadchodzące
delegation infolog pl Delegacja
delete infolog pl Kasuj
delete one record by passing its id. infolog pl Skasuj jeden rekord przekazując jego id.
delete the entry infolog pl Kasuj wpis
delete this entry infolog pl skasuj ten wpis
delete this entry and all listed sub-entries infolog pl Skasuj ten wpis oraz wszystkie podane podwpisy
deleted infolog pl usunięte
deletes the selected typ infolog pl kasuje wybrany typ
deletes this field infolog pl kasuje to pole
deletes this status infolog pl kasuje ten status
@ -120,18 +94,11 @@ description infolog pl Opis
determines the order the fields are displayed infolog pl określa kolejność wyświetlania pól
disables a status without deleting it infolog pl wyłącza status bez kasowania go
do you want a confirmation of the responsible on: accepting, finishing the task or both infolog pl czy chcesz potwierdzenia osoby odpowiedzialnej do: akceptacji, zakończenia zadania lub obydwu?
do you want a notification, if items get assigned to you or assigned items get updated? infolog pl Czy chcesz powiadomienia jeśli zostaną przypisane ci nowe zadania albo jeśli istniejące zostaną zaktualizowane?
do you want a notification, if items you are responsible for are about to start? infolog pl Czy chcesz powiadomienia, jeśli zadania za które jesteś odpowiedzialny niedługo się zaczynają?
do you want a notification, if items you are responsible for are due? infolog pl Czy chcesz powiadomienia jeśli nadszedł termin wykonania zadań za które jesteś odpowiedzialny?
do you want a notification, if items you created get updated? infolog pl Czy chcesz powiadomienia, jeśli zadania które utworzyłeś zostaną zaktualizowane?
do you want a notification, if items you delegated are about to start? infolog pl Czy chcesz powiadomienia, jeśli zadania które oddelegowałeś niedługo się zaczynają?
do you want a notification, if items you delegated are due? infolog pl Czy chcesz powiadomienia, jeśli dobiegł termin wykonania oddelegowanych przez ciebie zadań?
do you want to receive notifications as html-mails or plain text? infolog pl Czy chcesz otrzymywać powiadomienia jako HTML czy zwykły tekst?
do you want to see custom infolog types in the calendar? infolog pl Czy chcesz oglądać niestandardowe wpisy CRM Dziennik w kalendarzu?
don't show infolog infolog pl NIE pokazuj InfoLog
done infolog pl wykonano
download infolog pl Pobierz
duration infolog pl Czas trwania
e-mail: infolog pl E-Mail:
each value is a line like <id>[=<label>] infolog pl każda wartość jest linią, jak <id>[=<nazwa>]
edit infolog pl Edycja
edit or create categories for ingolog infolog pl Edytuj lub twórzy kategorie dla CRM Dziennik
@ -151,26 +118,18 @@ error: saving the entry infolog pl Błąd przy zapisywaniu zadania
error: the entry has been updated since you opened it for editing! infolog pl Błąd: zadanie zostało zaktualizowane odkąd otworzyłeś je do edycji!
existing links infolog pl Istniejące linki
fax infolog pl Fax
field must not be empty !!! infolog pl Pole nie może być puste !
fieldseparator infolog pl Separator pól
finish infolog pl Koniec
for which types should this field be used infolog pl dla jakich typów to pole będzie uzywane
from infolog pl Od
general infolog pl Ogólnie
global categories infolog pl Kategorie Globalne
group owner for infolog pl Właściciel grupowy dla
high infolog pl wysoki
history infolog pl Historia
history logging infolog pl Zapis historii
history logging and deleting of items infolog pl Zapis historii i usuwanie wpisów
how many describtion lines should be directly visible. further lines are available via a scrollbar. infolog pl Ile linii opisu ma być widocznych bezpośrednio. Pozostałe linie są dostępne przez skrolowanie.
id infolog pl id
id# infolog pl Id #
if a type has a group owner, all entries of that type will be owned by the given group and not the user who created it! infolog pl Jeżeli typ ma właściciela grupowego, wszystkie zadania tego typu będą własnością określonej grupy ZAMIAST użytkownika, który je utworzył!
if not set, the line with search and filters is hidden for less entries then "max matches per page" (as defined in your common preferences). infolog pl Jeżeli nie zaznaczone, wiersz z opcjami wyszukiwania oraz filtrami jest ukrywany dla mniejszej liczby zadań niż "po ile wyświetlać na stronie" (zgodnie z wartością ze wspólnych ustawień aplikacji).
import infolog pl Import
import next set infolog pl importuj nastepny zestaw
importance infolog pl Ważność
info log common pl CRM Dziennik
infolog common pl CRM Dziennik
infolog - delete infolog pl CRM Dziennik - Usuwanie
@ -179,9 +138,8 @@ infolog - import csv-file infolog pl CRM Dziennik - Import pliku CSV
infolog - new infolog pl CRM Dziennik - Nowy
infolog - new subproject infolog pl CRM Dziennik - Nowy podprojekt
infolog - subprojects from infolog pl CRM Dziennik - Podprojekt z
infolog copied - the copy can now be edited infolog pl Dziennik skopiowany - można teraz edytować kopię
infolog entry deleted infolog pl Wpis Dziennika skasowany
infolog entry saved infolog pl Wpis Dziennika zapisany
infolog entry deleted infolog pl Wpis nfoLog skasowany
infolog entry saved infolog pl Wpis InfoLog zapisany
infolog filter for the main screen infolog pl Filtr dziennika na stronę główną
infolog list infolog pl CRM Dziennik lista
infolog preferences common pl CRM Dziennik preferencje
@ -190,12 +148,10 @@ invalid filename infolog pl Niepoprawna nazwa pliku
label<br>helptext infolog pl Etykieta<br>Tekst pomocy
last changed infolog pl Ostatnia zmiana
last modified infolog pl Ostatnio modyfikowany
leave blank to get the used time calculated by timesheet entries infolog pl Pozostaw puste jeśli chcesz aby zużyty czas został obliczony na podstawie wpisów Czasu Pracy
leave it empty infolog pl zostaw puste
leave without saveing the entry infolog pl cofnij bez zapisywania wpisu
leaves without saveing infolog pl cofnij bez zapisywania
length<br>rows infolog pl Długość<br>Linie
limit number of description lines (default 5, 0 for no limit) infolog pl Ogranicz liczbę linii opisu (domyślnie 5, 0 - bez limitu)
link infolog pl Podłącz
links infolog pl Linki
links of this entry infolog pl Linki do tego wpisu
@ -205,12 +161,9 @@ location infolog pl Lokalizacja
longer textual description infolog pl dłuższy opis tekstowy
low infolog pl niski
max length of the input [, length of the inputfield (optional)] infolog pl maksymalna długość wprowadzanego tekstu [, długość pola (opcjonalnie)]
modifierer infolog pl Modyfikator
name must not be empty !!! infolog pl Nazwa nie może być pusta!!!
name of new type to create infolog pl nazwa nowego tworzonego typu
never hide search and filters infolog pl Nigdy nie ukrywaj pól wyszukiwania i filtrów
new %1 infolog pl Nowy %1
new %1 created by %2 at %3 infolog pl Nowy %1 utworzony przez %2 , %3
new name infolog pl nowa nazwa
new search infolog pl Nowe wyszukiwanie
no - cancel infolog pl Nie - Anuluj
@ -219,8 +172,6 @@ no details infolog pl bez szczegółów
no entries found, try again ... infolog pl nie znaleziono wpisów, spróbuj jeszcze raz
no filter infolog pl Bez filtra
no links or attachments infolog pl brak linków lub załączników
no project infolog pl Brak projektu
nonactive infolog pl niekaktywny
none infolog pl Brak
normal infolog pl normalny
not infolog pl Nie
@ -230,24 +181,18 @@ note infolog pl Notatka
number of records to read (%1) infolog pl Liczba rekordów do wczytania (%1)
number of row for a multiline inputfield or line of a multi-select-box infolog pl ilość wierszy dla pól składających się z wielu linijek lub zespołu boksów wielokrotnego wyboru
offer infolog pl oferta
one day after infolog pl jeden dzień po
one day in advance infolog pl jeden dzień przed
ongoing infolog pl W toku
only for details infolog pl Tylko dla szczegółów
only if i get assigned or removed infolog pl Tylko jeśli zostanę przypisany lub usunięty
only the attachments infolog pl tylko załączniki
only the links infolog pl tylko linki
open infolog pl otwarte
optional note to the link infolog pl opcjonalna notatka do linku
order infolog pl Kolejność
organization infolog pl Organizacja
overdue infolog pl zaległe
own infolog pl stworzone przez Ciebie - wszystkie
own open infolog pl stworzone przez Ciebie - otwarte
own overdue infolog pl stworzone przez Ciebie - zaległe
own upcoming infolog pl stworzone przez Ciebie - nadchodzące
parent infolog pl Nadrzędny
parent infolog infolog pl Nadrzędny Dziennik
path on (web-)serverside<br>eg. /var/samba/share infolog pl ścieżka na (web-)serwerze<br>np. /var/samba/Share
path to user and group files has to be outside of the webservers document-root!!! infolog pl Ścieżka do użytkownika i grupy MUSI BYĆ POZA rootem dokumentów www (document-root)!!!
pattern for search in addressbook infolog pl wzorzec poszukiwania w książce adresowej
@ -260,25 +205,13 @@ phonecall infolog pl Telefon
planned infolog pl zaplanowany
planned time infolog pl zaplanowany termin
price infolog pl Cena
pricelist infolog pl Lista cen
primary link infolog pl główny link
priority infolog pl Priorytet
private infolog pl Prywatne
project infolog pl Projekt
project settings: price, times infolog pl Ustawienia projektu: cena, terminy
projectmanager infolog pl Manger projektu
re-planned infolog pl Re-planowany
re-planned time infolog pl Re-planowany czas
re: infolog pl Odp:
read one record by passing its id. infolog pl Odczytaj jeden rekord przekazując jego id.
read rights (default) infolog pl prawda odczytu (domyślne)
receive notifications about due entries you are responsible for infolog pl Otrzymuj powiadomienia o zaległych zadaniach za które jesteś odpowiedzialny
receive notifications about due entries you delegated infolog pl Otrzymuj powiadomienia o zaległych zadaniach które oddelegowałeś
receive notifications about items assigned to you infolog pl Otrzymuj powiadomienia o zadaniach ci przydzielonych
receive notifications about own items infolog pl Otrzymuj powiadomienia o własnych zadaniach
receive notifications about starting entries you are responsible for infolog pl Otrzymuj powiadomienia o rozpoczęciu zadań za które jesteś odpowiedzialny
receive notifications about starting entries you delegated infolog pl Otrzymuj powiadomienia o rozpoczęciu zadań które oddelegowałeś
receive notifications as html-mails infolog pl Otrzymuj powiadomienia jako wiadomości HTML
reg. expr. for local ip's<br>eg. ^192.168.1. infolog pl wyrażenie regularne dla lokalnych IP<br>np. ^192\.168\.1\.
reg. expr. for local ip's<br>eg. ^192\.168\.1\. infolog pl wyrażenie regularne dla lokalnych IP<br>np. ^192\.168\.1\.
remark infolog pl Przypomnienie
@ -290,7 +223,6 @@ responsible upcoming infolog pl oddelegowane na Ciebie - nadchodzące
responsible user, priority infolog pl osoba odpowiedzialna, priorytet
returns a list / search for records. infolog pl Pokazuje listę wyszukanych rekordów
rights for the responsible infolog pl Uprawnienia do osoby odpowiedzialnej
same day infolog pl ten sam dzień
save infolog pl Zapisz
saves the changes made and leaves infolog pl zapisuje zmiany i wychodzi
saves this entry infolog pl Zapisuję ten wpis
@ -307,14 +239,12 @@ select an app to search in infolog pl Wybierz Aplikację aby w niej szukać
select an entry to link with infolog pl Wybierz wpis z którym połączysz
select to filter by owner infolog pl wybierz aby sortować po właścicielu
select to filter by responsible infolog pl wybierz aby sortować po osobie odpowiedzialnej
sender infolog pl Nadawca
set status to done infolog pl Ustaw status na wykonany
set status to done for all entries infolog pl Ustaw status na wykonany dla wszystkich wpisów
sets the status of this entry and its subs to done infolog pl Ustaw stan wpisu i jego wpisów podrzędnych na "gotowy".
should infolog show subtasks, -calls or -notes in the normal view or not. you can always view the subs via there parent. infolog pl Czy Dziennik CRM ma w domyślnym widoku wyświetlać wszystkie Zadania, Rozmowy i Notatki podrzędne? Możesz zawsze dotrzeć do nich poprzez ich <i>rodzica</i>.
should infolog show the links to other applications and/or the file-attachments in the infolog list (normal view when you enter infolog). infolog pl Czy Dziennik CRM powinien pokazywać odnośniki do innych aplikacji oraz/lub załączone pliki na liście zadań (domyślny widok gdy uruchamiany jest Dziennik CRM)?
should infolog show up on the main screen and with which filter. works only if you dont selected an application for the main screen (in your preferences). infolog pl Czy Dziennik CRM powinien pokazywać zadania na głównym ekranie a jeżeli tak, to z jakim filtrem. Działa tylko, jeżeli nie wybrano aplikacji na główny ekran (we wspólnych ustawieniach).
should infolog use full names (surname and familyname) or just the loginnames. infolog pl Czy CRM - Dziennik ma używać pełnych danych osobowych (imię, nazwisko), czy tylko loginów.
should the calendar show custom types too infolog pl Czy pokazywać osobiste typy w kalendarzu
should the infolog list show a unique numerical id, which can be used eg. as ticket id. infolog pl Czy na liście zadań Dziennika CRM pokazywać unikalny identyfikator numeryczny, który może być używany np. jako numer kolejny zadania?
should the infolog list show the column "last modified". infolog pl Czy na liście zadań Dziennika CRM pokazywać kolumnę "ostatnio zmodyfikowany"?
should the infolog list show the percent done only for status ongoing or two separate icons. infolog pl Czy na liście zadań Dziennika CRM pokazywać stopień zaawansowania tylko dla zadań "w toku", czy zawsze dwie osobne ikony?
@ -337,9 +267,7 @@ status ... infolog pl Status ...
sub infolog pl Podrz.
sub-entries become subs of the parent or main entries, if there's no parent infolog pl Zadani są podrzędne wobec zadań, z których je utworzono lub głównych zadań, jeżeli zadania z których je utworzono nie istnieją.
subject infolog pl Temat
sum infolog pl Suma
task infolog pl Zadanie
template infolog pl Szablon
test import (show importable records <u>only</u> in browser) infolog pl Import testowy (pokazuje importowane rekordy <u>tylko</u> w przeglądarce!)
the name used internaly (<= 10 chars), changeing it makes existing data unavailible infolog pl nazwa użyta wewnętrznie (<= 10 znaków), zmiana spowoduje niedostępność istniejących danych
the name used internaly (<= 20 chars), changeing it makes existing data unavailible infolog pl nazwa użyta wewnętrznie (<= 20 znaków), zmiana spowoduje niedostępność istniejących danych
@ -377,18 +305,11 @@ view this linked entry in its application infolog pl zoabcz podlinkowany wpis w
when should the todo or phonecall be started, it shows up from that date in the filter open or own open (startpage) infolog pl kiedy zadanie lub telefon powinno wystartować, to pokazuje z tej daty w filtrze otwartym lub jego otwartym (strona startowa)
which additional fields should the responsible be allowed to edit without having edit rights?<br />status, percent and date completed are always allowed. infolog pl Które dodatkowe pola powinna móc edytować osoba odpowiedzialna nie posiadając praw edycji? <br />Status, stopień zaawansowania oraz data wykonania są zawsze możliwe do edycji.
which implicit acl rights should the responsible get? infolog pl Które prawa ACL powinna otrzymać niejawnie osoba odpowiedzialna?
which types should the calendar show infolog pl Jakie typy powinien pokazywać kalendarz
whole query infolog pl Całe zapytanie
will-call infolog pl zadzwoni
write (add or update) a record by passing its fields. infolog pl Zapisz (dodaj lub uaktualnij) zapis przez przechodzenie po jego polach
yes - close infolog pl Tak - Zamknij
yes - close including sub-entries infolog pl Tak - Zamknij z wpisami podrzędnymi
yes - delete infolog pl Tak - Usuń
yes - delete including sub-entries infolog pl Tak - Usuń wraz z podrzędnymi
yes, noone can purge deleted items infolog pl Tak, nikt nie może wymazać usuniętych wpisów
yes, only admins can purge deleted items infolog pl Tak, tylko admin może wymazać usunięte wpisy
yes, with larger fontsize infolog pl Tak, z większą czcionką
yes, with purging of deleted items possible infolog pl Tak, z możliwością wymazywania istniejących wpisów
you can choose a categorie to be preselected, when you create a new infolog entry infolog pl Możesz wybrać kategorię automatycznie wybieraną przy tworzeniu nowego wpisu Dziennika
you can't delete one of the stock types !!! infolog pl Nie możesz usunąć jesdnego z typów!!!
you have entered an invalid ending date infolog pl Podałeś niepoprawną datę zakończenia
you have entered an invalid starting date infolog pl Podałeś niepoprawną datę rozpoczęcia

371
infolog/lang/egw_pt-br.lang Normal file
View File

@ -0,0 +1,371 @@
%1 days in advance infolog pt-br %1 dias para frente
%1 deleted infolog pt-br %1 removido
%1 deleted by %2 at %3 infolog pt-br %1 removido por %2 em %3
%1 modified infolog pt-br %1 modificado
%1 modified by %2 at %3 infolog pt-br %1 modificado por %2 em %3
%1 records imported infolog pt-br %1 registros importados
%1 records read (not yet imported, you may go %2back%3 and uncheck test import) infolog pt-br %1 arquivos lidos (não foi importado, você pode %2voltar%3 e efetuar o Teste de importação)
%1 you are responsible for is due at %2 infolog pt-br %1, você é o responsável. Excede o prazo em %2
%1 you are responsible for is starting at %2 infolog pt-br %1, você é o responsável. Inicia em %2
%1 you delegated is due at %2 infolog pt-br %1, você designou. Excede o prazo em %2
%1 you delegated is starting at %2 infolog pt-br %1, você designou. Inicia em %2
- subprojects from infolog pt-br - Subprojetos de
0% infolog pt-br 0%
10% infolog pt-br 10%
100% infolog pt-br 100%
20% infolog pt-br 20%
30% infolog pt-br 30%
40% infolog pt-br 40%
50% infolog pt-br 50%
60% infolog pt-br 60%
70% infolog pt-br 70%
80% infolog pt-br 80%
90% infolog pt-br 90%
<b>file-attachments via symlinks</b> instead of uploads and retrieval via file:/path for direct lan-clients infolog pt-br <b>Arquivos anexados</b> através do sistema de envio:/caminho para clientes de sua própria rede
a short subject for the entry infolog pt-br um nome curto para este registro
abort without deleting infolog pt-br Cancelar sem apagar
accept infolog pt-br aceitar
action infolog pt-br Ação
actual date and time infolog pt-br Data e Hora atuais
add infolog pt-br Adicionar
add a file infolog pt-br Adicionar um arquivo
add a new entry infolog pt-br Adicionar um novo registro
add a new note infolog pt-br Adicionar uma nota
add a new phonecall infolog pt-br Adicionar uma nova ligação
add a new sub-task, -note, -call to this entry infolog pt-br Adicionar um sub-tarefa, -nota, -ligação para este registro
add a new todo infolog pt-br Adicionar um Apontamento
add file infolog pt-br Adicionar um arquivo
add sub infolog pt-br adicionar uma Sub
add timesheet entry infolog pt-br Adicionar registro à planilha de tempo
add: infolog pt-br Adicionar:
all infolog pt-br Todos
all links and attachments infolog pt-br todos links e arquivos anexados
allows to set the status of an entry, eg. set a todo to done if it's finished (values depend on entry-typ) infolog pt-br permite configurar o status de um registro. Ex. marque um Apontamento com o Status finalizado quando esta estiver terminada. (Os valores dependem do tipo de registro)
apply the changes infolog pt-br Aplicar as alterações
archive infolog pt-br arquivar
are you shure you want to delete this entry ? infolog pt-br Você tem certeza que deseja apagar este registro?
attach a file infolog pt-br Anexar um arquivo
attach file infolog pt-br Anexar arquivo
attention: no contact with address %1 found. infolog pt-br Atenção: nenhum contato com endereço %1 encontrado.
back to main list infolog pt-br Retornar a lista principal
billed infolog pt-br cobrado
both infolog pt-br ambos
call infolog pt-br ligar
can be used to show further infolog types in the calendar or limit it to show eg. only tasks. infolog pt-br Pode ser usado para exibir tipos de Tarefas na Agenda or limitar sua exibição, por exemplo: somente tarefas.
cancel infolog pt-br Cancelar
cancelled infolog pt-br Cancelada
categories infolog pt-br Categorias
category infolog pt-br Categoria
change the status of an entry, eg. close it infolog pt-br Alterar o status de um registro (ex. fechado)
charset of file infolog pt-br Conjunto de carácteres do arquivo
check to set startday infolog pt-br Selecione para determinar a data de início
check to specify custom contact infolog pt-br Marque para especificar contato personalizado
click here to create the link infolog pt-br clique aqui para inserir um hyperlink
click here to start the search infolog pt-br clique aqui para iniciar uma pesquisa
close infolog pt-br Fechar
comment infolog pt-br Comentário
completed infolog pt-br Completada
configuration infolog pt-br Configuração
confirm infolog pt-br Confirme
contact infolog pt-br Contato
copy your changes to the clipboard, %1reload the entry%2 and merge them. infolog pt-br Copiar suas alterações para a área de transferência, %1recarregar o registro%2 e mesclá-los.
create new links infolog pt-br Criar um novo hyperlink
creates a new field infolog pt-br criar um novo campo
creates a new status with the given values infolog pt-br criar um status novo com os valores antigos
creates a new typ with the given name infolog pt-br criar um novo tipo com nomes antigos
creation infolog pt-br Criação
csv-fieldname infolog pt-br CSV-Nome do Campo
csv-filename infolog pt-br CSV-Nome do Arquivo
csv-import common pt-br CSV-Importar
custom infolog pt-br Personalizado
custom contact-address, leave empty to use information from most recent link infolog pt-br Personalizar endereço de contato, deixe vazio para usar a informação do link mais recente.
custom contact-information, leave emtpy to use information from most recent link infolog pt-br Personalizar informações de contato, deixe vazio para utilizar a informação do link mais recente.
custom fields infolog pt-br Campos Personalizáveis
custom fields, typ and status common pt-br Campos Personalizáveis, tipo e status
custom from infolog pt-br Personalizar de
custom regarding infolog pt-br Personalizar Atenção
custom status for typ infolog pt-br Personalizar status por tipo
customfields infolog pt-br Campos Perssonalizáveis
date completed infolog pt-br Data de Finalização
date completed (leave it empty to have it automatic set if status is done or billed) infolog pt-br Data de finalização (deixe em branco para ser automaticamente informada, quando o status for alterado para Completada ou Cobrada)
datecreated infolog pt-br data da criação
dates, status, access infolog pt-br Datas, Status, Acessos
days infolog pt-br dias
default category for new infolog entries infolog pt-br Categoria padrão para novos registros Tarefas
default filter for infolog infolog pt-br Filtro padrao
default status for a new log entry infolog pt-br status padrão para um novo registro
delegated infolog pt-br designado
delegated open infolog pt-br designado e aberto
delegated overdue infolog pt-br designado e prazo excedido
delegated upcomming infolog pt-br designado e prazo excedendo
delegation infolog pt-br Atribuição
delete infolog pt-br Apagar
delete one record by passing its id. infolog pt-br Apagar um registro contornando seu id.
delete the entry infolog pt-br Apagar o registro
delete this entry infolog pt-br Apagar este registro
delete this entry and all listed sub-entries infolog pt-br Apagar este registro e todos os sub-registros listados
deleted infolog pt-br removido
deletes the selected typ infolog pt-br Apagar os tipos selecionados
deletes this field infolog pt-br Apagar este campo
deletes this status infolog pt-br Apagar este status
description infolog pt-br Descrição
determines the order the fields are displayed infolog pt-br determine a ordem em que os campos aparecerão
disables a status without deleting it infolog pt-br desabilitar o status após a remoção
do you want a confirmation of the responsible on: accepting, finishing the task or both infolog pt-br você quer uma confirmação do responsável quanto à: aceitação, encerramento ou ambos
do you want a notification, if items get assigned to you or assigned items get updated? infolog pt-br Você quer receber uma notificação, caso itens forem designados a você ou caso itens designados a você forem atualizados?
do you want a notification, if items you are responsible for are about to start? infolog pt-br Você quer receber uma notificação, caso algum item esteja prestes a iniciar e você seja o responsável?
do you want a notification, if items you are responsible for are due? infolog pt-br Você quer receber uma notificação, caso itens excedam o prazo e você seja o responsável?
do you want a notification, if items you created get updated? infolog pt-br Você quer receber uma notificação, se itens que você criou forem atualizados?
do you want a notification, if items you delegated are about to start? infolog pt-br Você quer receber uma notificação, se itens que você designou estiverem prestes a iniciar?
do you want a notification, if items you delegated are due? infolog pt-br Você quer receber uma notificação, se itens que você designou excederem o prazo?
do you want to receive notifications as html-mails or plain text? infolog pt-br Você quer receber notificações em HTML ou texto puro?
don't show infolog infolog pt-br Não exibir Tarefas
done infolog pt-br Encerrado
download infolog pt-br Baixar
duration infolog pt-br Duração
each value is a line like <id>[=<label>] infolog pt-br cada valor por linha <id>[=<label>]
edit infolog pt-br Editar
edit or create categories for ingolog infolog pt-br Editar ou criar categorias para o Módulo Tarefas
edit rights (full edit rights incl. making someone else responsible!) infolog pt-br Permissões de edição (edição total inclui fazer outra pessoa responsável)
edit status infolog pt-br Editar o Status
edit the entry infolog pt-br Editar o registro
edit this entry infolog pt-br Editar este registro
empty for all infolog pt-br esvaziar tudo
enddate infolog pt-br Data Final
enddate can not be before startdate infolog pt-br A data final não pode ser anterior a data inicial
enter a custom contact, leave empty if linked entry should be used infolog pt-br Digitar um contato personalizado, deixe vazio para utilizar o que já existe
enter a custom phone/email, leave empty if linked entry should be used infolog pt-br Digitar um fone/e-mail personalizado, deixe vazio para utilizar o que já existe
enter a textual description of the log-entry infolog pt-br Digitar uma descrição para este novo registro
enter the query pattern infolog pt-br Digite o argumento da consulta
entry and all files infolog pt-br Este e todos os arquivos
error: saving the entry infolog pt-br Erro salvando o registro
error: the entry has been updated since you opened it for editing! infolog pt-br Erro: o registro foi atualizado desde sua abertura, por você, para edição!
existing links infolog pt-br Links existentes
fax infolog pt-br Fax
field must not be empty !!! infolog pt-br O campo não pode estar vazio!!!
fieldseparator infolog pt-br Separador de Campos
finish infolog pt-br finalizar
for which types should this field be used infolog pt-br para quais tipos este campo deve ser utilizado
from infolog pt-br De
general infolog pt-br Geral
group owner for infolog pt-br Grupo-proprietário para
high infolog pt-br alta
history logging infolog pt-br Registro de histórico
history logging and deleting of items infolog pt-br Registro de histórico e remoção de itens
id infolog pt-br Id
id# infolog pt-br Id#
if a type has a group owner, all entries of that type will be owned by the given group and not the user who created it! infolog pt-br Se um tipo tem um grupo-proprietário, todas as entradas deste tipo terão como dono o grupo informado e não o usuário que as criou.
if not set, the line with search and filters is hidden for less entries then "max matches per page" (as defined in your common preferences). infolog pt-br Se não informado, a linha com Pesquisa e Filtros ficará oculta para menos registros que "número máximo de registro por página" (definido em suas preferências).
import infolog pt-br Importar
import next set infolog pt-br importar o próximo conjunto
info log common pt-br Tarefas
infolog common pt-br Tarefas
infolog - delete infolog pt-br Apagar - Tarefas
infolog - edit infolog pt-br Editar - Tarefas
infolog - import csv-file infolog pt-br Importar arquivo CVS - Tarefas
infolog - new infolog pt-br Nova - Tarefas
infolog - new subproject infolog pt-br Novo Subprojeto - Tarefas
infolog - subprojects from infolog pt-br Novo Subprojeto de - Tarefas
infolog entry deleted infolog pt-br Tarefa removida
infolog entry saved infolog pt-br Tarefa salva
infolog filter for the main screen infolog pt-br Filtro para Tarefas na Página Inicial
infolog list infolog pt-br Lista de Tarefas
infolog preferences common pt-br Preferências - Tarefas
infolog-fieldname infolog pt-br Nome do campo - Tarefas
invalid filename infolog pt-br Nome de arquivo inválido
label<br>helptext infolog pt-br Etiqueta<br>Texto para ajuda
last changed infolog pt-br Última alteração
last modified infolog pt-br Última modificação
leave it empty infolog pt-br Deixe em branco
leave without saveing the entry infolog pt-br Sair sem salvar o registro
leaves without saveing infolog pt-br Sair sem salvar
length<br>rows infolog pt-br Largura<br>Colunas
link infolog pt-br Link
links infolog pt-br Links
links of this entry infolog pt-br Links para este registro
list all categories infolog pt-br Listar todas as categorias
list no subs/childs infolog pt-br não listar subs/filhos
location infolog pt-br Local
longer textual description infolog pt-br descrição detalhada
low infolog pt-br baixo
max length of the input [, length of the inputfield (optional)] infolog pt-br tamanho máximo para inclusão [, tamanho para o campo (opcional)]
modifierer infolog pt-br Modificador
name must not be empty !!! infolog pt-br O campo nome não pode estar vazio!!!
name of new type to create infolog pt-br nome para o novo tipo
never hide search and filters infolog pt-br Nunca ocultar Pesquisa e Filtros
new %1 infolog pt-br Novo %1
new %1 created by %2 at %3 infolog pt-br Novo %1 criado por %2 em %3
new name infolog pt-br novo nome
new search infolog pt-br Nova pesquisa
no - cancel infolog pt-br Não - Cancelar
no describtion, links or attachments infolog pt-br Sem descrição, links ou anexos
no details infolog pt-br Sem detalhes
no entries found, try again ... infolog pt-br nenhum registro encontrado, tente novamente ...
no filter infolog pt-br sem filtro
no links or attachments infolog pt-br sem links ou anexos
nonactive infolog pt-br não ativo
none infolog pt-br Nenhum
normal infolog pt-br normal
not infolog pt-br não
not assigned infolog pt-br não atribuido
not-started infolog pt-br Não iniciada
note infolog pt-br Nota
number of records to read (%1) infolog pt-br Número de registros para ler (%1)
number of row for a multiline inputfield or line of a multi-select-box infolog pt-br Número de linha para campos ou caixas de entrada multilinhas
offer infolog pt-br Livre
one day after infolog pt-br um dia depois
one day in advance infolog pt-br um dia adiantado
ongoing infolog pt-br Aberto
only for details infolog pt-br Somente para detalhes
only if i get assigned or removed infolog pt-br Somente se eu for designado ou removido
only the attachments infolog pt-br apenas arquivos anexos
only the links infolog pt-br apenas links
open infolog pt-br abrir
optional note to the link infolog pt-br Nota opcional para o Link
order infolog pt-br Ordem
overdue infolog pt-br atrasado
own infolog pt-br Suas tarefas
own open infolog pt-br Suas tarefas abertas
own overdue infolog pt-br Suas tarefas em Atraso
own upcoming infolog pt-br Suas tarefas futuras
parent infolog pt-br Pai
path on (web-)serverside<br>eg. /var/samba/share infolog pt-br caminho (web-)serverside<br>ex. /var/samba/Share
path to user and group files has to be outside of the webservers document-root!!! infolog pt-br O diretório para os arquivos de usuário ou grupo DEVE ESTAR FORA da raiz de documentos do servidor web!!!
pattern for search in addressbook infolog pt-br argumento de pesquisa em catálogo de endereços
pattern for search in projects infolog pt-br argumento de pesquisa nos Projetos
percent completed infolog pt-br Percentagem completada
permission denied infolog pt-br Permissão negada
phone infolog pt-br Chamadas Telefônicas
phone/email infolog pt-br Telefone/E-mail
phonecall infolog pt-br Chamadas Telefônicas
planned infolog pt-br Planejada
planned time infolog pt-br Horas planejadas
price infolog pt-br Preço
pricelist infolog pt-br Lista de preços
primary link infolog pt-br Link primário
priority infolog pt-br Prioridade
private infolog pt-br Particular
project infolog pt-br Projeto
project settings: price, times infolog pt-br Configurações do projeto: preço, tempo
re: infolog pt-br Re:
read one record by passing its id. infolog pt-br Ler um registro contornando seu id.
read rights (default) infolog pt-br Permissões de leitura (padrão)
receive notifications about due entries you are responsible for infolog pt-br Receber notificações sobre registros com prazos excedidos, cujo responsável seja você.
receive notifications about due entries you delegated infolog pt-br Receber notificações sobre registros com prazos excedidos que você tenha designado
receive notifications about items assigned to you infolog pt-br Receber notificações sobre registros designados a você
receive notifications about own items infolog pt-br Receber notificações sobre seus próprios registros
receive notifications about starting entries you are responsible for infolog pt-br Receber notificações sobre registros prestes a se iniciar, cujo responsável seja você.
receive notifications about starting entries you delegated infolog pt-br Receber notificações sobre registros prestes a se iniciar que você tenha designado.
receive notifications as html-mails infolog pt-br Receber notificações em formato HTML
reg. expr. for local ip's<br>eg. ^192.168.1. infolog pt-br reg. expr. for local IP's<br>eg. ^192\.168\.1\.
reg. expr. for local ip's<br>eg. ^192\.168\.1\. infolog pt-br reg. expr. for local IP's<br>eg. ^192\.168\.1\.
remark infolog pt-br Destaque
remove this link (not the entry itself) infolog pt-br Remover este link (não o registro)
responsible infolog pt-br Responsável
responsible open infolog pt-br Responsável pela abertura
responsible overdue infolog pt-br Responsável pela realização
responsible upcoming infolog pt-br Responsável futuro
responsible user, priority infolog pt-br Usuário responsável, prioridade
returns a list / search for records. infolog pt-br Retornar uma lista / pesquisar por registros
rights for the responsible infolog pt-br Permissões para o responsável
same day infolog pt-br mesmo dia
save infolog pt-br Salvar
saves the changes made and leaves infolog pt-br salvar as alterações realizadas e sair.
saves this entry infolog pt-br Salvar este registro
search infolog pt-br Pesquisar
search for: infolog pt-br Pesquizar por:
select infolog pt-br Selecionar
select a category for this entry infolog pt-br selecione uma categoria para este registro
select a price infolog pt-br Selecione um preço
select a priority for this task infolog pt-br selecione uma prioridade para esta tarefa
select a project infolog pt-br Selecione um projeto
select a responsible user: a person you want to delegate this task infolog pt-br selecione um responsável: a pessoa a quem você delegará essa tarefa
select a typ to edit it's status-values or delete it infolog pt-br selecione um tipo para editar o valor do status ou apague-o
select an app to search in infolog pt-br Selecione em que aplicativo a pesquisa será feita
select an entry to link with infolog pt-br Selecione um registro para link
select to filter by owner infolog pt-br Selecione para filtrar por proprietário
select to filter by responsible infolog pt-br Selecione para filtrar por responsável
sets the status of this entry and its subs to done infolog pt-br Configura o status deste registros e seus sub-registros como Completada
should infolog show subtasks, -calls or -notes in the normal view or not. you can always view the subs via there parent. infolog pt-br As sub tarefas de chamadas, notas, devem ser mostradas ou não . Você pode sempre pode visualizar as sub tarefas à partir da principal.
should infolog show the links to other applications and/or the file-attachments in the infolog list (normal view when you enter infolog). infolog pt-br Links para outros aplicativos ou anexos deverão ser exibidos na lista de Tarefas (quando você abrir Tarefas em modo normal de visualização).
should infolog show up on the main screen and with which filter. works only if you dont selected an application for the main screen (in your preferences). infolog pt-br O aplicativo Tarefas deverá ser exibido na Página Inicial ? Com qual filtro? Funciona somente se você não selecionou um aplicativo para sua Página Inicial (em suas preferências).
should infolog use full names (surname and familyname) or just the loginnames. infolog pt-br O aplicativo Tarefas deve usar o nome completo (nome e sobrenome) ou apenas o login de acesso.
should the infolog list show a unique numerical id, which can be used eg. as ticket id. infolog pt-br A lista do aplicativo Tarefas deverá exibir um identificador número único, que poderá ser usado, por exemplo, como identificador de Ticket.
should the infolog list show the column "last modified". infolog pt-br A lista do aplicativo Tarefas deverá exibir a coluna "Última alteração".
should the infolog list show the percent done only for status ongoing or two separate icons. infolog pt-br A lista do aplicativo Tarefas deverá exibir a percentagem já realizada somente para o status ou dois ícones separados.
should this entry only be visible to you and people you grant privat access via the acl infolog pt-br Este registro deve ser visível apenas para você e para usuários que você determine atráves das regras de controle de acesso, ou deve ser público?
show a column for used and planned times in the list. infolog pt-br Exibir uma coluna para tempo planejado e usado na lista.
show full usernames infolog pt-br Mostrar nomes completos
show in the infolog list infolog pt-br Mostrar na lista de tarefas
show last modified infolog pt-br Exibir última alteração
show status and percent done separate infolog pt-br Exibir status e percentagem já realizada separados
show ticket id infolog pt-br Exibir identificador do Ticket
show times infolog pt-br Exibir horários
small view infolog pt-br visão compacta
start a new search, cancel this link infolog pt-br iniciar um nova pesquisa, cancelar este link
startdate infolog pt-br Data Inicial
startdate enddate infolog pt-br Data inicial / Data Final
startdate for new entries infolog pt-br Data inicial para novas tarefas
startrecord infolog pt-br Iniciar registro
status infolog pt-br Status
status ... infolog pt-br Status ...
sub infolog pt-br Sub
sub-entries become subs of the parent or main entries, if there's no parent infolog pt-br Sub-entradas tornam-se "filhas" da "entrada-pai" ou, se não houver "entrada-pai", da entrada principal
subject infolog pt-br Assunto
sum infolog pt-br Soma
task infolog pt-br Pendências
template infolog pt-br Modelo
test import (show importable records <u>only</u> in browser) infolog pt-br Teste de Importação (mostrará arquivos importados <u>apenas</u> no navegador)
the name used internaly (<= 10 chars), changeing it makes existing data unavailible infolog pt-br o nome usado internamente (<= 10 caracteres), alterando-o fará dados existentes inacessíveis
the name used internaly (<= 20 chars), changeing it makes existing data unavailible infolog pt-br o nome usado internamente (<= 20 caracteres), alterando-o fará dados existentes inacessíveis
the text displayed to the user infolog pt-br o texto mostrado ao usuário
this is the filter infolog uses when you enter the application. filters limit the entries to show in the actual view. there are filters to show only finished, still open or futures entries of yourself or all users. infolog pt-br Estes são os filtros padrões do Módulo de Tarefas. Os filtros ajudam a limitar a visão atual. Existem filtros para mostrar tarefas encerradas, ou abertas, ou até todas as tarefas disponíveis a todos os usuários.
til when should the todo or phonecall be finished infolog pt-br até quando uma pendência ou chamada esteja encerrada.
times infolog pt-br Horários
to many might exceed your execution-time-limit infolog pt-br mostrar muitos registros pode provocar a violação do tempo limite do servidor
to what should the startdate of new entries be set. infolog pt-br Para que data inicial novas tarefas deverão ser configuradas.
today infolog pt-br Para Hoje
todays date infolog pt-br Data de hoje
todo infolog pt-br Pendências
translation infolog pt-br Tradução
typ infolog pt-br Tipo
typ '%1' already exists !!! infolog pt-br O tipo '%1' já existe!!!
type infolog pt-br Tipo
type ... infolog pt-br Tipo ...
type of customfield infolog pt-br Tipo de campo personalizado
type of the log-entry: note, phonecall or todo infolog pt-br Tipo de registro: Nota, Recado ou Pendência
unlink infolog pt-br Remover link
upcoming infolog pt-br futuras
urgency infolog pt-br urgência
urgent infolog pt-br urgente
used time infolog pt-br Horas utilizadas
valid path on clientside<br>eg. \\server\share or e:\ infolog pt-br valid path on clientside<br>eg. \\Server\Share or e:\
valid path on clientside<br>eg. \servershare or e: infolog pt-br valid path on clientside<br>eg. \\Server\Share or e:\
valid path on clientside<br>eg. servershare or e: infolog pt-br valid path on clientside<br>eg. \\Server\Share or e:\
values for selectbox infolog pt-br Valores para a caixa de seleção
view all subs of this entry infolog pt-br Ver todas as subs para este registro
view other subs infolog pt-br ver outras subs
view parent infolog pt-br Ver superiores
view subs infolog pt-br ver inferiores
view the parent of this entry and all his subs infolog pt-br Ver o registro superior e todos os inferiores
view this linked entry in its application infolog pt-br Ver esse link no aplicativo correspondente
when should the todo or phonecall be started, it shows up from that date in the filter open or own open (startpage) infolog pt-br Quando uma pendência ou chamada for iniciada ela deverá ser mostrada primeiro no filtro que esteja aberto ou na sua página inicial
which additional fields should the responsible be allowed to edit without having edit rights?<br />status, percent and date completed are always allowed. infolog pt-br Que registros adicionais o responsável estará autorizado a editar sem ter acesso à edição?<br/>Status, percentagem e data de finalização são sempre autorizados.
which implicit acl rights should the responsible get? infolog pt-br Que direitos implícitos o responsável deverá receber ?
which types should the calendar show infolog pt-br Quais tipos deverão ser exibidos pela Agenda
whole query infolog pt-br toda a consulta
will-call infolog pt-br a chamar
write (add or update) a record by passing its fields. infolog pt-br Escrever (adicionar ou atualizar) um registro contornando seus campos.
yes - delete infolog pt-br Sim - Apagar
yes - delete including sub-entries infolog pt-br Sim - Apagar inclusive sub-registros
yes, noone can purge deleted items infolog pt-br Sim, ninguém pode limpar registros removidos
yes, only admins can purge deleted items infolog pt-br Sim, somente administradores podem limpar registros removidos
yes, with larger fontsize infolog pt-br Sim, com fontes grandes
yes, with purging of deleted items possible infolog pt-br Sim, com possibilidade de limpeza de registros removidos
you can choose a categorie to be preselected, when you create a new infolog entry infolog pt-br Você pode selecionar uma categoria para ser pré-selecionada, quando você criar um novo registro.
you can't delete one of the stock types !!! infolog pt-br Você não pode apagar um destes tipos!!!
you have entered an invalid ending date infolog pt-br Você informou uma data final inválida
you have entered an invalid starting date infolog pt-br Você informou uma data inícial inválida
you have to enter a name, to create a new typ!!! infolog pt-br Você deve digitar um nome para criar um novo tipo!!!
you must enter a subject or a description infolog pt-br Você deve digitar um assunto ou uma descrição
your database is not up to date (%1 vs. %2), please run %3setup%4 to update your database. infolog pt-br Sua base de dados NÃO está atualizada (%1 x %2), por favor execute %3setup%4 para atualizá-la.

371
infolog/lang/egw_ru.lang Normal file
View File

@ -0,0 +1,371 @@
%1 days in advance infolog ru %1 дней(я) до
%1 deleted infolog ru %1 удален
%1 deleted by %2 at %3 infolog ru %1 удален %2 в %3
%1 modified infolog ru %1 изменен
%1 modified by %2 at %3 infolog ru %1 изменен %2 в %3
%1 records imported infolog ru %1 записей импортировано
%1 records read (not yet imported, you may go %2back%3 and uncheck test import) infolog ru %1 записей прочтено (но не импортировано, вы можете вернуться %2назад%3 и снять флаг с Тестировать Импорт)
%1 you are responsible for is due at %2 infolog ru %1 за который вы отвечаете должен быть завершен в %2
%1 you are responsible for is starting at %2 infolog ru %1 за который вы отвечаете должен начаться в %2
%1 you delegated is due at %2 infolog ru %1 который вы назначили должен быть завершен в %2
%1 you delegated is starting at %2 infolog ru %1 который вы назначили должен начаться в %2
- subprojects from infolog ru - Подпроект из
0% infolog ru 0%
10% infolog ru 10%
100% infolog ru 100%
20% infolog ru 20%
30% infolog ru 30%
40% infolog ru 40%
50% infolog ru 50%
60% infolog ru 60%
70% infolog ru 70%
80% infolog ru 80%
90% infolog ru 90%
<b>file-attachments via symlinks</b> instead of uploads and retrieval via file:/path for direct lan-clients infolog ru <b>вложение файлов через символические ссылки</b> вместо публикации и получение через file:/путь для подключенных непосредственно к локальной сети клиентов
a short subject for the entry infolog ru короткая тема для записи
abort without deleting infolog ru Прервать без удаления
accept infolog ru принять
action infolog ru Действие
actual date and time infolog ru актуальные дата и время
add infolog ru Добавить
add a file infolog ru Добавить файл
add a new entry infolog ru Добавить новую Запись
add a new note infolog ru Добавить новую Заметку
add a new phonecall infolog ru Добавить новый Телефонный Звонок
add a new sub-task, -note, -call to this entry infolog ru Добавить новую подзадачу, -заметку, -звонок к этой записи
add a new todo infolog ru Добавить новое Задание
add file infolog ru Добавить файл
add sub infolog ru Добавить Под...
add timesheet entry infolog ru Добавить запись табеля учета времени
add: infolog ru Добавить:
all infolog ru Все
all links and attachments infolog ru все ссылки и вложения
allows to set the status of an entry, eg. set a todo to done if it's finished (values depend on entry-typ) infolog ru позволяет установить состояние записи, напр. установить Задание в состояние "выполнено" если оно окончено (значение зависит от типа записи)
apply the changes infolog ru Применить изменения
archive infolog ru Архив
are you shure you want to delete this entry ? infolog ru Вы уверены, что хотите удалить эту запись ?
attach a file infolog ru Прикрепить файл
attach file infolog ru Прикрепить файл
attention: no contact with address %1 found. infolog ru Внимание: Контакт с адресом %1 не найден
back to main list infolog ru Назад к основному списку
billed infolog ru оплачно
both infolog ru оба
call infolog ru звонок
can be used to show further infolog types in the calendar or limit it to show eg. only tasks. infolog ru Может быть использовано для показа будущих типов ИнфоЖурнала или для ограничения показываемого например только задачами.
cancel infolog ru Отмена
cancelled infolog ru отменено
categories infolog ru Категории
category infolog ru Категория
change the status of an entry, eg. close it infolog ru Изменить состояние записи, например - закрыть ее.
charset of file infolog ru Кодировка файла.
check to set startday infolog ru отметить, чтобы установить дату начала
check to specify custom contact infolog ru Отметить для указания пользовательского контакта
click here to create the link infolog ru Нажмите здесь для создания Ссылки
click here to start the search infolog ru Нажмите здесь для начала поиска
close infolog ru Закрыть
comment infolog ru Комментарий
completed infolog ru Завершено
configuration infolog ru Конфигурация
confirm infolog ru Подтвердить
contact infolog ru Контакт
copy your changes to the clipboard, %1reload the entry%2 and merge them. infolog ru Скопируйте изменения в буфер обмена, %1перезагрузите запись%2 и объедините их.
create new links infolog ru Создать новые ссылки
creates a new field infolog ru Создать новое поле
creates a new status with the given values infolog ru создает новое состояние с данными значениями
creates a new typ with the given name infolog ru создает новый тип с данным именем
creation infolog ru Создание
csv-fieldname infolog ru Имя поля CSV
csv-filename infolog ru Имя файла CSV
csv-import common ru Импорт CSV
custom infolog ru Пользовательский
custom contact-address, leave empty to use information from most recent link infolog ru Пользовательский адрес для контакта, оставьте незаполненным для использования информации из наиболее свежей ссылки
custom contact-information, leave emtpy to use information from most recent link infolog ru Пользовательская информация для контакта, оставьте незаполненным для использования информации из наиболее свежей ссылки
custom fields infolog ru Пользовательские Поля
custom fields, typ and status common ru Пользовательские поля, тип и состояние
custom from infolog ru Пользовательский из (?)
custom regarding infolog ru Пользовательский относительно (?)
custom status for typ infolog ru Пользовательское состояние для типа
customfields infolog ru Пользовательские поля
date completed infolog ru Дата завершения
date completed (leave it empty to have it automatic set if status is done or billed) infolog ru Дата завершения (оставьте незаполненным, если хотите чтобы она автоматически установилась если состояние будет "сделано" или "оплачено")
datecreated infolog ru дата создания
dates, status, access infolog ru Даты, Состояние, Доступ
days infolog ru дни
default category for new infolog entries infolog ru Категория по умолчанию для новых записей ИнфоЖурнала
default filter for infolog infolog ru Фильтр по Умолчанию для ИнфоЖурнала
default status for a new log entry infolog ru статус по умолчанию для новой записи журнала
delegated infolog ru назначеные
delegated open infolog ru открытые назначенные
delegated overdue infolog ru просроченные назначенные
delegated upcomming infolog ru назначенные приближающиеся
delegation infolog ru Назначение
delete infolog ru Удалить
delete one record by passing its id. infolog ru Удалить одну запись по ее идентификатору
delete the entry infolog ru Удалить запись
delete this entry infolog ru Удалить эту запись
delete this entry and all listed sub-entries infolog ru Удалить эту запись и все перечисленные под-записи
deleted infolog ru удален
deletes the selected typ infolog ru удаляет выбранный тип
deletes this field infolog ru удаляет это поле
deletes this status infolog ru удаляет это состояние
description infolog ru Описание
determines the order the fields are displayed infolog ru оперделяет порядок, в котором отображаются поля
disables a status without deleting it infolog ru отключает состояние без удаления его
do you want a confirmation of the responsible on: accepting, finishing the task or both infolog ru хотите ли Вы подтверждения принятия под ответственность на: принятие, завершение задания или на оба.
do you want a notification, if items get assigned to you or assigned items get updated? infolog ru Вы желаете получать напоминания, при назначении вам заданий(записей) или при их изменении?
do you want a notification, if items you are responsible for are about to start? infolog ru Вы желаете получать напоминания при запуске задач(записей) за которые вы отвечаете?
do you want a notification, if items you are responsible for are due? infolog ru Вы желаете получать напоминания, если задачи за которые вы отвечаете должны быть уже завершены?
do you want a notification, if items you created get updated? infolog ru Вы желаете получать напоминания при изменении записей созданных вами?
do you want a notification, if items you delegated are about to start? infolog ru Вы желаете получать напоминания при запуске задач(записей) которые вам назначены?
do you want a notification, if items you delegated are due? infolog ru Вы желаете получать напоминания, если задачи которые вы назначили должны быть уже завершены?
do you want to receive notifications as html-mails or plain text? infolog ru Вы желаете получать напоминания в виде HTML или обычного текста?
don't show infolog infolog ru НЕ показывать ИнфоЖурнал
done infolog ru сделано
download infolog ru Получить
duration infolog ru Продолжительность
each value is a line like <id>[=<label>] infolog ru каждое значение - это строка вида <идентификатор>[=<метка>]
edit infolog ru Редактировать
edit or create categories for ingolog infolog ru Редактировать или созадть категории для ИнфоЖурнала
edit rights (full edit rights incl. making someone else responsible!) infolog ru редактировать права (полное редактирование прав, вкл. назначение кого-то еще ответственным!)
edit status infolog ru Редактировать состояние
edit the entry infolog ru Редактировать запись
edit this entry infolog ru Редактировать эту запись
empty for all infolog ru пусто для всех
enddate infolog ru До даты
enddate can not be before startdate infolog ru До даты не может быть раньше даты начала
enter a custom contact, leave empty if linked entry should be used infolog ru введите пользовательский контакт, оставьте незаполненным, если должна использоваться связанная запись
enter a custom phone/email, leave empty if linked entry should be used infolog ru введите пользовательский телефон/электронный адрес, оставьте незаполненным, если должна использоваться связанная запись
enter a textual description of the log-entry infolog ru введите текстовое описание для записи журнала
enter the query pattern infolog ru Введите шаблон вопроса
entry and all files infolog ru Запись и все файлы
error: saving the entry infolog ru Ошибка: сохраниние записи
error: the entry has been updated since you opened it for editing! infolog ru Ошибака: запись была обновлена с тех пор, как Вы открыли её для редактирования!
existing links infolog ru Существующие ссылки
fax infolog ru Факс
field must not be empty !!! infolog ru Поле должно быть заполнено !!!
fieldseparator infolog ru Разделитель полей
finish infolog ru окончание
for which types should this field be used infolog ru для каких типов это поле должно быть использовано
from infolog ru Из
general infolog ru Общий
group owner for infolog ru Группа-владелец для
high infolog ru высокий
history logging infolog ru Запись истории
history logging and deleting of items infolog ru Журналирование истории и удаление записей
id infolog ru Ид
id# infolog ru Ид№
if a type has a group owner, all entries of that type will be owned by the given group and not the user who created it! infolog ru Если тип имеет группу-владельца, все записи этого типа будут иметь владельцем данную группу, а НЕ пользователя, который их создал!
if not set, the line with search and filters is hidden for less entries then "max matches per page" (as defined in your common preferences). infolog ru Если не установлено, строка с поиском и фильтрами скрыта для количества записей меньшего, чем "макс. совпадений на страницу" (как определено в ваших общих настройках)
import infolog ru Импорт
import next set infolog ru Импортировать следующий набор
info log common ru ИнфоЖурнал
infolog common ru ИнфоЖурнал
infolog - delete infolog ru ИнфоЖурнал - Удаление
infolog - edit infolog ru ИнфоЖурнал - Редактирование
infolog - import csv-file infolog ru ИнфоЖурнал - импорт файла CSV
infolog - new infolog ru ИнфоЖурнал - Новый
infolog - new subproject infolog ru ИнфоЖурнал - Новый Подпроект
infolog - subprojects from infolog ru ИнфоЖурнал - Подпроект из
infolog entry deleted infolog ru Запись ИнфоЖурнала удалена
infolog entry saved infolog ru Запись ИнфоЖурнала сохранена
infolog filter for the main screen infolog ru Фильтр ИнфоЖурнала для основного экрана
infolog list infolog ru Список ИнфоЖурнала
infolog preferences common ru Настройки ИнфоЖурнала
infolog-fieldname infolog ru Название поля ИнфоЖурнала
invalid filename infolog ru Неправильное имя файла
label<br>helptext infolog ru Метка<br>Текст помощи
last changed infolog ru Последние изменения
last modified infolog ru Последние изменения
leave it empty infolog ru оставьте это незаполненным
leave without saveing the entry infolog ru выйти без сохранения записи
leaves without saveing infolog ru выйти без сохранения
length<br>rows infolog ru Длина<br>Строк
link infolog ru Ссылка
links infolog ru Ссылки
links of this entry infolog ru Ссылки этой записи
list all categories infolog ru Список всех категорий
list no subs/childs infolog ru Не пролистывать Подчиненные/Порожденные
location infolog ru Местоположение
longer textual description infolog ru более полное текстовое описание
low infolog ru низкий
max length of the input [, length of the inputfield (optional)] infolog ru максимальная длина ввода [, длина поля ввода (необязательно)]
modifierer infolog ru Кто изменил
name must not be empty !!! infolog ru Имя не должно быть пустым !!!
name of new type to create infolog ru имя вновь создаваемого типа
never hide search and filters infolog ru никогда не скрывать поиск и фильтры
new %1 infolog ru Новый %1
new %1 created by %2 at %3 infolog ru Новый %1 содан %2 в %3
new name infolog ru новое имя
new search infolog ru Новый поиск
no - cancel infolog ru Нет - Отменить
no describtion, links or attachments infolog ru без описания, ссылок или вложений
no details infolog ru без подробностей
no entries found, try again ... infolog ru не найдены записи, попробуйте снова
no filter infolog ru без Фильтра
no links or attachments infolog ru без ссылок или приложений
nonactive infolog ru неактивен
none infolog ru Ничего
normal infolog ru нормальный
not infolog ru нет
not assigned infolog ru не назначено
not-started infolog ru не начато
note infolog ru Заметка
number of records to read (%1) infolog ru количество записей к чтению (%1)
number of row for a multiline inputfield or line of a multi-select-box infolog ru количество строк в многострочном поле ввода или списке с множественным выбором
offer infolog ru предложено
one day after infolog ru через один день
one day in advance infolog ru за один день до
ongoing infolog ru в работе
only for details infolog ru Только для подробностей
only if i get assigned or removed infolog ru Только если Я присоединен или отсоединен
only the attachments infolog ru только вложения
only the links infolog ru только ссылки
open infolog ru открытые
optional note to the link infolog ru необязательная заметка к Ссылке
order infolog ru Порядок
overdue infolog ru просроченные
own infolog ru свои
own open infolog ru свои открытые
own overdue infolog ru свои просроченные
own upcoming infolog ru свои ожидаемые
parent infolog ru Родитель
path on (web-)serverside<br>eg. /var/samba/share infolog ru путь на (веб-)сервере<br>напр. /var/samba/Share
path to user and group files has to be outside of the webservers document-root!!! infolog ru Путь к файлам пользователя или группы должен быть ВНЕ корневого каталога для документов веб-сервера!!!
pattern for search in addressbook infolog ru шаблон поиска в Адресной книге
pattern for search in projects infolog ru шаблон для поиска в Проектах
percent completed infolog ru Завершено процентов
permission denied infolog ru Доступ запрещен
phone infolog ru Телефонный Звонок
phone/email infolog ru Телефон/E-mail
phonecall infolog ru Телефонный Звонок
planned infolog ru запланировано
planned time infolog ru запланированное время
price infolog ru Цена
pricelist infolog ru Прайслист
primary link infolog ru первичная ссылка
priority infolog ru Приоритет
private infolog ru Личный
project infolog ru Проект
project settings: price, times infolog ru Установки проекта: цена, времена
re: infolog ru Re:
read one record by passing its id. infolog ru Прочитать одну запись указанием её идентификатора
read rights (default) infolog ru права на чтение (по умолчанию)
receive notifications about due entries you are responsible for infolog ru Получать напоминания о необходимости завершения работ за которые вы отвечаете
receive notifications about due entries you delegated infolog ru Получать напоминания о необходимости завершения работ которые вы назначили
receive notifications about items assigned to you infolog ru Получать сообщения о записях соединенных с вами
receive notifications about own items infolog ru Получать сообщения о своих записях
receive notifications about starting entries you are responsible for infolog ru Получать сообщения о запуске записей за которые вы отвечаете
receive notifications about starting entries you delegated infolog ru Получать сообщения о запуске записей которые вам назначены
receive notifications as html-mails infolog ru Получать сообщения в виде HTML-писем
reg. expr. for local ip's<br>eg. ^192.168.1. infolog ru рег. выр. для локальных IP<br>напр. ^192\.168\.1\.
reg. expr. for local ip's<br>eg. ^192\.168\.1\. infolog ru рег. выр. для локальных IP<br>напр. ^192\.168\.1\.
remark infolog ru Примечание
remove this link (not the entry itself) infolog ru Удалить эту ссылку (не саму запись)
responsible infolog ru Ответственный
responsible open infolog ru ответственного открытые
responsible overdue infolog ru ответственного просроченные
responsible upcoming infolog ru ответственного ожидаемые
responsible user, priority infolog ru ответственный пользователь, приоритет
returns a list / search for records. infolog ru Возврщает список / поиск для записей.
rights for the responsible infolog ru Права для ответственного
same day infolog ru этот же день
save infolog ru Сохранить
saves the changes made and leaves infolog ru сохраняет сделанные изменения и выходит
saves this entry infolog ru Сохраняет эту запись
search infolog ru Поиск
search for: infolog ru Искать:
select infolog ru Выбрать
select a category for this entry infolog ru выбрать категорию для этой записи
select a price infolog ru Выбрать цену
select a priority for this task infolog ru выбрать приоритет для этой задачи
select a project infolog ru Выбрать проект
select a responsible user: a person you want to delegate this task infolog ru выбрать ответственного пользователя: человека, которому вы хотите поручить эту задачу
select a typ to edit it's status-values or delete it infolog ru выбрать тип для редактирования значения статуса или удалить
select an app to search in infolog ru Выбрать Приложение, в котором надо искать
select an entry to link with infolog ru Выбрать запись, с которой нужна связь
select to filter by owner infolog ru выбрать для фильтрации по владельцу
select to filter by responsible infolog ru выбрать для фильтрации по ответственному
sets the status of this entry and its subs to done infolog ru Устанавливает состояние этой записи и ей подчиненных в "сделано"
should infolog show subtasks, -calls or -notes in the normal view or not. you can always view the subs via there parent. infolog ru Должен ли ИнфоЖурнал показывает подзадания, -звонки или -заметки в нормальном виде или нет. Вы всегда можете посмотреть подэлементы через их родителей.
should infolog show the links to other applications and/or the file-attachments in the infolog list (normal view when you enter infolog). infolog ru Должен ли ИнфоЖурнал показывать ссылки на другие приложения и/или прикрепленные файлы в списке ИнфоЖурнала (нормальный вид при входе в ИнфоЖурнал)
should infolog show up on the main screen and with which filter. works only if you dont selected an application for the main screen (in your preferences). infolog ru Должен ли ИнфоЖурнал отображаться на основной странице и с каким фильтром. Работает только если Вы не выбрали приложение для основной страницы (в Ваших настройках).
should infolog use full names (surname and familyname) or just the loginnames. infolog ru Должен ли ИнфоЖурнал использовать полные имена (имя и фамилия) или только имена для входа
should the infolog list show a unique numerical id, which can be used eg. as ticket id. infolog ru Должен ли список ИнфоЖурнала показывать уникальные числовые идентификаторы, которые могут быть использованы, например, как идентификаторы талонов
should the infolog list show the column "last modified". infolog ru Должен ли список ИнфоЖурнала показывать колонку "последнее изменение"
should the infolog list show the percent done only for status ongoing or two separate icons. infolog ru Должен ли список ИнфоЖурнала показывать процент выполнения только статуса в работе или две разных пиктограммы.
should this entry only be visible to you and people you grant privat access via the acl infolog ru должна ли эта запись быть видима только для вас и тех, кому вы дали личный доступ через ACL
show a column for used and planned times in the list. infolog ru Показывать колонку для использованного и запланированого времени в списке.
show full usernames infolog ru Показывать полные имена пользователей
show in the infolog list infolog ru Показывать в списке ИнфоЖурнала
show last modified infolog ru Показывать последнее изменение
show status and percent done separate infolog ru Показывать состояние и процент выполнения отдельно
show ticket id infolog ru Показывать идентификатор талона
show times infolog ru Показывать количество раз (времена?)
small view infolog ru мелкий вид
start a new search, cancel this link infolog ru начать новый поиск, отменить эту ссылку
startdate infolog ru Дата Начала
startdate enddate infolog ru Дата Начала До Даты
startdate for new entries infolog ru Дата начала для новых записей
startrecord infolog ru Начальная запись
status infolog ru Состояние
status ... infolog ru Состояние ...
sub infolog ru Под
sub-entries become subs of the parent or main entries, if there's no parent infolog ru Под-записи становятся подзаписями родительских или главных записей если нет родителей
subject infolog ru Предмет
sum infolog ru Сумма
task infolog ru Задание
template infolog ru Шаблон
test import (show importable records <u>only</u> in browser) infolog ru Тест Импорта (показывает импортируемые записи <u>только</u> в браузере)
the name used internaly (<= 10 chars), changeing it makes existing data unavailible infolog ru имя, используется внутри (<=10 символов), его изменение делает существующие данные недоступными
the name used internaly (<= 20 chars), changeing it makes existing data unavailible infolog ru имя, используется внутри (<=20 символов), его изменение делает существующие данные недоступными
the text displayed to the user infolog ru текст, показываемый пользователю
this is the filter infolog uses when you enter the application. filters limit the entries to show in the actual view. there are filters to show only finished, still open or futures entries of yourself or all users. infolog ru Это фильтр, который ИнфоЖурнал использует, когда вы входите в приложение. Фильтры ограничивают записи, которые надо показать, в текущем режиме показа. Есть фильтры для показа только завершенных, открытых или будущих записей - ваших или всех пользователей.
til when should the todo or phonecall be finished infolog ru до того как Задание или Звонок будет завершено
times infolog ru Количество раз (времена?)
to many might exceed your execution-time-limit infolog ru слишком много превысит ваше ограничение на время исполнения
to what should the startdate of new entries be set. infolog ru Как должна устанавливаться дата начала новой записи.
today infolog ru Сегодня
todays date infolog ru сегодняшняя дата
todo infolog ru Задание
translation infolog ru Перевод
typ infolog ru Тип
typ '%1' already exists !!! infolog ru Тип '%1' уже существует !!!
type infolog ru Тип
type ... infolog ru Тип ...
type of customfield infolog ru Тип пользовательского поля
type of the log-entry: note, phonecall or todo infolog ru Тип записи: Заметка, Телефонный звонок или Задание
unlink infolog ru Расцепить ссылку
upcoming infolog ru приход
urgency infolog ru срочность
urgent infolog ru срочное
used time infolog ru использованное время
valid path on clientside<br>eg. \\server\share or e:\ infolog ru действительный путь на стороне клиента.<br>напр. \\Server\Share или e:\
valid path on clientside<br>eg. \servershare or e: infolog ru действительный путь на стороне клиента.<br>напр. \\Server\Share или e:\
valid path on clientside<br>eg. servershare or e: infolog ru действительный путь на стороне клиента.<br>напр. \\Server\Share или e:\
values for selectbox infolog ru Значения для списка выбора
view all subs of this entry infolog ru Смотреть все подзаписи этой записи
view other subs infolog ru смотреть другие подзаписи
view parent infolog ru смотреть родительскую запись
view subs infolog ru смотреть Подзаписи
view the parent of this entry and all his subs infolog ru Показать родительскую запись данной записи и всех подзаписей
view this linked entry in its application infolog ru показать данную запись в ее приложении
when should the todo or phonecall be started, it shows up from that date in the filter open or own open (startpage) infolog ru когда Задание или Телефонный Звонок должны начаться, они показываются с этой даты при открытии фильтра или просто открытии (стартовая страница) (?..)
which additional fields should the responsible be allowed to edit without having edit rights?<br />status, percent and date completed are always allowed. infolog ru Какие дополнительные поля должен мочь редактировать ответственный, не имея прав на редактирование?<br/>Состояние, процент и дата завершения уже разрешены.
which implicit acl rights should the responsible get? infolog ru Какие подразумеваемые права ACL должен получить ответственный?
which types should the calendar show infolog ru Какие типы календарь должен показывать
whole query infolog ru весь запрос
will-call infolog ru позвонит
write (add or update) a record by passing its fields. infolog ru Запишите (добавьте или обновите) запись прохождением её полей.
yes - delete infolog ru Да - Удалить
yes - delete including sub-entries infolog ru Да - Удалить включая подзаписи
yes, noone can purge deleted items infolog ru Да, никто не может уничтожить удаленые записи
yes, only admins can purge deleted items infolog ru Да, только администраторы могут уничтожить удаленые записи
yes, with larger fontsize infolog ru Да, с бо`льшим размером шрифта
yes, with purging of deleted items possible infolog ru Да, с возможностью уничтожения удаленых записей
you can choose a categorie to be preselected, when you create a new infolog entry infolog ru Вы можете выбратькатегорию, которая будет выбрана заранее при создании новой записи ИнфоЖурнала
you can't delete one of the stock types !!! infolog ru Вы не можете удалить один из основных типов!!!
you have entered an invalid ending date infolog ru Вы ввели неверную дату окончания
you have entered an invalid starting date infolog ru Вы ввели неверную дату начала
you have to enter a name, to create a new typ!!! infolog ru Вы должны ввести имя, чтобы создать новый тип!!!
you must enter a subject or a description infolog ru Вы должны ввести тему или описание
your database is not up to date (%1 vs. %2), please run %3setup%4 to update your database. infolog ru Ваша база данных устарела (%1 вместо %2),запустите %3установка%4 для обновления вашей базы данныхх.

404
infolog/lang/egw_sk.lang Normal file
View File

@ -0,0 +1,404 @@
%1 days in advance infolog sk %1 dní v predstihu
%1 deleted infolog sk %1 - odstránená
%1 deleted by %2 at %3 infolog sk %1 - odstránil/a: %2 %3
%1 modified infolog sk %1 - zmena
%1 modified by %2 at %3 infolog sk %1 - zmenu vykonal/a: %2 %3
%1 records imported infolog sk Naimportovaných bolo %1 záznamov
%1 records read (not yet imported, you may go %2back%3 and uncheck test import) infolog sk Načítaných bolo %1 záznamov (zatiaľ nenaimportované, môžete ísť %2späť%3 a odznačiť Test importu)
%1 you are responsible for is due at %2 infolog sk %1 - ste zodpovední, termín je %2
%1 you are responsible for is starting at %2 infolog sk %1 - ste zodpovední, začína sa %2
%1 you delegated is due at %2 infolog sk %1 - delegovali ste, termín je %2
%1 you delegated is starting at %2 infolog sk %1 - delegovali ste, začína sa %2
- subprojects from infolog sk - Podprojekty od (z)
0% infolog sk 0%
10% infolog sk 10%
100% infolog sk 100%
20% infolog sk 20%
30% infolog sk 30%
40% infolog sk 40%
50% infolog sk 50%
60% infolog sk 60%
70% infolog sk 70%
80% infolog sk 80%
90% infolog sk 90%
<b>file-attachments via symlinks</b> instead of uploads and retrieval via file:/path for direct lan-clients infolog sk <b>súborové prílohy pomocou symbolických odkazov</b> namiesto nahrávania a sťahovania pomocou file:/cesta pre priamych klientov LAN
a short subject for the entry infolog sk Krátky predmet záznamu
abort without deleting infolog sk Zrušiť bez odstránenia
accept infolog sk Prijať
action infolog sk Akcia
actions... infolog sk Akcie...
actual date and time infolog sk Aktuálny dátum a čas
add infolog sk Pridať
add a file infolog sk Pridať súbor
add a new entry infolog sk Pridať nový Záznam
add a new note infolog sk Pridať novú Poznámku
add a new phonecall infolog sk Pridať nový Telefonát
add a new sub-task, -note, -call to this entry infolog sk Pridať novú pod-úlohu, -poznámku, -hovor k tomuto záznamu
add a new todo infolog sk Pridať novú Úlohu
add file infolog sk Pridať súbor
add sub infolog sk Pridať Podradený záznam
add timesheet entry infolog sk Pridať záznam do Harmonogramu
add: infolog sk Pridať:
all infolog sk Všetko
all links and attachments infolog sk Všetky odkazy a prílohy
all projects infolog sk Všetky projekty
allows to set the status of an entry, eg. set a todo to done if it's finished (values depend on entry-typ) infolog sk umožňuje nastaviť stav záznamu, napr. nastaviť Úlohu ktorá sa má vykonať po ukončení (hodnoty závisia na type záznamu)
alternatives infolog sk Alternatívy
apply the changes infolog sk Uplatniť zmeny
archive infolog sk Archív
are you shure you want to close this entry ? infolog sk Naozaj chcete uzavrieť tento záznam?
are you shure you want to delete this entry ? infolog sk Naozaj chcete odstrániť tento záznam?
attach a file infolog sk Priložiť súbor
attach file infolog sk Priložiť súbor
attention: no contact with address %1 found. infolog sk Upozornenie: Kontakt s adresou %1 sa nenašiel.
back to main list infolog sk Naspäť na hlavný zoznam
billed infolog sk Vyúčtované
both infolog sk Obidvoje
call infolog sk Volať
can be used to show further infolog types in the calendar or limit it to show eg. only tasks. infolog sk Dá sa použiť na zobrazenie ďalších typov Záznamníka v Kalendári, alebo obmedziť zobrazovanie napr. len na úlohy.
cancel infolog sk Zrušiť
cancelled infolog sk Zrušené
categories infolog sk Kategórie
category infolog sk Kategória
change history infolog sk Zmeniť históriu
change the status of an entry, eg. close it infolog sk Zmeniť stav záznamu, napr. uzavrieť ho
charset of file infolog sk Znaková sada súboru
check to set startday infolog sk Ak chcete zadať dátum začiatku, označte to
check to specify custom contact infolog sk Ak chcete uviesť používateľsky definovaný kontakt, označte to
click here to create the link infolog sk Ak chcete vytvoriť odkaz, kliknite sem
click here to start the search infolog sk Ak chcete začať hľadanie, kliknite sem
close infolog sk Zavrieť
close all infolog sk Zavrieť všetko
close this entry and all listed sub-entries infolog sk Zavrieť tento záznam a všetky uvedené podzáznamy
comment infolog sk Poznámka
completed infolog sk Stav vybavenia
configuration infolog sk Konfigurácia
confirm infolog sk Potvrdiť
contact infolog sk Kontakt
copy of: infolog sk Kópia z:
copy your changes to the clipboard, %1reload the entry%2 and merge them. infolog sk Skopírujte zmeny do schránky, %1znovunačítajte záznam%2 a zlúčte ich.
create new links infolog sk Vytvoriť nové odkazy
creates a new field infolog sk vytvorí nové pole
creates a new status with the given values infolog sk Vytvorí nový stav so zadanými hodnotami
creates a new typ with the given name infolog sk Vytvorí nový typ podľa zadaného mena
creation infolog sk Poradové číslo
csv-fieldname infolog sk CSV-názov položky
csv-filename infolog sk CSV-názov súboru
csv-import common sk CSV-Import
custom infolog sk Používateľom definované
custom contact-address, leave empty to use information from most recent link infolog sk Používateľom definovaná kontaktná adresa - ak necháte prázdne, použije sa údaj z najčerstvejšieho odkazu
custom contact-information, leave emtpy to use information from most recent link infolog sk Používateľom definované kontaktné informácie - ak necháte prázdne, použije sa údaj z najčerstvejšieho odkazu
custom fields infolog sk Používateľské polia
custom fields, typ and status common sk Používateľské polia, typ a stav
custom from infolog sk Používateľom definované od
custom regarding infolog sk Používateľom definované ohľadom
custom status for typ infolog sk Používateľom definovaný stav pre typ
customfields infolog sk Používateľské polia
date completed infolog sk Dátum dokončenia
date completed (leave it empty to have it automatic set if status is done or billed) infolog sk Dátum dokončenia (ak ponecháte prázdne, vyplní sa automaticky podľa toho, kedy bol nastavený stav "hotovo", alebo kedy prebehlo vyúčtovanie).
datecreated infolog sk Dátum vytvorenia
dates, status, access infolog sk Dátum, Stav, Prístup
days infolog sk dní
default category for new infolog entries infolog sk Predvolená kategória pre nové záznamy Záznamníka
default filter for infolog infolog sk Predvolený filter pre Záznamník
default status for a new log entry infolog sk Predvolený stav pre nový záznam
delegated infolog sk Delegované
delegated open infolog sk Delegované - otvorené
delegated open and upcoming infolog sk Delegované - otvorené a blížiace sa
delegated overdue infolog sk Delegované - meškajúce
delegated upcomming infolog sk Delegované - blížiace sa
delegation infolog sk Delegovať
delete infolog sk Odstrániť
delete one record by passing its id. infolog sk Odstrániť konkrétny záznam podľa zadaného ID.
delete the entry infolog sk Odstrániť záznam
delete this entry infolog sk Odstrániť tento záznam
delete this entry and all listed sub-entries infolog sk Odstrániť tento záznam a všetky zobrazené podzáznamy
deleted infolog sk Odstránené
deletes the selected typ infolog sk Odstráni vybraný typ
deletes this field infolog sk odstráni toto pole
deletes this status infolog sk odstráni tento stav
description infolog sk Popis
determines the order the fields are displayed infolog sk Určuje poradie, v akom sa polia zobrazujú
disables a status without deleting it infolog sk Zablokuje stav bez toho, aby sa odstránil
do you want a confirmation of the responsible on: accepting, finishing the task or both infolog sk Budete vyžadovať potvrdenie zodpovedného k týmto akciám?: prijatiu, ukončeniu úlohy, alebo obidvom
do you want a notification, if items get assigned to you or assigned items get updated? infolog sk Chcete dostať pripomienku, keď nastanú zmeny v položkách, ktoré sú vám priradené?
do you want a notification, if items you are responsible for are about to start? infolog sk Chcete dostať pripomienku, keď sa majú začať položky, za ktoré ste zodpovední?
do you want a notification, if items you are responsible for are due? infolog sk Chcete dostať pripomienku, keď nastane termín splnenia tých položiek, za ktoré ste zodpovední?
do you want a notification, if items you created get updated? infolog sk Chcete dostať pripomienku, keď nastanú zmeny v položkách, ktoré ste vytvorili?
do you want a notification, if items you delegated are about to start? infolog sk Chcete dostať pripomienku, keď sa majú začať položky, ktoré ste delegovali?
do you want a notification, if items you delegated are due? infolog sk Chcete dostať pripomienku, keď nastane termín splnenia tých položiek, ktoré ste delegovali?
do you want to receive notifications as html-mails or plain text? infolog sk E-mailové pripomienky chcete dostávať ako HTML, alebo ako čistý text?
don't show infolog infolog sk NEzobrazovať Záznamník
done infolog sk Hotovo
download infolog sk Stiahnuť
due %1 infolog sk Termín %1
duration infolog sk Trvanie
e-mail: infolog sk E-mail:
each value is a line like <id>[=<label>] infolog sk každá hodnota je riadok v tvare <id>[=<label>]
edit infolog sk Upraviť
edit or create categories for ingolog infolog sk Upraviť alebo vytvoriť kategórie pre Záznamník
edit rights (full edit rights incl. making someone else responsible!) infolog sk Plné práva úprav (vrátane nastavenia niekoho iného za zodpovednú osobu!)
edit status infolog sk Upraviť stav
edit the entry infolog sk Upraviť záznam
edit this entry infolog sk Uprav tento záznam
empty for all infolog sk Prázdne znamená všetko
enddate infolog sk Termín splnenia
enddate can not be before startdate infolog sk Termín splnenia nemôže byť pred dátumom začatia
enter a custom contact, leave empty if linked entry should be used infolog sk Zadajte používateľsky definovaný kontakt, alebo ponechajte prázdne ak sa má použiť záznam pripojený odkazom
enter a custom phone/email, leave empty if linked entry should be used infolog sk zadajte používateľsky definovaný telefón/E-mail, alebo ponechajte prázdne ak sa má použiť záznam pripojený odkazom
enter a textual description of the log-entry infolog sk Zadajte text popisujúci tento záznam
enter the query pattern infolog sk Zadajte výraz pre vyhľadávanie
entry and all files infolog sk Záznam a všetky súbory
error: saving the entry infolog sk Chyba pri ukladaní záznamu
error: the entry has been updated since you opened it for editing! infolog sk Chyba: odkedy ste záznam otvorili pre úpravy, bol medzitým aktualizovaný!
existing links infolog sk Existujúce odkazy
fax infolog sk Fax
field must not be empty !!! infolog sk Pole nesmie byť prázdne!!!
fieldseparator infolog sk Oddeľovač polí
finish infolog sk Koniec
for which types should this field be used infolog sk Pre ktoré typy sa má použiť toto pole
from infolog sk Od
general infolog sk Hlavné
global categories infolog sk Globálne kategórie
group owner for infolog sk Skupinový vlastník pre
high infolog sk Vysoká
history infolog sk História
history logging infolog sk Zaznamenávanie histórie
history logging and deleting of items infolog sk Zaznamenávať históriu a odstránené položky?
how many describtion lines should be directly visible. further lines are available via a scrollbar. infolog sk Koľko riadkov z popisu má byť priamo viditeľných? Ďalšie riadky budú dostupné cez posuvník.
id infolog sk ID
id# infolog sk ID#
if a type has a group owner, all entries of that type will be owned by the given group and not the user who created it! infolog sk Ak tento typ MÁ skupinového vlastníka, všetky záznamy tohto typu bude vlastniť zadaná skupina a NIE používateľ, ktorý záznam vytvoril!
if not set, the line with search and filters is hidden for less entries then "max matches per page" (as defined in your common preferences). infolog sk Ak nie je nastavené, riadok vyhľadávania a filtrov je skrytý pre menej záznamov než "maximálny počet záznamov zobrazených na stránke" (ako je definované v bežných nastaveniach).
import infolog sk Import
import next set infolog sk Importovať ďalšiu sadu
importance infolog sk Dôležitosť
info log common sk Záznamník
infolog common sk Záznamník
infolog - delete infolog sk Záznamník - Odstrániť
infolog - edit infolog sk Záznamník - Upraviť
infolog - import csv-file infolog sk Záznamník - Importovať súbor CSV
infolog - new infolog sk Záznamník - Nové
infolog - new subproject infolog sk Záznamník - Nový podprojekt
infolog - subprojects from infolog sk Záznamník - Podprojekty od
infolog copied - the copy can now be edited infolog sk Záznamník skopírovaný - kópia sa už môže upravovať
infolog entry deleted infolog sk Záznam bol odstránený
infolog entry saved infolog sk Záznam bol uložený
infolog filter for the main screen infolog sk Filter Záznamníka pre hlavnú stránku
infolog list infolog sk Záznamník - Zoznam
infolog preferences common sk Predvoľby Záznamníka
infolog-fieldname infolog sk Záznamník - Názov poľa
invalid filename infolog sk Chybný názov súboru
label<br>helptext infolog sk Označenie<br>Pomocný text
last changed infolog sk Naposledy zmenené
last modified infolog sk Naposledy upravil
leave blank to get the used time calculated by timesheet entries infolog sk Ak chcete, aby bol čas spočítaný na základe záznamov Harmonogramu, tak ponechajte prázdne.
leave it empty infolog sk Ponechať prázdne
leave without saveing the entry infolog sk Odísť bez uloženia záznamu
leaves without saveing infolog sk Odísť bez uloženia
length<br>rows infolog sk Dĺžka<br>Riadky
limit number of description lines (default 5, 0 for no limit) infolog sk Obmedziť počet riadkov opisu (0 znamená bez limitu; predvolené: 5)
link infolog sk Odkaz
links infolog sk Odkazy
links of this entry infolog sk Odkazy pre tento záznam
list all categories infolog sk Zobraziť všetky kategórie
list no subs/childs infolog sk Neukazuj Podradené / Dcérske
location infolog sk Umiestnenie
longer textual description infolog sk Dlhší opisný text
low infolog sk Nízka
max length of the input [, length of the inputfield (optional)] infolog sk maximálna dĺžka vstupu [, dĺžka vstupného poľa (voliteľné)]
name must not be empty !!! infolog sk Názov nemôže byť prázdny!
name of new type to create infolog sk Názov novovytvoreného typu
never hide search and filters infolog sk Neschovávať hľadanie a filtre
new %1 infolog sk Nové: %1
new %1 created by %2 at %3 infolog sk Nové: %1, vytvoril %2 %3
new name infolog sk Nový názov
new search infolog sk Nové hľadanie
no - cancel infolog sk Nie - Zrušiť
no describtion, links or attachments infolog sk Žiadny popis, odkaz ani príloha
no details infolog sk Žiadne podrobnosti
no entries found, try again ... infolog sk Nenašiel som také záznamy, skúste znovu...
no filter infolog sk Žiadny filter
no links or attachments infolog sk Žiadne odkazy alebo prílohy
no project infolog sk Žiadny projekt
nonactive infolog sk Neaktívne
none infolog sk Žiadne
normal infolog sk Normálna
not infolog sk Nie
not assigned infolog sk Nepridelené
not-started infolog sk Ešte nezačaté
note infolog sk Poznámka
number of records to read (%1) infolog sk Počet záznamov, ktoré sa majú načítať (%1)
number of row for a multiline inputfield or line of a multi-select-box infolog sk číslo riadku pre viacriadkové vstupné pole alebo riadok vo výberovom okne
offer infolog sk Ponuka
one day after infolog sk Deň po
one day in advance infolog sk 1 deň v predstihu
ongoing infolog sk Prebiehajúce
only for details infolog sk Len pre podrobnosti
only if i get assigned or removed infolog sk Iba ak mi je to priradené alebo odobraté
only the attachments infolog sk Iba prílohy
only the links infolog sk Iba odkazy
open infolog sk Otvorené
open and upcoming infolog sk Otvorené a blížiace sa
optional note to the link infolog sk Voliteľná poznámka k odkazu
order infolog sk Triedenie
organization infolog sk Organizácia
overdue infolog sk Meškajúce
own infolog sk Vlastné
own open infolog sk Vlastné - otvorené
own open and upcoming infolog sk Vlastné - otvorené a blížiace sa
own overdue infolog sk Vlastné - meškajúce
own upcoming infolog sk Vlastné - blížiace sa
parent infolog sk Rodič
parent infolog infolog sk Rodičovský Záznamník
path on (web-)serverside<br>eg. /var/samba/share infolog sk cesta na strane (web-)servera <br>napr. /var/samba/Share
path to user and group files has to be outside of the webservers document-root!!! infolog sk Cesta k súborom používateľa a skupiny MUSÍ BYŤ MIMO document-root webservera!
pattern for search in addressbook infolog sk Výraz pre hľadanie v Adresári
pattern for search in projects infolog sk Výraz pre hľadanie v Projektoch
percent completed infolog sk Na koľko percent vyriešené
permission denied infolog sk Prístup bol odmietnutý
phone infolog sk Telefonát
phone/email infolog sk Telefón / E-mail
phonecall infolog sk Telefonát
planned infolog sk Plánované
planned time infolog sk Plánovaný čas
price infolog sk Cena
pricelist infolog sk Cenník
primary link infolog sk Primárny odkaz
priority infolog sk Priorita
private infolog sk Súkromné
project infolog sk Projekt
project settings: price, times infolog sk Nastavenia Projektu: cena, časy
projectmanager infolog sk Projektovník
re-planned infolog sk Pre-plánované
re-planned time infolog sk Pre-plánovaný čas
re: infolog sk Re:
read one record by passing its id. infolog sk Načítaj jednotlivý záznam zadaním id.
read rights (default) infolog sk Právo na čítanie (predvolené)
receive notifications about due entries you are responsible for infolog sk Dostávať pripomienky o záznamoch, ktorým nastáva termín splnenia a ste za ne zodpovední
receive notifications about due entries you delegated infolog sk Dostávať pripomienky o záznamoch, ktoré ste delegovali
receive notifications about items assigned to you infolog sk Dostávať pripomienky o položkách, ktoré Vám boli priradené
receive notifications about own items infolog sk Dostávať pripomienky o vlastných položkách
receive notifications about starting entries you are responsible for infolog sk Dostávať pripomienky o začínajúcich položkách, za ktoré ste zodpovední
receive notifications about starting entries you delegated infolog sk Dostávať pripomienky o začínajúcich položkách, ktoré ste delegovali
receive notifications as html-mails infolog sk Dostávať pripomienky ako HTML E-maily
reg. expr. for local ip's<br>eg. ^192.168.1. infolog sk reg. výraz pre lokálne IP adresy<br>napr. ^192\.168\.1\.
reg. expr. for local ip's<br>eg. ^192\.168\.1\. infolog sk reg. výraz pre lokálne IP adresy<br>napr. ^192\.168\.1\.
remark infolog sk Poznámka
remove this link (not the entry itself) infolog sk Odstrániť tento odkaz (nie záznam samotný)
responsible infolog sk Zodpovednosť
responsible open infolog sk Zodpovednosť - otvorené
responsible open and upcoming infolog sk Zodpovednosť - otvorené a blížiace sa
responsible overdue infolog sk Zodpovednosť - meškajúce
responsible upcoming infolog sk Zodpovednosť - blížiace sa
responsible user, priority infolog sk Zodpovedný používateľ, priorita
returns a list / search for records. infolog sk Vráti zoznam / hľadanie záznamov.
rights for the responsible infolog sk Práva pre zodpovednú osobu
same day infolog sk V ten deň
save infolog sk Uložiť
saves the changes made and leaves infolog sk Uloží vykonané zmeny a odíde
saves this entry infolog sk Uloží tento záznam
search infolog sk Hľadať
search for: infolog sk Hľadať (čo):
select infolog sk Vybrať
select a category for this entry infolog sk vyberte kategóriu pre tento záznam
select a price infolog sk Vyberte cenu
select a priority for this task infolog sk Vyberte prioritu pre túto úlohu
select a project infolog sk Vyberte projekt
select a responsible user: a person you want to delegate this task infolog sk vyberte zodpovedného používateľa: toho, na ktorého chcete delegovať túto úlohu
select a typ to edit it's status-values or delete it infolog sk vyberte typ pre úpravu hodnôt stavov, alebo ho odstráňte
select an app to search in infolog sk Vyberte, v ktorej aplikácii chcete hľadať
select an entry to link with infolog sk Vyberte záznam na ktorý chcete vytvoriť odkaz
select to filter by owner infolog sk Filtrovanie podľa vlastníka
select to filter by responsible infolog sk Filtrovanie podľa zodpovedného
sender infolog sk Odosielateľ
set status to done infolog sk Nastaviť stav na hotovo
set status to done for all entries infolog sk Nastaviť stav na hotovo pre všetky položky
sets the status of this entry and its subs to done infolog sk Nastaví stav záznamu a jeho podzáznamov na Hotovo
should infolog show subtasks, -calls or -notes in the normal view or not. you can always view the subs via there parent. infolog sk Má Záznamník zobrazovať Podúlohy, Podhovory alebo Podpoznámky v normálnom zobrazení alebo nie? Vždy ešte môžete zobraziť Podradené záznamy cez ich rodičovský záznam.
should infolog show the links to other applications and/or the file-attachments in the infolog list (normal view when you enter infolog). infolog sk Má Záznamník zobrazovať v zozname odkazy na iné aplikácie a/alebo súbory v prílohe (v normálnom pohľade, keď otvoríte Záznamník)?
should infolog show up on the main screen and with which filter. works only if you dont selected an application for the main screen (in your preferences). infolog sk Zobrazovať Záznamník na hlavnej stránke? Ak áno, s akým filtrom? Táto voľba funguje len v prípade, ak ste už nevybrali konkrétnu aplikáciu pre hlavnú stránku (vo vašich predvoľbách).
should infolog use full names (surname and familyname) or just the loginnames. infolog sk Má Záznamník používať plné mená (meno, priezvisko) alebo iba používateľské mená?
should the infolog list show a unique numerical id, which can be used eg. as ticket id. infolog sk Má sa v zozname Záznamníka zobrazovať jedinečné číselné ID, ktoré môže byť použité napr. ako ID problému?
should the infolog list show the column "last modified". infolog sk Zobrazovať v zozname Záznamníka aj stĺpec "Naposledy zmenené"?
should the infolog list show the percent done only for status ongoing or two separate icons. infolog sk Zobrazovať v zozname Záznamníka percentuálny stav vybavenia len pre stav "prebiehajúci", alebo ako dve samostatné ikony?
should this entry only be visible to you and people you grant privat access via the acl infolog sk Má byť tento záznam viditeľný IBA pre vás a pre ľudí, ktorým pridelíte súkromný prístup cez ACL?
show a column for used and planned times in the list. infolog sk V zozname zobraziť stĺpec pre použité a plánované časy.
show full usernames infolog sk Zobraziť plné používateľské mená
show in the infolog list infolog sk Zobraziť v zozname Záznamníka
show last modified infolog sk Zobraziť čas poslednej úpravy
show status and percent done separate infolog sk Zobraziť samostatne stav záznamu a percentuálny stav vybavenia
show ticket id infolog sk Zobraziť ID problému
show times infolog sk Zobraziť časy
small view infolog sk Zmenšený pohľad
start a new search, cancel this link infolog sk Spustiť nové vyhľadávanie, zrušiť tento odkaz
startdate infolog sk Dátum začatia
startdate enddate infolog sk Dátum začatia Termín splnenia
startdate for new entries infolog sk Dátum začatia pre nové záznamy
starting %1 infolog sk Začiatok %1
startrecord infolog sk Prvý záznam
status infolog sk Stav
status ... infolog sk Stav...
sub infolog sk Podradené
sub-entries become subs of the parent or main entries, if there's no parent infolog sk Podzáznamy sa dostanú pod rodičovské alebo hlavné záznamy, ak nie je k dispozícii rodičovský.
subject infolog sk Predmet
sum infolog sk Suma
task infolog sk Úloha
tasks of infolog sk Úlohy pre
template infolog sk Šablóna
test import (show importable records <u>only</u> in browser) infolog sk Test importu (zobraz importovateľné záznamy <u>iba</u> v prehliadači)
the name used internaly (<= 10 chars), changeing it makes existing data unavailible infolog sk vnútorne používané meno (<= 10 znakov), jeho zmena zneprístupní existujúce údaje
the name used internaly (<= 20 chars), changeing it makes existing data unavailible infolog sk vnútorne používané meno (<= 20 znakov), jeho zmena zneprístupní existujúce údaje
the text displayed to the user infolog sk Text zobrazený používateľovi
this is the filter infolog uses when you enter the application. filters limit the entries to show in the actual view. there are filters to show only finished, still open or futures entries of yourself or all users. infolog sk Toto je filter, ktorý Záznamník používa keď otvoríte aplikáciu. Filtre obmedzujú, ktoré záznamy sú zobrazené vo vybranom pohľade. Sú tu filtre na zobrazenie iba dokončených záznamov, alebo naopak -ešte otvorených, alebo budúcich záznamov, a to buď Vaších, alebo všetkých používateľov.
til when should the todo or phonecall be finished infolog sk Dokiaľ sa nedokončí Úloha alebo Telefonát
times infolog sk Časy
to many might exceed your execution-time-limit infolog sk Ak priveľa, môže dôjsť k prekročeniu Vašich obmedzení ohľadom povoleného času behu aplikácie
to what should the startdate of new entries be set. infolog sk Ako má byť nastavený dátum začatia u nových záznamov?
today infolog sk Dnes
todays date infolog sk Dnešný dátum
todo infolog sk Úloha
translation infolog sk Preklad
typ infolog sk Typ
typ '%1' already exists !!! infolog sk Typ '%1' už existuje !!!
type infolog sk Typ
type ... infolog sk Typ ...
type of customfield infolog sk Typ používateľského poľa
type of the log-entry: note, phonecall or todo infolog sk Typ záznamu: Poznámka, Telefonát alebo Úloha
unlink infolog sk Zruš odkaz
upcoming infolog sk Blížiace sa
urgency infolog sk Súrnosť
urgent infolog sk Súrna
used time infolog sk Použitý čas
valid path on clientside<br>eg. \\server\share or e:\ infolog sk platná cesta na strane klienta<br>napr. \\Server\Share alebo e:\
valid path on clientside<br>eg. \servershare or e: infolog sk platná cesta na strane klienta<br>napr. \\Server\Share alebo e:\
valid path on clientside<br>eg. servershare or e: infolog sk platná cesta na strane klienta<br>napr. \\Server\Share alebo e:\
values for selectbox infolog sk Hodnoty pre výberovú ponuku
view all subs of this entry infolog sk Zobraziť všetky podradené záznamy
view other subs infolog sk Zobraziť ostatné Podradené záznamy
view parent infolog sk Zobraziť Rodičovský záznam
view subs infolog sk Zobraziť Podradené záznamy
view the parent of this entry and all his subs infolog sk Zobraziť Rodičovský záznam a všetky jeho podradené
view this linked entry in its application infolog sk Zobraziť tento odkaz v jeho aplikácii
when should the todo or phonecall be started, it shows up from that date in the filter open or own open (startpage) infolog sk keď má byť začatá Úloha alebo Telefonát, ukáže sa akoby od tohto dátumu vo filtri "otvorené" alebo "moje otvorené" (úvodná stránka)
which additional fields should the responsible be allowed to edit without having edit rights?<br />status, percent and date completed are always allowed. infolog sk Ktoré prídavné položky majú byť zodpovednej osobe prístupné pre úpravy, a to aj bez pridelených práv na úpravy?<br />Stav, percentuálny stav vybavenia a dátum dokončenia sú prístupné vždy.
which implicit acl rights should the responsible get? infolog sk Ktoré implicitné ACL oprávnenia má zodpovedná osoba obdržať automaticky?
which types should the calendar show infolog sk Aké typy má kalendár ukazovať
whole query infolog sk Celý dopyt
will-call infolog sk Zavolá
write (add or update) a record by passing its fields. infolog sk Zapísať (pridať alebo upraviť) záznam použitím príslušných položiek.
yes - close infolog sk Áno - Zavrieť
yes - close including sub-entries infolog sk Áno - Zavrieť vrátane podzáznamov
yes - delete infolog sk Áno - Odstrániť
yes - delete including sub-entries infolog sk Áno - Odstrániť vrátane podzáznamov
yes, noone can purge deleted items infolog sk Áno, nikto nemôže vyčistiť odstránené položky
yes, only admins can purge deleted items infolog sk Áno, iba správcovia môžu vyčistiť odstránené položky
yes, with larger fontsize infolog sk Áno, s väčším písmom
yes, with purging of deleted items possible infolog sk Áno, s možnosťou vyčistenia odstránených položiek
you can choose a categorie to be preselected, when you create a new infolog entry infolog sk Môžete si vybrať, ktorá kategória bude predvybraná, keď budete vytvárať nový záznam Záznamníka.
you can't delete one of the stock types !!! infolog sk Nemôžete odstrániť východiskový typ!!
you have entered an invalid ending date infolog sk Zadali ste chybný termín ukončenia
you have entered an invalid starting date infolog sk Zadali ste chybný dátum začatia
you have to enter a name, to create a new typ!!! infolog sk Ak chcete vytvoriť nový typ, musíte zadať jeho názov!!
you must enter a subject or a description infolog sk Musíte zadať predmet alebo opis
your database is not up to date (%1 vs. %2), please run %3setup%4 to update your database. infolog sk Vaša databáza je neaktuálna (%1 oproti %2), prosím spustite %3setup%4 aby sa zaktualizovala.

361
infolog/lang/egw_sl.lang Normal file
View File

@ -0,0 +1,361 @@
%1 days in advance infolog sl %1 dni vnaprej
%1 deleted infolog sl %1 izbrisan
%1 deleted by %2 at %3 infolog sl %1 izbrisal %2 dne %3
%1 modified infolog sl %1 spremenjen
%1 modified by %2 at %3 infolog sl %1 spremenil %2 dne %3
%1 records imported infolog sl %1 zapisov uvoženih
%1 records read (not yet imported, you may go %2back%3 and uncheck test import) infolog sl %1 zapisov prebranih (niso še uvoženi, zato lahko greste %2nazaj%3 in jih odznačite za prenos)
%1 you are responsible for is due at %2 infolog sl %1, za katerega ste odgovorni, poteče %2
%1 you are responsible for is starting at %2 infolog sl %1, za katerega ste odgovorni, se začne %2
%1 you delegated is due at %2 infolog sl %1, katerega ste dodelili, poteče %2
%1 you delegated is starting at %2 infolog sl %1, katerega ste dodelili, se začne %2
- subprojects from infolog sl - Podprojekt iz
0% infolog sl 0%
10% infolog sl 10%
100% infolog sl 100%
20% infolog sl 20%
30% infolog sl 30%
40% infolog sl 40%
50% infolog sl 50%
60% infolog sl 60%
70% infolog sl 70%
80% infolog sl 80%
90% infolog sl 90%
<b>file-attachments via symlinks</b> instead of uploads and retrieval via file:/path for direct lan-clients infolog sl <b>priponke s simbolično povezavo</b> namesto prenosa na način file://path za neposredne LAN kliente
a short subject for the entry infolog sl Kratek predmet za vnos
abort without deleting infolog sl Prekliči brez brisanja
accept infolog sl Sprejmi
action infolog sl Dejanje
actual date and time infolog sl Dejanski datum in čas
add infolog sl Dodaj
add a file infolog sl Dodaj datoteko
add a new entry infolog sl Dodaj nov zapis
add a new note infolog sl Dodaj novo opombo
add a new phonecall infolog sl Dodaj nov telefonski klic
add a new sub-task, -note, -call to this entry infolog sl Dodaj novo podnalogo, opombo, klic za ta zapis
add a new todo infolog sl Dodaj novo opravilo
add file infolog sl Dodaj datoteko
add sub infolog sl Dodaj namestnika
add timesheet entry infolog sl Dodaj vnos timeheet
add: infolog sl Dodaj:
all infolog sl Vse
all links and attachments infolog sl Vse povezave in priponke
allows to set the status of an entry, eg. set a todo to done if it's finished (values depend on entry-typ) infolog sl Dovoli spreminjanje statusov zapisov, npr. določi Opravilo kot končano, če se konča (vrednost je odvisna od tipa vhoda)
apply the changes infolog sl Uveljavi spremembe
archive infolog sl Arhiv
are you shure you want to delete this entry ? infolog sl Ali ste prepričani, da želite izbrisati ta zapis?
attach a file infolog sl Pripni datoteko
attach file infolog sl Pripni datoteko
attention: no contact with address %1 found. infolog sl Pozor: Stik z naslovom %1 ni bil najden.
back to main list infolog sl Nazaj na glavni seznam
billed infolog sl Obračunan
both infolog sl Oba
call infolog sl Klic
can be used to show further infolog types in the calendar or limit it to show eg. only tasks. infolog sl Lahko se uporabi za prikaz nadaljnjih vrst Infodnevnika v koledarju ali za omejitev prikaza npr. samp opravil.
cancel infolog sl Prekliči
cancelled infolog sl Preklicano
categories infolog sl Kategorije
category infolog sl Kategorija
change the status of an entry, eg. close it infolog sl Sprememba statusa vnosa, npr. zapiranje
charset of file infolog sl Nabor znakov datoteke
check to set startday infolog sl Označite za postavitev začetnega dneva
check to specify custom contact infolog sl Potrdite za določitev kontakta po meri
click here to create the link infolog sl Kliknite tukaj da ustvarite povezavo
click here to start the search infolog sl Kliknite tukaj za začetek iskanja
close infolog sl Zapri
comment infolog sl Opomba
completed infolog sl Končano
configuration infolog sl Konfiguracija
confirm infolog sl Potrdi
contact infolog sl Kontakt
copy your changes to the clipboard, %1reload the entry%2 and merge them. infolog sl Kopira spremembe v odložišče, %1 ponovno naloži vnos %2 in jih zlije.
create new links infolog sl Ustvari nove povezave
creates a new field infolog sl Ustvari novo polje
creates a new status with the given values infolog sl Ustvari nov status s podanimi vrednostmi
creates a new typ with the given name infolog sl Ustvari nov tip s podanim imenom
creation infolog sl Datum ustvarjenja
csv-fieldname infolog sl CSV-Ime polja
csv-filename infolog sl CSV-Ime datoteke
csv-import common sl CSV-Uvoz
custom infolog sl Lasten
custom contact-address, leave empty to use information from most recent link infolog sl Lastni kontaktni naslov (pustite prazno za uporabo informacije iz najnovejše povezave)
custom contact-information, leave emtpy to use information from most recent link infolog sl Lastne kontaktne informacije (pustite prazno za uporabo informacije iz najnovejše povezave)
custom fields infolog sl Lastna polja
custom fields, typ and status common sl Lastna polja, tip in status
custom regarding infolog sl Lastno glede na
custom status for typ infolog sl Lastni satus za tip
customfields infolog sl Lastna polja
date completed infolog sl Datum zaključka
date completed (leave it empty to have it automatic set if status is done or billed) infolog sl Datum zaključka (pustite prazno, da se nastavi samodejno, če je status nastavljen na Končano ali Obračunano)
datecreated infolog sl Datum ustvarjanja
dates, status, access infolog sl Datumi, status, dostop
days infolog sl Dnevi
default filter for infolog infolog sl Napačen filter za InfoDnevnik
default status for a new log entry infolog sl Privzeti status za nov zapis v dnevnik
delegated infolog sl Dodeljeni
delegated open infolog sl Dodeljeni odprti
delegated overdue infolog sl Dodeljeni pretečeni
delegated upcomming infolog sl Dodeljeni prihajajoči
delegation infolog sl Pooblastitev
delete infolog sl Izbriši
delete one record by passing its id. infolog sl Izbriši zapis s podano šifro.
delete the entry infolog sl Izbriši vnos
delete this entry infolog sl Izbriši ta vnos
delete this entry and all listed sub-entries infolog sl Izbriši ta vnos in podrejene vnose
deleted infolog sl Izbrisano
deletes the selected typ infolog sl Izbriše izbrani tip
deletes this field infolog sl Izbriše to polje
deletes this status infolog sl Izbriše ta status
description infolog sl Opis
determines the order the fields are displayed infolog sl ugotovi vrstni red polj, ki so prikazana
disables a status without deleting it infolog sl Onemogoči status brez brisanja
do you want a confirmation of the responsible on: accepting, finishing the task or both infolog sl Ali želite potrdilo odgovornosti za: sprejemanje, končanje naloge ali oboje?
do you want a notification, if items get assigned to you or assigned items get updated? infolog sl Želite prejeti obvestilo, če je element dodeljen vam ali se dodeljeni elemeni posodobijo?
do you want a notification, if items you are responsible for are about to start? infolog sl Želite prejeti obvestilo, če se bodo elementi, za katere ste odgovorni kmalu začnejo?
do you want a notification, if items you are responsible for are due? infolog sl Želite prejeti obvestilo, če so elementi, za keter ste odgovorni pretečeni?
do you want a notification, if items you created get updated? infolog sl Želite prejeti obvestilo, če so elementi, ki ste jih ustvarili posodobljeni?
do you want a notification, if items you delegated are about to start? infolog sl Želite prejeti obvestilo, če se bodo elementi, za katere ste odgovorni kmalu začnejo?
do you want a notification, if items you delegated are due? infolog sl Želite prejeti obvestilo, če so elementi, za katere ste odgovorni pretečeni?
do you want to receive notifications as html-mails or plain text? infolog sl Želite prejemati obvestila v obliki HTML ali kot navadno besedilo?
don't show infolog infolog sl Ne prikaži InfoDnevnika
done infolog sl Končano
download infolog sl Prenos
duration infolog sl Trajanje
each value is a line like <id>[=<label>] infolog sl vsaka vrednost je vrstice kot <šifra>[=<oznaka>]
edit infolog sl Uredi
edit or create categories for ingolog infolog sl Uredi ali ustvari kategorije za InfoDnevnik
edit rights (full edit rights incl. making someone else responsible!) infolog sl Uredi pravice (vse pravice, vključno z vnosom namestnika!)
edit status infolog sl Uredi status
edit the entry infolog sl Uredi vnos
edit this entry infolog sl Uredi ta vnos
empty for all infolog sl Prazno za vse
enddate infolog sl Končni datum
enddate can not be before startdate infolog sl Končni datum ne more biti pred datumom začetka
enter a custom contact, leave empty if linked entry should be used infolog sl Vnesite lastni kontakt (pustite prazno, če naj bo uporabljen zapis povezave)
enter a custom phone/email, leave empty if linked entry should be used infolog sl Vpišite telefon/e-naslov (pustite prazno, če naj bo uporabljen zapis povezave)
enter a textual description of the log-entry infolog sl Vpišite opis vnosa v dnevnik
enter the query pattern infolog sl Vnesite vzorec poizvedbe
entry and all files infolog sl Zapis in vse datoteke
error: saving the entry infolog sl Napaka pri shranjevanju vnosa
error: the entry has been updated since you opened it for editing! infolog sl Napaka: vnos je bil spremenjen odkar ste ga odprli za urejanje!
existing links infolog sl Obstoječe povezave
fax infolog sl Faks
fieldseparator infolog sl Ločilo polj
finish infolog sl Konec
for which types should this field be used infolog sl Za ketere tipe naj bo to polje uporabljeno
from infolog sl Od
general infolog sl Splošno
group owner for infolog sl Skupinski lastnik za
high infolog sl Visoko
history logging infolog sl Beleenje zgodovine
history logging and deleting of items infolog sl Beleenje zgodovine in brisanje elementov
id infolog sl Šifra
if a type has a group owner, all entries of that type will be owned by the given group and not the user who created it! infolog sl Če ima vrsta skupinskega lastnika, bo lastnik vseh vnosov te vrste dana skupina in NE uporabnik, ki jo je ustvaril!
if not set, the line with search and filters is hidden for less entries then "max matches per page" (as defined in your common preferences). infolog sl Če ni nastavljen, je vrstica za iskanje in filtriranje skrita za manj vnosov kakor "maksimalno število zadetkov na stran" (kakor je definirano v vaših nastavitvah.
import infolog sl Uvoz
import next set infolog sl Uvozi naslednji niz
info log common sl InfoDnevnik
infolog common sl InfoDnevnik
infolog - delete infolog sl InfoDnevnik - Izbriši
infolog - edit infolog sl InfoDnevnik - Uredi
infolog - import csv-file infolog sl InfoDnevnik - Uvoz CSV-Datoteke
infolog - new infolog sl InfoDnevnik - Nov
infolog - new subproject infolog sl InfoDnevnik - Nov podprojekt
infolog - subprojects from infolog sl InfoDnevnik - Podprojekt od
infolog entry deleted infolog sl Vnos InfoDnevnika je bil izbrisan
infolog entry saved infolog sl Vnos InfoDnevnika je bil shranjen
infolog filter for the main screen infolog sl Filter InfoDnevnika za uvodni zaslon
infolog list infolog sl Seznam InfoDnevnika
infolog preferences common sl Nastavitve InfoDnevnika
infolog-fieldname infolog sl Info Log - ime polja
invalid filename infolog sl Napačno ime datoteke
label<br>helptext infolog sl Oznaka<br>Pomoč
last changed infolog sl Zadnjič spremenjeno
last modified infolog sl Zadnjič prilagojeno
leave it empty infolog sl Pusti prazno
leave without saveing the entry infolog sl Zapusti brez shranjevanja zapisa
leaves without saveing infolog sl Zapusti brez shranjevanja
length<br>rows infolog sl Dolžina<br>Vrstice
link infolog sl Povezava
links infolog sl Povezave
links of this entry infolog sl Povezave tega zapisa
list all categories infolog sl Seznam vseh kategorij.
list no subs/childs infolog sl Seznam nima podrejenih seznamov
location infolog sl Lokacija
longer textual description infolog sl Daljši opis
low infolog sl Nizko
max length of the input [, length of the inputfield (optional)] infolog sl Največja dolžina vhoda [dolžina vhodnega polja (neobvezno)]
name must not be empty !!! infolog sl Ime ne sme biti prazno!!!
name of new type to create infolog sl Ime novega tipa, ki ga ustvarjate
never hide search and filters infolog sl Nikoli skriti iskanja in filtrov
new %1 infolog sl Nov %1
new %1 created by %2 at %3 infolog sl Nov %1 ustvaril %2 dne %3
new name infolog sl Novo ime
new search infolog sl Novo iskanje
no - cancel infolog sl Ne - Prekliči
no describtion, links or attachments infolog sl Brez opisa, povezav ali priponk
no details infolog sl Brez podrobnosti
no entries found, try again ... infolog sl Noben zapis ni bil najden, poskusite znova ...
no filter infolog sl Brez filtra
no links or attachments infolog sl Brez povezav ali prilog
nonactive infolog sl Neaktivno
none infolog sl Noben
normal infolog sl Običajno
not infolog sl Ne
not assigned infolog sl Nedodeljen
not-started infolog sl Ne začet
note infolog sl Opomba
number of records to read (%1) infolog sl Število zapisov za branje (%1)
number of row for a multiline inputfield or line of a multi-select-box infolog sl Število vrstic vnosnega polja ali vrstica izbirnega polja
offer infolog sl Ponudba
one day after infolog sl En dan po
one day in advance infolog sl En dan vnaprej
ongoing infolog sl Nadaljevalni
only for details infolog sl Samo za podrobnosti
only if i get assigned or removed infolog sl Samo če sem dodeljen ali odstranjen
only the attachments infolog sl Samo priloge
only the links infolog sl Samo povezave
open infolog sl Odprt
optional note to the link infolog sl Neobvezna opomba za povezavo
order infolog sl Vrstni red
overdue infolog sl Zapadel
own infolog sl Lastni
own open infolog sl Lastni odprt
own overdue infolog sl Lastni zapadel
own upcoming infolog sl Lastni prihajajoč
path on (web-)serverside<br>eg. /var/samba/share infolog sl Pot na (web-)stran strežnika<br>npr. /var/samba/Share
path to user and group files has to be outside of the webservers document-root!!! infolog sl Pot do uporabnikov in skupine datotek MORA BITI ZUNAJ glavnega direktorija spletnega strežnika!
pattern for search in addressbook infolog sl Vzorec za iskanje po adresarju
pattern for search in projects infolog sl Vzorec za iskanje po Projektih
percent completed infolog sl Odstotek končanega
permission denied infolog sl Dostop zavrnjen
phone infolog sl Telefonski klic
phone/email infolog sl Telefon/E-naslov
phonecall infolog sl Telefonski klic
planned infolog sl Planirano
planned time infolog sl Planirani čas
price infolog sl Cena
priority infolog sl Prioriteta
private infolog sl Zasebno
project infolog sl Projekt
project settings: price, times infolog sl Nastavitve projekta: cena, časi
re: infolog sl Odg:
read one record by passing its id. infolog sl Preberi zapis s podano šifro.
read rights (default) infolog sl Pravice branja (privzeto)
receive notifications about due entries you are responsible for infolog sl Prejem obvestila o pretečenih vnosih, za ketere ste odgovorni
receive notifications about due entries you delegated infolog sl Prejem obvestila o pretečenih vnosih, ketere ste dodelili
receive notifications about items assigned to you infolog sl Prejem obvestila o elementih, dodeljenih vam
receive notifications about own items infolog sl Prejem obvestila o lastnih elementih
receive notifications about starting entries you are responsible for infolog sl Prejem obvestila o začetku vnosov, za ketere ste odgovorni
receive notifications about starting entries you delegated infolog sl Prejem obvestila o začetku vnosov, ki ste jih dodelili
receive notifications as html-mails infolog sl Prejem obvestila kot sporočilo HTML
reg. expr. for local ip's<br>eg. ^192.168.1. infolog sl Regularni izraz za lokalne IP-je<br>npr. ^192\.168\.1\.
reg. expr. for local ip's<br>eg. ^192\.168\.1\. infolog sl Regularni izraz za lokalne IP-je<br>npr. ^192\.168\.1\.
remark infolog sl Opazka
remove this link (not the entry itself) infolog sl Odstrani povezavo (ne pa tudi zapisa)
responsible infolog sl Odgovoren
responsible open infolog sl Odgovoren odprt
responsible overdue infolog sl Odgovoren pretekel
responsible upcoming infolog sl Odgovoren prihajajoč
responsible user, priority infolog sl Odgovoren uporabnik, prioriteta
returns a list / search for records. infolog sl Vrne seznam / iskanje zapisov.
rights for the responsible infolog sl Pravice odgovornega
same day infolog sl Isti dan
save infolog sl Shrani
saves the changes made and leaves infolog sl Shrani spremembe in zapusti
saves this entry infolog sl Shrani ta zapis
search infolog sl Poišči
search for: infolog sl Išči:
select infolog sl Izberi
select a category for this entry infolog sl Izberite kategorijo za ta zapis
select a price infolog sl Izberite ceno
select a priority for this task infolog sl Izberite prioriteto za to nalogo
select a project infolog sl Izberite projekt
select a responsible user: a person you want to delegate this task infolog sl Izberite odgovornega uporabnika: osebo, ki bo lahko urejala to nalogo
select a typ to edit it's status-values or delete it infolog sl Izberite tip za urejanje vrednosti statusa ali brisanje
select an app to search in infolog sl Izberite aplikacijo za iskanje
select an entry to link with infolog sl Izberite zapis s katerim želite povezati
select to filter by owner infolog sl Izberite za filtriranje po lastniku
select to filter by responsible infolog sl Izberite za filtriranje po odgovornosti
sets the status of this entry and its subs to done infolog sl Nastavi status tega vnosa in podrejenih vnosov na končano
should infolog show subtasks, -calls or -notes in the normal view or not. you can always view the subs via there parent. infolog sl Ali naj InfoDnevnik prikaže podrejena opravila - klice ali opombe - v splošnem oknu? Podrejena opravila lahko vedno vidite preko njihovih nadrejenih opravil?
should infolog show the links to other applications and/or the file-attachments in the infolog list (normal view when you enter infolog). infolog sl Ali naj InfoDnevnik prikaže povezave do drugih aplikacij in/ali datotečne povezave v seznamu InfoDnevnik (splošni pogled, ko vstopite v InfoDnevnik)?
should infolog show up on the main screen and with which filter. works only if you dont selected an application for the main screen (in your preferences). infolog sl Ali naj se InfoDnevnik prikaže v uvodnem zaslonu in s katerim filtrom. Deluje samo, če v nastavitveh za uvodni zaslon ne izberete aplikacije.
should infolog use full names (surname and familyname) or just the loginnames. infolog sl Ali naj InfoDnevnik uporabi polna imena (ime in priimek) ali samo uporabniška imena?
should the infolog list show a unique numerical id, which can be used eg. as ticket id. infolog sl Ali naj se InfoDnevnik prikaže številčno šifro, ki se lahko uporabi kot šifra opravila?
should the infolog list show the column "last modified". infolog sl Ali naj se InfoDnevnik prikaže stolpec "Zadnjič spremenjeno"?
should the infolog list show the percent done only for status ongoing or two separate icons. infolog sl Ali naj se InfoDnevnik prikaže odstotek opravljenega samo za status nadaljevalnega ali dve ločeni ikoni?
should this entry only be visible to you and people you grant privat access via the acl infolog sl Naj bo ta zapis viden samo vam in ljudem, ki jim dodelite pravice preko ACL-jev?
show a column for used and planned times in the list. infolog sl V seznamu prikaže stolpec za uporabljene in planirane čase.
show full usernames infolog sl Prikaži polna uporabniška imena
show in the infolog list infolog sl Prikaži v seznamu InfoDnevnika
show last modified infolog sl Prikaži zadnjič spremenjeno
show status and percent done separate infolog sl Prikaži status in odstotek opravljenega ločeno
show ticket id infolog sl Prikaži šifro opravila
show times infolog sl Prikaži čase
small view infolog sl Skrčen pogled
start a new search, cancel this link infolog sl Zaženi novo iskanje, prekini to povezavo
startdate infolog sl Začetni datum
startdate enddate infolog sl Začetni datum Končni datum
startdate for new entries infolog sl Začetni datum za nove vnose
startrecord infolog sl Začetni zapis
status infolog sl Status
status ... infolog sl Status ...
sub infolog sl Podrejeni
sub-entries become subs of the parent or main entries, if there's no parent infolog sl Podrejeni vnosi postanejo podrejeni staršu ali glavnemu vnosu, če ni starša
subject infolog sl Predmet
task infolog sl Opravila
template infolog sl Predloga
test import (show importable records <u>only</u> in browser) infolog sl Test uvoza (pokaži pomemebmne zapise <u>samo</u> v brskalniku)
the name used internaly (<= 10 chars), changeing it makes existing data unavailible infolog sl Ime, ki se uporablja interno (manj kot 10 znakov). Sprememba tega bo povzročila, da obstoječ podatek ne bo na voljo.
the name used internaly (<= 20 chars), changeing it makes existing data unavailible infolog sl Ime, ki se uporablja interno (manj kot 20 znakov). Sprememba tega bo povzročila, da obstoječ podatek ne bo na voljo.
the text displayed to the user infolog sl besedilo, ki se prikaže uporabniku
this is the filter infolog uses when you enter the application. filters limit the entries to show in the actual view. there are filters to show only finished, still open or futures entries of yourself or all users. infolog sl To je filter, ki ga InfoDnevnik uporablja, ko vstopite v aplikacijo. Filter omejuje število prikazanih zapisov v trenutnem pogledu. Obstajajo filtri, ki prikazujejo samo dokončane, še odprte ali zapise v prihodnosti, vaše ali tuje zapise.
til when should the todo or phonecall be finished infolog sl Do kdaj naj bo opravilo ali telefonski klic opravljen
times infolog sl Časi
to many might exceed your execution-time-limit infolog sl Preveč lahko preseže čas izvajanja
to what should the startdate of new entries be set. infolog sl Na kaj naj bo nastavljen začetni datum novega vnosa?
today infolog sl Danes
todays date infolog sl Današnji datum
todo infolog sl Opravila
translation infolog sl Prevod
typ infolog sl Tip
typ '%1' already exists !!! infolog sl Tip '%1' že obstaja!
type infolog sl Tip
type ... infolog sl Tip ...
type of customfield infolog sl Vrsta polja po meri
type of the log-entry: note, phonecall or todo infolog sl Tip log zapisa: opomba, telefonski klic ali Opravilo
unlink infolog sl Odstrani povezavo
upcoming infolog sl Prihajajoč
urgency infolog sl Nujnost
urgent infolog sl Nujno
used time infolog sl Porabljen čas
valid path on clientside<br>eg. \\server\share or e:\ infolog sl Veljavna pot na strani odjemalca<br>npr. \\Strežnik\Skupno ali e:\
valid path on clientside<br>eg. \servershare or e: infolog sl Veljavna pot na strani odjemalca<br>npr. \\Strežnik\Skupno ali e:\
valid path on clientside<br>eg. servershare or e: infolog sl Veljavna pot na strani odjemalca<br>npr. \\Strežnik\Skupno ali e:\
values for selectbox infolog sl Vrednosti za izbirno polje
view all subs of this entry infolog sl Poglej vse podrejene zapise
view other subs infolog sl Poglej ostale podrejene zapise
view parent infolog sl Poglej nadrejenega
view subs infolog sl Pogled podrejenih zapisov
view the parent of this entry and all his subs infolog sl Poglej nadrejenega in vse njegove podrejene zapise
view this linked entry in its application infolog sl Poglej ta povezani vnos v njegovi aplikaciji
when should the todo or phonecall be started, it shows up from that date in the filter open or own open (startpage) infolog sl Kdaj naj se Opravila ali Telefonski klic zaženejo?
which additional fields should the responsible be allowed to edit without having edit rights?<br />status, percent and date completed are always allowed. infolog sl Katera dodatna polja lahko odgovorni ureja brez pravic za urejanje?<br />Status, odstotek in datum opravljenega so vedno dovoljeni.
which implicit acl rights should the responsible get? infolog sl Katere pravice ACL naj dobi odgovorni?
which types should the calendar show infolog sl Katere vrste naj koledar prikaže
whole query infolog sl Celotno povpraševanje
will-call infolog sl Bodo klicani
write (add or update) a record by passing its fields. infolog sl Zapišite (dodajte ali posodobite) zapis s podanimi vrednostmi.
yes - delete infolog sl Da - Izbriši
yes - delete including sub-entries infolog sl Da - Izbriši, vključno s podrejenimi vnosi
yes, noone can purge deleted items infolog sl Da, nihče ne sme odstraniti izbrisanih elementov
yes, only admins can purge deleted items infolog sl Da, samo administratorji lahko odstranijo izbrisana elemente
yes, with larger fontsize infolog sl Da, z večjo pisavo
yes, with purging of deleted items possible infolog sl Da, z odstranitvijo vseh možnih elementov
you can't delete one of the stock types !!! infolog sl Ne morete izbrisati glavnega tipa!
you have entered an invalid ending date infolog sl Vnesli ste napačen končni datum
you have entered an invalid starting date infolog sl Vnesli ste napačen začetni datum
you have to enter a name, to create a new typ!!! infolog sl Vpisati morate ime za imenovanje novega tipa
you must enter a subject or a description infolog sl Vpisati morate predmet ali opis
your database is not up to date (%1 vs. %2), please run %3setup%4 to update your database. infolog sl Vaša podatkovna baza NI obnovljena (%1 vs. %2), prosimo poženite %3 nastavitev%4 za prenovo vaše podatkovne baze.

316
infolog/lang/egw_sv.lang Normal file
View File

@ -0,0 +1,316 @@
%1 records imported infolog sv %1 poster importerade
%1 records read (not yet imported, you may go %2back%3 and uncheck test import) infolog sv %1 poster lästa (ännu inte importerade, gå %2tillbaka%3 och avmarkera Testimport)
- subprojects from infolog sv - Underprojekt till
0% infolog sv 0%
10% infolog sv 10%
100% infolog sv 100%
20% infolog sv 20%
30% infolog sv 30%
40% infolog sv 40%
50% infolog sv 50%
60% infolog sv 60%
70% infolog sv 70%
80% infolog sv 80%
90% infolog sv 90%
<b>file-attachments via symlinks</b> instead of uploads and retrieval via file:/path for direct lan-clients infolog sv <b>Filbilagor via symlänk eller genväg</b> istället för uppladdning och hämtning via file:/sökväg för lokala användare
a short subject for the entry infolog sv Ett kort ämne för post
abort without deleting infolog sv Avbryt utan att radera
accept infolog sv Acceptera
action infolog sv Åtgärd
actual date and time infolog sv Faktiskt datum och tid
add infolog sv Lägg till
add a file infolog sv Lägg till fil
add a new entry infolog sv Lägg till Post
add a new note infolog sv Lägg till Anteckning
add a new phonecall infolog sv Lägg till Samtal
add a new sub-task, -note, -call to this entry infolog sv Lägg till ny Under- Uppgift, Anteckning eller Samtal till post
add a new todo infolog sv Lägg till Uppgift
add file infolog sv Lägg till fil
add sub infolog sv Lägg till under
add timesheet entry infolog sv Lägg till tidredovisnings post
add: infolog sv Lägg till:
all infolog sv Alla
all links and attachments infolog sv Alla länkar och bilagor
allows to set the status of an entry, eg. set a todo to done if it's finished (values depend on entry-typ) infolog sv Tillåter statusändring på post, ex. Sätt uppgift till färdig när klar.
apply the changes infolog sv Spara ändringar
are you shure you want to delete this entry ? infolog sv Vill du verkligen radera posten?
attach a file infolog sv Bifoga fil
attach file infolog sv Bifoga fil
attension: no contact with address %1 found. infolog sv Ingen kontakt med adressen %1 hittades
back to main list infolog sv Tillbaks till huvudlistan
billed infolog sv Fakturerad
both infolog sv Båda
call infolog sv Samtal
cancel infolog sv Avbryt
cancelled infolog sv Avbruten
categories infolog sv Kategorier
category infolog sv Kategori
change the status of an entry, eg. close it infolog sv Ändra status på post, t.ex. stäng den
charset of file infolog sv Filens teckenuppsättning
check to set startday infolog sv Markera för att sätta startdag
check to specify custom contact infolog sv Markera för att ange annan kontakt
click here to create the link infolog sv Klicka för att skapa länk
click here to start the search infolog sv Klicka för att starta sökning
close infolog sv Stäng
comment infolog sv Kommentar
completed infolog sv Färdig
configuration infolog sv Alternativ
confirm infolog sv Bekräfta
contact infolog sv Kontakt
copy your changes to the clipboard, %1reload the entry%2 and merge them. infolog sv Kopiera dina ändringar till urklipp, %1 ladda om posten och migrera.
create new links infolog sv Skapa länkar
creates a new field infolog sv Skapar fält
creates a new status with the given values infolog sv Skapar ett status med givet värde
creates a new typ with the given name infolog sv Skapar en typ med namnet
creation infolog sv Skapad
csv-fieldname infolog sv CSV fältnamn
csv-filename infolog sv CSV filnamn
csv-import common sv CSV import
custom infolog sv Anpassad
custom contact-address, leave empty to use information from most recent link infolog sv Anpassade kontaktadresser, lämna tom för att använda info från senaste länk
custom contact-information, leave emtpy to use information from most recent link infolog sv Anpassade kontaktadresser, lämna tom för att använda info från senaste länk
custom fields infolog sv Anpassade fält
custom fields, typ and status common sv Anpassade fält, typ och status
custom regarding infolog sv Anpassa beträffande
custom status for typ infolog sv Anpassad status för typ
customfields infolog sv Anpassat fält
date completed infolog sv Datum färdigt
date completed (leave it empty to have it automatic set if status is done or billed) infolog sv Datum färdigt (lämnas tomt för att automatiskt fyllas i vid färdigställande eller fakturering).
datecreated infolog sv Skapad datum
dates, status, access infolog sv Datum, status, åtkomst
days infolog sv Dagar
default filter for infolog infolog sv Standard filter för InfoLogg
default status for a new log entry infolog sv Standard status för nya loggar
delegation infolog sv Delegering
delete infolog sv Radera
delete one record by passing its id. infolog sv Ange ID för att radera post
delete the entry infolog sv Radera post
delete this entry infolog sv Radera posten
delete this entry and all listed sub-entries infolog sv Radera posten och alla under
deletes the selected typ infolog sv Raderar valda typer
deletes this field infolog sv Raderar fältet
deletes this status infolog sv Raderar denna status
description infolog sv Beskrivning
determines the order the fields are displayed infolog sv Bestämmer i vilken ordning fält ska visas
disables a status without deleting it infolog sv Inaktivera status utan att radera
do you want a confirmation of the responsible on: accepting, finishing the task or both infolog sv Vill du få bekräftelse från ansvarig angående acceptering, avslut eller båda?
do you want to see custom infolog types in the calendar? infolog sv Vill du se anpassade InfoLogg typer i kalendern?
don't show infolog infolog sv Visa INTE InfoLogg
done infolog sv Färdig
download infolog sv Ladda ner
duration infolog sv Varaktighet
each value is a line like <id>[=<label>] infolog sv Varje värde är en rad med formatet <id>[=<namn>]
edit infolog sv Ändra
edit or create categories for ingolog infolog sv Ändra eller skapa kategori för InfoLogg
edit rights (full edit rights incl. making someone else responsible!) infolog sv Skrivrättigheter (fulla ändringsrättingheter inkl. delegering)
edit status infolog sv Ändra status
edit the entry infolog sv Ändra post
edit this entry infolog sv Ändra posten
empty for all infolog sv Tom för alla
enddate infolog sv Slutdatum
enddate can not be before startdate infolog sv Slutdatum kan inte vara före startdatum
enter a custom contact, leave empty if linked entry should be used infolog sv Ange alternativ kontakt eller lämna tomt för att använda den länkade
enter a custom phone/email, leave empty if linked entry should be used infolog sv Ange alternativ telefon/e-post eller lämna tomt för att använda den länkade
enter a textual description of the log-entry infolog sv Skriv beskrivning av uppgiften
enter the query pattern infolog sv Söksträng
entry and all files infolog sv Post och alla filer
error: saving the entry infolog sv Kunde inte spara posten
error: the entry has been updated since you opened it for editing! infolog sv Posten har ändrats sedan du öppnade den för skrivning!
existing links infolog sv Befintliga länkar
fax infolog sv Fax
fieldseparator infolog sv Fältavskiljare
finish infolog sv Avsluta
for which types should this field be used infolog sv För vilka typer ska fältet användas?
from infolog sv Från
general infolog sv Allmänt
high infolog sv Hög
id infolog sv ID
if not set, the line with search and filters is hidden for less entries then "max matches per page" (as defined in your common preferences). infolog sv Om inte satt, kommer raden med sökning och filter vara gömd för färre poster än "max träffar per sida" (som du anav under allmäna alternativ).
import infolog sv Importera
import next set infolog sv Importera nästa
info log common sv InfoLogg
infolog common sv InfoLogg
infolog - delete infolog sv InfoLogg - Radera
infolog - edit infolog sv InfoLogg - Ändra
infolog - import csv-file infolog sv InfoLogg - importera CSV fil
infolog - new infolog sv InfoLogg - Ny
infolog - new subproject infolog sv InfoLogg - Nytt underprojekt
infolog - subprojects from infolog sv InfoLogg - Underprojekt till
infolog entry deleted infolog sv InfoLogg post raderad
infolog entry saved infolog sv InfoLogg post sparad
infolog filter for the main screen infolog sv InfoLogg filter för Hemsidan
infolog list infolog sv InfoLogg lista
infolog preferences common sv InfoLogg alternativ
infolog-fieldname infolog sv InfoLogg fältnamn
invalid filename infolog sv Ogiltigt filnamn
label<br>helptext infolog sv Etikett<br>Hjälptext
last changed infolog sv Senaste ändring
last modified infolog sv Senast ändrad
leave it empty infolog sv Lämnas tomt
leave without saveing the entry infolog sv Lämna utan att spara post
leaves without saveing infolog sv Lämnar utan att spara
length<br>rows infolog sv Längd<br>Rader
link infolog sv Länk
links infolog sv Länkar
links of this entry infolog sv Postens länkar
list all categories infolog sv Lista alla kategorier
list no subs/childs infolog sv Lista inte underobjekt
location infolog sv Placering
longer textual description infolog sv Längre beskrivning
low infolog sv Låg
max length of the input [, length of the inputfield (optional)] infolog sv Max längd på indatafältet
name must not be empty !!! infolog sv Namnet får inte vara tomt!
name of new type to create infolog sv Skapa typ med namnet
never hide search and filters infolog sv Göm aldrig sök eller filter
new name infolog sv Nytt namn
new search infolog sv Ny sökning
no - cancel infolog sv Nej - Avbryt
no describtion, links or attachments infolog sv Ingen beskrivning, länk eller bilaga
no details infolog sv Inga detaljer
no entries found, try again ... infolog sv Inga poster hittade, försök igen
no filter infolog sv Inga filter
no links or attachments infolog sv Inga länkar eller bilagor
none infolog sv Ingen
normal infolog sv Normal
not infolog sv Inte
not assigned infolog sv Inte tilldelad
not-started infolog sv Inte påbörjad
note infolog sv Anteckning
number of records to read (%1) infolog sv Antal poster att läsa (%1)
number of row for a multiline inputfield or line of a multi-select-box infolog sv Antal rader för flera raders indata eller rad i flervals ruta
offer infolog sv Erbjudande
ongoing infolog sv Pågående
only for details infolog sv Endast detaljer
only the attachments infolog sv Endast bilagor
only the links infolog sv Endast länkar
open infolog sv Öppen
optional note to the link infolog sv Frivillig anteckning till länken
order infolog sv Sortering
overdue infolog sv Förfallen
own infolog sv Mina
own open infolog sv Mina öppna
own overdue infolog sv Mina förfallna
own upcoming infolog sv Mina kommande
path on (web-)serverside<br>eg. /var/samba/share infolog sv Sökväg på servern<br>ex. /var/samba/Share
path to user and group files has to be outside of the webservers document-root!!! infolog sv Sökväg till användar och grupp filer MÅSTE VARA UTANFÖR webbserverns dokument root!
pattern for search in addressbook infolog sv Sökmönster i Adressboken
pattern for search in projects infolog sv Sökmönster i Projekt
percent completed infolog sv Procent färdig
permission denied infolog sv Åtkomst nekas
phone infolog sv Samtal
phone/email infolog sv Telefon/E-post
phonecall infolog sv Telefonsamtal
planned infolog sv Planerad
planned time infolog sv Planerad tid
price infolog sv Pris
priority infolog sv Prioritet
private infolog sv Privat
project infolog sv Projekt
project settings: price, times infolog sv Projekt inställningar: Pris, antal
re: infolog sv Sv:
read one record by passing its id. infolog sv Ange ID för att läsa post
read rights (default) infolog sv Läsrättighet (standard)
reg. expr. for local ip's<br>eg. ^192.168.1. infolog sv Giltigt uttryck för lokala IP-adresser.<br>dvs 192\.168\.1\.
reg. expr. for local ip's<br>eg. ^192\.168\.1\. infolog sv Giltigt uttryck för lokala IP-adresser.<br>dvs 192\.168\.1\.
remark infolog sv Anmärkning
remove this link (not the entry itself) infolog sv Radera länk (inte posten)
responsible infolog sv Ansvarig
responsible open infolog sv Ansvarig öppna
responsible overdue infolog sv Ansvarig förfallna
responsible upcoming infolog sv Ansvarig kommande
responsible user, priority infolog sv Ansvarig användare, prioritet
returns a list / search for records. infolog sv Returnerar lista / sök poster
rights for the responsible infolog sv Rättigheter åt den ansvarige
save infolog sv Spara
saves the changes made and leaves infolog sv Spara ändringar och avsluta
saves this entry infolog sv Spara post
search infolog sv Sök
search for: infolog sv Sök:
select infolog sv Välj
select a category for this entry infolog sv Välj kategori för posten
select a price infolog sv Välj pris
select a priority for this task infolog sv Välj prioritet för posten
select a project infolog sv Välj projekt
select a responsible user: a person you want to delegate this task infolog sv Välj ansvarig person, någon du delegerar ärendet till
select a typ to edit it's status-values or delete it infolog sv Välj typ att ändra eller radera
select an app to search in infolog sv Välj applikation att söka i
select an entry to link with infolog sv Välj post att länka med
select to filter by owner infolog sv Välj för att sortera enl ägare
select to filter by responsible infolog sv Välj för att sortera enl ansvarig
sets the status of this entry and its subs to done infolog sv Anger status för posten och alla under till färdig
should infolog show subtasks, -calls or -notes in the normal view or not. you can always view the subs via there parent. infolog sv Ska InfoLogg visa under- Uppgift, Samtal eller Anteckning i normala vyn eller inte? Du kan alltid visa dessa via topobjektet
should infolog show the links to other applications and/or the file-attachments in the infolog list (normal view when you enter infolog). infolog sv Ska InfoLogg visa länkar till andra applikationer och/eller bilaga i InfoLogg vyn (normal vy när du öppnar InfoLogg)
should infolog show up on the main screen and with which filter. works only if you dont selected an application for the main screen (in your preferences). infolog sv Ska InfoLogg visas på Startsidan och med vilket filter. Fungerar bara om du inte valt en annan applikation till STartsidan (under alternativ)
should infolog use full names (surname and familyname) or just the loginnames. infolog sv Ska InfoLogg visa fullständiga namn (förnamn och efternamn) eller bara inloggningsnamnet?
should the calendar show custom types too infolog sv Ska kalendern visa anpassade fält också
should the infolog list show a unique numerical id, which can be used eg. as ticket id. infolog sv Ska InfoLogg visa unika numeriska ID som även kan användas som t.ex. ärende ID
should the infolog list show the column "last modified". infolog sv Ska InfoLogg visa kolumnen "Senast ändrad"
should the infolog list show the percent done only for status ongoing or two separate icons. infolog sv Ska InfoLogg visa procent färdig enbart för status pågående eller två separata ikoner
should this entry only be visible to you and people you grant privat access via the acl infolog sv Ska denna post enbart visas för dig och de du givit tillgång till via ACL?
show a column for used and planned times in the list. infolog sv Visa kolumn för förbrukade och planerat antal i vyn
show full usernames infolog sv Visa fullständigt användarnamn
show in the infolog list infolog sv Visa i InfoLogg lista
show last modified infolog sv Visa senast ändrad
show status and percent done separate infolog sv Visa status och procent färdig separat
show ticket id infolog sv Visa ärende ID
show times infolog sv Visa antal
small view infolog sv Liten vy
start a new search, cancel this link infolog sv Gör ny sökning och avbryt denna länk
startdate infolog sv Startdatum
startdate enddate infolog sv Start- Slutdatum
startdate for new entries infolog sv Startdatum för nya poster
startrecord infolog sv Startposition
status infolog sv Status
status ... infolog sv Status ...
sub infolog sv Under
sub-entries become subs of the parent or main entries, if there's no parent infolog sv Underposter blir underordnade huvudposten om det inte finns någon förälder
subject infolog sv Ämne
task infolog sv Uppgift
test import (show importable records <u>only</u> in browser) infolog sv Testimport (visa importerara poster <u>bara</u> i browser)
the name used internaly (<= 10 chars), changeing it makes existing data unavailible infolog sv internt namn (<= 10 tecken), ändring gör existerande data otillgängligt
the name used internaly (<= 20 chars), changeing it makes existing data unavailible infolog sv internt namn (<= 20 tecken), ändring gör existerande data otillgängligt
the text displayed to the user infolog sv Texten som visas för användare
this is the filter infolog uses when you enter the application. filters limit the entries to show in the actual view. there are filters to show only finished, still open or futures entries of yourself or all users. infolog sv Detta filter använder InfoLogg när du öppnar applikationen. Filtren kan visa bara avslutade, öppna eller framtida objekt som tillhör dig eller alla.
til when should the todo or phonecall be finished infolog sv Tills när ska Uppgift och Samtal vara utförda
times infolog sv Antal
to many might exceed your execution-time-limit infolog sv För många kan gå över exekverings tidgränsen
to what should the startdate of new entries be set. infolog sv Till vilket startdatum ska nya poster sättas?
today infolog sv Idag
todays date infolog sv Dagens datum
todo infolog sv Uppgift
translation infolog sv Översättning
typ infolog sv Typ
typ '%1' already exists !!! infolog sv Typ '%1' finns redan
type infolog sv Typ
type ... infolog sv Typ ...
type of customfield infolog sv Typ av anpassat fält
type of the log-entry: note, phonecall or todo infolog sv Typ av InfoLogg: Anteckning, Samtal eller Uppgift
unlink infolog sv Radera länk
upcoming infolog sv Kommande
urgency infolog sv Prioritet
urgent infolog sv Brådskande
used time infolog sv Förbrukad tid
valid path on clientside<br>eg. \\server\share or e:\ infolog sv Giltid sökväg för klient<br>t ex \\server\share eller e:\
valid path on clientside<br>eg. \servershare or e: infolog sv Giltid sökväg för klient<br>t ex \\server\share eller e:\
valid path on clientside<br>eg. servershare or e: infolog sv Giltid sökväg för klient<br>t ex \\server\share eller e:\
values for selectbox infolog sv Värde för valrutan
view all subs of this entry infolog sv Visa alla under denna post
view other subs infolog sv Visa andra under
view parent infolog sv Visa rot
view subs infolog sv Visa under
view the parent of this entry and all his subs infolog sv Visa rot till denna post och dess underkategorier
view this linked entry in its application infolog sv Visa den länkade posten i dess applikation
when should the todo or phonecall be started, it shows up from that date in the filter open or own open (startpage) infolog sv När Uppgift och Samtal ska påbörjas visas de från aktuellt datum i Mina eller Mina öppna (Startsidan)
which additional fields should the responsible be allowed to edit without having edit rights?<br />status, percent and date completed are always allowed. infolog sv Vilka övriga fält ska ansvariga kunna ändra utan ändringsrättigheter?<br />Status, procent och datum är alltid tillåtna.
which implicit acl rights should the responsible get? infolog sv Vilka ACL rättigheter ska den ansvarigefå?
whole query infolog sv Hela frågan
will-call infolog sv Ska ringa
write (add or update) a record by passing its fields. infolog sv Fyll i fälten för att skapa eller uppdatera post
yes - delete infolog sv Ja - Radera
yes - delete including sub-entries infolog sv Ja - Radera inklusive under
you can't delete one of the stock types !!! infolog sv Du kan inte radera inbyggda typer
you have entered an invalid ending date infolog sv Ogiltigt Slutdatum
you have entered an invalid starting date infolog sv Ogiltigt Startdatum
you have to enter a name, to create a new typ!!! infolog sv Ange namn för att skapa typ
you must enter a subject or a description infolog sv Ange ämne eller beskrivning
your database is not up to date (%1 vs. %2), please run %3setup%4 to update your database. infolog sv Din databas är inte uppdaterad (%1 vs. %2), var god och kör %3installationen%4 för att uppdatera.

360
infolog/lang/egw_zh-tw.lang Normal file
View File

@ -0,0 +1,360 @@
%1 days in advance infolog zh-tw %1 天提出
%1 deleted infolog zh-tw %1 刪除了
%1 deleted by %2 at %3 infolog zh-tw %1 由 %2 在 %3 刪除了
%1 modified infolog zh-tw %1 更新了
%1 modified by %2 at %3 infolog zh-tw %1 由 %2 在 %3 更新了
%1 records imported infolog zh-tw %1 筆資料匯入
%1 records read (not yet imported, you may go %2back%3 and uncheck test import) infolog zh-tw 讀取 %1 筆資料(尚未匯入,您可以回到 %2上一頁%3 並且取消匯入測試)
%1 you are responsible for is due at %2 infolog zh-tw 您負責的 %1 到期日為 %2
%1 you are responsible for is starting at %2 infolog zh-tw 您負責的 %1 開始日期為 %2
%1 you delegated is due at %2 infolog zh-tw 您委派的 %1 到期日為 %2
%1 you delegated is starting at %2 infolog zh-tw 您委派的 %1 開始日期為 %2
- subprojects from infolog zh-tw - 附加專案來自
0% infolog zh-tw 0%
10% infolog zh-tw 10%
100% infolog zh-tw 100%
20% infolog zh-tw 20%
30% infolog zh-tw 30%
40% infolog zh-tw 40%
50% infolog zh-tw 50%
60% infolog zh-tw 60%
70% infolog zh-tw 70%
80% infolog zh-tw 80%
90% infolog zh-tw 90%
<b>file-attachments via symlinks</b> instead of uploads and retrieval via file:/path for direct lan-clients infolog zh-tw <b>透過符號連結來附加檔案</b>取代原本上傳檔案的方式
a short subject for the entry infolog zh-tw 一個簡短的主題
abort without deleting infolog zh-tw 取消刪除
accept infolog zh-tw 確定
action infolog zh-tw 動作
actual date and time infolog zh-tw 實際日期與時間
add infolog zh-tw 新增
add a file infolog zh-tw 新增檔案
add a new entry infolog zh-tw 新增個事件
add a new note infolog zh-tw 新增記事
add a new phonecall infolog zh-tw 新增電話聯絡清單
add a new sub-task, -note, -call to this entry infolog zh-tw 新增一個附屬工作、記事與連絡清單到這個事件
add a new todo infolog zh-tw 新增待辦事項
add file infolog zh-tw 新增檔案
add sub infolog zh-tw 新增子分類
add timesheet entry infolog zh-tw 新增時間表資料
add: infolog zh-tw 新增:
all infolog zh-tw 全部
all links and attachments infolog zh-tw 所有的連結與附加檔案
allows to set the status of an entry, eg. set a todo to done if it's finished (values depend on entry-typ) infolog zh-tw 允許設定事件的狀態,例如這個事件如果結束就將指定的待辦事項設定為完成(附屬在事件下的)
apply the changes infolog zh-tw 儲存異動
archive infolog zh-tw 歸檔
are you shure you want to delete this entry ? infolog zh-tw 您確定要刪除這個事件?
attach a file infolog zh-tw 附加一個檔案
attach file infolog zh-tw 附加檔案
attention: no contact with address %1 found. infolog zh-tw 注意:找不到住址為 %1 的聯絡人。
back to main list infolog zh-tw 回到主要清單
billed infolog zh-tw 已付款
both infolog zh-tw 皆
call infolog zh-tw 呼叫
can be used to show further infolog types in the calendar or limit it to show eg. only tasks. infolog zh-tw 可以用來在行事曆顯示未來的記事類型或是限制顯示,例如任務
cancel infolog zh-tw 取消
cancelled infolog zh-tw 取消了
categories infolog zh-tw 分類
category infolog zh-tw 類別
change the status of an entry, eg. close it infolog zh-tw 修改資料狀態,例如:關閉
charset of file infolog zh-tw 檔案字元編碼
check to set startday infolog zh-tw 設定開始日期
check to specify custom contact infolog zh-tw 勾選這個項目來指定自訂聯絡人
click here to create the link infolog zh-tw 點選這裡來建立連結
click here to start the search infolog zh-tw 點選這裡來開始搜尋
close infolog zh-tw 關閉
comment infolog zh-tw 備註
completed infolog zh-tw 完成了
configuration infolog zh-tw 設定
confirm infolog zh-tw 確認
contact infolog zh-tw 聯絡
copy your changes to the clipboard, %1reload the entry%2 and merge them. infolog zh-tw 複製異動資料到剪貼簿, %1重新讀取資料%2並且將他們合併。
create new links infolog zh-tw 建立新的連結
creates a new field infolog zh-tw 建立新的區域
creates a new status with the given values infolog zh-tw 使用輸入的值來建立新的狀態
creates a new typ with the given name infolog zh-tw 使用輸入的名稱來建立新的格式
creation infolog zh-tw 建立
csv-fieldname infolog zh-tw CSV-欄位名稱
csv-filename infolog zh-tw CSV-檔案名稱
csv-import common zh-tw CSV-匯入
custom infolog zh-tw 自訂
custom contact-address, leave empty to use information from most recent link infolog zh-tw 自訂聯絡住址,如果空白就會使用最近連結資訊
custom contact-information, leave emtpy to use information from most recent link infolog zh-tw 自訂聯絡資訊,如果空白就會使用最近連結資訊
custom fields infolog zh-tw 自訂欄位
custom fields, typ and status common zh-tw 自訂欄位、格式與狀態
custom regarding infolog zh-tw 自訂關於
custom status for typ infolog zh-tw 自訂格式的狀態
customfields infolog zh-tw 自定欄位
date completed infolog zh-tw 完成日期
date completed (leave it empty to have it automatic set if status is done or billed) infolog zh-tw 完成日期(保留空白會自動使用狀態改為完成或已付款的時間)
datecreated infolog zh-tw 建立日期
dates, status, access infolog zh-tw 日期、狀態、存取
days infolog zh-tw 日
default filter for infolog infolog zh-tw 記事本的預設過濾方式
default status for a new log entry infolog zh-tw 事件紀錄的預設狀態
delegated infolog zh-tw 委派
delegated open infolog zh-tw 進行中的委派
delegated overdue infolog zh-tw 過期的委派
delegated upcomming infolog zh-tw 即將開始的委派
delegation infolog zh-tw 授權
delete infolog zh-tw 刪除
delete one record by passing its id. infolog zh-tw 透過傳遞ID來刪除記錄
delete the entry infolog zh-tw 刪除這個事件
delete this entry infolog zh-tw 刪除這個事件
delete this entry and all listed sub-entries infolog zh-tw 刪除這筆資料與所有附屬的項目
deleted infolog zh-tw 刪除了
deletes the selected typ infolog zh-tw 刪除這個格式
deletes this field infolog zh-tw 刪除這個欄位
deletes this status infolog zh-tw 刪除這個狀態
description infolog zh-tw 描述
determines the order the fields are displayed infolog zh-tw 決定顯示的順序與欄位
disables a status without deleting it infolog zh-tw 停用一個狀態而不是刪除
do you want a confirmation of the responsible on: accepting, finishing the task or both infolog zh-tw 您希望做再次的確認在:同意,工作完成或是兩者都要
do you want a notification, if items get assigned to you or assigned items get updated? infolog zh-tw 項目指派給您或是指派的項目有更新時,您是否想要收到提醒?
do you want a notification, if items you are responsible for are about to start? infolog zh-tw 您負責的項目快要開始時,您是否想要收到提醒?
do you want a notification, if items you are responsible for are due? infolog zh-tw 您負責的項目過期時,您是否想要收到提醒?
do you want a notification, if items you created get updated? infolog zh-tw 您建立的項目更新時,您是否想要收到提醒?
do you want a notification, if items you delegated are about to start? infolog zh-tw 您指派的項目快要開始時,您是否想要收到提醒?
do you want a notification, if items you delegated are due? infolog zh-tw 您指派的項目過期時,您是否想要收到提醒?
do you want to receive notifications as html-mails or plain text? infolog zh-tw 您希望收到的提醒信件格式為 html 或純文字?
don't show infolog infolog zh-tw 不要顯示記事本
done infolog zh-tw 完成
download infolog zh-tw 下載
duration infolog zh-tw 進行期間
each value is a line like <id>[=<label>] infolog zh-tw 每個值都是這樣的線 <id>[=<label>]
edit infolog zh-tw 編輯
edit or create categories for ingolog infolog zh-tw 編輯或建立訊息紀錄的類別
edit rights (full edit rights incl. making someone else responsible!) infolog zh-tw 編輯權限(完整的編輯權限包括指定負責人!)
edit status infolog zh-tw 編輯狀態
edit the entry infolog zh-tw 編輯這個事件
edit this entry infolog zh-tw 編輯這個事件
empty for all infolog zh-tw 全部空白
enddate infolog zh-tw 結束日期
enddate can not be before startdate infolog zh-tw 結束日期不應該在開始日期之前
enter a custom contact, leave empty if linked entry should be used infolog zh-tw 輸入一個自訂的聯絡人,如果連結的事件必須使用就請保持空白
enter a custom phone/email, leave empty if linked entry should be used infolog zh-tw 輸入一個自訂的電話/email如果連結的事件必須使用就請保持空白
enter a textual description of the log-entry infolog zh-tw 輸入事件紀錄的原始描述
enter the query pattern infolog zh-tw 輸入查詢的方式
entry and all files infolog zh-tw 事件與所有檔案
error: saving the entry infolog zh-tw 錯誤: 儲存資料時
error: the entry has been updated since you opened it for editing! infolog zh-tw 錯誤: 在您開始編輯後這個資料已經被其他人更新了!
existing links infolog zh-tw 已經存在的連結
fax infolog zh-tw 傳真
fieldseparator infolog zh-tw 欄位分隔字元
finish infolog zh-tw 完成
for which types should this field be used infolog zh-tw 這個欄位使用什麼格式
from infolog zh-tw 從
general infolog zh-tw 一般
group owner for infolog zh-tw 群組擁有者
high infolog zh-tw 高
history logging infolog zh-tw 歷史記錄
history logging and deleting of items infolog zh-tw 歷史記錄與刪除項目
id infolog zh-tw ID
if a type has a group owner, all entries of that type will be owned by the given group and not the user who created it! infolog zh-tw 如果一個類型有群組擁有者,所有該類型的資料將會屬於指定的群組而非建立的使用者!
if not set, the line with search and filters is hidden for less entries then "max matches per page" (as defined in your common preferences). infolog zh-tw 如果沒有設定,在資料數量少於"每頁顯示資料數量上限"(在您的一般設定中)時會隱藏過濾與搜尋功能。
import infolog zh-tw 匯入
import next set infolog zh-tw 匯入下一組
info log common zh-tw 記事本
infolog common zh-tw 記事本
infolog - delete infolog zh-tw 記事本 - 刪除
infolog - edit infolog zh-tw 記事本 - 編輯
infolog - import csv-file infolog zh-tw 記事本 - 匯入CSV檔案
infolog - new infolog zh-tw 記事本 - 新增
infolog - new subproject infolog zh-tw 記事本 - 新增附屬專案
infolog - subprojects from infolog zh-tw 記事本 - 附屬專案自
infolog entry deleted infolog zh-tw 記事本資料刪除了
infolog entry saved infolog zh-tw 記事本資料儲存了
infolog filter for the main screen infolog zh-tw 首頁的記事本顯示規則
infolog list infolog zh-tw 記事本清單
infolog preferences common zh-tw 記事本設定
infolog-fieldname infolog zh-tw 記事本 - 欄位名稱
invalid filename infolog zh-tw 錯誤的檔案名稱
label<br>helptext infolog zh-tw 標籤<br>說明文字
last changed infolog zh-tw 最後修改
last modified infolog zh-tw 最後修改
leave it empty infolog zh-tw 保持空白
leave without saveing the entry infolog zh-tw 離開並且不要儲存這個事件
leaves without saveing infolog zh-tw 離開並且不要儲存
length<br>rows infolog zh-tw 長度<br>列
link infolog zh-tw 連結
links infolog zh-tw 連結
links of this entry infolog zh-tw 這個事件的連結
list all categories infolog zh-tw 列出所有類別
list no subs/childs infolog zh-tw 不要列出附屬資訊
location infolog zh-tw 位置
longer textual description infolog zh-tw 較長的原文描述
low infolog zh-tw 低
max length of the input [, length of the inputfield (optional)] infolog zh-tw 最大輸入長度[, 輸入欄位長度 (選填)]
name must not be empty !!! infolog zh-tw 名稱不能空白!
name of new type to create infolog zh-tw 建立新的格式名稱
never hide search and filters infolog zh-tw 永遠顯示搜尋與過濾功能
new %1 infolog zh-tw 新 %1
new %1 created by %2 at %3 infolog zh-tw 新 %1 由 %2 在 %3 建立
new name infolog zh-tw 新的名稱
new search infolog zh-tw 新的搜尋
no - cancel infolog zh-tw 否 - 取消
no describtion, links or attachments infolog zh-tw 沒有說明、連結或附加檔案
no details infolog zh-tw 沒有細節
no entries found, try again ... infolog zh-tw 沒有找到任何事件,請重新嘗試...
no filter infolog zh-tw 沒有過濾規則
no links or attachments infolog zh-tw 沒有連結或附加檔案
nonactive infolog zh-tw 停用
none infolog zh-tw 無
normal infolog zh-tw 一般
not infolog zh-tw 否
not assigned infolog zh-tw 不指定
not-started infolog zh-tw 未開始
note infolog zh-tw 記事
number of records to read (%1) infolog zh-tw 讀取的資料筆數(%1)
number of row for a multiline inputfield or line of a multi-select-box infolog zh-tw 輸入欄位或是下拉式選單的數量
offer infolog zh-tw 提供
one day after infolog zh-tw 一天後
one day in advance infolog zh-tw 一天前
ongoing infolog zh-tw 不間斷的
only for details infolog zh-tw 只顯示細節
only if i get assigned or removed infolog zh-tw 只在我被指派或移除時
only the attachments infolog zh-tw 只有附加檔案
only the links infolog zh-tw 只有連結
open infolog zh-tw 開啟
optional note to the link infolog zh-tw 連結的附加說明
order infolog zh-tw 順序
overdue infolog zh-tw 過期的
own infolog zh-tw 自己的
own open infolog zh-tw 開啟自己的
own overdue infolog zh-tw 自己的過期
own upcoming infolog zh-tw 自己的即將來臨
path on (web-)serverside<br>eg. /var/samba/share infolog zh-tw 伺服器(網站-)路徑<br>例如/var/samba/Share
path to user and group files has to be outside of the webservers document-root!!! infolog zh-tw 使用者與群組檔案路徑必須在網站伺服器的網頁根目錄之外!
pattern for search in addressbook infolog zh-tw 搜尋通訊錄的條件
pattern for search in projects infolog zh-tw 搜尋專案的條件
percent completed infolog zh-tw 完成比例
permission denied infolog zh-tw 拒絕存取
phone infolog zh-tw 聯絡清單
phone/email infolog zh-tw 電話/Email
phonecall infolog zh-tw 聯絡清單
planned infolog zh-tw 已規劃
planned time infolog zh-tw 已規劃時間
price infolog zh-tw 價格
priority infolog zh-tw 優先權
private infolog zh-tw 私人的
project infolog zh-tw 專案
project settings: price, times infolog zh-tw 專案設定: 價格、時間
re: infolog zh-tw 回覆:
read one record by passing its id. infolog zh-tw 透過傳遞ID來讀取記錄
read rights (default) infolog zh-tw 閱讀權限(預設)
receive notifications about due entries you are responsible for infolog zh-tw 接收您負責項目即將過期的提醒
receive notifications about due entries you delegated infolog zh-tw 接收您指派項目即將過期的提醒
receive notifications about items assigned to you infolog zh-tw 接收項目指派給您的提醒
receive notifications about own items infolog zh-tw 接收自己項目的提醒
receive notifications about starting entries you are responsible for infolog zh-tw 接收您負責項目即將開始的提醒
receive notifications about starting entries you delegated infolog zh-tw 接收您指派項目即將開始的提醒
receive notifications as html-mails infolog zh-tw 提醒信件格式為 html
reg. expr. for local ip's<br>eg. ^192.168.1. infolog zh-tw 本地端IP的過濾規則<br>例如^192\.168\.1\.
reg. expr. for local ip's<br>eg. ^192\.168\.1\. infolog zh-tw 本地端IP的過濾規則<br>例如^192\.168\.1\.
remark infolog zh-tw 標註
remove this link (not the entry itself) infolog zh-tw 移除這個連結(不是它的資料)
responsible infolog zh-tw 責任
responsible open infolog zh-tw 責任開始時間
responsible overdue infolog zh-tw 責任期間
responsible upcoming infolog zh-tw 即將產生的責任
responsible user, priority infolog zh-tw 負責人、順序
returns a list / search for records. infolog zh-tw 傳回一個清單 / 搜尋記錄
rights for the responsible infolog zh-tw 負責人權限
same day infolog zh-tw 同一天
save infolog zh-tw 儲存
saves the changes made and leaves infolog zh-tw 儲存後離開
saves this entry infolog zh-tw 儲存這個事件
search infolog zh-tw 搜尋
search for: infolog zh-tw 搜尋:
select infolog zh-tw 選擇
select a category for this entry infolog zh-tw 選擇這個事件的類別
select a price infolog zh-tw 選擇價格
select a priority for this task infolog zh-tw 選擇這個任務的優先權
select a project infolog zh-tw 選擇專案
select a responsible user: a person you want to delegate this task infolog zh-tw 選擇負責人:這個任務的代表人
select a typ to edit it's status-values or delete it infolog zh-tw 選擇一個格式來編輯它的狀態或是將它刪除
select an app to search in infolog zh-tw 選擇要搜尋的應用程式
select an entry to link with infolog zh-tw 選擇要連結的事件
select to filter by owner infolog zh-tw 依據擁有者過濾
select to filter by responsible infolog zh-tw 依據負責人過濾
sets the status of this entry and its subs to done infolog zh-tw 設定這筆資料與附屬項目的狀態為完成
should infolog show subtasks, -calls or -notes in the normal view or not. you can always view the subs via there parent. infolog zh-tw 記事本需要在一般瀏覽中顯示附屬任務,聯絡清單或是記事嗎?您可以在原始事件中看到附屬的資訊。
should infolog show the links to other applications and/or the file-attachments in the infolog list (normal view when you enter infolog). infolog zh-tw 記事本是否要顯示與其他應用程式的連結與/或附加檔案(通常會在進入記事本時看到)。
should infolog show up on the main screen and with which filter. works only if you dont selected an application for the main screen (in your preferences). infolog zh-tw 首頁要依據指定的規則顯示記事本嗎?如果您沒有設定預設的應用程式(偏好中)就會顯示。
should infolog use full names (surname and familyname) or just the loginnames. infolog zh-tw 記事本使用全名或是登入名稱?
should the infolog list show a unique numerical id, which can be used eg. as ticket id. infolog zh-tw 記事清單是否要顯示唯一的編號,其他功能可能會需要,例如傳票編號。
should the infolog list show the column "last modified". infolog zh-tw 記事本清單要顯示異動日期欄位嗎?
should the infolog list show the percent done only for status ongoing or two separate icons. infolog zh-tw 記事清單是否只要顯示進行中狀態完成的比率或是兩個獨立的圖示。
should this entry only be visible to you and people you grant privat access via the acl infolog zh-tw 這個事件只允許您以及您從ACL取得存取權限的人
show a column for used and planned times in the list. infolog zh-tw 在清單中顯示一個用於已使用與已規劃時間的欄位。
show full usernames infolog zh-tw 顯示完整的使用者姓名
show in the infolog list infolog zh-tw 在記事本清單中顯示
show last modified infolog zh-tw 顯示異動日期
show status and percent done separate infolog zh-tw 將狀態與完成度比率分開
show ticket id infolog zh-tw 顯示傳票編號
show times infolog zh-tw 顯示時間
small view infolog zh-tw 縮小
start a new search, cancel this link infolog zh-tw 開啟一個新的搜尋並且取消這個連結
startdate infolog zh-tw 開始日期
startdate enddate infolog zh-tw 開始日期 結束日期
startdate for new entries infolog zh-tw 新資料的開始日期
startrecord infolog zh-tw 開始紀錄
status infolog zh-tw 狀態
status ... infolog zh-tw 狀態...
sub infolog zh-tw 附屬
sub-entries become subs of the parent or main entries, if there's no parent infolog zh-tw 如果沒有父項目的資訊,附屬資料會歸屬到主要資料的父項目
subject infolog zh-tw 主題
task infolog zh-tw 待辦事項
template infolog zh-tw 樣板
test import (show importable records <u>only</u> in browser) infolog zh-tw 測試匯入(<u>只在</u>瀏覽器中顯示可以匯入的資料)
the name used internaly (<= 10 chars), changeing it makes existing data unavailible infolog zh-tw 內部名稱(小於10個字元),修改它將造成目前的資料無法瀏覽
the name used internaly (<= 20 chars), changeing it makes existing data unavailible infolog zh-tw 內部名稱(小於20個字元),修改它將造成目前的資料無法瀏覽
the text displayed to the user infolog zh-tw 給使用者的訊息
this is the filter infolog uses when you enter the application. filters limit the entries to show in the actual view. there are filters to show only finished, still open or futures entries of yourself or all users. infolog zh-tw 這是記事本在您進入這個應用程式時使用的過濾方式,過濾方式將會限制實際瀏覽的事件。過濾方式可以決定您自己或是所有使用者"已經完成的""進行中"或是"未來的事件"
til when should the todo or phonecall be finished infolog zh-tw 待辦事項或是聯絡清單應該完成的時間
times infolog zh-tw 時間
to many might exceed your execution-time-limit infolog zh-tw 超過了程式執行時間
to what should the startdate of new entries be set. infolog zh-tw 新資料的開始日期應該設定為什麼。
today infolog zh-tw 今天
todays date infolog zh-tw 今天日期
todo infolog zh-tw 待辦事項
translation infolog zh-tw 翻譯
typ infolog zh-tw 格式
typ '%1' already exists !!! infolog zh-tw '%1' 格式已經存在!
type infolog zh-tw 格式
type ... infolog zh-tw 格式 ...
type of customfield infolog zh-tw 自訂欄位類型
type of the log-entry: note, phonecall or todo infolog zh-tw 紀錄簿的格式:記事、聯絡清單或是待辦事項
unlink infolog zh-tw 刪除
upcoming infolog zh-tw 即將到來
urgency infolog zh-tw 緊急
urgent infolog zh-tw 緊急的
used time infolog zh-tw 使用的時間
valid path on clientside<br>eg. \\server\share or e:\ infolog zh-tw 用戶端可用的路徑<br>例如\\Server\Share 或是 e:\
valid path on clientside<br>eg. \servershare or e: infolog zh-tw 用戶端可用的路徑<br>例如\\Server\Share 或是 e:\
values for selectbox infolog zh-tw 選單的值
view all subs of this entry infolog zh-tw 顯示這個事件的所有附屬資訊
view other subs infolog zh-tw 顯示其他附屬資訊
view parent infolog zh-tw 顯示主要的
view subs infolog zh-tw 顯示附屬的
view the parent of this entry and all his subs infolog zh-tw 顯示這個事件的主要與所有附屬事件
view this linked entry in its application infolog zh-tw 在應用程式中顯示這個被連結的事件
when should the todo or phonecall be started, it shows up from that date in the filter open or own open (startpage) infolog zh-tw 待辦事項或是聯絡清單應該開始的時間?當過濾條件為"所有未完成"或是"自己未完成"時顯示在開始頁面。
which additional fields should the responsible be allowed to edit without having edit rights?<br />status, percent and date completed are always allowed. infolog zh-tw 哪些額外欄位即使負責人沒有編輯權限也能夠編輯?<br />狀態、比率與完成日期預設都會允許。
which implicit acl rights should the responsible get? infolog zh-tw 負責人可以取得哪個隱含的權限?
which types should the calendar show infolog zh-tw 行事曆要顯示哪些類型
whole query infolog zh-tw 整個查詢
will-call infolog zh-tw 將連絡
write (add or update) a record by passing its fields. infolog zh-tw 透過傳遞它的欄位來寫入(新增或更新)一筆記錄
yes - delete infolog zh-tw 確定 - 刪除
yes - delete including sub-entries infolog zh-tw 確定 - 刪除(包含附屬資料)
yes, noone can purge deleted items infolog zh-tw 是,沒有人可以排除刪除的項目
yes, only admins can purge deleted items infolog zh-tw 是,只有管理者可以排除刪除的項目
yes, with larger fontsize infolog zh-tw 是,使用較大的字體
yes, with purging of deleted items possible infolog zh-tw 是,與可能排除的項目
you can't delete one of the stock types !!! infolog zh-tw 您不能刪除股票的種類!
you have entered an invalid ending date infolog zh-tw 您輸入了錯誤的結束日期
you have entered an invalid starting date infolog zh-tw 您輸入了錯誤的開始日期
you have to enter a name, to create a new typ!!! infolog zh-tw 您必須輸入一個名稱來建立一個新的格式!
you must enter a subject or a description infolog zh-tw 您必須輸入主題或是描述
your database is not up to date (%1 vs. %2), please run %3setup%4 to update your database. infolog zh-tw 您的資料庫架構需要更新(%1 vs. %2),請執行%3安裝%4來更新資料庫。

280
infolog/lang/egw_zh.lang Normal file
View File

@ -0,0 +1,280 @@
%1 records imported infolog zh 已导入%1条记录
%1 records read (not yet imported, you may go %2back%3 and uncheck test import) infolog zh 已读取%1条记录(但尚未导入,您可以进入%2返回%3并且清除"测试导入"复选框)
%1 records read (not yet imported, you may go back and uncheck test import) infolog zh 已读取%1条记录(但尚未导入,您可以进入%2返回%3并且清除"测试导入"复选框)
- subprojects from infolog zh - 子项目来自
0% infolog zh 0%
10% infolog zh 10%
20% infolog zh 20%
30% infolog zh 30%
40% infolog zh 40%
50% infolog zh 50%
60% infolog zh 60%
70% infolog zh 70%
80% infolog zh 80%
90% infolog zh 90%
<b>file-attachments via symlinks</b> instead of uploads and retrieval via file:/path for direct lan-clients infolog zh <b>通过符号链接添加文件</b> 而不是通过局域网客户端的file:/path来上传和下载
a short subject for the entry infolog zh 请输入该事项的标题
abort without deleting infolog zh 放弃,不作任何删除
accept infolog zh 接受
action infolog zh 操作
add infolog zh 新建事项
add a file infolog zh 添加文件
add a new entry infolog zh 新建事项
add a new note infolog zh 新建备注
add a new phonecall infolog zh 新建函电
add a new sub-task, -note, -call to this entry infolog zh 新建该事项的子任务, 备注, 或函电
add a new todo infolog zh 新建任务
add file infolog zh 添加文件
add sub infolog zh 添加子类别
add: infolog zh 添加:
all infolog zh 全部
all links and attachments infolog zh 所有关联条目及关联文件
allows to set the status of an entry, eg. set a todo to done if it's finished (values depend on entry-typ) infolog zh 允许设置条目的状态, 例如, 设置一个任务已完成(设定值取决于条目类型)
applies the changes infolog zh 应用更改
are you shure you want to delete this entry ? infolog zh 确定要删除该事项吗?
are you sure you want to delete this entry infolog zh 确定要删除该事项
attach infolog zh 关联
attach a file infolog zh 添加文件
attach file infolog zh 添加关联文件
back to main list infolog zh 返回主列表
back to projectlist infolog zh 返回项目列表
billed infolog zh 付费
both infolog zh 都
call infolog zh 已致电
cancel infolog zh 取消
categories infolog zh 类别
category infolog zh 类别
change the status of an entry, eg. close it infolog zh 更改该事项的状态.
charset of file infolog zh 文件编码
check to set startday infolog zh 选中此项设定起始日期
click here to attach the file infolog zh 点击此处关联该文件
click here to create the link infolog zh 点击此处关联该条目
click here to start the search infolog zh 点击此处开始搜索
close infolog zh 关闭
comment infolog zh 注释
configuration infolog zh 全局配置
confirm infolog zh 确认
contact infolog zh 联系人
create new links infolog zh 新建关联
creates a new field infolog zh 新建字段
creates a new status with the given values infolog zh 新建状态
creates a new typ with the given name infolog zh 新建类型
csv-fieldname infolog zh CSV - 字段名
csv-filename infolog zh CSV文件名
csv-import common zh 从CSV文件导入
custom infolog zh 自定义
custom contact-address, leave empty to use information from most recent link infolog zh 请输入联系方式. 留空将自动设为首要关联
custom contact-information, leave emtpy to use information from most recent link infolog zh 请输入联系人信息. 留空将自动设为首要关联
custom fields infolog zh 自定义字段
custom fields, typ and status common zh 自定义字段, 类型及状态
custom regarding infolog zh 自定义关于
custom status for typ infolog zh 自定义该类型的可选状态:
customfields infolog zh Customfields
datecreated infolog zh 日期已创建
dates, status, access infolog zh 其他信息
days infolog zh 日
default infolog zh 默认状态
default filter for infolog infolog zh 浏览事项时的默认查看范围
default status for a new log entry infolog zh 新建该类型事项的默认状态
delegation infolog zh 委派
delete infolog zh 删除
delete all subs (if not subs will be subs of this enties parent or have no parent) infolog zh Delete all subs (if not subs will be subs of this enties parent or have no parent)
delete one record by passing its id. infolog zh 通过事项ID删除事件
delete the entry infolog zh 删除该事项
delete this entry infolog zh 删除该事项
deletes the selected typ infolog zh 删除选定类型
deletes this field infolog zh 删除该字段
deletes this status infolog zh 删除该状态
description infolog zh 内容
description can not exceed 8000 characters in length infolog zh 事项内容最多包含8000个字符
determines the order the fields are displayed infolog zh 设定字段的显示顺序
disables a status without deleting it infolog zh 禁用状态,但不删除
do you want a confirmation of the responsible on: accepting, finishing the task or both infolog zh 在接受或结束任务时,是否等待确认
done infolog zh 已完成
download infolog zh 下载
duration infolog zh 持续时间
each value is a line like <id>[=<label>] infolog zh 请设定选择框的选项, 格式: <选项名>=<显示名>, 每行代表一个选项.
edit infolog zh 编辑
edit or create categories for ingolog infolog zh 编辑或创建记事薄的类别
edit status infolog zh Edit Status
edit the entry infolog zh 编辑该事项
edit this entry infolog zh 编辑该事项
empty for all infolog zh 全部清空
enddate infolog zh 终止日期
enddate can not be before startdate infolog zh 终止日期必须在起始日期之后
enter a custom contact, leave empty if linked entry should be used infolog zh 输入自定义联系人, 留空则使用链接条目
enter a custom phone/email, leave empty if linked entry should be used infolog zh 输入自定义电话号码/EMail地址, 留空则使用链接条目
enter a search pattern infolog zh 输入关键字
enter a textual description of the log-entry infolog zh 输入该事项的具体内容
enter filename to upload and attach, use [browse...] to search for it infolog zh 请输入待关联文件的名称, 或通过点击[浏览]来选定文件
enter the query pattern infolog zh 输入查询模式
entry and all files infolog zh 条目及所有文件
existing links infolog zh 现有关联
fax infolog zh 传真
fieldseparator infolog zh 字段分隔符
file infolog zh 文件
finish infolog zh 结束
for which types should this field be used infolog zh 请选择字段所属类型, 即字段对哪些类型的事项可见
from infolog zh 寄件人
global categories infolog zh 全局类别
high infolog zh 高
id infolog zh 代号
import infolog zh 导入
import next set infolog zh 导入下一配置
info log common zh 记事薄
infolog common zh 记事薄
infolog index infolog zh 索引
infolog - delete infolog zh 记事薄-删除
infolog - edit infolog zh 记事薄-编辑
infolog - import csv-file infolog zh 记事薄 - 从CSV文件导入
infolog - new infolog zh 记事薄-新建
infolog - new subproject infolog zh 记事薄 - 新建子项目
infolog - subprojects from infolog zh 记事薄 - 子项目来自
infolog list infolog zh 浏览事项
infolog preferences common zh 记事薄个性化配置
infolog-fieldname infolog zh 记事薄 - 字段名
invalid filename infolog zh 文件名无效
label infolog zh 显示名
label<br>helptext infolog zh 标签<br>提示信息
last changed infolog zh 最近修改记录
last modified infolog zh 最近修改记录
leave without saveing the entry infolog zh 取消, 不保存对该事项的操作
leaves without saveing infolog zh 取消, 不作保存
length<br>rows infolog zh 长度<br>高度
link infolog zh 关联
links infolog zh 关联
links of this entry infolog zh 设定与事项相关的条目或文件
list no subs/childs infolog zh 不显示子事项
list all categories infolog zh 列出所有类别
longer textual description infolog zh 设定事项的具体内容
low infolog zh 低
max length of the input [, length of the inputfield (optional)] infolog zh 输入文本的最大长度[,文本框的宽度]
max number of entries to display on the main screen infolog zh 首页中显示的最大事项数目
name must not be empty !!! infolog zh 名称不能为空!!!
name of new type to create infolog zh 请输入新建类型的名称
new name infolog zh 新名称
new search infolog zh 重新搜索
no - cancel infolog zh 否
no entries found, try again ... infolog zh 未发现条目, 再次尝试 ...
no filter infolog zh 所有事项
no links or attachments infolog zh 不显示任何关联
none infolog zh 无
normal infolog zh 普通
not infolog zh not
not assigned infolog zh 未指定
note infolog zh 备注
number of records to read (%1) infolog zh 待读取记录数(%1)
number of records to read (<=200) infolog zh 读取的记录数(<=200)
number of row for a multiline inputfield or line of a multi-select-box infolog zh 文本框或多选框的高度
of infolog zh 总共
offer infolog zh 等待回应
ongoing infolog zh 正在进行
only the attachments infolog zh 仅显示关联文件
only the links infolog zh 仅显示关联条目
only up to this number of entries are displayed on the main screen. infolog zh 首页中最多可显示多少事项?
open infolog zh 待决事项
optional note about the link infolog zh 该关联的说明
optional note to the link infolog zh 该关联的说明
order infolog zh 顺序
overdue infolog zh 延误事项
own infolog zh 个人事项
own open infolog zh 待决的个人事项
own overdue infolog zh 延误的个人事项
own upcoming infolog zh 临近的个人事项
owner infolog zh 所有者
owner responsible infolog zh 负责人
path on (web-)serverside<br>eg. /var/samba/share infolog zh (Web)服务器端的路径<br>例如. /var/samba/Share
path to user and group files has to be outside of the webservers document-root!!! infolog zh 存放用户和组文件的路径不能在Web服务器的根目录内!!!
pattern for search in addressbook infolog zh 查找通讯录的模式
pattern for search in projects infolog zh 查找项目的模式
phone infolog zh 函电
phone/email infolog zh 电话号码/EMail地址
phonecall infolog zh 函电
priority infolog zh 优先级
private infolog zh 私有事项
project infolog zh 项目
re: infolog zh 回复:
read one record by passing its id. infolog zh 通过事项ID来读取记录
reg. expr. for local ip's<br>eg. ^192.168.1. infolog zh 本地IP地址的正则表达式<br>例如. ^192.168.1.
reg. expr. for local ip's<br>eg. ^192\.168\.1\. infolog zh 本地IP地址的正则表达式<br>例如.. ^192\.168\.1\.
remark infolog zh 注释
remove this link (not the entry itself) infolog zh 取消关联
responsible infolog zh 委派给
responsible user, priority, ... infolog zh 设定事项的委派及优先级
returns a list / search for records. infolog zh 返回一个清单 / 搜索记录
save infolog zh 确定
saves the changes made and leaves infolog zh 保存更改, 返回上一级页面
saves this entry infolog zh 保存该条目
search infolog zh 搜索
search for: infolog zh 查找:
select infolog zh 指定
select a category for this entry infolog zh 指定该事项的类别
select a primary contact, to show in the list infolog zh 指定在浏览事项时显示的首要关联
select a priority for this task infolog zh 指定该任务的优先级
select a responsible user: a person you want to delegate this task infolog zh 请指定受委派的用户, 将该事项委派给此人.
select a typ to edit it's status-values or delete it infolog zh 选定一个类型以编辑其状态值或删除
select an app to search in infolog zh 选定要查找的应用程序
select an entry to link with infolog zh 选定要链接的条目
should infolog display your open entries - not finised tasks, phonecalls or notes - on the main screen. works only if you dont selected an application for the main screen (in your preferences). infolog zh 待决事项指未完成的事项. 该选项仅当未设定首页应用程序时有效.
should infolog show subtasks, -calls or -notes in the normal view or not. you can always view the subs via there parent. infolog zh 浏览事项时是否显示子事项? 无论该选项如何设定, 用户总可以经由父事项来查看子事项.
should infolog show the links to other applications and/or the file-attachments in the infolog list (normal view when u enter infolog). infolog zh 浏览事项时是否显示各事项的关联条目和关联文件?
should infolog use full names (surname and familyname) or just the loginnames. infolog zh 在记事薄中显示姓名还是登录名.
should this entry only be visible to you and people you grant privat access via the acl infolog zh 是否仅所有者本人及其授权的用户可以访问该事项?
show full usernames infolog zh 显示完整的用户名
show in the infolog list infolog zh 浏览事项时是否显示关联?
show list of upcoming entries infolog zh 仅显示临近的事项
show open entries: tasks/calls/notes on main screen infolog zh 在首页显示待决事项
showing infolog zh 当前显示
small view infolog zh 小视图查看
start a new search, cancel this link infolog zh 不关联该条目, 重新开始搜索
startdate infolog zh 起始日期
startdate enddate infolog zh 起始日期 终止日期
startrecord infolog zh 起始记录
status infolog zh 状态
status ... infolog zh (状态)
sub infolog zh 子类别
subject infolog zh 标题
task infolog zh 任务
test import (show importable records <u>only</u> in browser) infolog zh 测试导入 (在浏览器中仅显示可导入的记录)
the name used internaly (<= 10 chars), changeing it makes existing data unavailible infolog zh 供系统使用的内部名称(最多10个字符), 更改该名称可能导致已有事项无效
the name used internaly (<= 20 chars), changeing it makes existing data unavailible infolog zh 供系统使用的内部名称(最多20个字符), 更改该名称可能导致已有事项无效
the name used internaly (<= 32 chars), changeing it makes existing data unavailible infolog zh 供系统使用的内部名称(最多32个字符), 更改该名称可能导致已有事项无效
the text displayed to the user infolog zh 字段的显示名
this is the filter infolog uses when you enter the application. filters limit the entries to show in the actual view. there are filters to show only finished, still open or futures entries of yourself or all users. infolog zh 浏览记事薄中的事项时, 默认查看哪些条目?
til when should the todo or phonecall be finished infolog zh 事项的中止日期, 仅对任务和函电有效.
to many might exceed your execution-time-limit infolog zh to many might exceed your execution-time-limit
today infolog zh 按日查看
todo infolog zh 任务
translation infolog zh 翻译
typ infolog zh 类型
typ '%1' already exists !!! infolog zh 类型'%1'已存在!!!
type infolog zh 类型
type ... infolog zh (类型)
type of the log-entry: note, phonecall or todo infolog zh 请选择该事项的类型: 任务, 函电或备注
unlink infolog zh 取消关联
upcoming infolog zh 临近事项
upload infolog zh 提交
urgency infolog zh 紧急
urgent infolog zh 最高
use button to search for address infolog zh 点击该按钮以查找地址
use button to search for project infolog zh 点击该按钮以查找项目
valid path on clientside<br>eg. \\\\\\\\server\\\\share or e:\\\\ infolog zh 客户端有效路径<br>例如. \\Server\Share 或 e:\
valid path on clientside<br>eg. \\\\server\\share or e:\\ infolog zh 客户端有效路径<br>例如. \\Server\Share 或 e:\
valid path on clientside<br>eg. \\server\share or e:\ infolog zh 客户端有效路径<br>例如. \\Server\Share 或 e:\
values for selectbox infolog zh 预设选项
view all subs of this entry infolog zh 查看该条目的所有子任务
view other subs infolog zh 查看其他子任务
view parent infolog zh 查看父任务
view subs infolog zh 查看子任务
view the parent of this entry and all his subs infolog zh 查看该条目的父任务及所有子任务
view this linked entry in its application infolog zh 查看该关联
when should the todo or phonecall be started, it shows up from that date in the filter open or own open (startpage) infolog zh 事项的起始日期, 仅对任务和函电有效.it shows up from that date in the filter open or own open (startpage)
whole query infolog zh 完整查询
will-call infolog zh 将致电
write (add or update) a record by passing its fields. infolog zh 通过事项的字段写入(新增或更新)一个记录
yes - delete infolog zh 是
you can't delete one of the stock types !!! infolog zh 无法删除stock类型!!!
you have entered an invalid ending date infolog zh 输入的终止日期无效
you have entered an invalid starting date infolog zh 输入的起始日期无效
you have to enter a name, to create a new typ!!! infolog zh 必须输入新建类型的名称!!!
you must enter a subject or a description infolog zh 必须输入事项的标题或内容

View File

@ -0,0 +1,118 @@
<?xml version="1.0" encoding="utf-8"?>
<entry type="array" name="importExportDefinitions">
<entry type="array" name="metainfo">
<entry type="string" name="type">importexport definitions</entry>
<entry type="string" name="charset">utf-8</entry>
<entry type="integer" name="entries">2</entry>
</entry>
<entry type="array" name="definitions">
<entry type="array" name="export-infolog">
<entry type="string" name="name">export-infolog</entry>
<entry type="string" name="application">infolog</entry>
<entry type="string" name="plugin">infolog_export_csv</entry>
<entry type="string" name="type">export</entry>
<entry type="array" name="allowed_users">
<entry type="string" name="0">Default</entry>
</entry>
<entry type="array" name="plugin_options">
<entry type="array" name="mapping">
<entry type="string" name="info_type">Type</entry>
<entry type="string" name="info_from">Contact</entry>
<entry type="string" name="info_subject">Subject</entry>
<entry type="string" name="info_des">Description</entry>
<entry type="string" name="info_addr">Phone/Email</entry>
<entry type="string" name="info_link_id">primary link</entry>
<entry type="string" name="info_cat">Category</entry>
<entry type="string" name="info_priority">Priority</entry>
<entry type="string" name="info_owner">Owner</entry>
<entry type="string" name="info_access">Access</entry>
<entry type="string" name="info_status">Status</entry>
<entry type="string" name="info_percent">Completed</entry>
<entry type="string" name="info_datecompleted">Date completed</entry>
<entry type="string" name="info_datemodified">Last changed</entry>
<entry type="string" name="info_location">Location</entry>
<entry type="string" name="info_startdate">Startdate</entry>
<entry type="string" name="info_enddate">Enddate</entry>
<entry type="string" name="info_responsible">Responsible</entry>
<entry type="string" name="info_planned_time">planned time</entry>
<entry type="string" name="info_used_time">used time</entry>
<entry type="string" name="pl_id">pricelist</entry>
<entry type="string" name="info_price">price</entry>
<entry type="string" name="all_custom_fields">infolog</entry>
</entry>
<entry type="string" name="delimiter">;</entry>
<entry type="string" name="charset">utf-8</entry>
<entry type="string" name="begin_with_fieldnames">label</entry>
<entry type="string" name="convert">1</entry>
</entry>
<entry type="string" name="modified">2011-03-02 09:58:16</entry>
</entry>
<entry type="array" name="import-infolog">
<entry type="string" name="name">import-infolog</entry>
<entry type="string" name="application">infolog</entry>
<entry type="string" name="plugin">infolog_import_infologs_csv</entry>
<entry type="string" name="type">import</entry>
<entry type="array" name="allowed_users">
<entry type="string" name="0">Default</entry>
</entry>
<entry type="array" name="plugin_options">
<entry type="string" name="fieldsep">;</entry>
<entry type="string" name="charset">utf-8</entry>
<entry type="string" name="num_header_lines">1</entry>
<entry type="array" name="csv_fields">
<entry type="string" name="0">info_from</entry>
<entry type="string" name="1">info_subject</entry>
<entry type="string" name="2">info_des</entry>
<entry type="string" name="3">info_addr</entry>
<entry type="string" name="4">info_link_id</entry>
<entry type="string" name="5">info_cat</entry>
<entry type="string" name="6">info_priority</entry>
<entry type="string" name="7">info_owner</entry>
<entry type="string" name="8">info_access</entry>
<entry type="string" name="9">info_status</entry>
<entry type="string" name="10">info_percent</entry>
<entry type="string" name="11">info_datecompleted</entry>
<entry type="string" name="12">info_datemodified</entry>
<entry type="string" name="13">info_location</entry>
<entry type="string" name="14">info_startdate</entry>
<entry type="string" name="15">info_enddate</entry>
<entry type="string" name="16">info_responsible</entry>
<entry type="string" name="17">info_planned_time</entry>
<entry type="string" name="18">info_used_time</entry>
<entry type="string" name="19">pl_id</entry>
<entry type="string" name="20">info_price</entry>
<entry type="string" name="21">no_csv_1</entry>
<entry type="string" name="22">no_csv_2</entry>
<entry type="string" name="23">no_csv_3</entry>
</entry>
<entry type="array" name="field_mapping">
<entry type="string" name="0">info_type</entry>
<entry type="string" name="1">info_from</entry>
<entry type="string" name="2">info_subject</entry>
<entry type="string" name="3">info_des</entry>
<entry type="string" name="4">info_addr</entry>
<entry type="string" name="5">info_link_id</entry>
<entry type="string" name="6">info_cat</entry>
<entry type="string" name="7">info_priority</entry>
<entry type="string" name="8">info_owner</entry>
<entry type="string" name="9">info_access</entry>
<entry type="string" name="10">info_status</entry>
<entry type="string" name="11">info_percent</entry>
<entry type="string" name="12">info_datecompleted</entry>
<entry type="string" name="13">info_datemodified</entry>
<entry type="string" name="14">info_location</entry>
<entry type="string" name="15">info_startdate</entry>
<entry type="string" name="16">info_enddate</entry>
<entry type="string" name="17">info_responsible</entry>
<entry type="string" name="18">info_planned_time</entry>
<entry type="string" name="19">info_used_time</entry>
<entry type="string" name="20">pl_id</entry>
<entry type="string" name="21">info_price</entry>
</entry>
<entry type="string" name="field_conversion"/>
<entry type="string" name="conditions"/>
</entry>
<entry type="string" name="modified">2011-03-01 18:06:41</entry>
</entry>
</entry>
</entry>

View File

@ -6,13 +6,13 @@
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @package infolog
* @subpackage setup
* @copyright (c) 2003-10 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2003-11 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
$setup_info['infolog']['name'] = 'infolog';
$setup_info['infolog']['version'] = '1.8';
$setup_info['infolog']['version'] = '1.9.003';
$setup_info['infolog']['app_order'] = 5;
$setup_info['infolog']['tables'] = array('egw_infolog','egw_infolog_extra');
$setup_info['infolog']['enable'] = 1;
@ -36,7 +36,7 @@ $setup_info['infolog']['description'] =
<p>Other documents / files can be linked to InfoLog entries and are store in the VFS
(eGroupWare\'s virtual file system).</p>';
$setup_info['infolog']['note'] =
'<p>There is a <b>CSV import filter</b> (in the admin-section) to import existing data.
'<p>There is a <b>CSV import</b> (in the admin-section) to import existing data.
It allows to interactivly assign fields, customize the values with regular
expressions and direct calls to php-functions (e.g. to link the phone calls
(again) to the addressbook entrys).</p>

View File

@ -0,0 +1,68 @@
<?php
/**
* EGroupware - InfoLog - Setup
*
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @package infolog
* @subpackage setup
* @copyright (c) 2003-11 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
$phpgw_baseline = array(
'egw_infolog' => array(
'fd' => array(
'info_id' => array('type' => 'auto','nullable' => False),
'info_type' => array('type' => 'varchar','precision' => '40','nullable' => False,'default' => 'task'),
'info_from' => array('type' => 'varchar','precision' => '255'),
'info_addr' => array('type' => 'varchar','precision' => '255'),
'info_subject' => array('type' => 'varchar','precision' => '255'),
'info_des' => array('type' => 'text'),
'info_owner' => array('type' => 'int','precision' => '4','nullable' => False),
'info_responsible' => array('type' => 'varchar','precision' => '255','nullable' => False,'default' => '0'),
'info_access' => array('type' => 'varchar','precision' => '10','default' => 'public'),
'info_cat' => array('type' => 'int','precision' => '4','nullable' => False,'default' => '0'),
'info_datemodified' => array('type' => 'int','precision' => '8','nullable' => False),
'info_startdate' => array('type' => 'int','precision' => '8','nullable' => False,'default' => '0'),
'info_enddate' => array('type' => 'int','precision' => '8','nullable' => False,'default' => '0'),
'info_id_parent' => array('type' => 'int','precision' => '4','nullable' => False,'default' => '0'),
'info_planned_time' => array('type' => 'int','precision' => '4','nullable' => False,'default' => '0'),
'info_replanned_time' => array('type' => 'int','precision' => '4','nullable' => False,'default' => '0'),
'info_used_time' => array('type' => 'int','precision' => '4','nullable' => False,'default' => '0'),
'info_status' => array('type' => 'varchar','precision' => '40','default' => 'done'),
'info_confirm' => array('type' => 'varchar','precision' => '10','default' => 'not'),
'info_modifier' => array('type' => 'int','precision' => '4','nullable' => False,'default' => '0'),
'info_link_id' => array('type' => 'int','precision' => '4','nullable' => False,'default' => '0'),
'info_priority' => array('type' => 'int','precision' => '2','default' => '1'),
'pl_id' => array('type' => 'int','precision' => '4'),
'info_price' => array('type' => 'float','precision' => '8'),
'info_percent' => array('type' => 'int','precision' => '2','default' => '0'),
'info_datecompleted' => array('type' => 'int','precision' => '8'),
'info_location' => array('type' => 'varchar','precision' => '255'),
'info_custom_from' => array('type' => 'int','precision' => '1'),
'info_uid' => array('type' => 'varchar','precision' => '255'),
'info_cc' => array('type' => 'varchar','precision' => '255'),
'caldav_name' => array('type' => 'varchar','precision' => '64','comment' => 'name part of CalDAV URL, if specified by client'),
'info_etag' => array('type' => 'int','precision' => '4','default' => '0','comment' => 'etag, not yet used'),
'info_created' => array('type' => 'int','precision' => '8'),
'info_creator' => array('type' => 'int','precision' => '4'),
),
'pk' => array('info_id'),
'fk' => array(),
'ix' => array('caldav_name',array('info_owner','info_responsible','info_status','info_startdate'),array('info_id_parent','info_owner','info_responsible','info_status','info_startdate')),
'uc' => array()
),
'egw_infolog_extra' => array(
'fd' => array(
'info_id' => array('type' => 'int','precision' => '4','nullable' => False),
'info_extra_name' => array('type' => 'varchar','precision' => '64','nullable' => False),
'info_extra_value' => array('type' => 'text','nullable' => False)
),
'pk' => array('info_id','info_extra_name'),
'fk' => array(),
'ix' => array(),
'uc' => array()
)
);

View File

@ -6,7 +6,7 @@
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @package infolog
* @subpackage setup
* @copyright (c) 2003-10 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2003-11 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
@ -634,3 +634,58 @@ function infolog_upgrade1_6()
{
return $GLOBALS['setup_info']['infolog']['currentver'] = '1.8';
}
function infolog_upgrade1_8()
{
$GLOBALS['egw_setup']->oProc->AddColumn('egw_infolog','info_cc',array(
'type' => 'varchar',
'precision' => '255'
));
return $GLOBALS['setup_info']['infolog']['currentver'] = '1.9.001';
}
/**
* Add column to store CalDAV name given by client and etag (not yet used!)
*/
function infolog_upgrade1_9_001()
{
$GLOBALS['egw_setup']->oProc->AddColumn('egw_infolog','caldav_name',array(
'type' => 'varchar',
'precision' => '64',
'comment' => 'name part of CalDAV URL, if specified by client'
));
$GLOBALS['egw_setup']->db->query('UPDATE egw_infolog SET caldav_name='.
$GLOBALS['egw_setup']->db->concat(
$GLOBALS['egw_setup']->db->to_varchar('info_id'),"'.ics'"),__LINE__,__FILE__);
$GLOBALS['egw_setup']->oProc->CreateIndex('egw_infolog','caldav_name');
$GLOBALS['egw_setup']->oProc->AddColumn('egw_infolog','info_etag',array(
'type' => 'int',
'precision' => '4',
'default' => '0',
'comment' => 'etag, not yet used'
));
$GLOBALS['egw_setup']->oProc->AddColumn('egw_infolog','info_created',array(
'type' => 'int',
'precision' => '8',
));
$GLOBALS['egw_setup']->oProc->AddColumn('egw_infolog','info_creator',array(
'type' => 'int',
'precision' => '4',
));
return $GLOBALS['setup_info']['infolog']['currentver'] = '1.9.002';
}
/**
* Fix caldav_name of subentries is identical with parent: not necessary
*/
function infolog_upgrade1_9_002()
{
return $GLOBALS['setup_info']['infolog']['currentver'] = '1.9.003';
}

View File

@ -0,0 +1,274 @@
<?xml version="1.0"?>
<!-- $Id$ -->
<overlay>
<template id="infolog.edit.description" template="" lang="" group="0" version="1.6.001">
<grid width="100%" height="245" border="0">
<columns>
<column width="100"/>
<column/>
</columns>
<rows>
<row class="th">
<description span="all" value="Description" options=",,,info_des"/>
</row>
<row class="row" valign="top">
<description/>
<textbox multiline="true" rows="13" no_lang="1" id="info_des" statustext="enter a textual description of the log-entry" class="description"/>
</row>
</rows>
</grid>
</template>
<template id="infolog.edit.links" template="" lang="" group="0" version="1.3.001">
<grid width="100%" height="245" overflow="auto">
<columns>
<column width="100"/>
<column/>
</columns>
<rows>
<row class="th" disabled="@status_only">
<description span="all" value="Create new links"/>
</row>
<row class="row" disabled="@status_only">
<link-to span="all" id="link_to"/>
</row>
<row>
<link-add span="all" id="link_to"/>
</row>
<row class="th">
<description span="all" value="Existing links"/>
</row>
<row class="row_off" valign="top">
<link-list span="all" id="link_to"/>
</row>
</rows>
</grid>
</template>
<template id="infolog.edit.delegation" template="" lang="" group="0" version="1.3.001">
<grid width="100%" height="245">
<columns>
<column width="100"/>
<column/>
</columns>
<rows>
<row class="th">
<description span="all" value="General"/>
</row>
<row class="row">
<description options=",,,info_priority" value="Priority"/>
<menulist>
<menupopup id="info_priority" statustext="select a priority for this task"/>
</menulist>
</row>
<row class="row">
<description options=",,,info_location" value="Location"/>
<textbox size="80" maxlength="255" id="info_location"/>
</row>
<row class="th">
<description span="all" value="Delegation"/>
</row>
<row class="row" valign="top">
<description options=",,,info_responsible" value="Responsible"/>
<listbox type="select-account" id="info_responsible" rows="10" options="both" statustext="select a responsible user: a person you want to delegate this task"/>
</row>
<row class="row" disabled="1">
<description options=",,,info_confirm" value="Confirm"/>
<menulist>
<menupopup id="info_confirm" statustext="do you want a confirmation of the responsible on: accepting, finishing the task or both"/>
</menulist>
</row>
</rows>
</grid>
</template>
<template id="infolog.edit.project" template="" lang="" group="0" version="1.5.004">
<grid width="100%" height="245">
<columns>
<column width="100"/>
<column/>
</columns>
<rows>
<row class="th">
<description span="all" value="Projectmanager"/>
</row>
<row class="row">
<description value="Project"/>
<projectmanager-select id="pm_id" onchange="1" options="None"/>
</row>
<row class="row">
<description value="Price"/>
<hbox span="all">
<projectmanager-pricelist id="pl_id" options="None" onchange="this.form['exec[info_price]'].value=this.options[this.selectedIndex].text.lastIndexOf('(') &lt; 0 ? '' : this.options[this.selectedIndex].text.slice(this.options[this.selectedIndex].text.lastIndexOf('(')+1,-1);"/>
<textbox type="float" id="info_price" span="all"/>
</hbox>
</row>
<row class="row">
<description options=",,,info_planned_time" value="planned time"/>
<date-duration id="info_planned_time" options=",$cont[duration_format]"/>
</row>
<row class="row">
<description options=",,,info_replanned_time" value="Re-planned time"/>
<date-duration id="info_replanned_time" options=",$cont[duration_format]"/>
</row>
<row class="row" valign="top" height="60%">
<description options=",,,info_used_time" value="used time" statustext="Leave blank to get the used time calculated by timesheet entries"/>
<date-duration id="info_used_time"/>
</row>
</rows>
</grid>
</template>
<template id="infolog.edit.customfields" template="" lang="" group="0" version="1.0.1.001">
<grid width="100%" height="245" class="row_on" spacing="0" padding="0" overflow="auto">
<columns>
<column/>
</columns>
<rows>
<row class="th" height="20">
<description value="Custom fields"/>
</row>
<row height="100%">
<customfields options="@info_type"/>
</row>
</rows>
</grid>
</template>
<template id="infolog.edit.history" template="" lang="" group="0" version="1.3.002">
<grid width="100%" height="245" overflow="auto">
<columns>
<column/>
</columns>
<rows>
<row valign="top">
<historylog id="history"/>
</row>
</rows>
</grid>
</template>
<template id="infolog.edit" template="" lang="" group="0" version="1.9.001">
<grid width="100%">
<columns>
<column width="103"/>
<column width="260"/>
<column width="140"/>
<column width="27%"/>
</columns>
<rows>
<row disabled="!@msg">
<html span="all" class="redItalic" align="center" id="msg" no_lang="1"/>
<description/>
<description/>
<description/>
</row>
<row class="th" height="28">
<description value="Type" options=",,,info_type"/>
<hbox span="all" options="0,0">
<menulist>
<menupopup id="info_type" onchange="1" statustext="Type of the log-entry: Note, Phonecall or ToDo" no_lang="1"/>
</menulist>
<int id="info_number" class="infoId" readonly="true"/>
</hbox>
</row>
<row class="row">
<description value="Contact" options=",,,info_from"/>
<vbox options="0,0">
<hbox options="0,0">
<link-entry id="info_contact" class="noWrap"/>
<checkbox id="info_custom_from" onchange="document.getElementById(form::name('info_from')).style.display=this.checked?'block':'none';" statustext="Check to specify custom contact"/>
</hbox>
<textbox size="36" maxlength="255" id="info_from" statustext="Custom contact-information, leave emtpy to use information from most recent link" blur="@blur_title" class="$cont[hide_from_css]"/>
</vbox>
<description options=",,,info_addr" value="Phone/Email"/>
<textbox size="30" maxlength="255" id="info_addr" statustext="Custom contact-address, leave empty to use information from most recent link" class="inputFullWidth"/>
</row>
<row class="row">
<description value="Category" options=",,,info_cat"/>
<menulist>
<menupopup type="select-cat" options="None" id="info_cat" statustext="select a category for this entry"/>
</menulist>
<hbox span="all">
<description value="Parent"/>
<link-entry options="infolog" id="info_id_parent" align="right" class="noWrap"/>
</hbox>
</row>
<row class="row">
<description value="Subject" options=",,,info_subject"/>
<textbox size="80" maxlength="255" span="all" id="info_subject" statustext="a short subject for the entry"/>
</row>
<row valign="top" height="250">
<tabbox span="all" id="description|links|delegation|project|customfields|history">
<tabs>
<tab label="Description" statustext="longer textual description"/>
<tab label="Links" statustext="Links of this entry"/>
<tab label="Delegation" statustext="responsible user, priority"/>
<tab label="Projectmanager" statustext="Project settings: price, times"/>
<tab label="Customfields" statustext="Custom fields"/>
<tab label="History" statustext="Change history"/>
</tabs>
<tabpanels>
<template id="infolog.edit.description"/>
<template id="infolog.edit.links"/>
<template id="infolog.edit.delegation"/>
<template id="infolog.edit.project"/>
<template id="infolog.edit.customfields"/>
<template id="infolog.edit.history"/>
</tabpanels>
</tabbox>
</row>
<row class="th" disabled="1">
<description span="all" value="Dates, Status, Access"/>
</row>
<row class="row_on">
<description value="Startdate" options=",,,info_startdate"/>
<date-time options=",2" id="info_startdate" statustext="when should the ToDo or Phonecall be started, it shows up from that date in the filter open or own open (startpage)"/>
<description value="Enddate" options=",,,info_enddate"/>
<date id="info_enddate" statustext="til when should the ToDo or Phonecall be finished"/>
</row>
<row class="row">
<description value="Status" options=",,,info_status"/>
<menulist>
<menupopup id="info_status" statustext="@status_help" onchange="if (this.value=='done' || this.value=='billed') set_element(this.form,'exec[info_percent]','100'); else if (this.value=='not-started') set_element(this.form,'exec[info_percent]','0');"/>
</menulist>
<description value="Completed" options=",,,info_percent"/>
<menulist>
<menupopup type="select-percent" id="info_percent" statustext="Percent completed" onchange="if (this.value==100 &amp;&amp; this.form['exec[info_status]'].value != 'done' &amp;&amp; this.form['exec[info_status]'].value != 'billed' &amp;&amp; this.form['exec[info_status]'].value != 'cancelled') this.form['exec[info_status]'].value='done'; else if (this.value != 100 &amp;&amp; this.form['exec[info_status]'].value != 'cancelled') this.form['exec[info_status]'].value=this.value != 0 ? 'ongoing' : 'not-started'; else if (this.value==0 &amp;&amp; this.form['exec[info_status]'].value != 'cancelled' &amp;&amp; this.form['exec[info_status]'].value != 'offer') this.form['exec[info_status]'].value='not-started'; "/>
</menulist>
</row>
<row class="row">
<description options=",,,info_datecompleted" value="Date completed"/>
<date-time id="info_datecompleted" statustext="Date completed (leave it empty to have it automatic set if status is done or billed)"/>
<description value="Private" options=",,,info_access"/>
<checkbox options="private,public" id="info_access" statustext="should this entry only be visible to you and people you grant privat access via the ACL"/>
</row>
<row class="row" disabled="!@info_owner">
<description value="Owner"/>
<menulist>
<menupopup type="select-account" id="info_owner" readonly="true"/>
</menulist>
<description value="Last modified"/>
<hbox options="0" orient="0">
<menulist>
<menupopup type="select-account" id="info_modifier" readonly="true"/>
</menulist>
<date-time class="lpadding5" id="info_datemodified" readonly="true"/>
</hbox>
</row>
<row>
<hbox span="3">
<button label="Save" id="button[save]" statustext="Saves this entry"/>
<button id="button[apply]" label="Apply" statustext="Apply the changes"/>
<button label="Cancel" id="button[cancel]" statustext="leave without saveing the entry" onclick="window.close();"/>
<menulist>
<menupopup id="action" statustext="Execute a further action for this entry" options="Actions..." onchange="this.form.submit(); this.value='';"/>
</menulist>
<checkbox label="Do not notify of these changes" id="no_notifications" span="2"/>
<html id="js"/>
</hbox>
<button label="Delete" align="right" id="button[delete]" statustext="delete this entry" onclick="return $cont[info_anz_subs] || confirm('Delete this entry');"/>
</row>
</rows>
</grid>
<styles>
.hideFrom input { display: none; }
.link_select select { width: 250px; }
.description textarea { width: 98%; }
</styles>
</template>
</overlay>

View File

@ -14,7 +14,7 @@
</hbox>
</template>
<template id="infolog.index.rows-noheader" template="" lang="" group="0" version="1.7.005">
<grid>
<grid width="100%" class="my_form">
<columns>
<column width="2%"/>
<column/>
@ -146,6 +146,7 @@
<description/>
<description/>
</hbox>
<image src="filemanager/navbar" options="/index.php?menuaction=filemanager.filemanager_ui.index&amp;path=/apps/infolog/$row_cont[info_id]" class="image16" label="Filemanager"/>
</vbox>
</row>
</rows>
@ -296,6 +297,7 @@
<button image="done" label="Set status to done" id="close[$row_cont[info_id]]" statustext="Sets the status of this entry to done"/>
<button image="done_all" label="Set status to done for all entries" id="close_all[$row_cont[info_id]]" statustext="Sets the status of this entry and its subs to done"/>
</hbox>
<image src="filemanager/navbar" options="/index.php?menuaction=filemanager.filemanager_ui.index&amp;path=/apps/infolog/$row_cont[info_id]" class="image16" label="Filemanager"/>
</vbox>
</row>
</rows>

View File

@ -215,7 +215,8 @@ class accounts
* Searches / lists accounts: users and/or groups
*
* @param array with the following keys:
* @param $param['type'] string/int 'accounts', 'groups', 'owngroups' (groups the user is a member of), 'both'
* @param $param['type'] string/int 'accounts', 'groups', 'owngroups' (groups the user is a member of), 'both',
* 'groupmembers' (members of groups the user is a member of), 'groupmembers+memberships' (incl. memberships too)
* or integer group-id for a list of members of that group
* @param $param['start'] int first account to return (returns offset or max_matches entries) or all if not set
* @param $param['order'] string column to sort after, default account_lid if unset
@ -233,7 +234,7 @@ class accounts
*/
function search($param)
{
//echo "<p>accounts::search(".print_r($param,True).") start: ".microtime()."</p>\n";
//error_log(__METHOD__.'('.array2string($param).')');
self::setup_cache();
$account_search = &self::$cache['account_search'];
$serial = serialize($param);
@ -245,7 +246,7 @@ class accounts
// no backend understands $param['app'] and sql does not understand group-parameters
// --> do an full search first and then filter and limit that search
elseif($param['app'] || $this->config['account_repository'] != 'ldap' &&
(is_numeric($param['type']) || $param['type'] == 'owngroups'))
(is_numeric($param['type']) || in_array($param['type'],array('owngroups','groupmembers','groupmembers+memberships'))))
{
$app = $param['app'];
unset($param['app']);
@ -254,14 +255,24 @@ class accounts
if ($this->config['account_repository'] != 'ldap' && is_numeric($param['type']))
{
$group = (int) $param['type'];
$members = $this->members($param['type'],true);
$param['type'] = 'accounts';
}
elseif ($param['type'] == 'owngroups')
{
$group = true;
$members = $this->memberships($GLOBALS['egw_info']['user']['account_id'],true);
$param['type'] = 'groups';
}
elseif(in_array($param['type'],array('groupmembers','groupmembers+memberships')))
{
$members = array();
foreach($this->memberships($GLOBALS['egw_info']['user']['account_id'],true) as $grp)
{
$members = array_unique(array_merge($members,$this->members($grp,true)));
if ($param['type'] == 'groupmembers+memberships') $members[] = $grp;
}
$param['type'] = $param['type'] == 'groupmembers+memberships' ? 'both' : 'accounts';
}
// call ourself recursive to get (evtl. cached) full search
$full_search = $this->search($param);
@ -272,9 +283,9 @@ class accounts
// we want the result merged, whatever it takes, as we only care for the ids
$valid = $this->split_accounts($app,!in_array($param['type'],array('accounts','groups')) ? 'merge' : $param['type']);
}
if ($group)
if (isset($members))
{
$members = is_int($group) ? $this->members($group,true) : $this->memberships($GLOBALS['egw_info']['user']['account_id'],true);
//error_log(__METHOD__.'() members='.array2string($members));
if (!$members) $members = array();
$valid = !$app ? $members : array_intersect($valid,$members); // use the intersection
}

View File

@ -82,20 +82,20 @@ class auth
}
/**
* return a random string of size $size
* return a random string of letters [0-9a-zA-Z] of size $size
*
* @param $size int-size of random string to return
*/
static function randomstring($size)
{
$s = '';
$random_char = array(
static $random_char = array(
'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f',
'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
'w','x','y','z','A','B','C','D','E','F','G','H','I','J','K','L',
'M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'
);
$s = '';
for ($i=0; $i<$size; $i++)
{
$s .= $random_char[mt_rand(1,61)];
@ -124,14 +124,17 @@ class auth
*
* encryption type set in setup and calls the appropriate encryption functions
*
* @param $cleartext cleartext password
* @param $encrypted encrypted password, can have a {hash} prefix, which overrides $type
* @param $type type of encryption
* @param $username used as optional key of encryption for md5_hmac
* @param string $cleartext cleartext password
* @param string $encrypted encrypted password, can have a {hash} prefix, which overrides $type
* @param string $type_i type of encryption
* @param string $username used as optional key of encryption for md5_hmac
* @param string &$type=null on return detected type of hash
* @return boolean
*/
static function compare_password($cleartext,$encrypted,$type,$username='')
static function compare_password($cleartext, $encrypted, $type_in, $username='', &$type=null)
{
// allow to specify the hash type to prefix the hash, to easy migrate passwords from ldap
$type = $type_in;
$saved_enc = $encrypted;
if (preg_match('/^\\{([a-z_5]+)\\}(.+)$/i',$encrypted,$matches))
{
@ -149,34 +152,100 @@ class auth
break;
default:
$encrypted = $saved_enc;
// ToDo: the others ...
break;
}
}
elseif($encrypted[0] == '$')
{
$type = 'crypt';
}
switch($type)
{
case 'plain':
if(strcmp($cleartext,$encrypted) == 0)
{
return True;
}
return False;
$ret = $cleartext === $encrypted;
break;
case 'smd5':
return self::smd5_compare($cleartext,$encrypted);
$ret = self::smd5_compare($cleartext,$encrypted);
break;
case 'sha':
return self::sha_compare($cleartext,$encrypted);
$ret = self::sha_compare($cleartext,$encrypted);
break;
case 'ssha':
return self::ssha_compare($cleartext,$encrypted);
$ret = self::ssha_compare($cleartext,$encrypted);
break;
case 'crypt':
case 'des':
case 'md5_crypt':
case 'blowish_crypt': // was for some time a typo in setup
case 'blowfish_crypt':
case 'ext_crypt':
return self::crypt_compare($cleartext,$encrypted,$type);
case 'sha256_crypt':
case 'sha512_crypt':
$ret = self::crypt_compare($cleartext, $encrypted, $type);
break;
case 'md5_hmac':
return self::md5_hmac_compare($cleartext,$encrypted,$username);
case 'md5':
$ret = self::md5_hmac_compare($cleartext,$encrypted,$username);
break;
default:
return strcmp(md5($cleartext),$encrypted) == 0 ? true : false;
$type = 'md5';
// fall through
case 'md5':
$ret = md5($cleartext) === $encrypted;
break;
}
//error_log(__METHOD__."('$cleartext', '$encrypted', '$type_in', '$username') type='$type' returning ".array2string($ret));
return $ret;
}
/**
* Parameters used for crypt: const name, salt prefix, len of random salt, postfix
*
* @var array
*/
static $crypt_params = array( //
'crypt' => array('CRYPT_STD_DES', '', 2, ''),
'ext_crypt' => array('CRYPT_EXT_DES', '_J9..', 4, ''),
'md5_crypt' => array('CRYPT_MD5', '$1$', 8, '$'),
//'old_blowfish_crypt' => array('CRYPT_BLOWFISH', '$2$', 13, ''), // old blowfish hash not in line with php.net docu, but could be in use
'blowfish_crypt' => array('CRYPT_BLOWFISH', '$2a$12$', 22, ''), // $2a$12$ = 2^12 = 4096 rounds
'sha256_crypt' => array('CRYPT_SHA256', '$5$', 16, '$'), // no "round=N$" --> default of 5000 rounds
'sha512_crypt' => array('CRYPT_SHA512', '$6$', 16, '$'), // no "round=N$" --> default of 5000 rounds
);
/**
* compare crypted passwords for authentication whether des,ext_des,md5, or blowfish crypt
*
* @param string $form_val user input value for comparison
* @param string $db_val stored value / hash (from database)
* @param string &$type detected crypt type on return
* @return boolean True on successful comparison
*/
static function crypt_compare($form_val, $db_val, &$type)
{
// detect type of hash by salt part of $db_val
list($first, $dollar, $salt, $salt2) = explode('$', $db_val);
foreach(self::$crypt_params as $type => $params)
{
list(,$prefix, $random, $postfix) = $params;
list(,$d) = explode('$', $prefix);
if ($dollar === $d || !$dollar && ($first[0] === $prefix[0] || $first[0] !== '_' && !$prefix))
{
$len = !$postfix ? strlen($prefix)+$random : strlen($prefix.$salt.$postfix);
// sha(256|512) might contain options, explicit $rounds=N$ prefix in salt
if (($type == 'sha256_crypt' || $type == 'sha512_crypt') && substr($salt, 0, 7) === 'rounds=')
{
$len += strlen($salt2)+1;
}
break;
}
}
$salt = substr($db_val, 0, $len);
$new_hash = crypt($form_val, $salt);
//error_log(__METHOD__."('$form_val', '$db_val') type=$type --> len=$len --> salt='$salt' --> new_hash='$new_hash' returning ".array2string($db_val === $new_hash));
return $db_val === $new_hash;
}
/**
@ -191,41 +260,30 @@ class auth
static function encrypt_ldap($password, $type=null)
{
if (is_null($type)) $type = $GLOBALS['egw_info']['server']['ldap_encryption_type'];
$salt = '';
switch(strtolower($type))
{
default: // eg. setup >> config never saved
case 'des':
$salt = self::randomstring(2);
$_password = crypt($password, $salt);
$e_password = '{crypt}'.$_password;
break;
case 'blowish_crypt': // was for some time a typo in setup
$type = $type == 'blowish_crypt' ? 'blowfish_crypt' : 'crypt';
// fall through
case 'crypt':
case 'sha256_crypt':
case 'sha512_crypt':
case 'blowfish_crypt':
if(@defined('CRYPT_BLOWFISH') && CRYPT_BLOWFISH == 1)
{
$salt = '$2$' . self::randomstring(13);
$e_password = '{crypt}'.crypt($password,$salt);
break;
}
self::$error = 'no blowfish crypt';
break;
case 'md5_crypt':
if(@defined('CRYPT_MD5') && CRYPT_MD5 == 1)
{
$salt = '$1$' . self::randomstring(9);
$e_password = '{crypt}'.crypt($password,$salt);
break;
}
self::$error = 'no md5 crypt';
break;
case 'ext_crypt':
if(@defined('CRYPT_EXT_DES') && CRYPT_EXT_DES == 1)
list($const, $prefix, $len, $postfix) = self::$crypt_params[$type];
if(defined($const) && constant($const) == 1)
{
$salt = self::randomstring(9);
$e_password = '{crypt}'.crypt($password,$salt);
$salt = $prefix.self::randomstring($len).$postfix;
$e_password = '{crypt}'.crypt($password, $salt);
break;
}
self::$error = 'no ext crypt';
self::$error = 'no '.str_replace('_', ' ', $type);
$e_password = false;
break;
case 'md5':
/* New method taken from the openldap-software list as recommended by
@ -235,7 +293,7 @@ class auth
break;
case 'smd5':
$salt = self::randomstring(16);
$hash = md5($password . $salt,true);
$hash = md5($password . $salt, true);
$e_password = '{SMD5}' . base64_encode($hash . $salt);
break;
case 'sha':
@ -243,14 +301,15 @@ class auth
break;
case 'ssha':
$salt = self::randomstring(16);
$hash = sha1($password . $salt,true);
$hash = sha1($password . $salt, true);
$e_password = '{SSHA}' . base64_encode($hash . $salt);
break;
case 'plain':
// if plain no type is prepended
$e_password =$password;
$e_password = $password;
break;
}
//error_log(__METHOD__."('$password', ".array2string($type).") returning ".array2string($e_password).(self::$error ? ' error='.self::$error : ''));
return $e_password;
}
@ -263,67 +322,43 @@ class auth
static function encrypt_sql($password)
{
/* Grab configured type, or default to md5() (old method) */
$type = @$GLOBALS['egw_info']['server']['sql_encryption_type']
? strtolower($GLOBALS['egw_info']['server']['sql_encryption_type'])
: 'md5';
$type = @$GLOBALS['egw_info']['server']['sql_encryption_type'] ?
strtolower($GLOBALS['egw_info']['server']['sql_encryption_type']) : 'md5';
switch($type)
{
case 'plain':
// since md5 is the default, type plain must be prepended, for eGroupware to understand
return '{PLAIN}'.$password;
case 'crypt':
if(@defined('CRYPT_STD_DES') && CRYPT_STD_DES == 1)
{
$salt = self::randomstring(2);
return crypt($password,$salt);
}
self::$error = 'no std crypt';
$e_password = '{PLAIN}'.$password;
break;
case 'blowfish_crypt':
if(@defined('CRYPT_BLOWFISH') && CRYPT_BLOWFISH == 1)
{
$salt = '$2$' . self::randomstring(13);
return crypt($password,$salt);
}
self::$error = 'no blowfish crypt';
break;
case 'md5_crypt':
if(@defined('CRYPT_MD5') && CRYPT_MD5 == 1)
{
$salt = '$1$' . self::randomstring(9);
return crypt($password,$salt);
}
self::$error = 'no md5 crypt';
break;
case 'ext_crypt':
if(@defined('CRYPT_EXT_DES') && CRYPT_EXT_DES == 1)
{
$salt = self::randomstring(9);
return crypt($password,$salt);
}
self::$error = 'no ext crypt';
break;
case 'smd5':
$salt = self::randomstring(16);
$hash = md5($password . $salt,true);
return '{SMD5}' . base64_encode($hash . $salt);
case 'sha':
return '{SHA}' . base64_encode(sha1($password,true));
case 'ssha':
$salt = self::randomstring(16);
$hash = sha1($password . $salt,true);
return '{SSHA}' . base64_encode($hash . $salt);
case 'md5':
default:
/* This is the old standard for password storage in SQL */
return md5($password);
$e_password = md5($password);
break;
// all other types are identical to ldap, so no need to doublicate the code here
case 'des':
case 'blowish_crypt': // was for some time a typo in setup
case 'crypt':
case 'sha256_crypt':
case 'sha512_crypt':
case 'blowfish_crypt':
case 'md5_crypt':
case 'ext_crypt':
case 'smd5':
case 'sha':
case 'ssha':
$e_password = self::encrypt_ldap($password, $type);
break;
default:
self::$error = 'no valid encryption available';
$e_password = false;
break;
}
if (!self::$error)
{
self::$error = 'no valid encryption available';
}
return False;
//error_log(__METHOD__."('$password') using '$type' returning ".array2string($e_password).(self::$error ? ' error='.self::$error : ''));
return $e_password;
}
/**
@ -423,31 +458,6 @@ class auth
return strcmp($orig_hash,$new_hash) == 0;
}
/**
* compare crypted passwords for authentication whether des,ext_des,md5, or blowfish crypt
*
* @param string $form_val user input value for comparison
* @param string $db_val stored value (from database)
* @param string $type crypt() type
* @return boolean True on successful comparison
*/
static function crypt_compare($form_val,$db_val,$type)
{
$saltlen = array(
'blowfish_crypt' => 16,
'md5_crypt' => 12,
'ext_crypt' => 9,
'crypt' => 2
);
// PHP's crypt(): salt + hash
// notice: "The encryption type is triggered by the salt argument."
$salt = substr($db_val, 0, (int)$saltlen[$type]);
$new_hash = crypt($form_val, $salt);
return strcmp($db_val,$new_hash) == 0;
}
/**
* compare md5_hmac-encrypted passwords for authentication (see RFC2104)
*

View File

@ -128,8 +128,10 @@ class auth_ldap implements auth_backend
// try to query password from ldap server (might fail because of ACL) and check if we need to migrate the hash
if (($sri = ldap_search($ldap, $userDN,"(objectclass=*)", array('userPassword'))) &&
($values = ldap_get_entries($ldap, $sri)) && isset($values[0]['userpassword'][0]) &&
($type = preg_match('/^{(.+)}/',$values[0]['userpassword'][0],$matches) ? $matches[1] : 'plain') &&
in_array(strtolower($type),explode(',',strtolower($GLOBALS['egw_info']['server']['pwd_migration_types']))))
($type = preg_match('/^{(.+)}/',$values[0]['userpassword'][0],$matches) ? strtolower($matches[1]) : 'plain') &&
// for crypt use auth::crypt_compare to detect correct sub-type, strlen("{crypt}")=7
($type != 'crypt' || auth::crypt_compare($passwd, substr($values[0]['userpassword'][0], 7), $type)) &&
in_array($type, explode(',',strtolower($GLOBALS['egw_info']['server']['pwd_migration_types']))))
{
$this->change_password($passwd, $passwd, $allValues[0]['uidnumber'][0], false);
}

View File

@ -69,22 +69,27 @@ class auth_sql implements auth_backend
{
return false;
}
if(!($match = auth::compare_password($passwd,$row['account_pwd'],$this->type,strtolower($username))) ||
preg_match('/^{(.+)}/',$row['account_pwd'],$matches) && // explicit specified hash, eg. from ldap
in_array(strtolower($matches[1]),explode(',',strtolower($GLOBALS['egw_info']['server']['pwd_migration_types']))))
if(!($match = auth::compare_password($passwd, $row['account_pwd'], $this->type, strtolower($username), $type)) ||
$type != $this->type && in_array($type, explode(',',strtolower($GLOBALS['egw_info']['server']['pwd_migration_types']))))
{
// do we have to migrate an old password ?
if($GLOBALS['egw_info']['server']['pwd_migration_allowed'] && !empty($GLOBALS['egw_info']['server']['pwd_migration_types']))
{
foreach(explode(',', $GLOBALS['egw_info']['server']['pwd_migration_types']) as $type)
if (!$match)
{
if(($match = auth::compare_password($passwd,$row['account_pwd'],$type,strtolower($username))))
foreach(explode(',', $GLOBALS['egw_info']['server']['pwd_migration_types']) as $type)
{
$encrypted_passwd = auth::encrypt_sql($passwd);
$this->_update_passwd($encrypted_passwd,$passwd,$row['account_id'],false,true);
break;
if(($match = auth::compare_password($passwd,$row['account_pwd'],$type,strtolower($username))))
{
break;
}
}
}
if ($match)
{
$encrypted_passwd = auth::encrypt_sql($passwd);
$this->_update_passwd($encrypted_passwd,$passwd,$row['account_id'],false,true);
}
}
if (!$match) return false;
}

View File

@ -222,6 +222,14 @@ class egw_db
* DB requires varchar columns to be truncated to the max. size (eg. Postgres)
*/
const CAPABILITY_REQUIRE_TRUNCATE_VARCHAR = 'require_truncate_varchar';
/**
* How to cast a column to varchar: CAST(%s AS varchar)
*
* MySQL requires to use CAST(%s AS char)!
*
* Use as: $sql = sprintf($GLOBALS['egw']->db->capabilities[egw_db::CAPABILITY_CAST_AS_VARCHAR],$expression);
*/
const CAPABILITY_CAST_AS_VARCHAR = 'cast_as_varchar';
/**
* default capabilities will be changed by method set_capabilities($ado_driver,$db_version)
*
@ -240,6 +248,7 @@ class egw_db
self::CAPABILITY_CLIENT_ENCODING => false,
self::CAPABILITY_CASE_INSENSITIV_LIKE => 'LIKE',
self::CAPABILITY_REQUIRE_TRUNCATE_VARCHAR => true,
self::CAPABILITY_CAST_AS_VARCHAR => 'CAST(%s AS varchar)',
);
var $prepared_sql = array(); // sql is the index
@ -265,6 +274,7 @@ class egw_db
$this->$var = $db_data[$key];
}
}
//if ($GLOBALS['egw_info']['server']['default_domain'] == 'ralfsmacbook.local') $this->query_log = '/tmp/query.log';
}
/**
@ -498,6 +508,7 @@ class egw_db
$this->capabilities[self::CAPABILITY_UNION] = (float) $db_version >= 4.0;
$this->capabilities[self::CAPABILITY_NAME_CASE] = 'preserv';
$this->capabilities[self::CAPABILITY_CLIENT_ENCODING] = (float) $db_version >= 4.1;
$this->capabilities[self::CAPABILITY_CAST_AS_VARCHAR] = 'CAST(%s AS char)';
break;
case 'postgres':
@ -836,7 +847,7 @@ class egw_db
if ($id === False) // function not supported
{
echo "<p>db::get_last_insert_id(table='$table',field='$field') not yet implemented for db-type '$this->Type' OR no insert operation before</p>\n";
function_backtrace();
echo '<p>'.function_backtrace()."</p>\n";
return -1;
}
return $id;
@ -1251,7 +1262,7 @@ class egw_db
case 'mssql':
return "DATEDIFF(second,'1970-01-01',($expr))";
}
}
/**
@ -1313,6 +1324,38 @@ class egw_db
return false;
}
/**
* Cast a column or sql expression to integer, necessary at least for postgreSQL
*
* @param string $expr
* @return string
*/
function to_int($expr)
{
switch($this->Type)
{
case 'pgsql':
return $expr.'::integer';
}
return $expr;
}
/**
* Cast a column or sql expression to varchar, necessary at least for postgreSQL
*
* @param string $expr
* @return string
*/
function to_varchar($expr)
{
switch($this->Type)
{
case 'pgsql':
return 'CAST('.$expr.' AS varchar)';
}
return $expr;
}
/**
* Correctly Quote Identifiers like table- or colmnnames for use in SQL-statements
*

View File

@ -389,7 +389,7 @@ abstract class egw_framework
{
if( $GLOBALS['egw_info']['user']['apps']['admin'] && $GLOBALS['egw_info']['user']['preferences']['common']['show_currentusers'])
{
$current_users = '<a href="' . egw::link('/index.php','menuaction=admin.uicurrentsessions.list_sessions') . '">' .
$current_users = '<a href="' . egw::link('/index.php','menuaction=admin.admin_accesslog.sessions') . '">' .
lang('Current users') . ': ' . $GLOBALS['egw']->session->session_count() . '</a>';
return $current_users;
}

View File

@ -132,6 +132,13 @@ class egw_session
*/
var $kp3;
/**
* Primary key of egw_access_log row for updates
*
* @var int
*/
var $sessionid_access_log;
/**
* name of XML-RPC/SOAP method called
*
@ -579,7 +586,7 @@ class egw_session
$this->register_session($this->login,$user_ip,$now,$this->session_flags);
if ($this->session_flags != 'A') // dont log anonymous sessions
{
$this->log_access($this->sessionid,$login,$user_ip,$this->account_id);
$this->sessionid_access_log = $this->log_access($this->sessionid,$login,$user_ip,$this->account_id);
}
self::appsession('account_previous_login','phpgwapi',$GLOBALS['egw']->auth->previous_login);
$GLOBALS['egw']->accounts->update_lastlogin($this->account_id,$user_ip);
@ -640,10 +647,11 @@ class egw_session
/**
* Write or update (for logout) the access_log
*
* @param string $sessionid id of session or 0 for unsuccessful logins
* @param string|int $sessionid PHP sessionid or 0 for unsuccessful logins
* @param string $login account_lid (evtl. with domain) or '' for settion the logout-time
* @param string $user_ip ip to log
* @param int $account_id numerical account_id
* @return int $sessionid primary key of egw_access_log for login, null otherwise
*/
private function log_access($sessionid,$login='',$user_ip='',$account_id='')
{
@ -652,17 +660,24 @@ class egw_session
if ($login)
{
$GLOBALS['egw']->db->insert(self::ACCESS_LOG_TABLE,array(
'sessionid' => $sessionid,
'session_php' => $sessionid,
'loginid' => $login,
'ip' => $user_ip,
'li' => $now,
'lo' => 0,
'account_id'=> $account_id,
),false,__LINE__,__FILE__);
$ret = $GLOBALS['egw']->db->get_last_insert_id(self::ACCESS_LOG_TABLE,'sessionid');
}
else
{
$GLOBALS['egw']->db->update(self::ACCESS_LOG_TABLE,array('lo' => $now),array('sessionid' => $sessionid),__LINE__,__FILE__);
$GLOBALS['egw']->db->update(self::ACCESS_LOG_TABLE,array(
'lo' => $now
),is_numeric($sessionid) ? array(
'sessionid' => $sessionid,
) : array(
'session_php' => $sessionid,
),__LINE__,__FILE__);
}
if ($GLOBALS['egw_info']['server']['max_access_log_age'])
{
@ -670,6 +685,8 @@ class egw_session
$GLOBALS['egw']->db->delete(self::ACCESS_LOG_TABLE,"li < $max_age",__LINE__,__FILE__);
}
//error_log(__METHOD__."('$sessionid', '$login', '$user_ip', $account_id) returning ".array2string($ret));
return $ret;
}
/**
@ -866,6 +883,10 @@ class egw_session
{
$this->update_dla();
}
elseif ($GLOBALS['egw_info']['flags']['currentapp'] == 'notifications')
{
$this->update_notification_heartbeat();
}
$this->account_id = $GLOBALS['egw']->accounts->name2id($this->account_lid,'account_lid','u');
if (!$this->account_id)
{
@ -964,6 +985,15 @@ class egw_session
//echo 'DEBUG: Sessions: account_id is empty!<br>'."\n";
return false;
}
// query accesslog-id, if not set in session (session is made persistent after login!)
if (!$this->sessionid_access_log)
{
$this->sessionid_access_log = $GLOBALS['egw']->db->select(self::ACCESS_LOG_TABLE,'sessionid',array(
'session_php' => $this->sessionid,
),__LINE__,__FILE__)->fetchColumn();
//error_log(__METHOD__."() sessionid=$this->sessionid --> sessionid_access_log=$this->sessionid_access_log");
}
if (self::ERROR_LOG_DEBUG) error_log("--> session::verify($sessionid) SUCCESS");
return true;
@ -976,16 +1006,23 @@ class egw_session
* @param string $kp3
* @return boolean true on success, false on error
*/
function destroy($sessionid, $kp3)
function destroy($sessionid, $kp3='')
{
if (!$sessionid && $kp3)
{
return false;
}
$this->log_access($this->sessionid); // log logout-time
$this->log_access($sessionid); // log logout-time
if (self::ERROR_LOG_DEBUG) error_log(__METHOD__."($sessionid,$kp3) parent::destroy()=$ret");
if (is_numeric($sessionid)) // do we have a access-log-id --> get PHP session id
{
$sessionid = $GLOBALS['egw']->db->select(self::ACCESS_LOG_TABLE,'session_php',array(
'sessionid' => $sessionid,
),__LINE__,__FILE__)->fetchColumn();
}
$GLOBALS['egw']->hooks->process(array(
'location' => 'session_destroyed',
'sessionid' => $sessionid,
@ -1011,12 +1048,12 @@ class egw_session
}
else
{
$sessions = self::session_list(0,'','',true);
$this->commit_session(); // close our own session
if (isset($sessions[$sessionid]) && session_module_name() == 'files')
session_id($sessionid);
if (session_start())
{
//echo '<p>'.__METHOD__."($session_id): unlink('".$sessions[$sessionid]['php_session_file']."')</p>\n";
@unlink($sessions[$sessionid]['php_session_file']);
session_destroy();
}
}
return true;
@ -1320,24 +1357,41 @@ class egw_session
}
/**
* Update session_action and session_dla (session last used time),
* Update session_action and session_dla (session last used time)
*/
private function update_dla()
{
if (isset($_GET['menuaction']))
// This way XML-RPC users aren't always listed as xmlrpc.php
if ($this->xmlrpc_method_called)
{
$action = $this->xmlrpc_method_called;
}
elseif (isset($_GET['menuaction']))
{
$action = $_GET['menuaction'];
}
else
{
$action = $_SERVER['PHP_SELF'];
// remove EGroupware path, if not installed in webroot
$egw_path = $GLOBALS['egw_info']['server']['webserver_url'];
if ($egw_path[0] != '/') $egw_path = parse_url($egw_path,PHP_URL_PATH);
if ($egw_path)
{
list(,$action) = explode($egw_path,$action,2);
}
}
// This way XML-RPC users aren't always listed as
// xmlrpc.php
if ($this->xmlrpc_method_called)
// update dla in access-log table, if we have an access-log row (non-anonymous session)
if ($this->sessionid_access_log)
{
$action = $this->xmlrpc_method_called;
$GLOBALS['egw']->db->update(self::ACCESS_LOG_TABLE,array(
'session_dla' => time(),
'session_action' => $action,
'lo' => null, // just in case it was (automatic) timed out before
),array(
'sessionid' => $this->sessionid_access_log,
),__LINE__,__FILE__);
}
$_SESSION[self::EGW_SESSION_VAR]['session_dla'] = time();
@ -1345,6 +1399,23 @@ class egw_session
if (self::ERROR_LOG_DEBUG) error_log(__METHOD__.'() _SESSION['.self::EGW_SESSION_VAR.']='.array2string($_SESSION[self::EGW_SESSION_VAR]));
}
/**
* Update notification_heartbeat time of session
*/
private function update_notification_heartbeat()
{
// update dla in access-log table, if we have an access-log row (non-anonymous session)
if ($this->sessionid_access_log)
{
$GLOBALS['egw']->db->update(self::ACCESS_LOG_TABLE,array(
'notification_heartbeat' => time(),
),array(
'sessionid' => $this->sessionid_access_log,
'lo IS NULL',
),__LINE__,__FILE__);
}
}
/**
* Read the diverse repositories / init classes with data from the just loged in user
*
@ -1473,32 +1544,80 @@ class egw_session
* Get a session list (of the current instance)
*
* @param int $start
* @param string $sort='session_dla' session_lid, session_id, session_started, session_logintime, session_action, or (default) session_dla
* @param string $order='DESC' ASC or DESC
* @param string $sort='DESC' ASC or DESC
* @param string $order='session_dla' session_lid, session_id, session_started, session_logintime, session_action, or (default) session_dla
* @param boolean $all_no_sort=False skip sorting and limiting to maxmatchs if set to true
* @return array with sessions (values for keys as in $sort) or array() if not supported by session-handler
* @return array with sessions (values for keys as in $sort)
*/
public static function session_list($start,$sort='DESC',$order='session_dla',$all_no_sort=False)
{
if (method_exists(self::$session_handler,'session_list'))
$sessions = array();
if (!preg_match('/^[a-z0-9_ ,]+$/i',$order_by=$order.' '.$sort))
{
return call_user_func(array(self::$session_handler,'session_list'),$start,$sort,$order,$all_no_sort);
$order_by = 'session_dla DESC';
}
return array();
foreach($GLOBALS['egw']->db->select(self::ACCESS_LOG_TABLE, '*', array(
'lo' => null,
'session_dla > '.(int)(time() - $GLOBALS['egw_info']['server']['sessions_timeout']),
'(notification_heartbeat IS NULL OR notification_heartbeat > '.self::heartbeat_limit().')',
), __LINE__, __FILE__, $all_no_sort ? false : $start, 'ORDER BY '.$order_by) as $row)
{
$sessions[$row['sessionid']] = $row;
}
return $sessions;
}
/**
* Query number of sessions (not more then once every N secs)
*
* @return int|boolean integer number of sessions or false if not supported by session-handler
* @return int number of active sessions
*/
public static function session_count()
{
if (method_exists(self::$session_handler,'session_count'))
return $GLOBALS['egw']->db->select(self::ACCESS_LOG_TABLE, 'COUNT(*)', array(
'lo' => null,
'session_dla > '.(int)(time() - $GLOBALS['egw_info']['server']['sessions_timeout']),
'(notification_heartbeat IS NULL OR notification_heartbeat > '.self::heartbeat_limit().')',
), __LINE__, __FILE__)->fetchColumn();
}
/**
* Get limit / latest time of heartbeat for session to be active
*
* @return int TS in server-time
*/
public static function heartbeat_limit()
{
static $limit;
if (is_null($limit))
{
return call_user_func(array(self::$session_handler,'session_count'));
$config = config::read('notifications');
if (!($popup_poll_interval = $config['popup_poll_interval']))
{
$popup_poll_interval = 60;
}
$limit = (int)(time() - $popup_poll_interval-10); // 10s grace periode
}
return false;
return $limit;
}
/**
* Check if given user can be reached via notifications
*
* Checks if notifications callback checked in not more then heartbeat_limit() seconds ago
*
* @param int $account_id
* @param int number of active sessions of given user with notifications running
*/
public static function notifications_active($account_id)
{
return $GLOBALS['egw']->db->select(self::ACCESS_LOG_TABLE, 'COUNT(*)', array(
'lo' => null,
'session_dla > '.(int)(time() - $GLOBALS['egw_info']['server']['sessions_timeout']),
'account_id' => $account_id,
'notifications_heartbeat > '.self::heartbeat_limit(),
), __LINE__, __FILE__)->fetchColumn();
}
/*

View File

@ -0,0 +1,264 @@
<?php
/**
* EGroupware - Ajax log file viewer (tail -f)
*
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker@outdoor-training.de>
* @copyright 2012 by RalfBecker@outdoor-training.de
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @version $Id$
*/
/**
* Ajax log file viewer (tail -f)
*
* To not allow to view arbitrary files, allowed filenames are stored in the session.
* Class fetches log-file periodically in chunks for 8k.
* If fetch returns no new content next request will be in 2s, otherwise in 200ms.
* As logfiles can be quiet huge, we display at max the last 32k of it!
*
* Example usage:
*
* $error_log = new egw_tail('/var/log/apache2/error_log');
* echo $error_log->show();
*
* Strongly prefered for security reasons is to use a path relative to EGroupware's files_dir,
* eg. new egw_tail('groupdav/somelog')!
*/
class egw_tail
{
/**
* Maximum size of single ajax request
*
* Currently also maximum size / 4 of displayed logfile content!
*/
const MAX_CHUNK_SIZE = 8192;
/**
* Contains allowed filenames to display, we can NOT allow to display arbitrary files!
*
* @param array
*/
protected $filenames;
/**
* Filename class is instanciated to view, set by constructor
*
* @param string
*/
protected $filename;
/**
* Methods allowed to call via menuaction
*
* @var array
*/
public $public_functions = array(
'download' => true,
);
/**
* Constructor
*
* @param string $filename=null if not starting with as slash relative to EGw files dir (this is strongly prefered for security reasons)
*/
public function __construct($filename=null)
{
$this->filenames =& egw_cache::getSession('phpgwapi', __CLASS__);
if ($filename)
{
$this->filename = $filename;
if (!$this->filenames || !in_array($filename,$this->filenames)) $this->filenames[] = $filename;
}
}
/**
* Ajax callback to load next chunk of log-file
*
* @param string $filename
* @param int $start=0 last position in log-file
* @throws egw_exception_wrong_parameter
*/
public function ajax_chunk($filename,$start=0)
{
if (!in_array($filename,$this->filenames))
{
throw new egw_exception_wrong_parameter("Not allowed to view '$filename'!");
}
if ($filename[0] != '/') $filename = $GLOBALS['egw_info']['server']['files_dir'].'/'.$filename;
if (file_exists($filename))
{
$size = filesize($filename);
if (!$start || $start < 0 || $start > $size || $size-$start > 4*self::MAX_CHUNK_SIZE)
{
$start = $size - 4*self::MAX_CHUNK_SIZE;
if ($start < 0) $start = 0;
}
$size = egw_vfs::hsize($size);
$content = file_get_contents($filename, false, null, $start, self::MAX_CHUNK_SIZE);
$length = bytes($content);
$writable = is_writable($filename) || is_writable(dirname($filename));
}
else
{
$start = $length = 0;
$content = '';
$writable = $size = false;
}
$response = egw_json_response::get();
$response->data(array( // send all responses as data
'size' => $size,
'writable' => $writable,
'next' => $start + $length,
'length' => $length,
'content' => $content,
));
}
/**
* Ajax callback to delete log-file
*
* @param string $filename
* @param boolean $truncate=false true: truncate file, false: delete file
* @throws egw_exception_wrong_parameter
*/
public function ajax_delete($filename,$truncate=false)
{
if (!in_array($filename,$this->filenames))
{
throw new egw_exception_wrong_parameter("Not allowed to view '$filename'!");
}
if ($filename[0] != '/') $filename = $GLOBALS['egw_info']['server']['files_dir'].'/'.$filename;
if ($truncate)
{
file_put_contents($filename, '');
}
else
{
unlink($filename);
}
}
/**
* Return html & javascript for logviewer
*
* @param string $header=null default $this->filename
* @param string $id='log'
* @return string
* @throws egw_exception_wrong_parameter
*/
public function show($header=null, $id='log')
{
if (!isset($this->filename))
{
throw new egw_exception_wrong_parameter("Must be instanciated with filename!");
}
if (is_null($header)) $header = $this->filename;
return '
<script type="text/javascript">
var '.$id.'_tail_start = 0;
function button_'.$id.'(button)
{
if (button.id != "clear_'.$id.'")
{
var ajax = new egw_json_request("home.egw_tail.ajax_delete",["'.$this->filename.'",button.id=="empty_'.$id.'"]);
ajax.sendRequest(true);
}
$j("#'.$id.'").text("");
}
function refresh_'.$id.'()
{
var ajax = new egw_json_request("home.egw_tail.ajax_chunk",["'.$this->filename.'",'.$id.'_tail_start]);
ajax.sendRequest(true,function(_data) {
if (_data.length) {
'.$id.'_tail_start = _data.next;
var log = $j("#'.$id.'").append(_data.content.replace(/</g,"&lt;"));
log.animate({ scrollTop: log.attr("scrollHeight") - log.height() + 20 }, 500);
}
if (_data.size === false)
{
$j("#download_'.$id.'").hide();
}
else
{
$j("#download_'.$id.'").show().attr("title","'.lang('Size').': "+_data.size);
}
if (_data.writable === false)
{
$j("#delete_'.$id.'").hide();
$j("#empty_'.$id.'").hide();
}
else
{
$j("#delete_'.$id.'").show();
$j("#empty_'.$id.'").show();
}
window.setTimeout(refresh_'.$id.',_data.length?200:2000);
});
}
function resize_'.$id.'()
{
$j("#'.$id.'").width(egw_getWindowInnerWidth()-20).height(egw_getWindowInnerHeight()-33);
}
$j(document).ready(function()
{
resize_'.$id.'();
refresh_'.$id.'();
});
$j(window).resize(resize_'.$id.');
</script>
<p style="float: left; margin: 5px"><b>'.htmlspecialchars($header).'</b></p>
<div style="float: right; margin: 2px; margin-right: 5px">
'.html::form(
html::input('clear_'.$id,lang('Clear window'),'button','id="clear_'.$id.'" onClick="button_'.$id.'(this)"')."\n".
html::input('delete_'.$id,lang('Delete file'),'button','id="delete_'.$id.'" onClick="button_'.$id.'(this)"')."\n".
html::input('empty_'.$id,lang('Empty file'),'button','id="empty_'.$id.'" onClick="button_'.$id.'(this)"')."\n".
html::input('download_'.$id,lang('Download'),'submit','id="download_'.$id.'"'),
'','/index.php',array(
'menuaction' => 'phpgwapi.egw_tail.download',
'filename' => $this->filename,
)).'
</div>
<pre class="tail" id="'.$id.'" style="clear: both; width: 99.5%; border: 2px groove silver; margin-bottom: 0; overflow: auto;"></pre>';
}
/**
* Download a file specified per GET parameter (must be in $this->filesnames!)
*
* @throws egw_exception_wrong_parameter
*/
public function download()
{
$filename = $_GET['filename'];
if (!in_array($filename,$this->filenames))
{
throw new egw_exception_wrong_parameter("Not allowed to download '$filename'!");
}
html::content_header(basename($filename),'text/plain');
if ($filename[0] != '/') $filename = $GLOBALS['egw_info']['server']['files_dir'].'/'.$filename;
for($n=ob_get_level(); $n > 0; --$n) ob_end_clean(); // stop all output buffering, to NOT run into memory_limit
readfile($filename);
common::egw_exit();
}
}
// some testcode, if this file is called via it's URL (you need to uncomment and adapt filename!)
/*if (isset($_SERVER['SCRIPT_FILENAME']) && $_SERVER['SCRIPT_FILENAME'] == __FILE__)
{
$GLOBALS['egw_info'] = array(
'flags' => array(
'currentapp' => 'admin',
'nonavbar' => true,
),
);
include_once '../../header.inc.php';
$error_log = new egw_tail('/opt/local/apache2/logs/error_log');
echo $error_log->show();
}*/

File diff suppressed because it is too large Load Diff

View File

@ -7,12 +7,15 @@
* @package api
* @subpackage groupdav
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2007-9 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2007-12 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @version $Id$
*/
/**
* EGroupware: GroupDAV access: abstract baseclass for groupdav/caldav/carddav handlers
*
* Permanent error_log() calls should use $this->groupdav->log($str) instead, to be send to PHP error_log()
* and our request-log (prefixed with "### " after request and response, like exceptions).
*/
abstract class groupdav_handler
{
@ -37,6 +40,12 @@ abstract class groupdav_handler
* @var accounts
*/
var $accounts;
/**
* Reference to the ACL class
*
* @var acl
*/
var $acl;
/**
* Translates method names into ACL bits
*
@ -53,18 +62,18 @@ abstract class groupdav_handler
* @var string
*/
var $app;
/**
* Calling groupdav object
*
* @var groupdav
*/
var $groupdav;
/**
* Base url of handler, need to prefix all pathes not automatic handled by HTTP_WebDAV_Server
*
* @var string
*/
var $base_uri;
/**
* principal URL
*
* @var string
*/
var $principalURL;
/**
* HTTP_IF_MATCH / etag of current request / last call to _common_get_put_delete() method
*
@ -78,34 +87,39 @@ abstract class groupdav_handler
*/
var $agent;
/**
* Extension to append to url/path
*
* @var string
*/
static $path_extension = '.ics';
/**
* Which attribute to use to contruct name part of url/path
*
* @var string
*/
static $path_attr = 'id';
/**
* Constructor
*
* @param string $app 'calendar', 'addressbook' or 'infolog'
* @param int $debug=null debug-level to set
* @param string $base_uri=null base url of handler
* @param string $principalURL=null pricipal url of handler
* @param groupdav $groupdav calling class
*/
function __construct($app,$debug=null,$base_uri=null,$principalURL=null)
function __construct($app, groupdav $groupdav)
{
$this->app = $app;
if (!is_null($debug)) $this->debug = $debug;
$this->base_uri = is_null($base_uri) ? $base_uri : $_SERVER['SCRIPT_NAME'];
if (is_null($principalURL))
{
$this->principalURL = (@$_SERVER["HTTPS"] === "on" ? "https:" : "http:") .
'//'.$_SERVER['HTTP_HOST'] . $_SERVER['SCRIPT_NAME'] . '/';
}
else
{
$this->principalURL = $principalURL.'principals/users/'.
$GLOBALS['egw_info']['user']['account_lid'].'/';
}
if (!is_null($parent->debug)) $this->debug = $groupdav->debug;
$this->base_uri = $groupdav->base_uri;
$this->groupdav = $groupdav;
$this->agent = self::get_agent();
$this->egw_charset = translation::charset();
$this->accounts = $GLOBALS['egw']->accounts;
$this->acl = $GLOBALS['egw']->acl;
}
/**
@ -162,16 +176,17 @@ abstract class groupdav_handler
/**
* Read an entry
*
* @param string/int $id
* @return array/boolean array with entry, false if no read rights, null if $id does not exist
* @param string|int $id
* @param string $path=null implementation can use it, used in call from _common_get_put_delete
* @return array|boolean array with entry, false if no read rights, null if $id does not exist
*/
abstract function read($id);
abstract function read($id /*,$path=null*/);
/**
* Check if user has the neccessary rights on an entry
*
* @param int $acl EGW_ACL_READ, EGW_ACL_EDIT or EGW_ACL_DELETE
* @param array/int $entry entry-array or id
* @param array|int $entry entry-array or id
* @return boolean null if entry does not exist, false if no access, true if access permitted
*/
abstract function check_access($acl,$entry);
@ -182,9 +197,10 @@ abstract class groupdav_handler
* @param array $props=array() regular props by the groupdav handler
* @param string $displayname
* @param string $base_uri=null base url of handler
* @param int $user=null account_id of owner of collection
* @return array
*/
static function extra_properties(array $props=array(), $displayname, $base_uri=null)
public function extra_properties(array $props=array(), $displayname, $base_uri=null, $user=null)
{
return $props;
}
@ -192,8 +208,8 @@ abstract class groupdav_handler
/**
* Get the etag for an entry, can be reimplemented for other algorithm or field names
*
* @param array/int $event array with event or cal_id
* @return string/boolean string with etag or false
* @param array|int $event array with event or cal_id
* @return string|boolean string with etag or false
*/
function get_etag($entry)
{
@ -206,7 +222,7 @@ abstract class groupdav_handler
// error_log(__METHOD__."(".array2string($entry).") Cant create etag!");
return false;
}
return 'EGw-'.$entry['id'].':'.(isset($entry['etag']) ? $entry['etag'] : $entry['modified']).'-wGE';
return $entry['id'].':'.(isset($entry['etag']) ? $entry['etag'] : $entry['modified']);
}
/**
@ -217,7 +233,7 @@ abstract class groupdav_handler
*/
static function etag2value($etag)
{
list(,$val) = explode(':',substr($etag,4,-4),2);
list(,$val) = explode(':',$etag,2);
return $val;
}
@ -230,19 +246,22 @@ abstract class groupdav_handler
*
* @param string $method GET, PUT, DELETE
* @param array &$options
* @param int $id
* @param int|string &$id on return self::$path_extension got removed
* @param boolean &$return_no_access=false if set to true on call, instead of '403 Forbidden' the entry is returned and $return_no_access===false
* @param boolean $ignore_if_match=false if true, ignore If-Match precondition
* @return array|string entry on success, string with http-error-code on failure, null for PUT on an unknown id
*/
function _common_get_put_delete($method,&$options,$id,&$return_no_access=false)
function _common_get_put_delete($method,&$options,&$id,&$return_no_access=false,$ignore_if_match=false)
{
if (self::$path_extension) $id = basename($id,self::$path_extension);
if ($this->app != 'principals' && !$GLOBALS['egw_info']['user']['apps'][$this->app])
{
if ($this->debug) error_log(__METHOD__."($method,,$id) 403 Forbidden: no app rights for '$this->app'");
return '403 Forbidden'; // no app rights
}
$extra_acl = $this->method2acl[$method];
if (!($entry = $this->read($id)) && ($method != 'PUT' || $entry === false) ||
if (!($entry = $this->read($id, $options['path'])) && ($method != 'PUT' || $entry === false) ||
($extra_acl != EGW_ACL_READ && $this->check_access($extra_acl,$entry) === false))
{
if ($return_no_access && !is_null($entry))
@ -261,29 +280,35 @@ abstract class groupdav_handler
$etag = $this->get_etag($entry);
// If the clients sends an "If-Match" header ($_SERVER['HTTP_IF_MATCH']) we check with the current etag
// of the calendar --> on failure we return 412 Precondition failed, to not overwrite the modifications
if (isset($_SERVER['HTTP_IF_MATCH']))
if (isset($_SERVER['HTTP_IF_MATCH']) && !$ignore_if_match)
{
if (strstr($_SERVER['HTTP_IF_MATCH'], $etag) === false)
$this->http_if_match = $_SERVER['HTTP_IF_MATCH'];
// strip of quotes around etag, if they exist, that way we allow etag with and without quotes
if ($this->http_if_match[0] == '"') $this->http_if_match = substr($this->http_if_match, 1, -1);
if ($this->http_if_match !== $etag)
{
$this->http_if_match = $_SERVER['HTTP_IF_MATCH'];
if ($this->debug) error_log(__METHOD__."($method,,$id) HTTP_IF_MATCH='$_SERVER[HTTP_IF_MATCH]', etag='$etag': 412 Precondition failed");
if ($this->debug) error_log(__METHOD__."($method,path=$options[path],$id) HTTP_IF_MATCH='$_SERVER[HTTP_IF_MATCH]', etag='$etag': 412 Precondition failed".array2string($entry));
return '412 Precondition Failed';
}
else
{
$this->http_if_match = $etag;
// if an IF_NONE_MATCH is given, check if we need to send a new export, or the current one is still up-to-date
if ($method == 'GET' && isset($_SERVER['HTTP_IF_NONE_MATCH']))
{
if ($this->debug) error_log(__METHOD__."($method,,$id) HTTP_IF_NONE_MATCH='$_SERVER[HTTP_IF_NONE_MATCH]', etag='$etag': 304 Not Modified");
return '304 Not Modified';
}
}
}
if (isset($_SERVER['HTTP_IF_NONE_MATCH']))
{
if ($this->debug) error_log(__METHOD__."($method,,$id) HTTP_IF_NONE_MATCH='$_SERVER[HTTP_IF_NONE_MATCH]', etag='$etag': 412 Precondition failed");
return '412 Precondition Failed';
$if_none_match = $_SERVER['HTTP_IF_NONE_MATCH'];
// strip of quotes around etag, if they exist, that way we allow etag with and without quotes
if ($if_none_match[0] == '"') $if_none_match = substr($if_none_match, 1, -1);
// if an IF_NONE_MATCH is given, check if we need to send a new export, or the current one is still up-to-date
if (in_array($method, array('GET','HEAD')) && $etag === $if_none_match)
{
if ($this->debug) error_log(__METHOD__."($method,,$id) HTTP_IF_NONE_MATCH='$_SERVER[HTTP_IF_NONE_MATCH]', etag='$etag': 304 Not Modified");
return '304 Not Modified';
}
if ($method == 'PUT' && ($if_none_match == '*' || $if_none_match == $etag))
{
if ($this->debug) error_log(__METHOD__."($method,,$id) HTTP_IF_NONE_MATCH='$_SERVER[HTTP_IF_NONE_MATCH]', etag='$etag': 412 Precondition failed");
return '412 Precondition Failed';
}
}
}
return $entry;
@ -294,13 +319,10 @@ abstract class groupdav_handler
*
* @static
* @param string $app 'calendar', 'addressbook' or 'infolog'
* @param int $user=null owner of the collection, default current user
* @param int $debug=null debug-level to set
* @param string $base_uri=null base url of handler
* @param string $principalURL=null pricipal url of handler
* @param groupdav $groupdav calling class
* @return groupdav_handler
*/
static function &app_handler($app,$debug=null,$base_uri=null,$principalURL=null)
static function app_handler($app, $groupdav)
{
static $handler_cache = array();
@ -309,13 +331,10 @@ abstract class groupdav_handler
$class = $app.'_groupdav';
if (!class_exists($class) && !class_exists($class = 'groupdav_'.$app)) return null;
$handler_cache[$app] = new $class($app,$debug,$base_uri,$principalURL);
$handler_cache[$app] = new $class($app, $groupdav);
}
$handler_cache[$app]->$debug = $debug;
$handler_cache[$app]->$base_uri = $base_uri;
$handler_cache[$app]->$principalURL = $principalURL;
if ($debug) error_log(__METHOD__."('$app', '$base_uri', '$principalURL')");
if ($debug) error_log(__METHOD__."('$app')");
return $handler_cache[$app];
}
@ -335,19 +354,23 @@ abstract class groupdav_handler
// identify the agent (GroupDAV client) from the HTTP_USER_AGENT header
$user_agent = strtolower($_SERVER['HTTP_USER_AGENT']);
foreach(array(
'carddav-sync' => 'carddav-sync', // dmfs.org CardDAV client for Android: CardDAV-Sync (Android) (like iOS/5.0.1 (9A405) dataaccessd/1.0) gzip
'iphone' => 'iphone', // Apple iPhone iCal
'davkit' => 'davkit', // Apple iCal 10.6
'coredav' => 'coredav', // Apple iCal 10.7
'calendarstore' => 'calendarstore', // Apple iCal 5.0.1 under OS X 10.7.2
'dataaccess' => 'dataaccess', // Apple addressbook iPhone
'cfnetwork' => 'cfnetwork', // Apple Addressbook 10.6/7
'bionicmessage.net' => 'funambol', // funambol GroupDAV connector from bionicmessage.net
'zideone' => 'zideone', // zideone outlook plugin
'lightning' => 'lightning', // Lighting (SOGo connector for addressbook)
'lightning' => 'lightning', // Lighting (incl. SOGo connector for addressbook)
'webkit' => 'webkit', // Webkit Browser (also reports KHTML!)
'akonadi' => 'akonadi', // new KDE PIM framework (also reports KHTML!)
'khtml' => 'kde', // KDE clients
'neon' => 'neon',
'ical4ol' => 'ical4ol', // iCal4OL client
'evolution' => 'evolution', // Evolution
'thunderbird' => 'thunderbird', // SOGo connector for addressbook, no Lightning installed
) as $pattern => $name)
{
if (strpos($user_agent,$pattern) !== false)
@ -370,6 +393,15 @@ abstract class groupdav_handler
if ((int)$matches[1] < 868) $agent .= '_old';
}
break;
case 'kde':
// Akonadi (new KDE Pim framework) unfortunately has same user-agent as old kde
// we can only assume KDE 4.7+ uses Akonadi native resource, while below this was not available
// Unfortunately the old pre-Akonadi GroupDAV resource can still be used, but we have no way of detecting it
if (preg_match('/KHTML\/([0-9.]+)/', $_SERVER['HTTP_USER_AGENT'], $matches) && (float)$matches[1] >= 4.7)
{
$agent = 'akonadi';
}
break;
}
}
}
@ -378,6 +410,147 @@ abstract class groupdav_handler
return $agent;
}
/**
* Return priviledges for current user, default is read and read-current-user-privilege-set
*
* Priviledges are for the collection, not the resources / entries!
*
* @param string $path path of collection
* @param int $user=null owner of the collection, default current user
* @return array with privileges
*/
public function current_user_privileges($path, $user=null)
{
static $grants;
if (is_null($grants))
{
$grants = $this->acl->get_grants($this->app, $this->app != 'addressbook');
}
$priviledes = array('read-current-user-privilege-set' => 'read-current-user-privilege-set');
if (!$user || $grants[$user] & EGW_ACL_READ)
{
$priviledes['read'] = 'read';
// allows on all calendars/addressbooks to write properties, as we store them on a per-user basis
// and only allow to modify explicit named properties in CalDAV, CardDAV or Calendarserver name-space
$priviledes['write-properties'] = 'write-properties';
}
if (!$user || $grants[$user] & EGW_ACL_ADD)
{
$priviledes['bind'] = 'bind'; // PUT for new resources
}
if (!$user || $grants[$user] & EGW_ACL_EDIT)
{
$priviledes['write-content'] = 'write-content'; // otherwise iOS calendar does not allow to add events
}
if (!$user || $grants[$user] & EGW_ACL_DELETE)
{
$priviledes['unbind'] = 'unbind'; // DELETE
}
// copy/move of existing resources might require write-properties, thought we do not support an explicit PROPATCH
return $priviledes;
}
/**
* Create the path/name for an entry
*
* @param array $entry
* @return string
*/
function get_path($entry)
{
return $entry[self::$path_attr].self::$path_extension;
}
/**
* Send response-headers for a PUT (or POST with add-member query parameter)
*
* @param int|array $entry id or array of new created entry
* @param string $path
* @param int|string $retval
* @param boolean $path_attr_is_name=true true: path_attr is ca(l|rd)dav_name, false: id (GroupDAV needs Location header)
*/
function put_response_headers($entry, $path, $retval, $path_attr_is_name=true)
{
// we should not return an etag here, as EGroupware never stores ical/vcard byte-by-byte
// as SOGO Connector requires ETag header to recognice as successful PUT, we are sending them again for it
// --> as all clients dislike not getting an ETag for a PUT, we sending it again even not storing byte-by-byte
//if (get_class($this) == 'addressbook_groupdav' && in_array(self::get_agent(),array('thunderbird','lightning')))
{
header('ETag: "'.$this->get_etag($entry).'"');
}
// send Location header only if we dont use caldav_name as path-attribute or
if ($retval !== true && (!$path_attr_is_name ||
// POST with add-member query parameter
$_SERVER['REQUEST_METHOD'] == 'POST' && isset($_GET['add-member'])))
{
$path = preg_replace('|(.*)/[^/]*|', '\1/', $path);
header('Location: '.$this->base_uri.$path.$this->get_path($entry));
}
}
/**
* Return calendars/addressbooks shared from other users with the current one
*
* return array account_id => account_lid pairs
*/
function get_shared()
{
return array();
}
/**
* Return appliction specific settings
*
* @param array $hook_data
* @return array of array with settings
*/
static function get_settings($hook_data)
{
return array();
}
/**
* Add a resource
*
* @param string $path path of collection, NOT entry!
* @param array $entry
* @param array $props
* @return array with values for keys 'path' and 'props'
*/
public function add_resource($path, array $entry, array $props)
{
foreach(array(
'getetag' => $this->get_etag($entry),
'getcontenttype' => 'text/calendar',
'getlastmodified' => $entry['modified'],
'displayname' => $entry['title'],
) as $name => $value)
{
if (!isset($props[$name]))
{
$props[$name] = $value;
}
}
// if requested add privileges
$privileges = array('read', 'read-current-user-privilege-set');
if ($this->groupdav->prop_requested('current-user-privilege-set') === true && !isset($props['current-user-privilege-set']))
{
if ($this->check_access(EGW_ACL_EDIT, $entry))
{
$privileges[] = 'write-content';
}
}
if ($this->groupdav->prop_requested('owner') === true && !isset($props['owner']) &&
($account_lid = $this->accounts->name2id($entry['owner'])))
{
$type = $this->accounts->get_type($entry['owner']) == 'u' ? 'users' : 'groups';
$props['owner'] = HTTP_WebDAV_Server::mkprop('href', $this->base_uri.'/principals/'.$type.'/'.$account_lid.'/');
}
// we urldecode here, as HTTP_WebDAV_Server uses a minimal (#?%) urlencoding for incomming pathes and urlencodes pathes in propfind
return $this->groupdav->add_resource($path.urldecode($this->get_path($entry)), $props, $privileges);
}
}
/**
@ -460,16 +633,15 @@ class groupdav_propfind_iterator implements Iterator
*
* @param groupdav_handler $handler
* @param array $filter filter for propfind call
* @param array $files=null extra files/responses to return too
* @param array $files=array() extra files/responses to return too
*/
public function __construct(groupdav_handler $handler, $path, array $filter,array &$files=null)
public function __construct(groupdav_handler $handler, $path, array $filter,array &$files=array())
{
if ($this->debug) error_log(__METHOD__."('$path', ".array2string($filter).",)");
$this->path = $path;
$this->handler = $handler;
$this->filter = $filter;
$this->files = $files;
$this->common_files = $files;
$this->files = $this->common_files = $files;
reset($this->files);
}
@ -507,6 +679,12 @@ class groupdav_propfind_iterator implements Iterator
if ($this->debug) error_log(__METHOD__."() returning TRUE");
return true;
}
// check if previous query gave less then CHUNK_SIZE entries --> we're done
if ($this->start && count($this->files) < self::CHUNK_SIZE)
{
if ($this->debug) error_log(__METHOD__."() returning FALSE (no more entries)");
return false;
}
// try query further files via propfind callback of handler and store result in $this->files
$this->files = $this->handler->propfind_callback($this->path,$this->filter,array($this->start,self::CHUNK_SIZE));
if (!is_array($this->files) || !($entries = count($this->files)))
@ -514,10 +692,10 @@ class groupdav_propfind_iterator implements Iterator
if ($this->debug) error_log(__METHOD__."() returning FALSE (no more entries)");
return false; // no further entries
}
$this->start += $entries;
$this->start += self::CHUNK_SIZE;
reset($this->files);
if ($this->debug) error_log(__METHOD__."() returning ".array2string(current($this->files) !== false));
if ($this->debug) error_log(__METHOD__."() this->start=$this->start, entries=$entries, count(this->files)=".count($this->files)." returning ".array2string(current($this->files) !== false));
return current($this->files) !== false;
}
@ -529,11 +707,9 @@ class groupdav_propfind_iterator implements Iterator
{
if ($this->debug) error_log(__METHOD__."()");
// query first set of files via propfind callback of handler and store result in $this->files
$this->start = 0;
$files = $this->handler->propfind_callback($this->path,$this->filter,array($this->start,self::CHUNK_SIZE));
$this->files = array_merge($this->common_files, $files);
$this->start += self::CHUNK_SIZE;
$this->files = $this->common_files;
if (!$this->files) $this->next(); // otherwise valid will return false and nothing get returned
reset($this->files);
}

View File

@ -7,7 +7,7 @@
* @package api
* @subpackage groupdav
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2010 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2010-12 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @version $Id$
*/
@ -16,6 +16,10 @@
*/
class groupdav_hooks
{
public $public_functions = array(
'log' => true,
);
/**
* Show GroupDAV preferences link in preferences
*
@ -54,62 +58,103 @@ class groupdav_hooks
if ($hook_data['setup'])
{
$addressbooks = array();
$apps = array('addressbook','calendar','infolog');
}
else
{
$user = $GLOBALS['egw_info']['user']['account_id'];
$addressbook_bo = new addressbook_bo();
$addressbooks = $addressbook_bo->get_addressbooks(EGW_ACL_READ);
unset($addressbooks[$user]); // Use P for personal addressbook
unset($addressbooks[$user.'p']);// ignore (optional) private addressbook for now
$apps = array_keys($GLOBALS['egw_info']['user']['apps']);
}
$addressbooks = array(
'P' => lang('Personal'),
'G' => lang('Primary Group'),
//'U' => lang('Accounts'), // not yet working
'O' => lang('All in one'),
'A' => lang('All'),
) + $addressbooks;
// rewriting owner=0 to 'U', as 0 get's always selected by prefs
if (!isset($addressbooks[0]))
foreach($apps as $app)
{
unset($addressbooks['U']);
}
else
{
unset($addressbooks[0]);
$class_name = $app.'_groupdav';
if (class_exists($class_name, true))
{
$settings += call_user_func(array($class_name,'get_settings'), $hook_data);
}
}
$settings['addressbook-home-set'] = array(
'type' => 'multiselect',
'label' => 'Addressbooks to sync with Apple clients',
'name' => 'addressbook-home-set',
'help' => 'Addressbooks for CardDAV attribute "addressbook-home-set".',
'values' => $addressbooks,
'xmlrpc' => True,
'admin' => False,
'default' => 'P',
$settings[] = array(
'type' => 'section',
'title' => 'Logging / debuging',
);
$settings['debug_level'] = array(
'type' => 'select',
'label' => 'Debug level for Apache/PHP error-log',
'label' => 'Enable logging',
'name' => 'debug_level',
'help' => 'Enables debug-messages to Apache/PHP error-log, allowing to diagnose problems on a per user basis.',
'help' => 'Enables logging of CalDAV/CardDAV traffic to diagnose problems with devices.',
'values' => array(
'0' => 'Off',
'r' => 'Requests and truncated responses',
'f' => 'Requests and full responses to files directory',
'1' => 'Debug 1 - function calls',
'2' => 'Debug 2 - more info',
'3' => 'Debug 3 - complete $_SERVER array',
'0' => lang('Off'),
'r' => lang('Requests and truncated responses to Apache error-log'),
'f' => lang('Requests and full responses to files directory'),
),
'xmlrpc' => true,
'admin' => false,
'default' => '0',
);
if ($GLOBALS['type'] === 'forced' || $GLOBALS['type'] === 'user' &&
$GLOBALS['egw_info']['user']['preferences']['groupdav']['debug-log'] !== 'never')
{
if ($GLOBALS['type'] === 'user')
{
$logs = array();
if (file_exists($log_dir=$GLOBALS['egw_info']['server']['files_dir'].'/groupdav') && ($files = scandir($log_dir)))
{
$account_lid_len = strlen($GLOBALS['egw_info']['user']['account_lid']);
foreach($files as $log)
{
if (substr($log,0,$account_lid_len+1) == $GLOBALS['egw_info']['user']['account_lid'].'-' &&
substr($log,-4) == '.log')
{
$logs['groupdav/'.$log] = egw_time::to(filemtime($log_dir.'/'.$log)).': '.
str_replace('!','/',substr($log,$account_lid_len+1,-4));
}
}
}
$link = egw::link('/index.php',array(
'menuaction' => 'groupdav.groupdav_hooks.log',
'filename' => '',
));
$onchange = "egw_openWindowCentered('$link'+encodeURIComponent(this.value), '_blank', 1000, 500); this.value=''";
}
else // allow to force users to NOT be able to delete their profiles
{
$logs = array('never' => lang('Never'));
}
$settings['show-log'] = array(
'type' => 'select',
'label' => 'Show log of following device',
'name' => 'show-log',
'help' => lang('You need to set enable logging to "%1" to create/update a log.',
lang('Requests and full responses to files directory')),
'values' => $logs,
'xmlrpc' => True,
'admin' => False,
'onchange' => $onchange,
);
}
return $settings;
}
/**
* Open log window for log-file specified in GET parameter filename (relative to files_dir)
*
* $_GET['filename'] has to be in groupdav sub-dir of files_dir and start with account_lid of current user
*
* @throws egw_exception_wrong_parameter
*/
public function log()
{
$filename = $_GET['filename'];
if (!preg_match('|^groupdav/'.preg_quote($GLOBALS['egw_info']['user']['account_lid'],'|').'-[^/]+\.log$|',$filename))
{
throw new egw_exception_wrong_parameter("Access denied to file '$filename'!");
}
$GLOBALS['egw_info']['flags']['css'] = '
body { background-color: #e0e0e0; }
pre.tail { background-color: white; padding-left: 5px; margin-left: 5px; }
';
$header = str_replace('!','/',substr($filename,10+strlen($GLOBALS['egw_info']['user']['account_lid']),-4));
$tail = new egw_tail($filename);
$GLOBALS['egw']->framework->render($tail->show($header),false,false);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -616,7 +616,23 @@ class schema_proc
$blob_column_included = $auto_column_included = False;
foreach($aTableDef['fd'] as $name => $data)
{
if ($aDefaults && isset($aDefaults[$name])) // use given default
// new auto column with no default or explicit NULL as default (can be an existing column too!)
if ($data['type'] == 'auto' &&
(!isset($old_table_def['fd'][$name]) && (!$aDefaults || !isset($aDefaults[$name])) ||
$aDefaults && strtoupper($aDefaults[$name]) == 'NULL'))
{
$sequence_name = $sTableName.'_'.$name.'_seq';
switch($GLOBALS['egw_setup']->db->Type)
{
case 'mysql':
$value = 'NULL'; break;
case 'pgsql':
$value = "nextval('$sequence_name'::regclass)"; break;
default:
$value = "nextval('$sequence_name')"; break;
}
}
elseif ($aDefaults && isset($aDefaults[$name])) // use given default
{
$value = $aDefaults[$name];
}
@ -650,6 +666,11 @@ class schema_proc
{
$value = 'NULL';
}
// some stuff is NOT to be quoted
elseif (in_array(strtoupper($data['default']),array('CURRENT_TIMESTAMP','CURRENT_DATE','NULL','NOW()')))
{
$value = $data['default'];
}
else
{
$value = $this->m_odb->quote(isset($data['default']) ? $data['default'] : '',$data['type']);
@ -690,10 +711,11 @@ class schema_proc
$Ok = $this->RenameTable($sTableName,$tmp_name);
}
$Ok = $Ok && $this->CreateTable($sTableName,$aTableDef) &&
$this->m_odb->query("$extra INSERT INTO $sTableName (".
$this->m_odb->query($sql_copy_data="$extra INSERT INTO $sTableName (".
implode(',',array_keys($aTableDef['fd'])).
") SELEcT $distinct $select FROM $tmp_name",__LINE__,__FILE__) &&
$this->DropTable($tmp_name);
//error_log($sql_copy_data);
if (!$Ok)
{

View File

@ -470,7 +470,8 @@ class Horde_iCalendar {
{
// Default values.
$requiredAttributes['PRODID'] = '-//The Horde Project//Horde_iCalendar Library' . (defined('HORDE_VERSION') ? ', Horde ' . constant('HORDE_VERSION') : '') . '//EN';
$requiredAttributes['METHOD'] = 'PUBLISH';
// METHOD is only required for iTip, but not for CalDAV, therefore removing it here calendar_ical sets it anyway by default
//$requiredAttributes['METHOD'] = 'PUBLISH';
foreach ($requiredAttributes as $name => $default_value) {
if (is_a($this->getattribute($name), 'PEAR_Error')) {
@ -821,17 +822,15 @@ class Horde_iCalendar {
case 'ORG':
$value = trim($value);
// As of rfc 2426 2.4.2 semicolon, comma, and colon must
// be escaped (comma is unescaped after splitting below).
$value = str_replace(array('\\n', '\\N', '\\;', '\\:'),
array("\n", "\n", ';', ':'),
// be escaped (semicolon is unescaped after splitting below).
$value = str_replace(array('\\n', '\\N', '\\,', '\\:'),
array("\n", "\n", ',', ':'),
$value);
// Split by unescaped semicolons:
$values = preg_split('/(?<!\\\\);/', $value);
$value = str_replace('\\;', ';', $value);
$values = str_replace('\\;', ';', $values);
$value = str_replace('\\,', ',', $value);
$values = str_replace('\\,', ',', $values);
$this->setAttribute($tag, trim($value), $params, true, $values);
break;
@ -840,15 +839,13 @@ class Horde_iCalendar {
case 'CATEGORIES':
$value = trim($value);
// As of rfc 2426 2.4.2 semicolon, comma, and colon must
// be escaped (semicolon is unescaped after splitting below).
$value = str_replace(array('\\n', '\\N', '\\,', '\\:'),
array("\n", "\n", ',', ':'),
// be escaped (comma is unescaped after splitting below).
$value = str_replace(array('\\n', '\\N', '\\;', '\\:'),
array("\n", "\n", ';', ':'),
$value);
// Split by unescaped commas:
$values = preg_split('/(?<!\\\\),/', $value);
$value = str_replace('\\;', ';', $value);
$values = str_replace('\\;', ';', $values);
$value = str_replace('\\,', ',', $value);
$values = str_replace('\\,', ',', $values);
$this->setAttribute($tag, trim($value), $params, true, $values);
@ -860,16 +857,14 @@ class Horde_iCalendar {
$value = trim($value);
// vCalendar 1.0 and vCard 2.1 only escape semicolons
// and use unescaped semicolons to create lists.
$value = str_replace(array('\\n', '\\N', '\\;', '\\:'),
array("\n", "\n", ';', ':'),
$value = str_replace(array('\\n', '\\N', '\\,', '\\:'),
array("\n", "\n", ',', ':'),
$value);
// Split by unescaped semicolons:
$values = preg_split('/(?<!\\\\);/', $value);
$value = str_replace('\\;', ';', $value);
$values = str_replace('\\;', ';', $values);
$value = str_replace('\\,', ',', $value);
$values = str_replace('\\,', ',', $values);
$this->setAttribute($tag, trim($value), $params, true, $values);
} else {
$value = trim($value);

View File

@ -12,7 +12,7 @@
/* Basic information about this app */
$setup_info['phpgwapi']['name'] = 'phpgwapi';
$setup_info['phpgwapi']['title'] = 'eGroupWare API';
$setup_info['phpgwapi']['version'] = '1.8.002';
$setup_info['phpgwapi']['version'] = '1.8.004';
$setup_info['phpgwapi']['versions']['current_header'] = '1.29';
$setup_info['phpgwapi']['enable'] = 3;
$setup_info['phpgwapi']['app_order'] = 1;
@ -28,8 +28,6 @@ $setup_info['phpgwapi']['tables'][] = 'egw_applications';
$setup_info['phpgwapi']['tables'][] = 'egw_acl';
$setup_info['phpgwapi']['tables'][] = 'egw_accounts';
$setup_info['phpgwapi']['tables'][] = 'egw_preferences';
$setup_info['phpgwapi']['tables'][] = 'egw_sessions';
$setup_info['phpgwapi']['tables'][] = 'egw_app_sessions';
$setup_info['phpgwapi']['tables'][] = 'egw_access_log';
$setup_info['phpgwapi']['tables'][] = 'egw_hooks';
$setup_info['phpgwapi']['tables'][] = 'egw_languages';
@ -39,7 +37,6 @@ $setup_info['phpgwapi']['tables'][] = 'egw_categories';
$setup_info['phpgwapi']['tables'][] = 'egw_log';
$setup_info['phpgwapi']['tables'][] = 'egw_log_msg';
$setup_info['phpgwapi']['tables'][] = 'egw_interserv';
$setup_info['phpgwapi']['tables'][] = 'egw_vfs';
$setup_info['phpgwapi']['tables'][] = 'egw_history_log';
$setup_info['phpgwapi']['tables'][] = 'egw_async';
$setup_info['phpgwapi']['tables'][] = 'egw_api_content_history';

View File

@ -0,0 +1,475 @@
<?php
/**
* eGroupWare - API Setup
*
* Current DB schema
*
* @link http://www.egroupware.org
* @package api
* @subpackage setup
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
$phpgw_baseline = array(
'egw_config' => array(
'fd' => array(
'config_app' => array('type' => 'varchar','precision' => '50','nullable' => False),
'config_name' => array('type' => 'varchar','precision' => '255','nullable' => False),
'config_value' => array('type' => 'text')
),
'pk' => array('config_app','config_name'),
'fk' => array(),
'ix' => array(),
'uc' => array()
),
'egw_applications' => array(
'fd' => array(
'app_id' => array('type' => 'auto','precision' => '4','nullable' => False),
'app_name' => array('type' => 'varchar','precision' => '25','nullable' => False),
'app_enabled' => array('type' => 'int','precision' => '4','nullable' => False),
'app_order' => array('type' => 'int','precision' => '4','nullable' => False),
'app_tables' => array('type' => 'text','nullable' => False),
'app_version' => array('type' => 'varchar','precision' => '20','nullable' => False,'default' => '0.0'),
'app_icon' => array('type' => 'varchar','precision' => '32'),
'app_icon_app' => array('type' => 'varchar','precision' => '25'),
'app_index' => array('type' => 'varchar','precision' => '64')
),
'pk' => array('app_id'),
'fk' => array(),
'ix' => array(array('app_enabled','app_order')),
'uc' => array('app_name')
),
'egw_acl' => array(
'fd' => array(
'acl_appname' => array('type' => 'varchar','precision' => '50','nullable' => False),
'acl_location' => array('type' => 'varchar','precision' => '255','nullable' => False),
'acl_account' => array('type' => 'int','precision' => '4','nullable' => False),
'acl_rights' => array('type' => 'int','precision' => '4')
),
'pk' => array('acl_appname','acl_location','acl_account'),
'fk' => array(),
'ix' => array('acl_account',array('acl_location','acl_account'),array('acl_appname','acl_account')),
'uc' => array()
),
'egw_accounts' => array(
'fd' => array(
'account_id' => array('type' => 'auto','nullable' => False),
'account_lid' => array('type' => 'varchar','precision' => '64','nullable' => False),
'account_pwd' => array('type' => 'varchar','precision' => '128','nullable' => False),
'account_lastlogin' => array('type' => 'int','precision' => '4'),
'account_lastloginfrom' => array('type' => 'varchar','precision' => '255'),
'account_lastpwd_change' => array('type' => 'int','precision' => '4'),
'account_status' => array('type' => 'char','precision' => '1','nullable' => False,'default' => 'A'),
'account_expires' => array('type' => 'int','precision' => '4'),
'account_type' => array('type' => 'char','precision' => '1'),
'account_primary_group' => array('type' => 'int','precision' => '4','nullable' => False,'default' => '0')
),
'pk' => array('account_id'),
'fk' => array(),
'ix' => array(),
'uc' => array('account_lid')
),
'egw_preferences' => array(
'fd' => array(
'preference_owner' => array('type' => 'int','precision' => '4','nullable' => False),
'preference_app' => array('type' => 'varchar','precision' => '25','nullable' => False),
'preference_value' => array('type' => 'text','nullable' => False)
),
'pk' => array('preference_owner','preference_app'),
'fk' => array(),
'ix' => array(),
'uc' => array()
),
'egw_access_log' => array(
'fd' => array(
'sessionid' => array('type' => 'auto','nullable' => False,'comment' => 'primary key'),
'loginid' => array('type' => 'varchar','precision' => '64','nullable' => False,'comment' => 'username used to login'),
'ip' => array('type' => 'varchar','precision' => '40','nullable' => False,'comment' => 'ip of user'),
'li' => array('type' => 'int','precision' => '8','nullable' => False,'comment' => 'TS if login'),
'lo' => array('type' => 'int','precision' => '8','comment' => 'TD of logout'),
'account_id' => array('type' => 'int','precision' => '4','nullable' => False,'default' => '0','comment' => 'numerical account id'),
'session_dla' => array('type' => 'int','precision' => '8','comment' => 'TS of last user action'),
'session_action' => array('type' => 'varchar','precision' => '64','comment' => 'menuaction or path of last user action'),
'session_php' => array('type' => 'varchar','precision' => '64','nullable' => False,'comment' => 'php session-id or error-message'),
'notification_heartbeat' => array('type' => 'int','precision' => '8','comment' => 'TS of last notification request')
),
'pk' => array('sessionid'),
'fk' => array(),
'ix' => array('li','lo','session_dla','notification_heartbeat'),
'uc' => array()
),
'egw_hooks' => array(
'fd' => array(
'hook_id' => array('type' => 'auto','nullable' => False),
'hook_appname' => array('type' => 'varchar','precision' => '255'),
'hook_location' => array('type' => 'varchar','precision' => '255'),
'hook_filename' => array('type' => 'varchar','precision' => '255')
),
'pk' => array('hook_id'),
'ix' => array(),
'fk' => array(),
'uc' => array()
),
'egw_languages' => array(
'fd' => array(
'lang_id' => array('type' => 'varchar','precision' => '5','nullable' => False),
'lang_name' => array('type' => 'varchar','precision' => '50','nullable' => False)
),
'pk' => array('lang_id'),
'fk' => array(),
'ix' => array(),
'uc' => array()
),
'egw_lang' => array(
'fd' => array(
'lang' => array('type' => 'varchar','precision' => '5','nullable' => False,'default' => ''),
'app_name' => array('type' => 'varchar','precision' => '32','nullable' => False,'default' => 'common'),
'message_id' => array('type' => 'varchar','precision' => '128','nullable' => False,'default' => ''),
'content' => array('type' => 'text')
),
'pk' => array('lang','app_name','message_id'),
'fk' => array(),
'ix' => array(),
'uc' => array()
),
'egw_nextid' => array(
'fd' => array(
'id' => array('type' => 'int','precision' => '4','nullable' => True),
'appname' => array('type' => 'varchar','precision' => '25','nullable' => False)
),
'pk' => array('appname'),
'fk' => array(),
'ix' => array(),
'uc' => array()
),
'egw_categories' => array(
'fd' => array(
'cat_id' => array('type' => 'auto','precision' => '4','nullable' => False),
'cat_main' => array('type' => 'int','precision' => '4','nullable' => False,'default' => '0'),
'cat_parent' => array('type' => 'int','precision' => '4','nullable' => False,'default' => '0'),
'cat_level' => array('type' => 'int','precision' => '2','nullable' => False,'default' => '0'),
'cat_owner' => array('type' => 'varchar','precision' => '255','nullable' => False,'default' => '0'),
'cat_access' => array('type' => 'varchar','precision' => '7'),
'cat_appname' => array('type' => 'varchar','precision' => '50','nullable' => False),
'cat_name' => array('type' => 'varchar','precision' => '150','nullable' => False),
'cat_description' => array('type' => 'varchar','precision' => '255','nullable' => False),
'cat_data' => array('type' => 'text'),
'last_mod' => array('type' => 'int','precision' => '8','nullable' => False)
),
'pk' => array('cat_id'),
'fk' => array(),
'ix' => array(array('cat_appname','cat_owner','cat_parent','cat_level')),
'uc' => array()
),
'egw_log' => array(
'fd' => array(
'log_id' => array('type' => 'auto','precision' => '4','nullable' => False),
'log_date' => array('type' => 'timestamp','nullable' => False),
'log_user' => array('type' => 'int','precision' => '4','nullable' => False),
'log_app' => array('type' => 'varchar','precision' => '50','nullable' => False),
'log_severity' => array('type' => 'char','precision' => '1','nullable' => False)
),
'pk' => array('log_id'),
'fk' => array(),
'ix' => array(),
'uc' => array()
),
'egw_log_msg' => array(
'fd' => array(
'log_msg_log_id' => array('type' => 'int','precision' => '4','nullable' => False),
'log_msg_seq_no' => array('type' => 'int','precision' => '4','nullable' => False),
'log_msg_date' => array('type' => 'timestamp','nullable' => False),
'log_msg_tx_fid' => array('type' => 'varchar','precision' => '4','nullable' => True),
'log_msg_tx_id' => array('type' => 'varchar','precision' => '4','nullable' => True),
'log_msg_severity' => array('type' => 'char','precision' => '1','nullable' => False),
'log_msg_code' => array('type' => 'varchar','precision' => '30','nullable' => False),
'log_msg_msg' => array('type' => 'text','nullable' => False),
'log_msg_parms' => array('type' => 'text','nullable' => False),
'log_msg_file' => array('type' => 'varchar','precision' => '255','nullable' => False),
'log_msg_line' => array('type' => 'int','precision' => '4','nullable' => False)
),
'pk' => array('log_msg_log_id','log_msg_seq_no'),
'fk' => array(),
'ix' => array(),
'uc' => array()
),
'egw_interserv' => array(
'fd' => array(
'server_id' => array('type' => 'auto','nullable' => False),
'server_name' => array('type' => 'varchar','precision' => '64','nullable' => True),
'server_host' => array('type' => 'varchar','precision' => '255','nullable' => True),
'server_url' => array('type' => 'varchar','precision' => '255','nullable' => True),
'trust_level' => array('type' => 'int','precision' => '4'),
'trust_rel' => array('type' => 'int','precision' => '4'),
'username' => array('type' => 'varchar','precision' => '64','nullable' => True),
'password' => array('type' => 'varchar','precision' => '255','nullable' => True),
'admin_name' => array('type' => 'varchar','precision' => '255','nullable' => True),
'admin_email' => array('type' => 'varchar','precision' => '255','nullable' => True),
'server_mode' => array('type' => 'varchar','precision' => '16','nullable' => False,'default' => 'xmlrpc'),
'server_security' => array('type' => 'varchar','precision' => '16','nullable' => True)
),
'pk' => array('server_id'),
'fk' => array(),
'ix' => array(),
'uc' => array()
),
'egw_history_log' => array(
'fd' => array(
'history_id' => array('type' => 'auto','precision' => '4','nullable' => False),
'history_record_id' => array('type' => 'int','precision' => '4','nullable' => False),
'history_appname' => array('type' => 'varchar','precision' => '64','nullable' => False),
'history_owner' => array('type' => 'int','precision' => '4','nullable' => False),
'history_status' => array('type' => 'varchar','precision' => '64','nullable' => False),
'history_new_value' => array('type' => 'text','nullable' => False),
'history_timestamp' => array('type' => 'timestamp','nullable' => False,'default' => 'current_timestamp'),
'history_old_value' => array('type' => 'text','nullable' => False)
),
'pk' => array('history_id'),
'fk' => array(),
'ix' => array(array('history_appname','history_record_id','history_status','history_timestamp')),
'uc' => array()
),
'egw_async' => array(
'fd' => array(
'async_id' => array('type' => 'varchar','precision' => '255','nullable' => False),
'async_next' => array('type' => 'int','precision' => '4','nullable' => False),
'async_times' => array('type' => 'varchar','precision' => '255','nullable' => False),
'async_method' => array('type' => 'varchar','precision' => '80','nullable' => False),
'async_data' => array('type' => 'text','nullable' => False),
'async_account_id' => array('type' => 'int','precision' => '4','nullable' => False,'default' => '0')
),
'pk' => array('async_id'),
'fk' => array(),
'ix' => array(),
'uc' => array()
),
'egw_api_content_history' => array(
'fd' => array(
'sync_appname' => array('type' => 'varchar','precision' => '60','nullable' => False),
'sync_contentid' => array('type' => 'varchar','precision' => '60','nullable' => False),
'sync_added' => array('type' => 'timestamp'),
'sync_modified' => array('type' => 'timestamp'),
'sync_deleted' => array('type' => 'timestamp'),
'sync_id' => array('type' => 'auto','nullable' => False),
'sync_changedby' => array('type' => 'int','precision' => '4','nullable' => False)
),
'pk' => array('sync_id'),
'fk' => array(),
'ix' => array('sync_added','sync_modified','sync_deleted','sync_changedby',array('sync_appname','sync_contentid')),
'uc' => array()
),
'egw_links' => array(
'fd' => array(
'link_id' => array('type' => 'auto','nullable' => False),
'link_app1' => array('type' => 'varchar','precision' => '25','nullable' => False),
'link_id1' => array('type' => 'varchar','precision' => '50','nullable' => False),
'link_app2' => array('type' => 'varchar','precision' => '25','nullable' => False),
'link_id2' => array('type' => 'varchar','precision' => '50','nullable' => False),
'link_remark' => array('type' => 'varchar','precision' => '100'),
'link_lastmod' => array('type' => 'int','precision' => '8','nullable' => False),
'link_owner' => array('type' => 'int','precision' => '4','nullable' => False),
'deleted' => array('type' => 'timestamp')
),
'pk' => array('link_id'),
'fk' => array(),
'ix' => array('deleted',array('link_app1','link_id1','link_lastmod'),array('link_app2','link_id2','link_lastmod')),
'uc' => array()
),
'egw_addressbook' => array(
'fd' => array(
'contact_id' => array('type' => 'auto','nullable' => False),
'contact_tid' => array('type' => 'char','precision' => '1','default' => 'n'),
'contact_owner' => array('type' => 'int','precision' => '8','nullable' => False),
'contact_private' => array('type' => 'int','precision' => '1','default' => '0'),
'cat_id' => array('type' => 'varchar','precision' => '255'),
'n_family' => array('type' => 'varchar','precision' => '64'),
'n_given' => array('type' => 'varchar','precision' => '64'),
'n_middle' => array('type' => 'varchar','precision' => '64'),
'n_prefix' => array('type' => 'varchar','precision' => '64'),
'n_suffix' => array('type' => 'varchar','precision' => '64'),
'n_fn' => array('type' => 'varchar','precision' => '128'),
'n_fileas' => array('type' => 'varchar','precision' => '255'),
'contact_bday' => array('type' => 'varchar','precision' => '12'),
'org_name' => array('type' => 'varchar','precision' => '128'),
'org_unit' => array('type' => 'varchar','precision' => '64'),
'contact_title' => array('type' => 'varchar','precision' => '64'),
'contact_role' => array('type' => 'varchar','precision' => '64'),
'contact_assistent' => array('type' => 'varchar','precision' => '64'),
'contact_room' => array('type' => 'varchar','precision' => '64'),
'adr_one_street' => array('type' => 'varchar','precision' => '64'),
'adr_one_street2' => array('type' => 'varchar','precision' => '64'),
'adr_one_locality' => array('type' => 'varchar','precision' => '64'),
'adr_one_region' => array('type' => 'varchar','precision' => '64'),
'adr_one_postalcode' => array('type' => 'varchar','precision' => '64'),
'adr_one_countryname' => array('type' => 'varchar','precision' => '64'),
'contact_label' => array('type' => 'text'),
'adr_two_street' => array('type' => 'varchar','precision' => '64'),
'adr_two_street2' => array('type' => 'varchar','precision' => '64'),
'adr_two_locality' => array('type' => 'varchar','precision' => '64'),
'adr_two_region' => array('type' => 'varchar','precision' => '64'),
'adr_two_postalcode' => array('type' => 'varchar','precision' => '64'),
'adr_two_countryname' => array('type' => 'varchar','precision' => '64'),
'tel_work' => array('type' => 'varchar','precision' => '40'),
'tel_cell' => array('type' => 'varchar','precision' => '40'),
'tel_fax' => array('type' => 'varchar','precision' => '40'),
'tel_assistent' => array('type' => 'varchar','precision' => '40'),
'tel_car' => array('type' => 'varchar','precision' => '40'),
'tel_pager' => array('type' => 'varchar','precision' => '40'),
'tel_home' => array('type' => 'varchar','precision' => '40'),
'tel_fax_home' => array('type' => 'varchar','precision' => '40'),
'tel_cell_private' => array('type' => 'varchar','precision' => '40'),
'tel_other' => array('type' => 'varchar','precision' => '40'),
'tel_prefer' => array('type' => 'varchar','precision' => '32'),
'contact_email' => array('type' => 'varchar','precision' => '128'),
'contact_email_home' => array('type' => 'varchar','precision' => '128'),
'contact_url' => array('type' => 'varchar','precision' => '128'),
'contact_url_home' => array('type' => 'varchar','precision' => '128'),
'contact_freebusy_uri' => array('type' => 'varchar','precision' => '128'),
'contact_calendar_uri' => array('type' => 'varchar','precision' => '128'),
'contact_note' => array('type' => 'text'),
'contact_tz' => array('type' => 'varchar','precision' => '8'),
'contact_geo' => array('type' => 'varchar','precision' => '32'),
'contact_pubkey' => array('type' => 'text'),
'contact_created' => array('type' => 'int','precision' => '8'),
'contact_creator' => array('type' => 'int','precision' => '4','nullable' => False),
'contact_modified' => array('type' => 'int','precision' => '8','nullable' => False),
'contact_modifier' => array('type' => 'int','precision' => '4'),
'contact_jpegphoto' => array('type' => 'blob'),
'account_id' => array('type' => 'int','precision' => '4'),
'contact_etag' => array('type' => 'int','precision' => '4','default' => '0'),
'contact_uid' => array('type' => 'varchar','precision' => '255'),
'adr_one_countrycode' => array('type' => 'varchar','precision' => '2'),
'adr_two_countrycode' => array('type' => 'varchar','precision' => '2'),
'carddav_name' => array('type' => 'varchar','precision' => '64','comment' => 'name part of CardDAV URL, if specified by client')
),
'pk' => array('contact_id'),
'fk' => array(),
'ix' => array('contact_owner','cat_id','n_fileas','contact_modified','contact_uid','carddav_name',array('n_family','n_given'),array('n_given','n_family'),array('org_name','n_family','n_given')),
'uc' => array('account_id')
),
'egw_addressbook_extra' => array(
'fd' => array(
'contact_id' => array('type' => 'int','precision' => '4','nullable' => False),
'contact_owner' => array('type' => 'int','precision' => '8'),
'contact_name' => array('type' => 'varchar','precision' => '255','nullable' => False),
'contact_value' => array('type' => 'text')
),
'pk' => array('contact_id','contact_name'),
'fk' => array(),
'ix' => array(array('contact_name','contact_value(32)')),
'uc' => array()
),
'egw_addressbook_lists' => array(
'fd' => array(
'list_id' => array('type' => 'auto','nullable' => False),
'list_name' => array('type' => 'varchar','precision' => '80','nullable' => False),
'list_owner' => array('type' => 'int','precision' => '4','nullable' => False),
'list_created' => array('type' => 'int','precision' => '8'),
'list_creator' => array('type' => 'int','precision' => '4')
),
'pk' => array('list_id'),
'fk' => array(),
'ix' => array(),
'uc' => array(array('list_owner','list_name'))
),
'egw_addressbook2list' => array(
'fd' => array(
'contact_id' => array('type' => 'int','precision' => '4','nullable' => False),
'list_id' => array('type' => 'int','precision' => '4','nullable' => False),
'list_added' => array('type' => 'int','precision' => '8'),
'list_added_by' => array('type' => 'int','precision' => '4')
),
'pk' => array('contact_id','list_id'),
'fk' => array(),
'ix' => array(),
'uc' => array()
),
'egw_sqlfs' => array(
'fd' => array(
'fs_id' => array('type' => 'auto','nullable' => False),
'fs_dir' => array('type' => 'int','precision' => '4','nullable' => False),
'fs_name' => array('type' => 'varchar','precision' => '200','nullable' => False),
'fs_mode' => array('type' => 'int','precision' => '2','nullable' => False),
'fs_uid' => array('type' => 'int','precision' => '4','nullable' => False,'default' => '0'),
'fs_gid' => array('type' => 'int','precision' => '4','nullable' => False,'default' => '0'),
'fs_created' => array('type' => 'timestamp','precision' => '8','nullable' => False),
'fs_modified' => array('type' => 'timestamp','precision' => '8','nullable' => False),
'fs_mime' => array('type' => 'varchar','precision' => '96','nullable' => False),
'fs_size' => array('type' => 'int','precision' => '8','nullable' => False),
'fs_creator' => array('type' => 'int','precision' => '4','nullable' => False),
'fs_modifier' => array('type' => 'int','precision' => '4'),
'fs_active' => array('type' => 'bool','nullable' => False,'default' => 't'),
'fs_content' => array('type' => 'blob'),
'fs_link' => array('type' => 'varchar','precision' => '255')
),
'pk' => array('fs_id'),
'fk' => array(),
'ix' => array(array('fs_dir','fs_active','fs_name')),
'uc' => array()
),
'egw_index_keywords' => array(
'fd' => array(
'si_id' => array('type' => 'auto','nullable' => False),
'si_keyword' => array('type' => 'varchar','precision' => '64','nullable' => False),
'si_ignore' => array('type' => 'bool')
),
'pk' => array('si_id'),
'fk' => array(),
'ix' => array(),
'uc' => array('si_keyword')
),
'egw_index' => array(
'fd' => array(
'si_app' => array('type' => 'varchar','precision' => '25','nullable' => False),
'si_app_id' => array('type' => 'varchar','precision' => '50','nullable' => False),
'si_id' => array('type' => 'int','precision' => '4','nullable' => False),
'si_owner' => array('type' => 'int','precision' => '4','nullable' => False)
),
'pk' => array('si_app','si_app_id','si_id'),
'fk' => array(),
'ix' => array('si_id'),
'uc' => array()
),
'egw_cat2entry' => array(
'fd' => array(
'ce_app' => array('type' => 'varchar','precision' => '25','nullable' => False),
'ce_app_id' => array('type' => 'varchar','precision' => '50','nullable' => False),
'cat_id' => array('type' => 'int','precision' => '4','nullable' => False),
'ce_owner' => array('type' => 'int','precision' => '4','nullable' => False)
),
'pk' => array('ce_app','ce_app_id','cat_id'),
'fk' => array(),
'ix' => array('cat_id'),
'uc' => array()
),
'egw_locks' => array(
'fd' => array(
'lock_token' => array('type' => 'varchar','precision' => '255','nullable' => False),
'lock_path' => array('type' => 'varchar','precision' => '255','nullable' => False),
'lock_expires' => array('type' => 'int','precision' => '8','nullable' => False),
'lock_owner' => array('type' => 'varchar','precision' => '255'),
'lock_recursive' => array('type' => 'bool','nullable' => False,'default' => '0'),
'lock_write' => array('type' => 'bool','nullable' => False,'default' => '0'),
'lock_exclusive' => array('type' => 'bool','nullable' => False,'default' => '0'),
'lock_created' => array('type' => 'int','precision' => '8','default' => '0'),
'lock_modified' => array('type' => 'int','precision' => '8','default' => '0')
),
'pk' => array('lock_token'),
'fk' => array(),
'ix' => array('lock_path','lock_expires'),
'uc' => array()
),
'egw_sqlfs_props' => array(
'fd' => array(
'fs_id' => array('type' => 'int','precision' => '4','nullable' => False),
'prop_namespace' => array('type' => 'varchar','precision' => '64','nullable' => False),
'prop_name' => array('type' => 'varchar','precision' => '64','nullable' => False),
'prop_value' => array('type' => 'text')
),
'pk' => array('fs_id','prop_namespace','prop_name'),
'fk' => array(),
'ix' => array(),
'uc' => array()
)
);

View File

@ -67,7 +67,7 @@ function phpgwapi_upgrade1_7_003()
{
// resetting owner for all global (and group) cats to -1
$GLOBALS['egw_setup']->db->update('egw_categories',array('cat_owner' => -1),'cat_owner <= 0',__LINE__,__FILE__);
return $GLOBALS['setup_info']['phpgwapi']['currentver'] = '1.8.001';
}
@ -80,15 +80,294 @@ function phpgwapi_upgrade1_8_001()
}
/**
* Downgrade from trunk
*
* Reserve 1.8.003 in case we want to create a separte security update
*
* @return string
*/
function phpgwapi_upgrade1_8_002()
{
return $GLOBALS['setup_info']['phpgwapi']['currentver'] = '1.8.003';
}
/**
* Update methods for 1.8.004 (called below)
*/
/**
* Add index to improve import of contacts using a custom field as primary key
*
* @return string
*/
function phpgwapi_upgrade1_9_001()
{
return phpgwapi_upgrade1_7_003();
$GLOBALS['egw_setup']->oProc->CreateIndex('egw_addressbook_extra',
array('contact_name','contact_value(32)'));
return $GLOBALS['setup_info']['phpgwapi']['currentver'] = '1.9.002';
}
function phpgwapi_upgrade1_9_002()
{
return phpgwapi_upgrade1_7_003();
/* done by RefreshTable() anyway
$GLOBALS['egw_setup']->oProc->AddColumn('egw_links','deleted',array(
'type' => 'timestamp'
));*/
$GLOBALS['egw_setup']->oProc->RefreshTable('egw_links',array(
'fd' => array(
'link_id' => array('type' => 'auto','nullable' => False),
'link_app1' => array('type' => 'varchar','precision' => '25','nullable' => False),
'link_id1' => array('type' => 'varchar','precision' => '50','nullable' => False),
'link_app2' => array('type' => 'varchar','precision' => '25','nullable' => False),
'link_id2' => array('type' => 'varchar','precision' => '50','nullable' => False),
'link_remark' => array('type' => 'varchar','precision' => '100'),
'link_lastmod' => array('type' => 'int','precision' => '8','nullable' => False),
'link_owner' => array('type' => 'int','precision' => '4','nullable' => False),
'deleted' => array('type' => 'timestamp')
),
'pk' => array('link_id'),
'fk' => array(),
'ix' => array('deleted',array('link_app1','link_id1','link_lastmod'),array('link_app2','link_id2','link_lastmod')),
'uc' => array()
));
return $GLOBALS['setup_info']['phpgwapi']['currentver'] = '1.9.003';
}
function phpgwapi_upgrade1_9_003()
{
$GLOBALS['egw_setup']->oProc->AddColumn('egw_addressbook','adr_one_countrycode',array(
'type' => 'varchar',
'precision' => '2'
));
$GLOBALS['egw_setup']->oProc->AddColumn('egw_addressbook','adr_two_countrycode',array(
'type' => 'varchar',
'precision' => '2'
));
return $GLOBALS['setup_info']['phpgwapi']['currentver'] = '1.9.004';
}
/**
* Update script to populate country codes
*
* Sets country code for any recognized country in any installed language, then clears the country name
* to avoid conflicts / confusion.
*/
function phpgwapi_upgrade1_9_004()
{
// Get all installed translations for names
$country = new country();
$country_query = 'SELECT DISTINCT message_id, content
FROM ' . translation::LANG_TABLE . '
WHERE message_id IN ("' . implode('","', array_values($country->countries())) . '")
ORDER BY message_id';
$result = $GLOBALS['egw_setup']->oProc->query($country_query, __LINE__, __FILE__);
$country_list = array();
$current_name = null;
$id = null;
foreach($result as $row) {
if($row['message_id'] != $current_name) {
$current_name = $row['message_id'];
$id = array_search(strtoupper($current_name), $country->countries());
if(!$id) continue;
}
$country_list[$id][] = $row['content'];
}
// Build conversion
$case = 'CASE UPPER(adr_%1$s_countryname)';
foreach($country_list as $key => $names) {
foreach($names as $name) {
$case .= "\n" . "WHEN UPPER(\"$name\") THEN '$key'";
}
}
$case .= ' END';
$sql = 'UPDATE egw_addressbook SET ';
$sql .= "adr_one_countrycode = (" . sprintf($case, 'one') . '),';
$sql .= "adr_two_countrycode = (" . sprintf($case, 'two') . ')';
// Change names
$GLOBALS['egw_setup']->oProc->query($sql,__LINE__,__FILE__);
// Clear text names
$GLOBALS['egw_setup']->oProc->query('UPDATE egw_addressbook SET adr_one_countryname = NULL WHERE adr_one_countrycode IS NOT NULL',__LINE__,__FILE__);
$GLOBALS['egw_setup']->oProc->query('UPDATE egw_addressbook SET adr_two_countryname = NULL WHERE adr_two_countrycode IS NOT NULL',__LINE__,__FILE__);
return $GLOBALS['setup_info']['phpgwapi']['currentver'] = '1.9.005';
}
/**
* Add index to li (login time) column to speed up maintenance (periodic delete of old rows)
*
* Delete some obsolete / since a long time not used tables:
* - egw_vfs (replaced by egw_sqlfs in 1.6)
* - egw_(app_)sessions (not used since 1.4)
*/
function phpgwapi_upgrade1_9_005()
{
$GLOBALS['egw_setup']->oProc->CreateIndex('egw_access_log','li');
$GLOBALS['egw_setup']->oProc->DropTable('egw_app_sessions');
$GLOBALS['egw_setup']->oProc->DropTable('egw_sessions');
$GLOBALS['egw_setup']->oProc->DropTable('egw_vfs');
return $GLOBALS['setup_info']['phpgwapi']['currentver'] = '1.9.006';
}
/**
* Add column to store CalDAV name given by client
*/
function phpgwapi_upgrade1_9_006()
{
$GLOBALS['egw_setup']->oProc->AddColumn('egw_addressbook','carddav_name',array(
'type' => 'varchar',
'precision' => '64',
'comment' => 'name part of CardDAV URL, if specified by client'
));
$GLOBALS['egw_setup']->db->query($sql='UPDATE egw_addressbook SET carddav_name='.
$GLOBALS['egw_setup']->db->concat(
$GLOBALS['egw_setup']->db->to_varchar('contact_id'),"'.vcf'"),__LINE__,__FILE__);
$GLOBALS['egw_setup']->oProc->CreateIndex('egw_addressbook','carddav_name');
return $GLOBALS['setup_info']['phpgwapi']['currentver'] = '1.9.007';
}
/**
* Add columns for session list (dla, action), make sessionid primary key and TS 64bit
*/
function phpgwapi_upgrade1_9_007()
{
$GLOBALS['egw_setup']->oProc->RefreshTable('egw_access_log',array(
'fd' => array(
'sessionid' => array('type' => 'auto','nullable' => False,'comment' => 'primary key'),
'loginid' => array('type' => 'varchar','precision' => '64','nullable' => False,'comment' => 'username used to login'),
'ip' => array('type' => 'varchar','precision' => '40','nullable' => False,'comment' => 'ip of user'),
'li' => array('type' => 'int','precision' => '8','nullable' => False,'comment' => 'TS if login'),
'lo' => array('type' => 'int','precision' => '8','comment' => 'TD of logout'),
'account_id' => array('type' => 'int','precision' => '4','nullable' => False,'default' => '0','comment' => 'numerical account id'),
'session_dla' => array('type' => 'int','precision' => '8','comment' => 'TS of last user action'),
'session_action' => array('type' => 'varchar','precision' => '64','comment' => 'menuaction or path of last user action'),
'session_php' => array('type' => 'char','precision' => '64','nullable' => False,'comment' => 'php session-id or error-message'),
'notification_heartbeat' => array('type' => 'int','precision' => '8','comment' => 'TS of last notification request')
),
'pk' => array('sessionid'),
'fk' => array(),
'ix' => array('li','lo','session_dla','notification_heartbeat'),
'uc' => array()
),array(
'session_php' => 'sessionid',
'sessionid' => 'NULL', // to NOT copy old sessionid, but create a new sequence
));
return $GLOBALS['setup_info']['phpgwapi']['currentver'] = '1.9.008';
}
/**
* Alter column cat_owner to varchar(255) to support multiple owners/groups per cat
*/
function phpgwapi_upgrade1_9_008()
{
$GLOBALS['egw_setup']->oProc->AlterColumn('egw_categories','cat_owner',array(
'type' => 'varchar',
'precision' => '255',
'nullable' => False,
'default' => '0'
));
return $GLOBALS['setup_info']['phpgwapi']['currentver'] = '1.9.009';
}
/**
* Alter column account_pwd to varchar(128) to allow to store sha256_crypt hashes
*
* Enable password migration to new default "securest available", if current hash is the default (sql: md5, ldap: des)
* or the 1.9.009 migration to ssha is running
*/
function phpgwapi_upgrade1_9_009()
{
$GLOBALS['egw_setup']->oProc->AlterColumn('egw_accounts','account_pwd',array(
'type' => 'varchar',
'precision' => '128',
'nullable' => False
));
// query password hashing from database
$config = array(
'auth_type' => 'sql',
'account_repository' => null, // default is auth_type
'sql_encryption_type' => 'md5',
'ldap_encryption_type' => 'des',
'pwd_migration_allowed' => null, // default off
'pwd_migration_types' => null,
);
foreach($GLOBALS['egw_setup']->db->select('egw_config','config_name,config_value',array(
'config_app' => 'phpgwapi',
'config_name' => array_keys($config),
),__LINE__,__FILE__) as $row)
{
$config[$row['config_name']] = $row['config_value'];
}
if (!isset($config['account_repository'])) $config['account_repository'] = $config['auth_type'];
// changing pw hashing only, if we auth agains our own account repository and no migration already active
if ($config['auth_type'] == $config['account_repository'] &&
(!$config['pwd_migration_allowed'] || $config['pwd_migration_types'] == 'md5,crypt')) // 1.9.009 migration to ssha
{
require_once EGW_SERVER_ROOT.'/setup/inc/hook_config.inc.php'; // for sql_passwdhashes to get securest available password hash
sql_passwdhashes(array(), true, $securest);
// OpenLDAP has no own support for extended crypt like sha512_crypt, but relys the OS crypt implementation,
// do NOT automatically migrate to anything above SSHA for OS other then Linux (Darwin can not auth anymore!)
if ($config['auth_type'] == 'sql' && in_array($config['sql_encryption_type'], array('md5','ssha')) ||
$config['auth_type'] == 'ldap'&& in_array($config['ldap_encryption_type'], array('des','ssha')) &&
(PHP_OS == 'Linux' || $securest == 'ssha'))
{
$config['pwd_migration_types'] = 'md5,crypt'; // des is called crypt in hash
if ($config['pwd_migration_allowed'] && $securest != 'ssha') $config['pwd_migration_types'] .= ',ssha';
$config['sql_encryption_type'] = $config['ldap_encryption_type'] = $securest;
$config['pwd_migration_allowed'] = 'True';
echo "<p>Enabling password migration to $securest</p>\n";
}
foreach($config as $name => $value)
{
$GLOBALS['egw_setup']->db->insert('egw_config',array(
'config_value' => $value,
),array(
'config_app' => 'phpgwapi',
'config_name' => $name,
),__LINE__,__FILE__);
}
}
return $GLOBALS['setup_info']['phpgwapi']['currentver'] = '1.9.012'; // was 1.9.010, but in case someone updates from an older Trunk
}
/**
* Add index for contact_modified to improve performance of ctag generation on big installtions
*/
function phpgwapi_upgrade1_9_012()
{
$GLOBALS['egw_setup']->oProc->CreateIndex('egw_addressbook','contact_modified');
return $GLOBALS['setup_info']['phpgwapi']['currentver'] = '1.8.004';
}
/**
* Combiupdate 1.8.004: includes Trunk updates 1.9.001-1.9.010+1.9.013
*
* @return string
*/
function phpgwapi_upgrade1_8_003()
{
foreach(array('1.9.001','1.9.002','1.9.003','1.9.004','1.9.005','1.9.006','1.9.007','1.9.008','1.9.009','1.9.012') as $version)
{
$func = 'phpgwapi_upgrade'.str_replace('.', '_', $version);
$func();
}
return $GLOBALS['setup_info']['phpgwapi']['currentver'] = '1.8.004';
}

View File

@ -108,7 +108,7 @@ class bo_acl
$perm_cats[$cat['id']] = $s;
}
}
return isset($perm_cats)?$perm_cats:array();
return $perm_cats;
}

View File

@ -1,6 +1,6 @@
<?php
/**
* eGroupWare - resources
* EGroupware - resources
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package resources
@ -16,7 +16,7 @@
*
* @package resources
*/
class bo_resources
class resources_bo
{
const PICTURE_NAME = '.picture.jpg';
var $resource_icons = '/resources/templates/default/images/resource_icons/';
@ -24,7 +24,7 @@ class bo_resources
/**
* Instance of resources so object
*
* @var so_resources
* @var resources_so
*/
var $so;
/**
@ -38,9 +38,9 @@ class bo_resources
*/
var $cats;
function bo_resources()
function __construct()
{
$this->so =& CreateObject('resources.so_resources');
$this->so = new resources_so();
$this->acl =& CreateObject('resources.bo_acl');
$this->cats = $this->acl->egw_cats;
@ -69,7 +69,6 @@ class bo_resources
}
}
if ($this->debug) _debug_array($query);
$criteria = array('name' => $query['search'], 'short_description' => $query['search'], 'inventory_number' => $query['search']);
$read_onlys = 'res_id,name,short_description,quantity,useable,bookable,buyable,cat_id,location,storage_info';
$accessory_of = $query['view_accs_of'] ? $query['view_accs_of'] : -1;
@ -91,7 +90,9 @@ class bo_resources
$filter['cat_id'] = array_keys($readcats);
}
// if there is no catfilter -> this means you have no rights, so set the cat filter to null
if (!isset($filter['cat_id']) || empty($filter['cat_id'])) $filter['cat_id'] = NUll;
if (!isset($filter['cat_id']) || empty($filter['cat_id'])) {
$filter['cat_id'] = NUll;
}
if ($query['show_bookable'])
{
@ -100,7 +101,8 @@ class bo_resources
$order_by = $query['order'] ? $query['order'].' '. $query['sort'] : '';
$start = (int)$query['start'];
$rows = $this->so->search($criteria,$read_onlys,$order_by,'','%',$empty=False,$op='OR',$start,$filter,$join='',$need_full_no_count=false);
$query['col_filter'] = $filter;
$this->so->get_rows($query, $rows, $readonlys);
$nr = $this->so->total;
// we are called to serve bookable resources (e.g. calendar-dialog)
@ -157,7 +159,7 @@ class bo_resources
}
}
}
$rows[$num]['picture_thumb'] = $this->get_picture($resource['res_id']);
$rows[$num]['picture_thumb'] = $this->get_picture($resource);
$rows[$num]['admin'] = $this->acl->get_cat_admin($resource['cat_id']);
}
return $nr;
@ -318,7 +320,7 @@ class bo_resources
*/
function get_calendar_info($res_id)
{
//echo "<p>bo_resources::get_calendar_info(".print_r($res_id,true).")</p>\n";
//echo "<p>resources_bo::get_calendar_info(".print_r($res_id,true).")</p>\n";
if(!is_array($res_id) && $res_id < 1) return;
$data = $this->so->search(array('res_id' => $res_id),self::TITLE_COLS.',useable');
@ -366,9 +368,10 @@ class bo_resources
* @param string|array $pattern if it's a string it is the string we will search for as a criteria, if it's an array we
* will seach for 'search' key in this array to get the string criteria. others keys handled are actually used
* for calendar disponibility.
* @param array $options Array of options for the search
*
*/
function link_query( $pattern )
function link_query( $pattern, Array &$options = array() )
{
if (is_array($pattern))
{
@ -385,7 +388,11 @@ class bo_resources
'cat_id' => array_flip((array)$this->acl->get_cats(EGW_ACL_READ)),
//'accessory_of' => '-1'
);
$data = $this->so->search($criteria,$only_keys,$order_by='name',$extra_cols='',$wildcard='%',$empty,$op='OR',false,$filter);
$limit = false;
if($options['start'] || $options['num_rows']) {
$limit = array($options['start'], $options['num_rows']);
}
$data = $this->so->search($criteria,$only_keys,$order_by='',$extra_cols='',$wildcard='%',$empty,$op='OR',$limit,$filter);
// maybe we need to check disponibility of the searched resources in the calendar if $pattern ['exec'] contains some extra args
$show_conflict=False;
if (is_array($pattern) && isset($pattern['exec']) )
@ -499,6 +506,7 @@ class bo_resources
error_log(__METHOD__." No Data found for Resource with id ".$resource['res_id']);
}
}
$options['total'] = $this->so->total;
return $list;
}
@ -613,21 +621,18 @@ class bo_resources
/**
* get resource picture either from vfs or from symlink
* Cornelius Weiss <egw@von-und-zu-weiss.de>
* @param int $res_id id of resource
* @param int|array $resource res-id or whole resource array
* @param bool $fullsize false = thumb, true = full pic
* @return string url of picture
*/
function get_picture($res_id=0,$fullsize=false)
function get_picture($resource,$fullsize=false)
{
if ($res_id > 0)
{
$src = $this->so->get_value('picture_src',$res_id);
}
#echo $scr."<br>". $this->pictures_dir."<br>";
switch($src)
if ($resource && !is_array($resource)) $resource = $this->read($resource);
switch($resource['picture_src'])
{
case 'own_src':
$picture = egw_link::vfs_path('resources',$res_id,self::PICTURE_NAME,true); // vfs path
$picture = egw_link::vfs_path('resources',$resource['res_id'],self::PICTURE_NAME,true); // vfs path
if ($fullsize)
{
$picture = egw::link(egw_vfs::download_url($picture));
@ -636,17 +641,17 @@ class bo_resources
{
$picture = egw::link('/etemplate/thumbnail.php',array('path' => $picture));
}
//$picture=$GLOBALS['egw_info']['server'].$picture;
#echo $picture."<br>";
break;
case 'cat_src':
list($picture) = $this->cats->return_single($this->so->get_value('cat_id',$res_id));
list($picture) = $this->cats->return_single($resource['cat_id']);
$picture = unserialize($picture['data']);
if($picture['icon'])
{
$picture = $GLOBALS['egw_info']['server']['webserver_url'].'/phpgwapi/images/'.$picture['icon'];
break;
}
// fall through
case 'gen_src':
default :
$picture = $GLOBALS['egw_info']['server']['webserver_url'].$this->resource_icons;
@ -656,7 +661,6 @@ class bo_resources
}
/**
* remove_picture
* removes picture from vfs
*
* Cornelius Weiss <egw@von-und-zu-weiss.de>

View File

@ -0,0 +1,145 @@
<?php
/**
* eGroupWare - Resources - importexport
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package Resources
* @subpackage importexport
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright Nathan Gray
* @version $Id$
*/
/**
* class resources_egw_record
*
* compability layer for iface_egw_record needet for importexport
*/
class resources_egw_record implements importexport_iface_egw_record
{
private $identifier = '';
private $record = array();
/**
* constructor
* reads record from backend if identifier is given.
*
* @param string $_identifier
*/
public function __construct( $_identifier='' ) {
$this->identifier = $_identifier;
if($this->identifier) {
$this->record = ExecMethod('resources.resources_bo.read', $this->identifier);
}
}
/**
* magic method to set attributes of record
*
* @param string $_attribute_name
*/
public function __get($_attribute_name) {
return $this->record[$_attribute_name];
}
/**
* magig method to set attributes of record
*
* @param string $_attribute_name
* @param data $data
*/
public function __set($_attribute_name, $data) {
$this->record[$_attribute_name] = $data;
}
/**
* converts this object to array.
* @abstract We need such a function cause PHP5
* dosn't allow objects do define it's own casts :-(
* once PHP can deal with object casts we will change to them!
*
* @return array complete record as associative array
*/
public function get_record_array() {
return $this->record;
}
/**
* gets title of record
*
*@return string tiltle
*/
public function get_title() {
if (empty($this->record)) {
$this->get_record();
}
return $this->record['name'];
}
/**
* sets complete record from associative array
*
* @todo add some checks
* @return void
*/
public function set_record(array $_record){
$this->record = $_record;
}
/**
* gets identifier of this record
*
* @return string identifier of current record
*/
public function get_identifier() {
return $this->identifier;
}
/**
* saves record into backend
*
* @return string identifier
*/
public function save ( $_dst_identifier ) {
}
/**
* copys current record to record identified by $_dst_identifier
*
* @param string $_dst_identifier
* @return string dst_identifier
*/
public function copy ( $_dst_identifier ) {
}
/**
* moves current record to record identified by $_dst_identifier
* $this will become moved record
*
* @param string $_dst_identifier
* @return string dst_identifier
*/
public function move ( $_dst_identifier ) {
}
/**
* delets current record from backend
*
*/
public function delete () {
}
/**
* destructor
*
*/
public function __destruct() {
}
} // end of egw_addressbook_record
?>

View File

@ -0,0 +1,129 @@
<?php
/**
* eGroupWare
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package resources
* @subpackage importexport
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright Nathan Gray
* @version $Id$
*/
/**
* export resources to CSV
*/
class resources_export_csv implements importexport_iface_export_plugin {
/**
* Exports records as defined in $_definition
*
* @param egw_record $_definition
*/
public function export( $_stream, importexport_definition $_definition) {
$options = $_definition->plugin_options;
$bo = new resources_bo();
$selection = array();
if ($options['selection'] == 'selected') {
// ui selection with checkbox 'selected'
$query = egw_cache::getSession('resources', 'get_rows');
$query['num_rows'] = -1; // all
unset($query['store_state']);
$bo->get_rows($query,$selection,$readonlys);
}
elseif ( $options['selection'] == 'all' ) {
$query = array(
'num_rows' => -1,
); // all
$bo->get_rows($query,$selection,$readonlys);
} else {
$selection = explode(',',$options['selection']);
}
$export_object = new importexport_export_csv($_stream, (array)$options);
$export_object->set_mapping($options['mapping']);
// Check if we need to load the custom fields
$need_custom = false;
foreach(config::get_customfields('resources') as $field => $settings) {
if($options['mapping']['#'.$field]) {
$need_custom = true;
break;
}
}
$types = importexport_export_csv::$types;
$types['select-bool'] = array('bookable');
foreach ($selection as $record) {
if(!is_array($record) || !$record['res_id']) continue;
if($need_custom) {
$record = $bo->read($record['res_id']);
}
$resource = new resources_egw_record();
$resource->set_record($record);
if($options['convert']) {
importexport_export_csv::convert($resource, $types, 'resources');
} else {
// Implode arrays, so they don't say 'Array'
foreach($resource->get_record_array() as $key => $value) {
if(is_array($value)) $resource->$key = implode(',', $value);
}
}
$export_object->export_record($resource);
unset($resource);
}
}
/**
* returns translated name of plugin
*
* @return string name
*/
public static function get_name() {
return lang('Resources CSV export');
}
/**
* returns translated (user) description of plugin
*
* @return string descriprion
*/
public static function get_description() {
return lang("Exports a list of resources to a CSV File.");
}
/**
* retruns file suffix for exported file
*
* @return string suffix
*/
public static function get_filesuffix() {
return 'csv';
}
public static function get_mimetype() {
return 'text/csv';
}
/**
* return html for options.
* this way the plugin has all opportunities for options tab
*
*/
public function get_options_etpl() {
}
/**
* returns selectors information
*
*/
public function get_selectors_etpl() {
return array(
'name' => 'resources.export_csv_selectors'
);
}
}

View File

@ -0,0 +1,173 @@
<?php
/**
* eGroupWare - resources
* General hook object for resources
* It encapsulats all the diffent hook methods
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package resources
* @link http://www.egroupware.org
* @version $Id$
*/
/**
* General hook object for resources
* It encapsulats all the diffent hook methods
* @package resources
*/
class resources_hooks
{
function admin_prefs_sidebox($args)
{
$this->acl =& CreateObject('resources.bo_acl');
$appname = 'resources';
$location = is_array($args) ? $args['location'] : $args;
if ($location == 'sidebox_menu')
{
$title = $GLOBALS['egw_info']['apps']['resources']['title'].' '.lang('Menu');
$file = array(
'Resources list' => egw::link('/index.php',array('menuaction' => 'resources.resources_ui.index' )),
);
if($this->acl->get_cats(EGW_ACL_ADD))
{
$file['Add resource'] = "javascript:egw_openWindowCentered2('".egw::link('/index.php',array(
'menuaction' => 'resources.resources_ui.edit',
),false)."','_blank',800,600,'yes')";
}
display_sidebox($appname,$title,$file);
}
/* if ($GLOBALS['egw_info']['user']['apps']['preferences'] && $location != 'admin')
{
$file = array(
'Preferences' => egw::link('/preferences/preferences.php','appname='.$appname),
'Grant Access' => egw::link('/index.php','menuaction=preferences.uiaclprefs.index&acl_app='.$appname),
'Edit Categories' => 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(
'Global Categories' => egw::link('/index.php',array(
'menuaction' => 'admin.uicategories.index',
'appname' => $appname,
'global_cats'=> true)),
'Configure Access Permissions' => egw::link('/index.php',
'menuaction=resources.ui_acl.acllist'),
'Custom Fields'=>egw::link('/index.php',
'menuaction=admin.customfields.edit&appname=resources'),
);
if ($location == 'admin')
{
display_section($appname,$file);
}
else
{
display_sidebox($appname,lang('Admin'),$file);
}
}
}
function search_link($args)
{
return array(
'query' => 'resources.resources_bo.link_query',
'title' => 'resources.resources_bo.link_title',
'titles' => 'resources.resources_bo.link_titles',
'view' => array(
'menuaction' => 'resources.resources_ui.show'
),
'view_id' => 'res_id',
'view_popup' => '850x600',
'view_list' => 'resources.resources_ui.index',
'add' => array(
'menuaction' => 'resources.resources_ui.edit',
),
'add_app' => 'link_app',
'add_id' => 'link_id',
'add_popup' => '800x600',
'find_extra' => array('name_preg' => '/^(?(?=^.picture.jpg$)|.+)$/'), // remove pictures from regular attachment list
);
}
function calendar_resources($args)
{
return array(
'widget' => 'resources_select',// widget to use for the selection of resources
'info' => 'resources.resources_bo.get_calendar_info',// info method, returns array with id, type & name for a given id
'max_quantity' => 'useable',// if set, key for max. quantity in array returned by info method
'new_status' => 'resources.resources_bo.get_calendar_new_status',// method returning the status for new items, else 'U' is used
'type' => 'r',// one char type-identifiy for this resources
'icon' => 'calicon',//icon
'participants_header' => lang('resources'), // header of participants from this type
'cal_sidebox' => array(
'menu_title' => lang('Select resources'),
'file' => 'resources.resources_ui.get_calendar_sidebox'
)
);
}
/**
* Handle deleted category
*
* Resources' ACL _requires_ a category.
* Moves all resources to parent, if it exists. If it doesn't, another category is created.
*/
function delete_category($args)
{
$cat = categories::read($args['cat_id']);
if(!$cat) return; // Can't find current cat?
if($cat['parent'] == 0)
{
// No parent, try the default cat from setup
$categories = new categories('', 'resources');
$default = $categories->name2id('General resources');
if($default)
{
$new_cat_id = $default;
}
else
{
// Default missing, look for 'No category'
$new_cat_id = $categories->name2id('No category');
if($new_cat_id == 0) {
// No category not there, add it
$new_cat_id = $categories->add(array(
'name' => 'No category',
'description' => 'This category has been added to rescue resources whose category was deleted.',
'parent' => 0
));
$admin = -2;
ExecMethod2('resources.bo_acl.set_rights', $new_cat_id, array($admin), array($admin), array($admin), array($admin),array($admin));
}
}
}
else
{
$new_cat_id = $cat['parent'];
}
// Get any resources affected
$query = array('filter' => $args['cat_id']);
$bo = new resources_bo();
$bo->get_rows($query, $resources, $readonly);
foreach($resources as $resource)
{
$resource['cat_id'] = $new_cat_id;
$bo->save($resource);
}
}
}

View File

@ -0,0 +1,312 @@
<?php
/**
* eGroupWare import CSV plugin to import resources
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package resources
* @subpackage importexport
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright Nathan Gray
* @version $Id$
*/
/**
* class to import resources from CSV
*/
class resources_import_csv implements importexport_iface_import_plugin {
private static $plugin_options = array(
'fieldsep', // char
'charset', // string
'contact_owner', // int
'update_cats', // string {override|add} overides record
// with cat(s) from csv OR add the cat from
// csv file to exeisting cat(s) of record
'num_header_lines', // int number of header lines
'field_conversion', // array( $csv_col_num => conversion)
'field_mapping', // array( $csv_col_num => adb_filed)
'conditions', /* => array containing condition arrays:
'type' => exists, // exists
'string' => '#kundennummer',
'true' => array(
'action' => update,
'last' => true,
),
'false' => array(
'action' => insert,
'last' => true,
),*/
);
/**
* actions wich could be done to data entries
*/
protected static $actions = array( 'none', 'update', 'insert', 'delete', );
/**
* conditions for actions
*
* @var array
*/
protected static $conditions = array( 'exists' );
/**
* @var definition
*/
private $definition;
/**
* @var bo
*/
private $bo;
/**
* @var bool
*/
private $dry_run = false;
/**
* @var bool is current user admin?
*/
private $is_admin = false;
/**
* @var int
*/
private $user = null;
/**
* List of import errors
*/
protected $errors = array();
/**
* List of actions, and how many times that action was taken
*/
protected $results = array();
/**
* imports entries according to given definition object.
* @param resource $_stream
* @param string $_charset
* @param definition $_definition
*/
public function import( $_stream, importexport_definition $_definition ) {
$import_csv = new importexport_import_csv( $_stream, array(
'fieldsep' => $_definition->plugin_options['fieldsep'],
'charset' => $_definition->plugin_options['charset'],
));
$this->definition = $_definition;
// user, is admin ?
$this->is_admin = isset( $GLOBALS['egw_info']['user']['apps']['admin'] ) && $GLOBALS['egw_info']['user']['apps']['admin'];
$this->user = $GLOBALS['egw_info']['user']['account_id'];
// dry run?
$this->dry_run = isset( $_definition->plugin_options['dry_run'] ) ? $_definition->plugin_options['dry_run'] : false;
// fetch the resource bo
$this->bo = new resources_bo();
// set FieldMapping.
$import_csv->mapping = $_definition->plugin_options['field_mapping'];
// set FieldConversion
$import_csv->conversion = $_definition->plugin_options['field_conversion'];
//check if file has a header lines
if ( isset( $_definition->plugin_options['num_header_lines'] ) && $_definition->plugin_options['num_header_lines'] > 0) {
$import_csv->skip_records($_definition->plugin_options['num_header_lines']);
} elseif(isset($_definition->plugin_options['has_header_line']) && $_definition->plugin_options['has_header_line']) {
// First method is preferred
$import_csv->skip_records(1);
}
// Start counting successes
$count = 0;
$this->results = array();
// Failures
$this->errors = array();
while ( $record = $import_csv->get_record() ) {
$success = false;
// don't import empty records
if( count( array_unique( $record ) ) < 2 ) continue;
// Automatically handle text categories without explicit translation
$record['cat_id'] = importexport_helper_functions::cat_name2id($record['cat_id']);
if ( $_definition->plugin_options['conditions'] ) {
foreach ( $_definition->plugin_options['conditions'] as $condition ) {
$results = array();
switch ( $condition['type'] ) {
// exists
case 'exists' :
if($record[$condition['string']]) {
$results = $this->bo->so->search(
array( $condition['string'] => $record[$condition['string']]),
False
);
}
if ( is_array( $results ) && count( array_keys( $results )) >= 1) {
// apply action to all contacts matching this exists condition
$action = $condition['true'];
foreach ( (array)$results as $resource ) {
$record['res_id'] = $resource['res_id'];
if ( $_definition->plugin_options['update_cats'] == 'add' ) {
if ( !is_array( $resource['cat_id'] ) ) $resource['cat_id'] = explode( ',', $resource['cat_id'] );
if ( !is_array( $record['cat_id'] ) ) $record['cat_id'] = explode( ',', $record['cat_id'] );
$record['cat_id'] = implode( ',', array_unique( array_merge( $record['cat_id'], $resource['cat_id'] ) ) );
}
$success = $this->action( $action['action'], $record, $import_csv->get_current_position() );
}
} else {
$action = $condition['false'];
$success = ($this->action( $action['action'], $record, $import_csv->get_current_position() ));
}
break;
// not supported action
default :
die('condition / action not supported!!!');
break;
}
if ($action['last']) break;
}
} else {
// unconditional insert
$success = $this->action( 'insert', $record, $import_csv->get_current_position() );
}
if($success) $count++;
}
return $count;
}
/**
* perform the required action
*
* @param int $_action one of $this->actions
* @param array $_data contact data for the action
* @return bool success or not
*/
private function action ( $_action, $_data, $record_num = 0 ) {
switch ($_action) {
case 'none' :
return true;
case 'update' :
// Only update if there are changes
$old = $this->bo->read($_data['res_id']);
// Merge to deal with fields not in import record
$_data = array_merge($old, $_data);
// Fall through
case 'insert' :
if($_action == 'insert') {
// Backend doesn't like inserting with ID specified, it can overwrite
unset($_data['res_id']);
}
if ( $this->dry_run ) {
//print_r($_data);
$this->results[$_action]++;
return true;
} else {
$result = $this->bo->save( $_data );
if($result) {
$this->errors[$record_num] = $result;
return false;
} else {
$this->results[$_action]++;
return true;
}
}
default:
throw new egw_exception('Unsupported action');
}
}
/**
* returns translated name of plugin
*
* @return string name
*/
public static function get_name() {
return lang('Resources CSV import');
}
/**
* returns translated (user) description of plugin
*
* @return string descriprion
*/
public static function get_description() {
return lang("Imports a list of resources from a CSV file.");
}
/**
* retruns file suffix(s) plugin can handle (e.g. csv)
*
* @return string suffix (comma seperated)
*/
public static function get_filesuffix() {
return 'csv';
}
/**
* return etemplate components for options.
* @abstract We can't deal with etemplate objects here, as an uietemplate
* objects itself are scipt orientated and not "dialog objects"
*
* @return array (
* name => string,
* content => array,
* sel_options => array,
* preserv => array,
* )
*/
public function get_options_etpl() {
// lets do it!
}
/**
* returns etemplate name for slectors of this plugin
*
* @return string etemplate name
*/
public function get_selectors_etpl() {
// lets do it!
}
/**
* Returns errors that were encountered during importing
* Maximum of one error message per record, but you can append if you need to
*
* @return Array (
* record_# => error message
* )
*/
public function get_errors() {
return $this->errors;
}
/**
* Returns a list of actions taken, and the number of records for that action.
* Actions are things like 'insert', 'update', 'delete', and may be different for each plugin.
*
* @return Array (
* action => record count
* )
*/
public function get_results() {
return $this->results;
}
} // end of iface_export_plugin
?>

Some files were not shown because too many files have changed in this diff Show More