mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-21 23:43:17 +01:00
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:
parent
f3649bf021
commit
3752c35a5a
@ -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'
|
||||
|
654
admin/inc/class.admin_customfields.inc.php
Normal file
654
admin/inc/class.admin_customfields.inc.php
Normal 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;
|
||||
}
|
||||
}
|
@ -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 {}
|
||||
|
@ -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&appname='+widget.getArrayMgr('content').getRoot().getEntry('nm[appname]')+'&use_private='+widget.getArrayMgr('content').getRoot().getEntry('use_private'),false,'450x380');"/>
|
||||
<buttononly label="Add" id="add" onclick="egw.open_link('admin.admin_customfields.edit&appname='+widget.getArrayMgr('content').getRoot().getEntry('nm[appname]')+'&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"/>
|
||||
|
@ -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'),
|
||||
);
|
||||
|
@ -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');
|
||||
}
|
||||
|
@ -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');
|
||||
}
|
||||
|
||||
|
||||
|
@ -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')
|
||||
{
|
||||
|
@ -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 По избор - статус за тип
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 Προσαρμοσμένα πεδία
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 فیلدهای سفارشی
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 שדות מותאמים אישית
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 Пользовательское состояние для типа
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 Дата створення
|
||||
|
@ -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 自定欄位
|
||||
|
@ -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
|
||||
|
@ -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')
|
||||
{
|
||||
|
@ -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')
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user