add ajax=true to diverse admin pages of apps, changing customfields class to autoloadable admin_customfields (with old class extending new one, just in case), fix wrong English phrase "typ" in infolog

This commit is contained in:
Ralf Becker 2016-08-12 11:35:30 +02:00
parent dd7466dcb3
commit dceafd2400
40 changed files with 711 additions and 684 deletions

View File

@ -85,7 +85,7 @@ class addressbook_hooks
if ($GLOBALS['egw_info']['server']['contact_repository'] != 'ldap')
{
$file['Custom fields'] = Egw::link('/index.php',array(
'menuaction' => 'admin.customfields.index',
'menuaction' => 'admin.admin_customfields.index',
'appname' => $appname,
'use_private'=> 1,
'ajax' => 'true'

View File

@ -0,0 +1,654 @@
<?php
/**
* EGgroupware admin - UI for adding custom fields
*
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @author Cornelius Weiss <nelius-AT-von-und-zu-weiss.de>
* @package admin
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
use EGroupware\Api;
use EGroupware\Api\Framework;
use EGroupware\Api\Etemplate;
/**
* Customfields class - manages customfield definitions in egw_config table
*
* The repository name (config_name) is 'customfields'.
*
* Applications can have customfields by sub-type by having a template
* named '<appname>.admin.types'. See admin.customfields.types as an
* example, but the template can even be empty if types are handled by the
* application in another way.
*
* Applications can extend this class to customize the custom fields and handle
* extra information from the above template by extending and implementing
* update() and app_index().
*/
class admin_customfields
{
/**
* appname of app which want to add / edit its customfields
*
* @var string
*/
var $appname;
/**
* Allow custom fields to be restricted to certain users/groups
*/
protected $use_private = false;
/**
* userdefiened types e.g. type of infolog
*
* @var array
*/
var $types2 = array();
var $content_types,$fields;
/**
* Currently selected content type (if used by app)
* @var string
*/
protected $content_type = null;
var $public_functions = array(
'index' => true,
'edit' => True
);
/**
* Instance of etemplate class
*
* @var etemplate
*/
var $tmpl;
/**
* @var Description of the options or value format for each cf_type
*/
public static $type_option_help = array(
'search' => 'set get_rows, get_title and id_field, or use @path to read options from a file in EGroupware directory',
'select' => 'each value is a line like id[=label], or use @path to read options from a file in EGroupware directory',
'radio' => 'each value is a line like id[=label], or use @path to read options from a file in EGroupware directory',
'button' => 'each value is a line like label=[javascript]'
);
/**
* Custom fields can also have length and rows set, but these are't used for all types
* If not set to true here, the field will be disabled when selecting the type
*/
public static $type_attribute_flags = array(
'text' => array('cf_len' => true, 'cf_rows' => true),
'float' => array('cf_len' => true),
'label' => array('cf_values' => true),
'select' => array('cf_len' => false, 'cf_rows' => true, 'cf_values' => true),
'date' => array('cf_len' => true, 'cf_rows' => false, 'cf_values' => true),
'date-time' => array('cf_len' => true, 'cf_rows' => false, 'cf_values' => true),
'select-account' => array('cf_len' => false, 'cf_rows' => true),
'htmlarea' => array('cf_len' => true, 'cf_rows' => true),
'button' => array('cf_values' => true),
'ajax_select' => array('cf_values' => true),
'radio' => array('cf_values' => true),
'checkbox' => array('cf_values' => true),
'filemanager' => array('cf_values' => true),
);
/**
* Constructor
*
* @param string $appname
*/
function __construct($appname='')
{
if (($this->appname = $appname))
{
$this->fields = Api\Storage\Customfields::get($this->appname,true);
$this->content_types = Api\Config::get_content_types($this->appname);
}
$this->so = new Api\Storage\Base('phpgwapi','egw_customfields',null,'',true);
}
/**
* List custom fields
*/
public function index($content = array())
{
// determine appname
$this->appname = $this->appname ? $this->appname : ($_GET['appname'] ? $_GET['appname'] : ($content['appname'] ? $content['appname'] : false));
if(!$this->appname) die(lang('Error! No appname found'));
$this->use_private = !isset($_GET['use_private']) || (boolean)$_GET['use_private'] || $content['use_private'];
// Read fields, constructor doesn't always know appname
$this->fields = Api\Storage\Customfields::get($this->appname,true);
$this->tmpl = new Etemplate();
$this->tmpl->read('admin.customfields');
// do we manage content-types?
$test = new Etemplate();
if($test->read($this->appname.'.admin.types')) $this->manage_content_types = true;
// Handle incoming - types, options, etc.
if($this->manage_content_types)
{
if(count($this->content_types) == 0)
{
$this->content_types = Api\Config::get_content_types($this->appname);
}
if (count($this->content_types)==0)
{
// if you define your default types of your app with the search_link hook, they are available here, if no types were found
$this->content_types = (array)Api\Link::get_registry($this->appname,'default_types');
}
// Set this now, we need to know it for updates
$this->content_type = $content['content_types']['types'] ? $content['content_types']['types'] : (array_key_exists(0,$this->content_types) ? $this->content_types[0] : key($this->content_types));
// Common type changes - add, delete
if($content['content_types']['delete'])
{
$this->delete_content_type($content);
}
elseif($content['content_types']['create'])
{
if(($new_type = $this->create_content_type($content)))
{
$content['content_types']['types'] = $this->content_type = $new_type;
}
unset($content['content_types']['create']);
unset($content['content_types']['name']);
}
// No common type change and type didn't change, try an update
elseif($this->content_type && is_array($content) && $this->content_type == $content['old_content_type'])
{
$this->update($content);
}
}
// Custom field deleted from nextmatch
if($content['nm']['action'] == 'delete')
{
foreach($this->fields as $name => $data)
{
if(in_array($data['id'],$content['nm']['selected']))
{
unset($this->fields[$name]);
}
}
// save changes to repository
$this->save_repository();
}
$content['nm']= Api\Cache::getSession('admin', 'customfield-index');
if (!is_array($content['nm']))
{
// Initialize nextmatch
$content['nm'] = array(
'get_rows' => 'admin.admin_customfields.get_rows',
'no_cat' => 'true',
'no_filter' => 'true',
'no_filter2' => 'true',
'row_id' => 'cf_id',
'order' => 'cf_order',// IO name of the column to sort
'sort' => 'ASC',// IO direction of the sort: 'ASC' or 'DESC'
'actions' => $this->get_actions()
);
}
$content['nm']['appname'] = $this->appname;
$content['nm']['use_private'] = $this->use_private;
// Set up sub-types
if($this->manage_content_types)
{
foreach($this->content_types as $type => $entry)
{
if(!is_array($entry))
{
$this->content_types[$type] = array('name' => $entry);
$entry = $this->content_types[$type];
}
$this->types2[$type] = $entry['name'];
}
$sel_options['types'] = $sel_options['cf_type2'] = $this->types2;
$content['type_template'] = $this->appname . '.admin.types';
$content['content_types']['appname'] = $this->appname;
$content['content_type_options'] = $this->content_types[$this->content_type]['options'];
$content['content_type_options']['type'] = $this->types2[$this->content_type];
if ($this->content_types[$this->content_type]['non_deletable'])
{
$content['content_types']['non_deletable'] = true;
}
if ($this->content_types['']['no_add'])
{
$content['content_types']['no_add'] = true;
}
if ($content['content_types']['non_deletable'] && $content['content_types']['no_add'])
{
// Hide the whole line if you can't add or delete
$content['content_types']['no_edit_types'] = true;
}
// do NOT allow to delete original contact content-type for addressbook,
// as it only creates support problems as users incidently delete it
if ($this->appname == 'addressbook' && $this->content_type == 'n')
{
$readonlys['content_types']['delete'] = true;
}
$content['nm']['type2'] = true;
}
else
{
// Disable content types
$this->tmpl->disableElement('content_types', true);
}
$preserve = array(
'appname' => $this->appname,
'use_private' => $this->use_private,
'old_content_type' => $this->content_type
);
// Allow extending app a change to change content before display
$readonlys = null;
static::app_index($content, $sel_options, $readonlys, $preserve);
// Make sure app css gets loaded, extending app might cause et2 to miss it
Framework::includeCSS('admin','app');
$GLOBALS['egw_info']['flags']['app_header'] = $GLOBALS['egw_info']['apps'][$this->appname]['title'].' - '.lang('Custom fields');
// Some logic to make sure extending class (if there is one) gets called
// when etemplate2 comes back instead of parent class
$exec = get_class() == get_called_class() ? 'admin.admin_customfields.index' : $this->appname . '.' . get_called_class() . '.index';
$this->tmpl->exec($exec,$content,$sel_options,$readonlys,$preserve);
}
/**
* Edit/Create Custom fields with type
*
* @author Ralf Becker <ralfbecker-AT-outdoor-training.de>
* @param array $content Content from the eTemplate Exec
*/
function edit($content = null)
{
$cf_id = $_GET['cf_id'] ? (int)$_GET['cf_id'] : (int)$content['cf_id'];
// determine appname
$this->appname = $this->appname ? $this->appname : ($_GET['appname'] ? $_GET['appname'] : ($content['cf_app'] ? $content['cf_app'] : false));
if(!$this->appname)
{
if($cf_id && $this->so)
{
$content = $this->so->read($cf_id);
$this->appname = $content['cf_app'];
}
}
if(!$this->appname)
{
die(lang('Error! No appname found'));
}
$this->use_private = !isset($_GET['use_private']) || (boolean)$_GET['use_private'] || $content['use_private'];
// Read fields, constructor doesn't always know appname
$this->fields = Api\Storage\Customfields::get($this->appname,true);
// Update based on info returned from template
if (is_array($content))
{
list($action) = @each($content['button']);
switch($action)
{
case 'delete':
$this->so->delete($cf_id);
Framework::refresh_opener('Deleted', 'admin', $cf_id /* Conflicts with Api\Accounts 'delete'*/);
Framework::window_close();
break;
case 'save':
case 'apply':
if(!$cf_id && $this->fields[$content['cf_name']])
{
Framework::message(lang("Field '%1' already exists !!!",$content['cf_name']),'error');
$content['cf_name'] = '';
break;
}
if(empty($content['cf_label']))
{
$content['cf_label'] = $content['cf_name'];
}
if (!empty($content['cf_values']))
{
$values = array();
if($content['cf_values'][0] === '@')
{
$values['@'] = substr($content['cf_values'], $content['cf_values'][1] === '=' ? 2:1);
}
else
{
foreach(explode("\n",trim($content['cf_values'])) as $line)
{
list($var_raw,$value) = explode('=',trim($line),2);
$var = trim($var_raw);
$values[$var] = trim($value)==='' ? $var : $value;
}
}
$content['cf_values'] = $values;
}
$update_content = array();
foreach($content as $key => $value)
{
if(substr($key,0,3) == 'cf_')
{
$update_content[substr($key,3)] = $value;
}
}
Api\Storage\Customfields::update($update_content);
if(!$cf_id)
{
$this->fields = Api\Storage\Customfields::get($this->appname,true);
$cf_id = (int)$this->fields[$content['cf_name']]['id'];
}
Framework::refresh_opener('Saved', 'admin', $cf_id, 'edit');
if ($action != 'save')
{
break;
}
//fall through
case 'cancel':
Framework::window_close();
}
}
else
{
$content['use_private'] = !isset($_GET['use_private']) || (boolean)$_GET['use_private'];
}
// do we manage content-types?
$test = new Etemplate();
if($test->read($this->appname.'.admin.types')) $this->manage_content_types = true;
$this->tmpl = new Etemplate();
$this->tmpl->read('admin.customfield_edit');
Api\Translation::add_app('infolog'); // til we move the translations
$GLOBALS['egw_info']['flags']['app_header'] = $GLOBALS['egw_info']['apps'][$this->appname]['title'].' - '.lang('Custom fields');
$sel_options = array();
$readonlys = array();
//echo 'customfields=<pre style="text-align: left;">'; print_r($this->fields); echo "</pre>\n";
$content['cf_order'] = (count($this->fields)+1) * 10;
$content['use_private'] = $this->use_private;
if($cf_id)
{
$content = array_merge($content, $this->so->read($cf_id));
$this->appname = $content['cf_app'];
if($content['cf_private'])
{
$content['cf_private'] = explode(',',$content['cf_private']);
}
if($content['cf_name'])
{
$readonlys['cf_name'] = true;
}
$content['cf_values'] = json_decode($content['cf_values'], true);
}
else
{
$readonlys['button[delete]'] = true;
}
if (is_array($content['cf_values']))
{
$values = '';
foreach($content['cf_values'] as $var => $value)
{
$values .= (!empty($values) ? "\n" : '').$var.'='.$value;
}
$content['cf_values'] = $values;
}
// Show sub-type row, and get types
if($this->manage_content_types)
{
if(count($this->content_types) == 0)
{
$this->content_types = Api\Config::get_content_types($this->appname);
}
if (count($this->content_types)==0)
{
// if you define your default types of your app with the search_link hook, they are available here, if no types were found
$this->content_types = (array)Api\Link::get_registry($this->appname, 'default_types');
}
foreach($this->content_types as $type => $entry)
{
$this->types2[$type] = is_array($entry) ? $entry['name'] : $entry;
}
$sel_options['cf_type2'] = $this->types2;
}
else
{
$content['no_types'] = true;
}
// Include type-specific value help
foreach(self::$type_option_help as $key => $value)
{
$content['options'][$key] = lang($value);
}
$content['statustext'] = $content['options'][$content['cf_type']];
$content['attributes'] = self::$type_attribute_flags;
$this->tmpl->exec('admin.admin_customfields.edit',$content,$sel_options,$readonlys,array(
'cf_id' => $cf_id,
'cf_app' => $this->appname,
'cf_name' => $content['cf_name'],
'use_private' => $this->use_private,
),2);
}
/**
* Allow extending apps a change to interfere and add content to support
* their custom template. This is called right before etemplate->exec().
*/
protected function app_index(&$content, &$sel_options, &$readonlys)
{
unset($content, $sel_options, $readonlys); // not used, as this is a stub
// This is just a stub.
}
/**
* Get actions / context menu for index
*
* Changes here, require to log out, as $content['nm'] get stored in session!
*
* @return array see nextmatch_widget::egw_actions()
*/
protected function get_actions()
{
$actions = array(
'open' => array( // does edit if allowed, otherwise view
'caption' => 'Open',
'default' => true,
'allowOnMultiple' => false,
'url' => 'menuaction=admin.admin_customfields.edit&cf_id=$id&use_private='.$this->use_private,
'popup' => '500x380',
'group' => $group=1,
'disableClass' => 'th',
),
'add' => array(
'caption' => 'Add',
'url' => 'menuaction=admin.admin_customfields.edit&appname='.$this->appname.'&use_private='.$this->use_private,
'popup' => '500x380',
'group' => $group,
),
'delete' => array(
'caption' => 'Delete',
'confirm' => 'Delete this entry',
'confirm_multiple' => 'Delete these entries',
'group' => ++$group,
'disableClass' => 'rowNoDelete',
),
);
return $actions;
}
function update(&$content)
{
$this->content_types[$this->content_type]['options'] = $content['content_type_options'];
// save changes to repository
$this->save_repository();
}
/**
* deletes custom field from customfield definitions
*/
function delete_field(&$content)
{
unset($this->fields[key($content['fields']['delete'])]);
// save changes to repository
$this->save_repository();
}
function delete_content_type(&$content)
{
unset($this->content_types[$content['content_types']['types']]);
unset($this->status[$content['content_types']['types']]);
// save changes to repository
$this->save_repository();
}
/**
* create a new custom field
*/
function create_field(&$content)
{
$new_name = trim($content['fields'][count($content['fields'])-1]['name']);
if (empty($new_name) || isset($this->fields[$new_name]))
{
$content['error_msg'] .= empty($new_name) ?
lang('You have to enter a name, to create a new field!!!') :
lang("Field '%1' already exists !!!",$new_name);
}
else
{
$this->fields[$new_name] = $content['fields'][count($content['fields'])-1];
if(!$this->fields[$new_name]['label']) $this->fields[$new_name]['label'] = $this->fields[$new_name]['name'];
$this->save_repository();
}
}
/**
* Validate and create a new content type
*
* @param array $content
* @return string|boolean New type ID, or false for error
*/
function create_content_type(&$content)
{
$new_name = trim($content['content_types']['name']);
$new_type = false;
if (empty($new_name))
{
$this->tmpl->set_validation_error('content_types[name]','You have to enter a name, to create a new type!!!');
}
else
{
foreach($this->content_types as $type)
{
if($type['name'] == $new_name)
{
$this->tmpl->set_validation_error('content_types[name]',lang("type '%1' already exists !!!",$new_name));
return false;
}
}
// search free type character
for($i=97;$i<=122;$i++)
{
if (!$this->content_types[chr($i)] &&
// skip letter of deleted type for addressbook content-types, as it gives SQL error
// content-type are lowercase, Api\Contacts::DELETED_TYPE === 'D', but DB is case-insensitive
($this->appname !== 'addressbook' || chr($i) !== strtolower(Api\Contacts::DELETED_TYPE)))
{
$new_type = chr($i);
break;
}
}
$this->content_types[$new_type] = array('name' => $new_name);
$this->save_repository();
}
return $new_type;
}
/**
* save changes to repository
*/
function save_repository()
{
//echo '<p>uicustomfields::save_repository() \$this->fields=<pre style="text-aling: left;">'; print_r($this->fields); echo "</pre>\n";
$config = new Api\Config($this->appname);
$config->read_repository();
$config->value('types',$this->content_types);
$config->save_repository();
Api\Storage\Customfields::save($this->appname, $this->fields);
}
/**
* get customfields of using application
*
* @deprecated use Api\Storage\Customfields::get() direct, no need to instanciate this UI class
* @author Cornelius Weiss
* @param boolean $all_private_too =false should all the private fields be returned too
* @return array with customfields
*/
function get_customfields($all_private_too=false)
{
return Api\Storage\Customfields::get($this->appname,$all_private_too);
}
/**
* get_content_types of using application
*
* @deprecated use Api\Config::get_content_types() direct, no need to instanciate this UI class
* @author Cornelius Weiss
* @return array with content-types
*/
function get_content_types()
{
return Api\Config::get_content_types($this->appname);
}
/**
* Get list of customfields for the nextmatch
*/
public function get_rows(&$query, &$rows, &$readonlys)
{
$rows = array();
$query['col_filter']['cf_app'] = $query['appname'];
$total = $this->so->get_rows($query, $rows, $readonlys);
unset($query['col_filter']['cf_app']);
foreach($rows as &$row)
{
$row['cf_values'] = json_decode($row['cf_values'], true);
if (is_array($row['cf_values']))
{
$values = '';
foreach($row['cf_values'] as $var => $value)
{
$values .= (!empty($values) ? "\n" : '').$var.'='.$value;
}
$row['cf_values'] = $values;
}
}
return $total;
}
}

View File

@ -10,645 +10,9 @@
* @version $Id$
*/
use EGroupware\Api;
use EGroupware\Api\Framework;
use EGroupware\Api\Etemplate;
/**
* Customfields class - manages customfield definitions in egw_config table
*
* The repository name (config_name) is 'customfields'.
*
* Applications can have customfields by sub-type by having a template
* named '<appname>.admin.types'. See admin.customfields.types as an
* example, but the template can even be empty if types are handled by the
* application in another way.
*
* Applications can extend this class to customize the custom fields and handle
* extra information from the above template by extending and implementing
* update() and app_index().
* Wrapper allowing apps to use old name
*/
class customfields
{
/**
* appname of app which want to add / edit its customfields
*
* @var string
*/
var $appname;
/**
* Allow custom fields to be restricted to certain users/groups
*/
protected $use_private = false;
/**
* userdefiened types e.g. type of infolog
*
* @var array
*/
var $types2 = array();
var $content_types,$fields;
/**
* Currently selected content type (if used by app)
* @var string
*/
protected $content_type = null;
var $public_functions = array(
'index' => true,
'edit' => True
);
/**
* Instance of etemplate class
*
* @var etemplate
*/
var $tmpl;
/**
* @var Description of the options or value format for each cf_type
*/
public static $type_option_help = array(
'search' => 'set get_rows, get_title and id_field, or use @path to read options from a file in EGroupware directory',
'select' => 'each value is a line like id[=label], or use @path to read options from a file in EGroupware directory',
'radio' => 'each value is a line like id[=label], or use @path to read options from a file in EGroupware directory',
'button' => 'each value is a line like label=[javascript]'
);
/**
* Custom fields can also have length and rows set, but these are't used for all types
* If not set to true here, the field will be disabled when selecting the type
*/
public static $type_attribute_flags = array(
'text' => array('cf_len' => true, 'cf_rows' => true),
'float' => array('cf_len' => true),
'label' => array('cf_values' => true),
'select' => array('cf_len' => false, 'cf_rows' => true, 'cf_values' => true),
'date' => array('cf_len' => true, 'cf_rows' => false, 'cf_values' => true),
'date-time' => array('cf_len' => true, 'cf_rows' => false, 'cf_values' => true),
'select-account' => array('cf_len' => false, 'cf_rows' => true),
'htmlarea' => array('cf_len' => true, 'cf_rows' => true),
'button' => array('cf_values' => true),
'ajax_select' => array('cf_values' => true),
'radio' => array('cf_values' => true),
'checkbox' => array('cf_values' => true),
'filemanager' => array('cf_values' => true),
);
/**
* Constructor
*
* @param string $appname
*/
function __construct($appname='')
{
if (($this->appname = $appname))
{
$this->fields = Api\Storage\Customfields::get($this->appname,true);
$this->content_types = Api\Config::get_content_types($this->appname);
}
$this->so = new Api\Storage\Base('phpgwapi','egw_customfields',null,'',true);
}
/**
* List custom fields
*/
public function index($content = array())
{
// determine appname
$this->appname = $this->appname ? $this->appname : ($_GET['appname'] ? $_GET['appname'] : ($content['appname'] ? $content['appname'] : false));
if(!$this->appname) die(lang('Error! No appname found'));
$this->use_private = !isset($_GET['use_private']) || (boolean)$_GET['use_private'] || $content['use_private'];
// Read fields, constructor doesn't always know appname
$this->fields = Api\Storage\Customfields::get($this->appname,true);
$this->tmpl = new Etemplate();
$this->tmpl->read('admin.customfields');
// do we manage content-types?
$test = new Etemplate();
if($test->read($this->appname.'.admin.types')) $this->manage_content_types = true;
// Handle incoming - types, options, etc.
if($this->manage_content_types)
{
if(count($this->content_types) == 0)
{
$this->content_types = Api\Config::get_content_types($this->appname);
}
if (count($this->content_types)==0)
{
// if you define your default types of your app with the search_link hook, they are available here, if no types were found
$this->content_types = (array)Api\Link::get_registry($this->appname,'default_types');
}
// Set this now, we need to know it for updates
$this->content_type = $content['content_types']['types'] ? $content['content_types']['types'] : (array_key_exists(0,$this->content_types) ? $this->content_types[0] : key($this->content_types));
// Common type changes - add, delete
if($content['content_types']['delete'])
{
$this->delete_content_type($content);
}
elseif($content['content_types']['create'])
{
if(($new_type = $this->create_content_type($content)))
{
$content['content_types']['types'] = $this->content_type = $new_type;
}
unset($content['content_types']['create']);
unset($content['content_types']['name']);
}
// No common type change and type didn't change, try an update
elseif($this->content_type && is_array($content) && $this->content_type == $content['old_content_type'])
{
$this->update($content);
}
}
// Custom field deleted from nextmatch
if($content['nm']['action'] == 'delete')
{
foreach($this->fields as $name => $data)
{
if(in_array($data['id'],$content['nm']['selected']))
{
unset($this->fields[$name]);
}
}
// save changes to repository
$this->save_repository();
}
$content['nm']= Api\Cache::getSession('admin', 'customfield-index');
if (!is_array($content['nm']))
{
// Initialize nextmatch
$content['nm'] = array(
'get_rows' => 'admin.customfields.get_rows',
'no_cat' => 'true',
'no_filter' => 'true',
'no_filter2' => 'true',
'row_id' => 'cf_id',
'order' => 'cf_order',// IO name of the column to sort
'sort' => 'ASC',// IO direction of the sort: 'ASC' or 'DESC'
'actions' => $this->get_actions()
);
}
$content['nm']['appname'] = $this->appname;
$content['nm']['use_private'] = $this->use_private;
// Set up sub-types
if($this->manage_content_types)
{
foreach($this->content_types as $type => $entry)
{
if(!is_array($entry))
{
$this->content_types[$type] = array('name' => $entry);
$entry = $this->content_types[$type];
}
$this->types2[$type] = $entry['name'];
}
$sel_options['types'] = $sel_options['cf_type2'] = $this->types2;
$content['type_template'] = $this->appname . '.admin.types';
$content['content_types']['appname'] = $this->appname;
$content['content_type_options'] = $this->content_types[$this->content_type]['options'];
$content['content_type_options']['type'] = $this->types2[$this->content_type];
if ($this->content_types[$this->content_type]['non_deletable'])
{
$content['content_types']['non_deletable'] = true;
}
if ($this->content_types['']['no_add'])
{
$content['content_types']['no_add'] = true;
}
if ($content['content_types']['non_deletable'] && $content['content_types']['no_add'])
{
// Hide the whole line if you can't add or delete
$content['content_types']['no_edit_types'] = true;
}
// do NOT allow to delete original contact content-type for addressbook,
// as it only creates support problems as users incidently delete it
if ($this->appname == 'addressbook' && $this->content_type == 'n')
{
$readonlys['content_types']['delete'] = true;
}
$content['nm']['type2'] = true;
}
else
{
// Disable content types
$this->tmpl->disableElement('content_types', true);
}
$preserve = array(
'appname' => $this->appname,
'use_private' => $this->use_private,
'old_content_type' => $this->content_type
);
// Allow extending app a change to change content before display
$readonlys = null;
static::app_index($content, $sel_options, $readonlys, $preserve);
// Make sure app css gets loaded, extending app might cause et2 to miss it
Framework::includeCSS('admin','app');
$GLOBALS['egw_info']['flags']['app_header'] = $GLOBALS['egw_info']['apps'][$this->appname]['title'].' - '.lang('Custom fields');
// Some logic to make sure extending class (if there is one) gets called
// when etemplate2 comes back instead of parent class
$exec = get_class() == get_called_class() ? 'admin.customfields.index' : $this->appname . '.' . get_called_class() . '.index';
$this->tmpl->exec($exec,$content,$sel_options,$readonlys,$preserve);
}
/**
* Edit/Create Custom fields with type
*
* @author Ralf Becker <ralfbecker-AT-outdoor-training.de>
* @param array $content Content from the eTemplate Exec
*/
function edit($content = null)
{
$cf_id = $_GET['cf_id'] ? (int)$_GET['cf_id'] : (int)$content['cf_id'];
// determine appname
$this->appname = $this->appname ? $this->appname : ($_GET['appname'] ? $_GET['appname'] : ($content['cf_app'] ? $content['cf_app'] : false));
if(!$this->appname)
{
if($cf_id && $this->so)
{
$content = $this->so->read($cf_id);
$this->appname = $content['cf_app'];
}
}
if(!$this->appname)
{
die(lang('Error! No appname found'));
}
$this->use_private = !isset($_GET['use_private']) || (boolean)$_GET['use_private'] || $content['use_private'];
// Read fields, constructor doesn't always know appname
$this->fields = Api\Storage\Customfields::get($this->appname,true);
// Update based on info returned from template
if (is_array($content))
{
list($action) = @each($content['button']);
switch($action)
{
case 'delete':
$this->so->delete($cf_id);
Framework::refresh_opener('Deleted', 'admin', $cf_id /* Conflicts with Api\Accounts 'delete'*/);
Framework::window_close();
break;
case 'save':
case 'apply':
if(!$cf_id && $this->fields[$content['cf_name']])
{
Framework::message(lang("Field '%1' already exists !!!",$content['cf_name']),'error');
$content['cf_name'] = '';
break;
}
if(empty($content['cf_label']))
{
$content['cf_label'] = $content['cf_name'];
}
if (!empty($content['cf_values']))
{
$values = array();
if($content['cf_values'][0] === '@')
{
$values['@'] = substr($content['cf_values'], $content['cf_values'][1] === '=' ? 2:1);
}
else
{
foreach(explode("\n",trim($content['cf_values'])) as $line)
{
list($var_raw,$value) = explode('=',trim($line),2);
$var = trim($var_raw);
$values[$var] = trim($value)==='' ? $var : $value;
}
}
$content['cf_values'] = $values;
}
$update_content = array();
foreach($content as $key => $value)
{
if(substr($key,0,3) == 'cf_')
{
$update_content[substr($key,3)] = $value;
}
}
Api\Storage\Customfields::update($update_content);
if(!$cf_id)
{
$this->fields = Api\Storage\Customfields::get($this->appname,true);
$cf_id = (int)$this->fields[$content['cf_name']]['id'];
}
Framework::refresh_opener('Saved', 'admin', $cf_id, 'edit');
if ($action != 'save')
{
break;
}
//fall through
case 'cancel':
Framework::window_close();
}
}
else
{
$content['use_private'] = !isset($_GET['use_private']) || (boolean)$_GET['use_private'];
}
// do we manage content-types?
$test = new Etemplate();
if($test->read($this->appname.'.admin.types')) $this->manage_content_types = true;
$this->tmpl = new Etemplate();
$this->tmpl->read('admin.customfield_edit');
Api\Translation::add_app('infolog'); // til we move the translations
$GLOBALS['egw_info']['flags']['app_header'] = $GLOBALS['egw_info']['apps'][$this->appname]['title'].' - '.lang('Custom fields');
$sel_options = array();
$readonlys = array();
//echo 'customfields=<pre style="text-align: left;">'; print_r($this->fields); echo "</pre>\n";
$content['cf_order'] = (count($this->fields)+1) * 10;
$content['use_private'] = $this->use_private;
if($cf_id)
{
$content = array_merge($content, $this->so->read($cf_id));
$this->appname = $content['cf_app'];
if($content['cf_private'])
{
$content['cf_private'] = explode(',',$content['cf_private']);
}
if($content['cf_name'])
{
$readonlys['cf_name'] = true;
}
$content['cf_values'] = json_decode($content['cf_values'], true);
}
else
{
$readonlys['button[delete]'] = true;
}
if (is_array($content['cf_values']))
{
$values = '';
foreach($content['cf_values'] as $var => $value)
{
$values .= (!empty($values) ? "\n" : '').$var.'='.$value;
}
$content['cf_values'] = $values;
}
// Show sub-type row, and get types
if($this->manage_content_types)
{
if(count($this->content_types) == 0)
{
$this->content_types = Api\Config::get_content_types($this->appname);
}
if (count($this->content_types)==0)
{
// if you define your default types of your app with the search_link hook, they are available here, if no types were found
$this->content_types = (array)Api\Link::get_registry($this->appname, 'default_types');
}
foreach($this->content_types as $type => $entry)
{
$this->types2[$type] = is_array($entry) ? $entry['name'] : $entry;
}
$sel_options['cf_type2'] = $this->types2;
}
else
{
$content['no_types'] = true;
}
// Include type-specific value help
foreach(self::$type_option_help as $key => $value)
{
$content['options'][$key] = lang($value);
}
$content['statustext'] = $content['options'][$content['cf_type']];
$content['attributes'] = self::$type_attribute_flags;
$this->tmpl->exec('admin.customfields.edit',$content,$sel_options,$readonlys,array(
'cf_id' => $cf_id,
'cf_app' => $this->appname,
'cf_name' => $content['cf_name'],
'use_private' => $this->use_private,
),2);
}
/**
* Allow extending apps a change to interfere and add content to support
* their custom template. This is called right before etemplate->exec().
*/
protected function app_index(&$content, &$sel_options, &$readonlys)
{
unset($content, $sel_options, $readonlys); // not used, as this is a stub
// This is just a stub.
}
/**
* Get actions / context menu for index
*
* Changes here, require to log out, as $content['nm'] get stored in session!
*
* @return array see nextmatch_widget::egw_actions()
*/
protected function get_actions()
{
$actions = array(
'open' => array( // does edit if allowed, otherwise view
'caption' => 'Open',
'default' => true,
'allowOnMultiple' => false,
'url' => 'menuaction=admin.customfields.edit&cf_id=$id&use_private='.$this->use_private,
'popup' => '500x380',
'group' => $group=1,
'disableClass' => 'th',
),
'add' => array(
'caption' => 'Add',
'url' => 'menuaction=admin.customfields.edit&appname='.$this->appname.'&use_private='.$this->use_private,
'popup' => '500x380',
'group' => $group,
),
'delete' => array(
'caption' => 'Delete',
'confirm' => 'Delete this entry',
'confirm_multiple' => 'Delete these entries',
'group' => ++$group,
'disableClass' => 'rowNoDelete',
),
);
return $actions;
}
function update(&$content)
{
$this->content_types[$this->content_type]['options'] = $content['content_type_options'];
// save changes to repository
$this->save_repository();
}
/**
* deletes custom field from customfield definitions
*/
function delete_field(&$content)
{
unset($this->fields[key($content['fields']['delete'])]);
// save changes to repository
$this->save_repository();
}
function delete_content_type(&$content)
{
unset($this->content_types[$content['content_types']['types']]);
unset($this->status[$content['content_types']['types']]);
// save changes to repository
$this->save_repository();
}
/**
* create a new custom field
*/
function create_field(&$content)
{
$new_name = trim($content['fields'][count($content['fields'])-1]['name']);
if (empty($new_name) || isset($this->fields[$new_name]))
{
$content['error_msg'] .= empty($new_name) ?
lang('You have to enter a name, to create a new field!!!') :
lang("Field '%1' already exists !!!",$new_name);
}
else
{
$this->fields[$new_name] = $content['fields'][count($content['fields'])-1];
if(!$this->fields[$new_name]['label']) $this->fields[$new_name]['label'] = $this->fields[$new_name]['name'];
$this->save_repository();
}
}
/**
* Validate and create a new content type
*
* @param array $content
* @return string|boolean New type ID, or false for error
*/
function create_content_type(&$content)
{
$new_name = trim($content['content_types']['name']);
$new_type = false;
if (empty($new_name))
{
$this->tmpl->set_validation_error('content_types[name]','You have to enter a name, to create a new type!!!');
}
else
{
foreach($this->content_types as $type)
{
if($type['name'] == $new_name)
{
$this->tmpl->set_validation_error('content_types[name]',lang("type '%1' already exists !!!",$new_name));
return false;
}
}
// search free type character
for($i=97;$i<=122;$i++)
{
if (!$this->content_types[chr($i)] &&
// skip letter of deleted type for addressbook content-types, as it gives SQL error
// content-type are lowercase, Api\Contacts::DELETED_TYPE === 'D', but DB is case-insensitive
($this->appname !== 'addressbook' || chr($i) !== strtolower(Api\Contacts::DELETED_TYPE)))
{
$new_type = chr($i);
break;
}
}
$this->content_types[$new_type] = array('name' => $new_name);
$this->save_repository();
}
return $new_type;
}
/**
* save changes to repository
*/
function save_repository()
{
//echo '<p>uicustomfields::save_repository() \$this->fields=<pre style="text-aling: left;">'; print_r($this->fields); echo "</pre>\n";
$config = new Api\Config($this->appname);
$config->read_repository();
$config->value('types',$this->content_types);
$config->save_repository();
Api\Storage\Customfields::save($this->appname, $this->fields);
}
/**
* get customfields of using application
*
* @deprecated use Api\Storage\Customfields::get() direct, no need to instanciate this UI class
* @author Cornelius Weiss
* @param boolean $all_private_too =false should all the private fields be returned too
* @return array with customfields
*/
function get_customfields($all_private_too=false)
{
return Api\Storage\Customfields::get($this->appname,$all_private_too);
}
/**
* get_content_types of using application
*
* @deprecated use Api\Config::get_content_types() direct, no need to instanciate this UI class
* @author Cornelius Weiss
* @return array with content-types
*/
function get_content_types()
{
return Api\Config::get_content_types($this->appname);
}
/**
* Get list of customfields for the nextmatch
*/
public function get_rows(&$query, &$rows, &$readonlys)
{
$rows = array();
$query['col_filter']['cf_app'] = $query['appname'];
$total = $this->so->get_rows($query, $rows, $readonlys);
unset($query['col_filter']['cf_app']);
foreach($rows as &$row)
{
$row['cf_values'] = json_decode($row['cf_values'], true);
if (is_array($row['cf_values']))
{
$values = '';
foreach($row['cf_values'] as $var => $value)
{
$values .= (!empty($values) ? "\n" : '').$var.'='.$value;
}
$row['cf_values'] = $values;
}
}
return $total;
}
}
class customfields extends admin_customfields {}

View File

@ -83,7 +83,7 @@
</grid>
</template>
<template id="admin.customfields.add" template="" lang="" group="0" version="1.9.001">
<buttononly label="Add" id="add" onclick="egw.open_link('admin.customfields.edit&amp;appname='+widget.getArrayMgr('content').getRoot().getEntry('nm[appname]')+'&amp;use_private='+widget.getArrayMgr('content').getRoot().getEntry('use_private'),false,'450x380');"/>
<buttononly label="Add" id="add" onclick="egw.open_link('admin.admin_customfields.edit&amp;appname='+widget.getArrayMgr('content').getRoot().getEntry('nm[appname]')+'&amp;use_private='+widget.getArrayMgr('content').getRoot().getEntry('use_private'),false,'450x380');"/>
</template>
<template id="admin.customfields" template="" lang="" group="0" version="1.9.001">
<template id="@type_template"/>

View File

@ -85,8 +85,8 @@ class calendar_hooks
{
$file = Array(
'Site Configuration' => Egw::link('/index.php','menuaction=admin.admin_config.index&appname=calendar&ajax=true'),
'Custom fields' => Egw::link('/index.php','menuaction=admin.customfields.index&appname=calendar'),
'Global Categories' => Egw::link('/index.php','menuaction=admin.admin_categories.index&appname=calendar'),
'Custom fields' => Egw::link('/index.php','menuaction=admin.customfields.index&appname=calendar&ajax=true'),
'Global Categories' => Egw::link('/index.php','menuaction=admin.admin_categories.index&appname=calendar&ajax=true'),
'Category ACL' => Egw::link('/index.php','menuaction=calendar.calendar_uiforms.cat_acl'),
'Update timezones' => Egw::link('/index.php','menuaction=calendar.calendar_timezones.update'),
);

View File

@ -519,8 +519,8 @@ class calendar_ui
{
$file = Array(
'Configuration'=>Egw::link('/index.php','menuaction=admin.admin_config.index&appname=calendar&ajax=true'),
'Custom Fields'=>Egw::link('/index.php','menuaction=admin.customfields.index&appname=calendar'),
'Global Categories' =>Egw::link('/index.php','menuaction=admin.admin_categories.index&appname=calendar'),
'Custom Fields'=>Egw::link('/index.php','menuaction=admin.customfields.index&appname=calendar&ajax=true'),
'Global Categories' =>Egw::link('/index.php','menuaction=admin.admin_categories.index&appname=calendar&ajax=true'),
);
$GLOBALS['egw']->framework->sidebox($appname,lang('Admin'),$file,'admin');
}

View File

@ -13,11 +13,10 @@
use EGroupware\Api;
use EGroupware\Api\Etemplate;
require_once(EGW_INCLUDE_ROOT . '/admin/inc/class.customfields.inc.php');
/**
* Administration of custom fields, type and status
*/
class infolog_customfields extends customfields
class infolog_customfields extends admin_customfields
{
public $appname = 'infolog';
/**
@ -50,6 +49,8 @@ class infolog_customfields extends customfields
$this->config_data = Api\Config::read('infolog');
$this->fields = &$this->bo->customfields;
$this->group_owners =& $this->bo->group_owners;
Api\Translation::add_app('infolog');
}

View File

@ -110,13 +110,19 @@ class infolog_hooks
{
$file = Array(
'Site configuration' => Egw::link('/index.php',array(
'menuaction' => 'infolog.infolog_ui.admin' )),
'menuaction' => 'infolog.infolog_ui.admin',
'ajax' => 'true',
)),
'Global Categories' => Egw::link('/index.php',array(
'menuaction' => 'admin.admin_categories.index',
'appname' => $appname,
'global_cats'=> True)),
'Custom fields, typ and status' => Egw::link('/index.php',array(
'menuaction' => 'infolog.infolog_customfields.index')),
'global_cats'=> True,
'ajax' => 'true',
)),
'Custom fields, type and status' => Egw::link('/index.php',array(
'menuaction' => 'infolog.infolog_customfields.index',
'ajax' => 'true',
)),
);
if ($location == 'admin')
{

View File

@ -87,7 +87,7 @@ 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 fields, type and status common bg По избор - полета, тип и статус
custom from infolog bg Форма по избор
custom regarding infolog bg По избор - относно
custom status for typ infolog bg По избор - статус за тип

View File

@ -77,7 +77,7 @@ 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 fields, type 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

View File

@ -94,7 +94,7 @@ custom infolog cs Uživatelsky definované
custom contact-address, leave empty to use information from most recent link infolog cs Uživatelsky definovaná kontaktní adresa, ponechte prázdné pokud chcete použít informace z posledního odkazu
custom contact-information, leave emtpy to use information from most recent link infolog cs Uživatelsky definované kontaktní údaje, ponechte prázdné pro použití informací z posledního odkazu
custom fields infolog cs Uživatelsky definované položky
custom fields, typ and status common cs Uživatelsky definované položky, typy a stavy
custom fields, type and status common cs Uživatelsky definované položky, typy a stavy
custom from infolog cs Uživatelsky definované od
custom regarding infolog cs Uživatelsky definovaný ohled
custom status for typ infolog cs Uživatelsky definovaný stav pro typ

View File

@ -67,7 +67,7 @@ custom infolog da Egne
custom contact-address, leave empty to use information from most recent link infolog da Egen kontakt-adresser, efterlad tom for at bruge informationen fra det seneste link
custom contact-information, leave emtpy to use information from most recent link infolog da Egen kontakt-information, efterlad tom for at bruge informationen fra det seneste link
custom fields infolog da Egne felter
custom fields, typ and status common da Egne felter, type og status
custom fields, type and status common da Egne felter, type og status
custom status for typ infolog da Egen status for type
customfields infolog da Egne felter
datecreated infolog da dato oprettet

View File

@ -120,7 +120,7 @@ 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 fields, type 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

View File

@ -78,7 +78,7 @@ 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 fields, type and status common el Προσαρμοσμένα πεδία,τύπος και κατάσταση
custom regarding infolog el Προσαρμοσμένη αναφορά
custom status for typ infolog el Προσαρμοσμένη κατάσταση για τον τύπο
customfields infolog el Προσαρμοσμένα πεδία

View File

@ -120,7 +120,7 @@ custom infolog en Custom fields
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 fields, type 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

View File

@ -110,7 +110,7 @@ custom infolog es-es Personalizado
custom contact-address, leave empty to use information from most recent link infolog es-es Dirección de contacto personalizada. Dejar en blanco para usar la información del enlace más reciente
custom contact-information, leave emtpy to use information from most recent link infolog es-es Información de contacto personalizada. Dejar en blanco para usar la información del enlace más reciente
custom fields infolog es-es Campos personalizados
custom fields, typ and status common es-es Campos personalizados, tipo y estado
custom fields, type and status common es-es Campos personalizados, tipo y estado
custom from infolog es-es Personalizado desde
custom regarding infolog es-es Recuerdos personales
custom status for typ infolog es-es Estado personalizado para el tipo

View File

@ -63,7 +63,7 @@ csv-filename infolog eu CSV-fitxategiaren izena
csv-import common eu CSV-inportatu
custom infolog eu Pertsonalizatua
custom fields infolog eu Eremu pertsonalizatuak
custom fields, typ and status common eu Eremu pertsonalizatuak, mota eta egoera
custom fields, type and status common eu Eremu pertsonalizatuak, mota eta egoera
custom regarding infolog eu Oroitzapen pertsonalak
custom status for typ infolog eu Egoera pertsonalizatua motarentzat
customfields infolog eu Eremu pertsonalizatuak

View File

@ -73,7 +73,7 @@ custom infolog fa سفارشی
custom contact-address, leave empty to use information from most recent link infolog fa نشانی سفارشی مخاطب(ین).ذینفع(ان)، برای استفاده از پیوندهایی که اخیرا استفاده شده اند، خالی بگذارید
custom contact-information, leave emtpy to use information from most recent link infolog fa اطلاعات سفارشی مخاطب(ین).ذینفع(ان)، برای استفاده از پیوندهایی که اخیرا استفاده شده اند، خالی بگذارید
custom fields infolog fa فیلدهای سفارشی
custom fields, typ and status common fa فیلدهای سفارشی، نوع و وضعیت
custom fields, type and status common fa فیلدهای سفارشی، نوع و وضعیت
custom regarding infolog fa عطف سفارشی
custom status for typ infolog fa وضعیت سفارشی برای نوع
customfields infolog fa فیلدهای سفارشی

View File

@ -112,7 +112,7 @@ custom infolog fi Muokkaa
custom contact-address, leave empty to use information from most recent link infolog fi Yhteystieto, jätä tyhjäksi jos viimeisintä linkkiä käytetään
custom contact-information, leave emtpy to use information from most recent link infolog fi Yhteystieto, jätä tyhjäksi jos viimeisintä linkkiä käytetään
custom fields infolog fi Lisäkentät
custom fields, typ and status common fi Lisäkentät, tyyppi ja tila
custom fields, type and status common fi Lisäkentät, tyyppi ja tila
custom from infolog fi Muokkaa:
custom regarding infolog fi Muokkaa:
custom status for typ infolog fi Muokattava tila tehtävätyypille

View File

@ -119,7 +119,7 @@ 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 fields, type and status common fr Champs, types et statuts personnalisés
custom from infolog fr Personnalisé de
custom regarding infolog fr Personnalisé concernant
custom status for typ infolog fr Statuts personnalisés pour le type

View File

@ -64,7 +64,7 @@ custom infolog hr Proizvoljno
custom contact-address, leave empty to use information from most recent link infolog hr Proizvoljna adresa za kontakt, ostavite prazno za upotrebu informacija iz zadnjeg lika-a
custom contact-information, leave emtpy to use information from most recent link infolog hr Custom contact-information, leave emtpy to use information from most recent link
custom fields infolog hr Proizvoljna polja
custom fields, typ and status common hr Proizvoljna polja, vrste i status
custom fields, type and status common hr Proizvoljna polja, vrste i status
custom regarding infolog hr Custom regarding
custom status for typ infolog hr Proizvoljan status za tip
customfields infolog hr Proizvoljna polja

View File

@ -93,7 +93,7 @@ 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 fields, type 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

View File

@ -81,7 +81,7 @@ 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 fields, type 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

View File

@ -120,7 +120,7 @@ custom infolog it Personalizzato
custom contact-address, leave empty to use information from most recent link infolog it Indirizzo personalizzato, lascia vuoto per usare le informazioni dal link più recente
custom contact-information, leave emtpy to use information from most recent link infolog it Informazione personalizzata del contatto, lascia vuoto per usare le informazioni dal link più recente
custom fields infolog it Campi Personalizzati
custom fields, typ and status common it Campi personalizzati, tipo e stato
custom fields, type and status common it Campi personalizzati, tipo e stato
custom from infolog it Campo Da personalizzato
custom regarding infolog it Considerazione personale
custom status for typ infolog it Stato personalizzato per il tipo

View File

@ -55,7 +55,7 @@ custom infolog iw מותאם אישית
custom contact-address, leave empty to use information from most recent link infolog iw כתובת איש קשר מותאם אישית -פ להשאיר ריק כדי להשתמש במידע מהקישור האחרון
custom contact-information, leave emtpy to use information from most recent link infolog iw נתוני איש קשר מותאם אישית -פ להשאיר ריק כדי להשתמש במידע מהקישור האחרון
custom fields infolog iw שדות מותאמים אישית
custom fields, typ and status common iw שדות מותאמים אישית, סוג וסטאטוס
custom fields, type and status common iw שדות מותאמים אישית, סוג וסטאטוס
custom regarding infolog iw בנושא מותאם אישית
custom status for typ infolog iw סטאטוס מותאם אישית עבור סוג
customfields infolog iw שדות מותאמים אישית

View File

@ -63,7 +63,7 @@ custom infolog lv Klients
custom contact-address, leave empty to use information from most recent link infolog lv Klienta kontakts-adrese, atstāj tukšu, lai izmantotu informāciju no pēdējās saites
custom contact-information, leave emtpy to use information from most recent link infolog lv Klienta kontakts- informācija, atstāj tukšu, lai izmantotu informāciju no pēdējās saites
custom fields infolog lv Klienta lauki
custom fields, typ and status common lv Klienta lauki, tips un statuss
custom fields, type and status common lv Klienta lauki, tips un statuss
custom regarding infolog lv Par klientu
custom status for typ infolog lv Klienta veida statuss
customfields infolog lv Klienta lauki

View File

@ -91,7 +91,7 @@ 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 fields, type 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

View File

@ -73,7 +73,7 @@ custom infolog no Egendefinert
custom contact-address, leave empty to use information from most recent link infolog no Egendefinert kontakt-adresse, la være tom for å bruke informasjonen fra sist brukte lenke
custom contact-information, leave emtpy to use information from most recent link infolog no Egendefinert kontaktinformasjon, la være tom for å bruke informasjonen fra sist brukte lenke
custom fields infolog no Egendefinert felt
custom fields, typ and status common no Egendefinerte felter, type og status
custom fields, type and status common no Egendefinerte felter, type og status
custom regarding infolog no Egendefinert angående
custom status for typ infolog no Egendefinert status for type
customfields infolog no Egendefinertfelt

View File

@ -79,7 +79,7 @@ custom infolog pl Własne
custom contact-address, leave empty to use information from most recent link infolog pl Własne dane adresowe dla kontaktu, zostaw puste aby użyć informacji z dodawanego linku
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 fields, type and status common pl Typ i status pola własnego
custom regarding infolog pl Custom regarding
custom status for typ infolog pl Własny status dla typu
customfields infolog pl Polawłasne

View File

@ -92,7 +92,7 @@ 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 fields, type 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

View File

@ -114,7 +114,7 @@ custom infolog pt Personalizar
custom contact-address, leave empty to use information from most recent link infolog pt Personalizar endereço de contacto, deixar vazio para utilizar informação da ligação mais recente
custom contact-information, leave emtpy to use information from most recent link infolog pt Personalizar informação de contacto, deixar vazio para utilizar informação da ligação mais recente
custom fields infolog pt Personalizar campos
custom fields, typ and status common pt Personalizar campos, tipo e estado
custom fields, type and status common pt Personalizar campos, tipo e estado
custom from infolog pt Personalizado
custom regarding infolog pt Personalizar tendo em conta
custom status for typ infolog pt Personalizar estado do tipo

View File

@ -115,7 +115,7 @@ 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 fields, type and status common ru Пользовательские поля, тип и состояние
custom from infolog ru Пользовательский из (?)
custom regarding infolog ru Пользовательский относительно (?)
custom status for typ infolog ru Пользовательское состояние для типа

View File

@ -118,7 +118,7 @@ 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 fields, type 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

View File

@ -90,7 +90,7 @@ 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 fields, type 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

View File

@ -78,7 +78,7 @@ 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 fields, type 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

View File

@ -57,7 +57,7 @@ custom infolog uk Власне
custom contact-address, leave empty to use information from most recent link infolog uk Додаткові адреси контактів, залиште порожнім, щоб використати інформацію з останнього посилання
custom contact-information, leave emtpy to use information from most recent link infolog uk Додаткові інформація контактів, залиште порожнім, щоб використати інформацію з останнього посилання
custom fields infolog uk Власні поля
custom fields, typ and status common uk Власні поля, типи та статуси
custom fields, type and status common uk Власні поля, типи та статуси
custom status for typ infolog uk Власний статус типу
customfields infolog uk Власні поля
datecreated infolog uk Дата створення

View File

@ -90,7 +90,7 @@ 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 fields, type and status common zh-tw 自訂欄位、格式與狀態
custom regarding infolog zh-tw 自訂關於
custom status for typ infolog zh-tw 自訂格式的狀態
customfields infolog zh-tw 自定欄位

View File

@ -75,7 +75,7 @@ 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 fields, type and status common zh 自定义字段, 类型及状态
custom regarding infolog zh 自定义关于
custom status for typ infolog zh 自定义该类型的可选状态:
customfields infolog zh Customfields

View File

@ -63,7 +63,7 @@ class resources_hooks
'Configure Access Permissions' => Egw::link('/index.php',
'menuaction=resources.resources_acl_ui.index&ajax=true'),
'Custom Fields'=>egw::link('/index.php',
'menuaction=admin.customfields.index&appname=resources'),
'menuaction=admin.customfields.index&appname=resources&ajax=true'),
);
if ($location == 'admin')
{

View File

@ -132,12 +132,14 @@ class timesheet_hooks
{
$file = Array(
'Site Configuration' => Egw::link('/index.php','menuaction=admin.admin_config.index&appname=' . $appname,'&ajax=true'),
'Custom fields' => Egw::link('/index.php','menuaction=admin.customfields.index&appname='.$appname.'&use_private=1'),
'Custom fields' => Egw::link('/index.php','menuaction=admin.customfields.index&appname='.$appname.'&use_private=1&ajax=true'),
'Global Categories' => Egw::link('/index.php',array(
'menuaction' => 'admin.admin_categories.index',
'appname' => $appname,
'global_cats'=> True)),
'Edit Status' => Egw::link('/index.php','menuaction=timesheet.timesheet_ui.editstatus'),
'global_cats'=> True,
'ajax' => 'true',
)),
'Edit Status' => Egw::link('/index.php','menuaction=timesheet.timesheet_ui.editstatus&ajax=true'),
);
if ($location == 'admin')
{