* Switch to nextmatch and edit dialog for customfield list

This commit is contained in:
Nathan Gray 2014-10-22 19:55:27 +00:00
parent bc864c4103
commit 2f53633dca
7 changed files with 407 additions and 182 deletions

View File

@ -72,9 +72,10 @@ class addressbook_hooks
if ($GLOBALS['egw_info']['server']['contact_repository'] != 'ldap')
{
$file['Custom fields'] = egw::link('/index.php',array(
'menuaction' => 'admin.customfields.edit',
'menuaction' => 'admin.customfields.index',
'appname' => $appname,
'use_private'=> 1,
'ajax' => 'true'
));
}
if ($location == 'admin')

View File

@ -25,6 +25,11 @@ class customfields
*/
var $appname;
/**
* Allow custom fields to be restricted to certain users/groups
*/
protected $use_private = false;
/**
* userdefiened types e.g. type of infolog
*
@ -34,6 +39,7 @@ class customfields
var $content_types,$fields;
var $public_functions = array(
'index' => true,
'edit' => True
);
/**
@ -55,27 +61,31 @@ class customfields
$this->fields = egw_customfields::get($this->appname,true);
$this->content_types = config::get_content_types($this->appname);
}
$this->so = new so_sql('phpgwapi','egw_customfields',null,'',true);
}
/**
* Edit/Create Custom fields with type
*
* @author Ralf Becker <ralfbecker-AT-outdoor-training.de>
* @param array $content Content from the eTemplate Exec
* List custom fields
*/
function edit($content = null)
public function index($content = array())
{
// determine appname
$this->appname = $_GET['appname'] ? $_GET['appname'] : ($content['appname'] ? $content['appname'] : false);
if(!$this->appname) die(lang('Error! No appname found'));
translation::add_app('infolog'); // til we move the translations
$this->tmpl = new etemplate_new();
// do we manage content-types?
if($this->tmpl->read($this->appname.'.admin.types')) $this->manage_content_types = true;
$this->use_private = !isset($_GET['use_private']) || (boolean)$_GET['use_private'] || $content['use_private'];
// Read fields, constructor doesn't always know appname
$this->fields = egw_customfields::get($this->appname,true);
$this->tmpl = new etemplate_new();
$this->tmpl->read('admin.customfields');
// do we manage content-types?
$test = new etemplate_new();
if($test->read($this->appname.'.admin.types')) $this->manage_content_types = true;
// Handle incoming
if($this->manage_content_types)
{
$this->content_types = config::get_content_types($this->appname);
@ -83,84 +93,58 @@ class customfields
{
// 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)egw_link::get_registry($this->appname,'default_types');
//error_log( array2string($this->content_types));
}
}
elseif (!($this->tmpl instanceof etemplate_widget))
{
$this->tmpl->children[0]['data'][2]['A']['disabled'] = true;
$this->tmpl->children[0]['data'][3]['A']['disabled'] = true;
}
elseif(method_exists($this->tmpl, 'disableElement'))
{
// et2
$this->tmpl->disableElement('content_types', true);
}
if (is_array($content))
{
//echo '<pre style="text-align: left;">'; print_r($content); echo "</pre>\n";
if($this->manage_content_types)
{
$this->content_type = $content['content_types']['types'];
}
if($content['content_types']['delete']) $this->delete_content_type($content);
elseif($content['content_types']['create']) $this->create_content_type($content);
elseif($content['fields']['delete']) $this->delete_field($content);
elseif($content['fields']['create']) $this->create_field($content);
else
elseif($content['content_types']['create'])
{
list($action) = @each($content['button']);
switch($action)
$content['content_types']['types'] = $this->create_content_type($content);
unset($content['content_types']['name']);
}
}
if($content['nm']['action'] == 'delete')
{
foreach($this->fields as $name => $data)
{
if(in_array($data['id'],$content['nm']['selected']))
{
default:
if (!$content['fields']['create'] && !$content['fields']['delete'])
{
break; // type change
}
case 'save':
case 'apply':
$this->update($content);
if ($action != 'save')
{
break;
}
//fall through
case 'cancel':
egw::redirect_link('/index.php', array('menuaction'=>'admin.admin_ui.index','ajax'=>'true'), 'admin');
unset($this->fields[$name]);
}
}
// save changes to repository
$this->save_repository();
}
else
$content['nm']= $GLOBALS['egw']->session->appsession('customfield-index','admin');
if (!is_array($content['nm']))
{
if($this->manage_content_types)
{
$content_types = array_keys($this->content_types);
$this->content_type = $content_types[0];
}
$content['use_private'] = !isset($_GET['use_private']) || (boolean)$_GET['use_private'];
// 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()
);
}
$GLOBALS['egw_info']['flags']['app_header'] = $GLOBALS['egw_info']['apps'][$this->appname]['title'].' - '.lang('Custom fields');
$sel_options = array();
$readonlys = array();
$content['nm']['appname'] = $this->appname;
$content['nm']['use_private'] = $this->use_private;
// Set up sub-types
if($this->manage_content_types)
{
$content['content_types']['app-name'] = $this->appname;
foreach($this->content_types as $type => $entry)
{
$this->types2[$type] = $entry['name'];
}
$content['content_types']['options-types'] = $this->types2;
$sel_options['types'] = $this->types2;
if($this->tmpl instanceof etemplate_widget)
{
}
else
{
$this->tmpl->children[0]['data'][3]['A']['name'] = $this->appname.'.admin.types';
$this->tmpl->children[0]['data'][3]['A']['size'] = 'content_type_options';
}
$sel_options['types'] = $sel_options['cf_type2'] = $this->types2;
$content['content_types']['appname'] = $this->appname;
$content_types = array_keys($this->content_types);
$this->content_type = $content['content_types']['types'] ? $content['content_types']['types'] : $content_types[0];
$content['content_type_options'] = $this->content_types[$this->content_type]['options'];
$content['content_type_options']['type'] = $this->content_types[$this->content_type]['name'];
if ($this->content_types[$this->content_type]['non_deletable'])
@ -176,54 +160,201 @@ class customfields
// Hide the whole line if you can't add or delete
$content['content_types']['no_edit_types'] = true;
}
}
//echo 'customfields=<pre style="text-align: left;">'; print_r($this->fields); echo "</pre>\n";
$content['fields'] = array('use_private' => $content['use_private']);
$n = 0;
foreach($this->fields as $name => $data)
{
if(!is_array($data))
// 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')
{
$data = array();
$data['label'] = $name;
$data['order'] = ($n+1) * 10;
$readonlys['content_types']['delete'] = true;
}
if (is_array($data['values']))
{
$values = '';
foreach($data['values'] as $var => $value)
{
$values .= (!empty($values) ? "\n" : '').$var.'='.$value;
}
$data['values'] = $values;
}
$content['fields'][++$n] = (array)$data + array(
'name' => $name
);
$preserv_fields[$n]['old_name'] = $name;
$readonlys['fields']["create$name"] = True;
$content['nm']['type2'] = true;
}
$content['fields'][++$n] = array('name'=>'','order' => 10 * $n); // new line for create
if($this->manage_content_types) $content['fields']['type2'] = 'enable';
$readonlys['fields']["delete[]"] = True;
//echo '<p>uicustomfields.edit(content = <pre style="text-align: left;">'; print_r($content); echo "</pre>\n";
//echo 'readonlys = <pre style="text-align: left;">'; print_r($readonlys); echo "</pre>\n";
$sel_options['type2'] = $this->types2 + array('tmpl' => 'template');
// 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')
else
{
$readonlys['content_types']['delete'] = true;
// Disable content types
$this->tmpl->disableElement('content_types', true);
}
$this->tmpl->exec('admin.customfields.edit',$content,$sel_options,$readonlys,array(
'fields' => $preserv_fields,
$GLOBALS['egw_info']['flags']['app_header'] = $GLOBALS['egw_info']['apps'][$this->appname]['title'].' - '.lang('Custom fields');
$this->tmpl->exec('admin.customfields.index',$content,$sel_options,$readonlys,array(
'appname' => $this->appname,
'use_private' => $content['use_private'],
'use_private' => $this->use_private,
));
}
/**
* 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 = $_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 = egw_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);
egw_framework::refresh_opener('Saved', 'admin', $cf_id /* Conflicts with accounts 'delete'*/);
egw_framework::window_close();
break;
case 'save':
case 'apply':
$content['cf_type2'] = implode(',',(array)$content['cf_type2']);
$content['cf_private'] = implode(',',(array)$content['cf_private']);
if (!empty($content['cf_values']))
{
$values = array();
foreach(explode("\n",$content['cf_values']) as $line)
{
list($var,$value) = explode('=',trim($line),2);
$var = trim($var);
$values[$var] = empty($value) ? $var : $value;
}
$content['cf_values'] = $values;
}
$content['cf_values'] = json_encode($content['cf_values']);
$this->so->save($content);
egw_framework::refresh_opener('Saved', 'admin', $cf_id, 'edit');
if ($action != 'save')
{
break;
}
//fall through
case 'cancel':
egw_framework::window_close();
}
}
else
{
$content['use_private'] = !isset($_GET['use_private']) || (boolean)$_GET['use_private'];
}
// do we manage content-types?
$test = new etemplate_new();
if($test->read($this->appname.'.admin.types')) $this->manage_content_types = true;
$this->tmpl = new etemplate_new();
$this->tmpl->read('admin.customfield_edit');
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']);
}
}
$content['cf_values'] = json_decode($content['cf_values'], 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)
{
$this->content_types = 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)egw_link::get_registry($this->appname,'default_types');
}
foreach($this->content_types as $type => $entry)
{
$this->types2[$type] = $entry['name'];
}
$sel_options['cf_type2'] = $this->types2 + array('tmpl' => 'template');
}
else
{
$content['no_types'] = true;
}
$this->tmpl->exec('admin.customfields.edit',$content,$sel_options,$readonlys,array(
'cf_id' => $cf_id,
'cf_app' => $this->appname,
'use_private' => $this->use_private,
));
}
/**
* 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()
*/
private 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' => '450x380',
'group' => $group=1,
'disableClass' => 'th',
),
'add' => array(
'caption' => 'Add',
'url' => 'menuaction=admin.customfields.edit&appname='.$this->appname.'&use_private='.$this->use_private,
'popup' => '450x380',
'group' => $group,
),
'delete' => array(
'caption' => 'Delete',
'confirm' => 'Delete this entry',
'confirm_multiple' => 'Delete these entries',
'group' => ++$group,
'disableClass' => 'rowNoDelete',
),
);
return $actions;
}
function update_fields(&$content)
{
foreach($content['fields'] as $field)
@ -344,14 +475,21 @@ class customfields
function create_content_type(&$content)
{
$new_name = trim($content['content_types']['name']);
if (empty($new_name) || isset($this->fields[$new_name]))
$new_type = false;
if (empty($new_name))
{
$content['error_msg'] .= empty($new_name) ?
lang('You have to enter a name, to create a new type!!!') :
lang("type '%1' already exists !!!",$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 $letter => $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++)
{
@ -367,6 +505,7 @@ class customfields
$this->content_types[$new_type] = array('name' => $new_name);
$this->save_repository();
}
return $new_type;
}
/**
@ -408,4 +547,30 @@ class customfields
return 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

@ -0,0 +1,79 @@
<?xml version="1.0"?>
<!-- $Id$ -->
<overlay>
<template id="admin.customfield_edit" template="" lang="" group="0" version="1.9.001">
<grid width="100%">
<columns>
<column/>
<column/>
<column/>
</columns>
<rows>
<row disabled="@no_types">
<description value="Type"/>
<menulist>
<menupopup id="cf_type2" no_lang="1" rows="3"/>
</menulist>
<description/>
</row>
<row>
<description statustext="the name used internaly (&amp;lt;= 20 chars), changeing it makes existing data unavailible" value="Name"/>
<textbox statustext="the name used internaly (&lt;= 20 chars), changeing it makes existing data unavailible" id="cf_name" size="20" maxlength="32" needed="true"/>
<description/>
</row>
<row>
<description value="Label"/>
<vbox>
<textbox statustext="the text displayed to the user" id="cf_label" maxlength="255"/>
<description id="cf_label"/>
</vbox>
<description/>
</row>
<row>
<description value="Type of field"/>
<vbox>
<customfields-types statustext="Type of customfield" id="cf_type"/>
</vbox>
<description/>
</row>
<row>
<description value="Required"/>
<checkbox id="cf_needed"/>
<description/>
</row>
<row disabled="!@use_private">
<description value="Private"/>
<taglist-account statustext="Select accounts for which the custom field should be visible" id="cf_private" rows="3" account_type="both" span="2"/>
<description/>
</row>
<row>
<description statustext="each value is a line like &lt;id&gt;[=&lt;label&gt;]" value="Options"/>
<textbox multiline="true" statustext="each value is a line like id[=label]" id="cf_values" rows="4" cols="30" span="2" width="99%"/>
</row>
<row>
<description value="Length"/>
<textbox statustext="max length of the input [, length of the inputfield (optional)]" id="cf_len" size="5"/>
<description/>
</row>
<row>
<description value="Rows"/>
<textbox type="integer" blur="1" statustext="number of row for a multiline inputfield or line of a multi-select-box" id="cf_rows" min="0" max="10" size="2"/>
<description/>
</row>
<row>
<description value="Order"/>
<textbox type="integer" statustext="determines the order the fields are displayed" id="cf_order" min="1" size="3"/>
<description/>
</row>
<row class="dialogFooterToolbar">
<hbox span="2">
<button statustext="Saves this entry" label="Save" id="button[save]" image="save" background_image="1"/>
<button statustext="Apply the changes" label="Apply" id="button[apply]" image="apply" background_image="1"/>
<button statustext="leave without saveing the entry" label="Cancel" id="button[cancel]" onclick="window.close();" image="cancel" background_image="1"/>
</hbox>
<button align="right" statustext="delete this entry" label="Delete" id="button[delete]" image="delete" background_image="1" span="all"/>
</row>
</rows>
</grid>
</template>
</overlay>

View File

@ -12,8 +12,8 @@
<column disabled="@no_add"/>
</columns>
<rows>
<row disabled="@no_edit_types">
<description id="app-name"/>
<row>
<description id="appname"/>
<description value="- type"/>
<menulist>
<menupopup id="types" no_lang="1" onchange="1"/>
@ -25,56 +25,61 @@
</rows>
</grid>
</template>
<template id="admin.customfields.header_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');"/>
</template>
<template id="admin.customfields.fields" template="" lang="" group="0" version="1.9.001">
<grid>
<grid readonly="true">
<columns>
<column disabled="!@type2"/>
<column/>
<column/>
<column/>
<column disabled="!@use_private"/>
<column/>
<column/>
<column/>
<column/>
<column disabled="!@type2" width="50px"/>
<column width="30%"/>
<column width="30%"/>
<column width="100px"/>
<column width="50px"/>
<column width="100px" disabled="!@use_private"/>
<column width="60%"/>
<column width="50px"/>
<column width="50px"/>
<column width="120px"/>
</columns>
<rows>
<row class="th">
<description value="Type"/>
<description statustext="the name used internaly (&lt;= 20 chars), changeing it makes existing data unavailible" value="Name"/>
<description value="Label"/>
<description value="Type of field"/>
<description value="Private"/>
<description statustext="each value is a line like &lt;id&gt;[=&lt;label&gt;]" value="Options"/>
<nextmatch-header label="Type" id="cf_type2"/>
<nextmatch-sortheader label="Name" id="cf_name"/>
<nextmatch-sortheader label="Label" id="cf_label"/>
<nextmatch-header label="Type of field" id="cf_type"/>
<nextmatch-header label="Required" id="cf_needed"/>
<nextmatch-header label="Private" id="cf_private"/>
<nextmatch-header label="Options"/>
<vbox>
<description value="Length"/>
<description value="Rows"/>
<nextmatch-header label="Length"/>
<nextmatch-header label="Rows"/>
</vbox>
<description value="Order"/>
<description align="center" statustext="deletes this field" value="Action"/>
<nextmatch-sortheader label="Order" id="cf_order"/>
<nextmatch-sortheader label="Last modified" id="cf_modified"/>
</row>
<row class="row" valign="top">
<listbox statustext="for which types should this field be used" id="${row}[type2]" no_lang="1" rows="3"/>
<textbox statustext="the name used internaly (&lt;= 20 chars), changeing it makes existing data unavailible" id="${row}[name]" size="20" maxlength="32"/>
<menulist>
<menupopup id="${row}[cf_type2]" no_lang="1" multiple="true" empty_label="All"/>
</menulist>
<description id="${row}[cf_name]" />
<vbox>
<textbox statustext="the text displayed to the user" id="${row}[label]" maxlength="255"/>
<description id="${row}[label]"/>
<description id="${row}[cf_label]" no_lang="1"/>
<description id="${row}[cf_label]"/>
</vbox>
<customfields-types statustext="Type of customfield" id="{$row}[cf_type]"/>
<checkbox id="${row}[cf_needed]" selected_value="1" unselected_value="0"/>
<select-account id="${row}[cf_private]" account_type="both"/>
<description id="${row}[cf_values]" no_lang="1"/>
<vbox>
<customfields-types statustext="Type of customfield" id="{$row}[type]"/>
<checkbox label="required" id="${row}[needed]"/>
<description id="${row}[cf_len]" no_lang="1"/>
<description id="${row}[cf_rows]" no_lang="1"/>
</vbox>
<taglist-account statustext="Select accounts for which the custom field should be visible" id="${row}[private]" rows="3" account_type="both"/>
<textbox multiline="true" statustext="each value is a line like id[=label]" id="${row}[values]" rows="4" cols="30"/>
<description id="${row}[cf_order]" no_lang="1"/>
<vbox>
<textbox statustext="max length of the input [, length of the inputfield (optional)]" id="${row}[len]" size="5"/>
<textbox type="integer" blur="1" statustext="number of row for a multiline inputfield or line of a multi-select-box" id="${row}[rows]" min="0" max="10" size="2"/>
<date-time id="${row}[cf_modified]"/>
<select-account id="${row}[cf_modifier]"/>
</vbox>
<textbox type="integer" statustext="determines the order the fields are displayed" id="${row}[order]" min="1" size="3"/>
<hbox>
<button statustext="deletes this field" label="Delete" id="delete[$row_cont[name]]"/>
<button statustext="creates a new field" label="Create" id="create$row_cont[name]"/>
</hbox>
</row>
</rows>
</grid>
@ -83,38 +88,13 @@
<grid>
<columns>
<column/>
<column/>
<column/>
<column/>
<column/>
<column/>
<column width="80%"/>
</columns>
<rows>
<row>
<description align="center" id="error_msg" no_lang="1" span="all" class="msg"/>
<description/>
<description/>
<description/>
<description/>
<description/>
<description no_lang="1"/>
</row>
<row>
<template id="admin.customfields.types" content="content_types" span="all"/>
</row>
<row>
<template span="all"/>
</row>
<row>
<template id="admin.customfields.fields" content="fields" span="all"/>
</row>
<row>
<hbox span="all">
<button statustext="saves the changes made and leaves" label="Save" id="button[save]"/>
<button statustext="applies the changes" label="Apply" id="button[apply]"/>
<button statustext="leaves without saveing" label="Cancel" id="button[cancel]"/>
</hbox>
<nextmatch id="nm" template="admin.customfields.fields" header_row="admin.customfields.header_add" span="all"/>
</row>
</rows>
</grid>

View File

@ -106,7 +106,7 @@ class filemanager_hooks
$file = Array(
'Site Configuration' => egw::link('/index.php','menuaction=admin.uiconfig.index&appname='.self::$appname),
'Custom fields' => egw::link('/index.php','menuaction=admin.customfields.edit&appname='.self::$appname),
'Custom fields' => egw::link('/index.php','menuaction=admin.customfields.index&appname='.self::$appname.'&ajax=true'),
'Check virtual filesystem' => egw::link('/index.php','menuaction=filemanager.filemanager_admin.fsck'),
'VFS mounts and versioning' => egw::link('/index.php', 'menuaction=filemanager.filemanager_admin.index'),
);

View File

@ -54,7 +54,7 @@ class resources_hooks
'Configure Access Permissions' => egw::link('/index.php',
'menuaction=resources.ui_acl.acllist'),
'Custom Fields'=>egw::link('/index.php',
'menuaction=admin.customfields.edit&appname=resources'),
'menuaction=admin.customfields.index&appname=resources'),
);
if ($location == 'admin')
{

View File

@ -116,7 +116,7 @@ class timesheet_hooks
{
$file = Array(
'Site Configuration' => egw::link('/index.php','menuaction=admin.uiconfig.index&appname=' . $appname),
'Custom fields' => egw::link('/index.php','menuaction=admin.customfields.edit&appname='.$appname.'&use_private=1'),
'Custom fields' => egw::link('/index.php','menuaction=admin.customfields.index&appname='.$appname.'&use_private=1'),
'Global Categories' => egw::link('/index.php',array(
'menuaction' => 'admin.admin_categories.index',
'appname' => $appname,