2002-09-17 23:31:45 +02:00
< ? php
2008-06-22 09:16:40 +02:00
/**
* eGroupWare eTemplate Extension - Nextmatch Widget
*
* @ license http :// opensource . org / licenses / gpl - license . php GPL - GNU General Public License
2011-05-07 10:47:58 +02:00
* @ copyright 2002 - 11 by RalfBecker @ outdoor - training . de
2008-06-22 09:16:40 +02:00
* @ package etemplate
* @ subpackage extensions
* @ link http :// www . egroupware . org
* @ author Ralf Becker < RalfBecker @ outdoor - training . de >
* @ version $Id $
*/
/**
* eTemplate Extension : Widget that show only a certain number of data - rows and allows to modifiy the rows shown ( scroll ) .
*
* This widget replaces the old nextmatch - class . It is independent of the UI ,
* as it only uses etemplate - widgets and has therefor no render - function
*
* $content [ $id ] = array ( // I = value set by the app, 0 = value on return / output
* 'get_rows' => // I method/callback to request the data for the rows eg. 'notes.bo.get_rows'
* 'filter_label' => // I label for filter (optional)
* 'filter_help' => // I help-msg for filter (optional)
* 'no_filter' => True // I disable the 1. filter
* 'no_filter2' => True // I disable the 2. filter (params are the same as for filter)
* 'no_cat' => True // I disable the cat-selectbox
* 'cat_app' => // I application the cat's should be from, default app in get_rows
2011-05-06 20:10:50 +02:00
* 'cat_is_select' => // I true||'no_lang' use selectbox instead of category selection, default null
2008-06-22 09:16:40 +02:00
* 'template' => // I template to use for the rows, if not set via options
* 'header_left' => // I template to show left of the range-value, left-aligned (optional)
* 'header_right' => // I template to show right of the range-value, right-aligned (optional)
* 'bottom_too' => True // I show the nextmatch-line (arrows, filters, search, ...) again after the rows
* 'never_hide' => True // I never hide the nextmatch-line if less then maxmatch entries
* 'lettersearch' => True // I show a lettersearch
* 'searchletter' => // I0 active letter of the lettersearch or false for [all]
* 'start' => // IO position in list
* 'num_rows' => // IO number of rows to show, defaults to maxmatches from the general prefs
* 'cat_id' => // IO category, if not 'no_cat' => True
* 'search' => // IO search pattern
* 'order' => // IO name of the column to sort after (optional for the sortheaders)
* 'sort' => // IO direction of the sort: 'ASC' or 'DESC'
* 'col_filter' => // IO array of column-name value pairs (optional for the filterheaders)
* 'filter' => // IO filter, if not 'no_filter' => True
* 'filter_no_lang' => True // I set no_lang for filter (=dont translate the options)
* 'filter_onchange' => 'this.form.submit();' // I onChange action for filter, default: this.form.submit();
* 'filter2' => // IO filter2, if not 'no_filter2' => True
* 'filter2_no_lang' => True // I set no_lang for filter2 (=dont translate the options)
* 'filter2_onchange' => 'this.form.submit();' // I onChange action for filter2, default: this.form.submit();
* 'rows' => // O content set by callback
* 'total' => // O the total number of entries
* 'sel_options' => // O additional or changed sel_options set by the callback and merged into $tmpl->sel_options
* 'no_columnselection' => // I turns off the columnselection completly, turned on by default
* 'columnselection-pref' => // I name of the preference (plus 'nextmatch-' prefix), default = template-name
* 'default_cols' => // I columns to use if there's no user or default pref (! as first char uses all but the named columns), default all columns
* 'options-selectcols' => // I array with name/label pairs for the column-selection, this gets autodetected by default. A name => false suppresses a column completly.
* 'return' => // IO allows to return something from the get_rows function if $query is a var-param!
2011-02-28 18:32:32 +01:00
* 'csv_fields' => // I false=disable csv export, true or unset=enable it with auto-detected fieldnames or preferred importexport definition,
* array with name => label or name => array ( 'label' => label , 'type' => type ) pairs ( type is a eT widget - type )
* or name of import / export definition
2011-04-27 09:49:46 +02:00
* 'row_id' => // I key into row content to set it's value as tr id, eg. 'id'
* 'actions' => // I array with actions, see nextmatch_widget::egw_actions
* 'action_links' => // I array with enabled actions or ones which should be checked if they are enabled
* optional , default id of all first level actions plus the ones with enabled = 'javaScript:...'
2011-05-06 20:10:50 +02:00
* 'action_var' => 'action' // I name of var to return choosen action, default 'action'
2011-05-02 15:34:59 +02:00
* 'action' => // O string selected action
2011-04-27 09:49:46 +02:00
* 'selected' => // O array with selected id's
2011-05-02 15:34:59 +02:00
* 'checkboxes' => // O array with checkbox id as key and boolean checked value
* 'select_all' => // O boolean value of select_all checkbox, reference to above value for key 'select_all'
2008-06-22 09:16:40 +02:00
* );
*/
class nextmatch_widget
{
2006-04-20 19:12:30 +02:00
/**
2008-06-22 09:16:40 +02:00
* Prefix for custom field names
2006-04-20 19:12:30 +02:00
*
*/
2008-06-22 09:16:40 +02:00
const CF_PREFIX = '#' ;
2002-09-17 23:31:45 +02:00
2008-06-22 09:16:40 +02:00
/**
* exported methods of this class
* @ var array
*/
2009-10-11 14:37:48 +02:00
public $public_functions = array (
2008-06-22 09:16:40 +02:00
'pre_process' => True ,
'post_process' => True ,
);
/**
* availible extensions and there names for the editor
* @ var array
*/
2009-10-11 14:37:48 +02:00
public $human_name = array (
2008-06-22 09:16:40 +02:00
'nextmatch' => 'Nextmatch' ,
'nextmatch-sortheader' => 'Nextmatch Sortheader' ,
'nextmatch-filterheader' => 'Nextmatch Filterheader' ,
'nextmatch-accountfilter' => 'Nextmatch Accountfilter' ,
'nextmatch-customfilter' => 'Nextmatch Custom Filterheader' ,
'nextmatch-header' => 'Nextmatch Header' ,
'nextmatch-customfields' => 'Nextmatch Custom Fields Header' ,
);
2008-06-11 06:05:10 +02:00
2005-02-18 23:44:42 +01:00
/**
2008-06-22 09:16:40 +02:00
* Turn on debug messages ( mostly in post_process )
2005-02-18 23:44:42 +01:00
*
2008-06-22 09:16:40 +02:00
* @ var boolean
*/
2009-10-11 14:37:48 +02:00
public $debug = false ;
2008-06-22 09:16:40 +02:00
/**
* Vars used to comunicated for the custom field header
2005-02-18 23:44:42 +01:00
*
2008-06-22 09:16:40 +02:00
* @ var unknown_type
2005-02-18 23:44:42 +01:00
*/
2008-06-22 09:16:40 +02:00
private $selectcols ;
public $cf_header ;
private $cfs ;
/**
* Constructor of the extension
*
* @ param string $ui '' for html
*/
2009-10-11 14:37:48 +02:00
public function __construct ( $ui )
2002-09-17 23:31:45 +02:00
{
2008-06-22 09:16:40 +02:00
}
2008-01-19 06:36:20 +01:00
2008-06-22 09:16:40 +02:00
/**
* returns last part of a form - name
*
* @ param string $name
* @ return string
*/
static private function last_part ( $name )
{
list ( $last ) = self :: get_parts ( $name , - 1 , 1 );
return $last ;
}
2002-09-17 23:31:45 +02:00
2008-06-22 09:16:40 +02:00
/**
* returns last part of a form - name
*
* @ param string $name
* @ param int $offset positive or negative offset ( negative is count from the end )
* @ param int $length = null positiv means return $length elements , negative return til negative offset in $length , default = null means all
* @ return array
*/
static private function get_parts ( $name , $offset , $length = null )
{
$parts = explode ( '[' , str_replace ( ']' , '' , $name ));
2008-08-16 09:40:04 +02:00
// php5.1 seems to have a bug: array_slice($parts,$offeset) != array_slice($parts,$offeset,null)
return is_null ( $length ) ? array_slice ( $parts , $offset ) : array_slice ( $parts , $offset , $length );
2008-06-22 09:16:40 +02:00
}
2008-06-11 06:05:10 +02:00
2008-06-22 09:16:40 +02:00
/**
* Return global storages shared within the parts of a nextmatch widget ( including all nextmatch -* subwidgets )
*
* @ param string $name
* @ param string $type
* @ return mixed reference to storage : use =& to assign !
*/
static private function & get_nm_global ( $name , $type )
{
static $nm_globals = array ();
2003-12-09 01:03:41 +01:00
2008-06-22 09:16:40 +02:00
// extract the original nextmatch name from $name, taken into account the type of nextmatch-* subwidgets
$nm_global = implode ( '/' , self :: get_parts ( $name , 1 , $type == 'nextmatch' ? null : - 2 ));
//echo '<p>'.__METHOD__."($name,$type) = $nm_global</p>\n";
2008-06-11 06:20:39 +02:00
2008-06-22 09:16:40 +02:00
return $nm_globals [ $nm_global ];
}
2008-06-11 06:20:39 +02:00
2008-06-22 09:16:40 +02:00
/**
* pre - processing of the extension
*
* This function is called before the extension gets rendered
*
* @ param string $name form - name of the control
* @ param mixed & $value value / existing content , can be modified
* @ param array & $cell array with the widget , can be modified for ui - independent widgets
* @ param array & $readonlys names of widgets as key , to be made readonly
* @ param mixed & $extension_data data the extension can store persisten between pre - and post - process
* @ param etemplate & $tmpl reference to the template we belong too
* @ return boolean true if extra label is allowed , false otherwise
*/
2009-10-11 14:37:48 +02:00
public function pre_process ( $name , & $value , array & $cell , & $readonlys , & $extension_data , etemplate & $tmpl )
2008-06-22 09:16:40 +02:00
{
$nm_global =& self :: get_nm_global ( $name , $cell [ 'type' ]);
//echo "<p>nextmatch_widget.pre_process(name='$name',type='$cell[type]'): value = "; _debug_array($value);
//echo "<p>nextmatch_widget.pre_process(name='$name',type='$cell[type]'): nm_global = "; _debug_array($nm_global);
2008-06-11 06:20:39 +02:00
2008-06-22 09:16:40 +02:00
$extension_data = array (
'type' => $cell [ 'type' ]
);
switch ( $cell [ 'type' ])
2002-09-17 23:31:45 +02:00
{
2008-06-22 09:16:40 +02:00
case 'nextmatch-header' :
$cell [ 'type' ] = 'label' ;
return true ; // false = no extra label
case 'nextmatch-sortheader' : // Option: default sort: ASC(default) or DESC
$extension_data [ 'default_sort' ] = $cell [ 'size' ] && preg_match ( '/^(ASC|DESC)/i' , $cell [ 'size' ], $matches ) ? strtoupper ( $matches [ 1 ]) : 'ASC' ;
2008-10-05 18:56:37 +02:00
$cell [ 'type' ] = $cell [ 'readonly' ] ? 'label' : 'button' ;
2008-06-22 09:16:40 +02:00
$cell [ 'onchange' ] = True ;
if ( ! $cell [ 'help' ])
2006-03-27 17:08:47 +02:00
{
2008-06-22 09:16:40 +02:00
$cell [ 'help' ] = 'click to order after that criteria' ;
2006-03-27 17:08:47 +02:00
}
2008-10-05 18:56:37 +02:00
if ( self :: last_part ( $name ) == $nm_global [ 'order' ] && ! $cell [ 'readonly' ]) // we're the active column
2005-03-14 00:38:37 +01:00
{
2008-06-22 09:16:40 +02:00
$sorting = $cell ;
unset ( $sorting [ 'align' ]);
unset ( $sorting [ 'span' ]);
$cell = etemplate :: empty_cell ( 'hbox' , '' , array (
'span' => $cell [ 'span' ],
'size' => '2,,0,0' ,
1 => $sorting ,
2 => etemplate :: empty_cell ( 'image' , $nm_global [ 'sort' ] != 'DESC' ? 'down' : 'up' ),
));
$class = 'activ_sortcolumn' ;
2005-03-14 00:38:37 +01:00
}
else
{
2008-06-22 09:16:40 +02:00
$class = 'inactiv_sortcolumn' ;
}
$parts = explode ( ',' , $cell [ 'span' ]);
$parts [ 1 ] .= ( $parts [ 1 ] ? ' ' : '' ) . $class ;
$cell [ 'span' ] = implode ( ',' , $parts );
return True ;
case 'nextmatch-accountfilter' : // Option: as for selectbox: [extra-label(default ALL)[,#lines(default 1)]]
$cell [ 'size' ] = 'select-account,' . $cell [ 'size' ];
// fall through
case 'nextmatch-customfilter' : // Option: widget-name, options as for selectbox
list ( $type , $cell [ 'size' ]) = explode ( ',' , $cell [ 'size' ], 2 );
// fall through
case 'nextmatch-filterheader' : // Option: as for selectbox: [extra-label(default ALL)[,#lines(default 1)]]
if ( ! $type ) $type = 'select' ;
$cell [ 'type' ] = $type ;
2009-09-25 09:59:34 +02:00
if ( ! $cell [ 'size' ] && $type != 'link-entry' ) // link-entry without option shows application selection!
2008-06-22 09:16:40 +02:00
{
$cell [ 'size' ] = 'All' ;
}
if ( ! $cell [ 'help' ])
{
$cell [ 'help' ] = 'select which values to show' ;
2005-03-14 00:38:37 +01:00
}
2008-06-22 09:16:40 +02:00
$cell [ 'onchange' ] = $cell [ 'noprint' ] = True ;
$parts = explode ( ',' , $cell [ 'span' ]);
$parts [ 1 ] .= ( $parts [ 1 ] ? ' ' : '' ) . 'filterheader' ;
$cell [ 'span' ] = implode ( ',' , $parts );
$extension_data [ 'old_value' ] = $value = $nm_global [ 'col_filter' ][ self :: last_part ( $name )];
return True ;
case 'nextmatch-customfields' :
2009-10-11 14:37:48 +02:00
$extra_label = $this -> pre_process_cf_header ( $cell , $tmpl , $value , $nm_global );
2009-09-23 17:27:33 +02:00
$extension_data [ 'old_value' ] = $value ;
return $extra_label ;
2008-06-22 09:16:40 +02:00
}
2011-03-22 18:06:02 +01:00
2009-10-11 14:37:48 +02:00
// does NOT work with php5.2.6+
2008-06-23 08:46:58 +02:00
if ( version_compare ( PHP_VERSION , '5.2.6' , '>=' ))
{
$value [ 'bottom_too' ] = false ;
}
2008-06-22 09:16:40 +02:00
// presetting the selectboxes with their default values, to NOT loop, because post-process thinks they changed
if ( ! isset ( $value [ 'cat_id' ])) $value [ 'cat_id' ] = '' ;
if ( ! isset ( $value [ 'search' ])) $value [ 'search' ] = '' ;
foreach ( array ( 'filter' , 'filter2' ) as $f )
{
if ( ! isset ( $value [ $f ]))
{
list ( $value [ $f ]) = isset ( $tmpl -> sel_options [ $f ]) ? @ each ( $tmpl -> sel_options [ $f ]) : @ each ( $value [ 'options-' . $f ]);
if ( ! is_string ( $value [ $f ])) $value [ $f ] = ( string ) $value [ $f ];
2005-03-05 10:25:17 +01:00
}
2008-06-22 09:16:40 +02:00
}
// save values in persistent extension_data to be able use it in post_process
2008-11-13 17:55:03 +01:00
unset ( $value [ 'rows' ]);
2008-06-22 09:16:40 +02:00
$extension_data += $value ;
$value [ 'no_csv_export' ] = $value [ 'csv_fields' ] === false ||
2008-10-16 13:17:49 +02:00
$GLOBALS [ 'egw_info' ][ 'server' ][ 'export_limit' ] && ! is_numeric ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'export_limit' ]) &&
! isset ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'apps' ][ 'admin' ]);
2008-06-22 09:16:40 +02:00
if ( ! $value [ 'filter_onchange' ]) $value [ 'filter_onchange' ] = 'this.form.submit();' ;
if ( ! $value [ 'filter2_onchange' ]) $value [ 'filter2_onchange' ] = 'this.form.submit();' ;
2010-04-12 09:23:21 +02:00
if ( ! isset ( $value [ 'cat_app' ])) list ( $value [ 'cat_app' ]) = explode ( '.' , $value [ 'get_rows' ]); // if no cat_app set, use the app from the get_rows func
2006-03-27 00:17:55 +02:00
2008-06-22 09:16:40 +02:00
if ( ! ( $max = ( int ) $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'common' ][ 'maxmatchs' ])) $max = 15 ;
$row_options = array ();
foreach ( array ( 5 , 12 , 25 , 50 , 100 , 200 , 500 , 999 ) as $n )
{
if ( $n - 5 <= $max && $max <= $n + 5 ) $n = $max ;
$row_options [ $n ] = $n ;
}
2009-01-23 20:33:19 +01:00
if ( ! isset ( $row_options [ $max ]) || ! isset ( $row_options [ $value [ 'num_rows' ]]))
2008-06-22 09:16:40 +02:00
{
$row_options [ $max ] = $max ;
2009-01-23 20:33:19 +01:00
$row_options [ $value [ 'num_rows' ]] = $value [ 'num_rows' ];
2008-06-22 09:16:40 +02:00
ksort ( $row_options );
}
$value [ 'options-num_rows' ] =& $row_options ;
if ( ! isset ( $value [ 'num_rows' ])) $extension_data [ 'num_rows' ] = $value [ 'num_rows' ] = $max ;
if ( $value [ 'num_rows' ] != $max )
{
$GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'common' ][ 'maxmatchs' ] = $max = ( int ) $value [ 'num_rows' ];
}
if ( ! $value [ 'no_columnselection' ])
{
// presetting the options for selectcols empty, so the get_rows function can set it
2009-09-30 17:04:49 +02:00
$value [ 'options-selectcols' ] = is_array ( $value [ 'options-selectcols' ]) ? $value [ 'options-selectcols' ] : array ();
2008-06-22 09:16:40 +02:00
}
2008-11-13 17:55:03 +01:00
$rows = array ();
2009-10-14 21:06:54 +02:00
if ( ! is_array ( $readonlys )) $readonlys = array ();
2009-10-11 14:37:48 +02:00
if (( $total = $extension_data [ 'total' ] = $value [ 'total' ] = self :: call_get_rows ( $value , $rows , $readonlys [ 'rows' ])) === false )
{
//error_log(__METHOD__."() etemplate::set_validation_error('$name') '$value[get_rows]' is no valid method!!!");
etemplate :: set_validation_error ( $name , __METHOD__ . " ( $cell[name] ): ' $value[get_rows] ' is no valid method !!! " );
}
2008-11-13 17:55:03 +01:00
// allow the get_rows function to override / set sel_options
2008-11-16 08:20:23 +01:00
if ( isset ( $rows [ 'sel_options' ]) && is_array ( $rows [ 'sel_options' ]))
2008-06-22 09:16:40 +02:00
{
2008-11-13 17:55:03 +01:00
$tmpl -> sel_options = array_merge ( $tmpl -> sel_options , $rows [ 'sel_options' ]);
unset ( $rows [ 'sel_options' ]);
2008-06-22 09:16:40 +02:00
}
2008-11-13 17:55:03 +01:00
$value [ 'rows' ] =& $rows ;
unset ( $rows );
2008-06-22 09:16:40 +02:00
list ( $template , $options ) = explode ( ',' , $cell [ 'size' ]);
if ( ! $value [ 'template' ] && $template ) // template name can be supplied either in $value['template'] or the options-field
{
$value [ 'template' ] = $template ;
}
if ( ! is_object ( $value [ 'template' ]))
{
2009-06-08 18:21:14 +02:00
$value [ 'template' ] = new etemplate ( $value [ 'template' ], $tmpl -> as_array ());
2008-06-22 09:16:40 +02:00
}
2009-02-24 08:42:12 +01:00
if ( is_array ( $value [ 'rows' ][ 0 ])) // pad 0 based arrays with rows-1 false values
{
for ( $i = 1 ; $i < $value [ 'template' ] -> rows ; $i ++ )
{
array_unshift ( $value [ 'rows' ], false );
}
}
2008-06-22 09:16:40 +02:00
$extension_data [ 'template' ] = $value [ 'template' ] -> name ; // used for the column-selection, and might be set in get_rows()
$extension_data [ 'columnselection_pref' ] = $value [ 'columnselection_pref' ];
if ( $total < 1 && $value [ 'template' ] -> rows > 1 )
{
$value [ 'template' ] -> data [ 0 ][ 'h' . $value [ 'template' ] -> rows ] .= ',1' ; // disable the last data row
}
if ( ! $value [ 'never_hide' ] && $total <= $max && $options && $value [ 'search' ] == '' &&
( $value [ 'no_cat' ] || ! $value [ 'cat_id' ]) &&
( $value [ 'no_filter' ] || ! $value [ 'filter' ] || $value [ 'filter' ] == 'none' ) &&
( $value [ 'no_filter2' ] || ! $value [ 'filter2' ] || $value [ 'filter2' ] == 'none' ))
{ // disable whole nextmatch line if no scrolling necessary
if ( $value [ 'header_left' ] || $value [ 'header_right' ])
2002-10-01 20:26:30 +02:00
{
2009-06-08 18:21:14 +02:00
$nextmatch = new etemplate ( 'etemplate.nextmatch_widget.header_only' );
2008-06-22 09:16:40 +02:00
$cell [ 'size' ] = $cell [ 'name' ];
$cell [ 'obj' ] = & $nextmatch ;
$cell [ 'name' ] = $nextmatch -> name ;
2002-10-16 16:11:36 +02:00
}
else
{
2008-06-22 09:16:40 +02:00
$cell [ 'size' ] = $cell [ 'name' ] . '[rows]' ;
$cell [ 'obj' ] = & $value [ 'template' ];
$cell [ 'name' ] = $value [ 'template' ] -> name ;
}
}
else
{
2009-06-08 18:21:14 +02:00
$nextmatch = new etemplate ( 'etemplate.nextmatch_widget' );
2008-08-21 20:09:20 +02:00
$nextmatch -> read ( 'etemplate.nextmatch_widget' );
2008-06-22 09:16:40 +02:00
// keep the editor away from the generated tmpls
$nextmatch -> no_onclick = true ;
2007-02-14 08:11:42 +01:00
2011-05-06 20:10:50 +02:00
// category is a selectbox, not a category select widget
if ( $value [ 'cat_is_select' ] && ( $cat_widget =& $nextmatch -> get_widget_by_name ( 'cat_id' )))
{
$cat_widget [ 'type' ] = 'select' ;
if ( $value [ 'cat_is_select' ] == 'no_lang' ) $cat_widget [ 'no_lang' ] = true ;
}
2008-06-22 09:16:40 +02:00
if ( $value [ 'lettersearch' ])
{
$lettersearch =& $nextmatch -> get_widget_by_name ( 'lettersearch' ); // hbox for the letters
if (( $alphabet = lang ( 'alphabet' )) == 'alphabet*' ) $alphabet = 'a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z' ;
$alphabet = explode ( ',' , $alphabet );
$alphabet [ 'all' ] = lang ( 'all' );
foreach ( $alphabet as $key => $letter )
2005-03-14 00:38:37 +01:00
{
2008-06-22 09:16:40 +02:00
// make each letter internally behave like a button
$form_name = $name . '[searchletter][' . ( $key === 'all' ? $key : $letter ) . ']' ;
2009-03-16 13:58:24 +01:00
etemplate :: $request -> set_to_process ( $form_name , 'button' );
2008-06-22 09:16:40 +02:00
if ( ! $key ) $letterbox =& $lettersearch [ 1 ]; // to re-use the first child
$letterbox = etemplate :: empty_cell ( 'label' , $letter , array (
'label' => $letter ,
'span' => ',lettersearch' . ( $letter == ( string ) $value [ 'searchletter' ] ||
$key === 'all' && ! $value [ 'searchletter' ] ? '_active' : '' ),
'no_lang' => 2 ,
'align' => $key == 'all' ? 'right' : '' ,
2009-03-16 13:58:24 +01:00
'onclick' => 'return submitit(' . etemplate :: $name_form . " ,' $form_name '); " ,
2008-06-22 09:16:40 +02:00
));
// if not the first (re-used) child, add it to the parent
if ( $key ) etemplate :: add_child ( $lettersearch , $letterbox );
unset ( $letterbox );
2005-03-14 00:38:37 +01:00
}
2009-03-16 13:58:24 +01:00
//_debug_array(etemplate::$request->to_process);
2002-10-01 20:26:30 +02:00
}
2008-06-22 09:16:40 +02:00
if ( isset ( $value [ 'no_search' ])) $value [ 'no_start_search' ] = $value [ 'no_search' ];
foreach ( array ( 'no_cat' => 'cat_id' , 'no_filter' => 'filter' , 'no_filter2' => 'filter2' , 'no_search' => 'search' , 'no_start_search' => 'start_search' ) as $val_name => $cell_name )
2002-09-17 23:31:45 +02:00
{
2008-06-22 09:16:40 +02:00
if ( isset ( $value [ $val_name ])) $nextmatch -> disable_cells ( $cell_name , $value [ $val_name ]);
2002-09-17 23:31:45 +02:00
}
2008-06-22 09:16:40 +02:00
foreach ( array ( 'header_left' , 'header_right' ) as $name )
2005-02-27 22:32:06 +01:00
{
2008-06-22 09:16:40 +02:00
if ( ! $value [ $name ]) $nextmatch -> disable_cells ( '@' . $name );
2005-02-27 22:32:06 +01:00
}
2008-06-22 09:16:40 +02:00
foreach ( array ( 'filter' , 'filter2' ) as $cell_name )
2002-09-17 23:31:45 +02:00
{
2008-06-22 09:16:40 +02:00
if ( isset ( $value [ $cell_name . '_no_lang' ])) $nextmatch -> set_cell_attribute ( $cell_name , 'no_lang' , $value [ $cell_name . '_no_lang' ]);
2002-09-17 23:31:45 +02:00
}
2008-06-22 09:16:40 +02:00
$start = $value [ 'start' ];
$end = $start + $max > $total ? $total : $start + $max ;
$value [ 'range' ] = $total ? ( 1 + $start ) . ' - ' . $end : '0' ;
$nextmatch -> set_cell_attribute ( 'first' , 'readonly' , $start <= 0 );
$nextmatch -> set_cell_attribute ( 'left' , 'readonly' , $start <= 0 );
$nextmatch -> set_cell_attribute ( 'right' , 'readonly' , $start + $max >= $total );
$nextmatch -> set_cell_attribute ( 'last' , 'readonly' , $start + $max >= $total );
$cell [ 'size' ] = $cell [ 'name' ];
$cell [ 'obj' ] = & $nextmatch ;
$cell [ 'name' ] = $nextmatch -> name ;
}
// preset everything for the column selection
if ( ! $value [ 'no_columnselection' ])
{
$name = is_object ( $value [ 'template' ]) ? $value [ 'template' ] -> name : $value [ 'template' ];
list ( $app ) = explode ( '.' , $name );
if ( isset ( $value [ 'columnselection_pref' ])) $name = $value [ 'columnselection_pref' ];
$this -> selectcols = $value [ 'selectcols' ] = $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ $app ][ 'nextmatch-' . $name ];
// fetching column-names & -labels from the template
2009-11-14 09:45:38 +01:00
if ( $this -> cols_from_tpl ( $value [ 'template' ], $value [ 'options-selectcols' ], $name2col , $value [ 'rows' ], $value [ 'selectcols' ]))
2007-03-07 13:30:17 +01:00
{
2008-06-22 09:16:40 +02:00
//_debug_array($name2col);
//_debug_array($value['options-selectcols']);
// getting the selected colums from the prefs (or if not set a given default or all)
if ( ! $value [ 'selectcols' ])
2007-03-07 13:30:17 +01:00
{
2008-06-22 09:16:40 +02:00
$value [ 'selectcols' ] = array_keys ( $value [ 'options-selectcols' ]);
if ( isset ( $value [ 'default_cols' ]))
2007-03-07 13:30:17 +01:00
{
2009-09-28 14:52:50 +02:00
if ( $value [ 'default_cols' ][ 0 ] == '!' )
2007-03-07 13:30:17 +01:00
{
2008-06-22 09:16:40 +02:00
$value [ 'selectcols' ] = array_diff ( $value [ 'selectcols' ], explode ( ',' , substr ( $value [ 'default_cols' ], 1 )));
2007-03-07 13:30:17 +01:00
}
2008-06-22 09:16:40 +02:00
else
2008-01-19 06:36:20 +01:00
{
2008-06-22 09:16:40 +02:00
$value [ 'selectcols' ] = $value [ 'default_cols' ];
2008-01-19 06:36:20 +01:00
}
2007-03-07 13:30:17 +01:00
}
2008-06-22 09:16:40 +02:00
$this -> selectcols = $value [ 'selectcols' ];
}
if ( ! is_array ( $value [ 'selectcols' ])) $value [ 'selectcols' ] = explode ( ',' , $value [ 'selectcols' ]);
2009-11-14 09:45:38 +01:00
foreach ( array_unique ( array_keys ( $value [ 'options-selectcols' ] + $name2col )) as $name )
2008-06-22 09:16:40 +02:00
{
// set 'no_'.$col for each column-name to true, if the column is not selected
// (and the value is not set be the get_rows function / programmer!)
if ( ! isset ( $value [ 'rows' ][ 'no_' . $name ])) $value [ 'rows' ][ 'no_' . $name ] = ! in_array ( $name , $value [ 'selectcols' ]);
// setting '@no_'.$name as disabled attr for each column, that has only a single nextmatch-header
if ( is_object ( $value [ 'template' ]))
2007-03-07 13:30:17 +01:00
{
2008-06-22 09:16:40 +02:00
$col = $name2col [ $name ];
list (, $disabled ) = $value [ 'template' ] -> set_column_attributes ( $col );
//echo "<p>$col: $name: $disabled</p>\n";
if ( ! isset ( $disabled )) $value [ 'template' ] -> set_column_attributes ( $col , 0 , '@no_' . $name );
2007-03-07 13:30:17 +01:00
}
}
2008-06-22 09:16:40 +02:00
//_debug_array($value);
if ( is_object ( $nextmatch ))
{
$size =& $nextmatch -> get_cell_attribute ( 'selectcols' , 'size' );
if ( $size > count ( $value [ 'options-selectcols' ])) $size = '0' . count ( $value [ 'options-selectcols' ]);
if ( ! $GLOBALS [ 'egw_info' ][ 'user' ][ 'apps' ][ 'admin' ])
{
$nextmatch -> disable_cells ( 'default_prefs' );
}
}
// should reset on each submit
unset ( $value [ 'default_prefs' ]);
2007-03-07 13:30:17 +01:00
}
2002-09-17 23:31:45 +02:00
}
2011-02-28 18:32:32 +01:00
// Check for preferred import/export definition
2011-03-24 15:45:12 +01:00
$name = is_object ( $extension_data [ 'template' ]) ? $extension_data [ 'template' ] -> name : $extension_data [ 'template' ];
list ( $app ) = explode ( '.' , $name );
$definition = $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ $app ][ 'nextmatch-export-definition' ];
2011-04-27 18:54:35 +02:00
if ( ! $value [ 'no_csv_export' ] && ( $definition || ! is_array ( $value [ 'csv_fields' ])))
{
if ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'apps' ][ 'importexport' ] && ( $definition || $value [ 'csv_fields' ]) && is_object ( $nextmatch ))
{
2011-03-27 18:14:08 +02:00
$nextmatch -> set_cell_attribute ( 'export' , 'onclick' ,
2011-02-28 18:32:32 +01:00
" egw_openWindowCentered2(' " . egw :: link ( '/index.php' , array (
'menuaction' => 'importexport.importexport_export_ui.export_dialog' ,
'appname' => $app ,
2011-03-29 16:51:26 +02:00
'definition' => $definition ? $definition : $value [ 'csv_fields' ]
2011-03-27 18:14:08 +02:00
)) .
2011-02-28 18:32:32 +01:00
" ', '_blank', 850, 440, 'yes'); return false; "
);
}
}
if ( ! $value [ 'filter_onchange' ]) $value [ 'filter_onchange' ] = 'this.form.submit();' ;
2008-06-22 09:16:40 +02:00
$cell [ 'type' ] = 'template' ;
$cell [ 'label' ] = $cell [ 'help' ] = '' ;
2008-06-11 06:05:10 +02:00
2008-06-22 09:16:40 +02:00
foreach ( array ( 'sort' , 'order' , 'col_filter' ) as $n ) // save them for the sortheader
2008-01-19 06:36:20 +01:00
{
2008-06-22 09:16:40 +02:00
$nm_global [ $n ] = $value [ $n ];
}
$value [ 'bottom' ] = $value ; // copy the values for the bottom-bar
2008-06-11 06:05:10 +02:00
2011-04-16 23:45:13 +02:00
// pass actions and row_id to etemplate::show_grid()
$value [ 'rows' ][ '_actions' ] =& $value [ 'actions' ];
2011-04-26 21:50:03 +02:00
$value [ 'rows' ][ '_action_links' ] =& $value [ 'action_links' ];
2011-04-16 23:45:13 +02:00
$value [ 'rows' ][ '_row_id' ] =& $value [ 'row_id' ];
2011-05-05 09:30:30 +02:00
// values are NOT yet used on client side, but give warnings if array are not converted to strings
2011-05-06 20:10:50 +02:00
$extension_data [ 'action_var' ] = ! empty ( $value [ 'action_var' ]) ? $value [ 'action_var' ] : 'action' ;
$value [ $extension_data [ 'action_var' ]] = $value [ 'checkboxes' ] = $value [ 'selected' ] = '' ;
2011-05-05 09:30:30 +02:00
/*
$selected = $checkboxes = array ();
foreach (( array ) $values [ 'selected' ] as $id )
{
$selected [] = strpos ( $id , ',' ) === false ? $id : '"' . str_replace ( '"' , '""' , $id ) . '"' ;
}
$value [ 'selected' ] = implode ( ',' , $selected );
foreach (( array ) $value [ 'checkboxes' ] as $name => $checked )
{
$checkboxes [] = $name . ':' . ( int ) $checked ;
}
$value [ 'checkboxes' ] = implode ( ',' , $checkboxes );
*/
2008-06-22 09:16:40 +02:00
return False ; // NO extra Label
}
2008-06-11 06:05:10 +02:00
2011-04-30 10:17:14 +02:00
/**
* Default maximum lenght for context submenus , longer menus are put as a " More " submenu
*/
2011-04-30 10:21:19 +02:00
const DEFAULT_MAX_MENU_LENGTH = 14 ;
2011-04-30 10:17:14 +02:00
2011-04-16 23:45:13 +02:00
/**
* Return egw_actions
*
* The following attributes are understood for actions on eTemplate / PHP side :
* - string 'id' id of the action ( set as key not attribute ! )
* - string 'caption' name / label or action , get ' s automatic translated
* - boolean 'no_lang' do NOT translate caption , default false
* - string 'icon' icon , eg . 'edit' or 'infolog/task' , if no app given app of template or API is used
* - string 'iconUrl' full url of icon , better use 'icon'
2011-04-17 10:42:07 +02:00
* - boolean | string 'allowOnMultiple' should action be shown if multiple lines are marked , or string 'only' , default true !
* - boolean | string 'enabled' is action available , or string with javascript function to call , default true !
2011-04-17 22:45:21 +02:00
* - string 'disableClass' class name to use with enabled = 'javaScript:nm_not_disableClass'
* ( add that css class in get_rows (), if row lacks rights for an action )
2011-04-16 23:45:13 +02:00
* - boolena 'hideOnDisabled' hide disabled actions , default false
2011-04-17 10:42:07 +02:00
* - string 'type' type of action , default 'popup' for contenxt menus , 'drag' or 'drop'
2011-04-16 23:45:13 +02:00
* - boolean 'default' is that action the default action , default false
* - array 'children' array with actions of submenu
* - int 'group' to group items , default all actions are in one group
2011-04-17 22:45:21 +02:00
* - string 'onExecute' javascript to run , default 'javaScript:nm_action' ,
* which runs action specified in nm_action attribute :
2011-04-16 23:45:13 +02:00
* - string 'nm_action'
* + 'alert' debug action , shows alert with action caption , id and id ' s of selected rows
2011-04-17 20:53:45 +02:00
* + 'submit' default action , sets nm [ action ], nm [ selected ] and nm [ select_all ]
2011-04-16 23:45:13 +02:00
* + 'location' redirects / set location . href to 'url' attribute
* + 'popup' opens popup with url given in 'url' attribute
* - string 'url' url for location or popup
* - string 'target' target for location or popup
* - string 'width' for popup
* - string 'height' for popup
2011-04-17 20:53:45 +02:00
* - string 'confirm' confirmation message
* - string 'confirm_multiple' confirmation message for multiple selected , defaults to 'confirm'
2011-04-16 23:45:13 +02:00
*
* That ' s what we should return looks JSON encoded like
* [
* {
* " id " : " folder_open " ,
* " iconUrl " : " imgs/folder.png " ,
* " caption " : " Open folder " ,
* " onExecute " : " javaScript:nm_action " ,
* " allowOnMultiple " : false ,
* " type " : " popup " ,
* " default " : true
* },
* ]
*
* @ param array $actions id indexed array of actions / array with valus for keys : 'iconUrl' , 'caption' , 'onExecute' , ...
* @ param string $template_name = '' name of the template , used as default for app name of images
* @ param string $prefix = '' prefix for ids
2011-04-26 21:50:03 +02:00
* @ param array & $action_links = array () on return all first - level actions plus the ones with enabled = 'javaScript:...'
2011-04-30 10:17:14 +02:00
* @ param int $max_length = self :: DEFAULT_MAX_MENU_LENGTH automatic pagination , not for first menu level !
2011-06-02 22:03:34 +02:00
* @ param array $default_attrs = null default attributes
2011-04-16 23:45:13 +02:00
* @ return array
*/
2011-04-30 10:17:14 +02:00
public static function egw_actions ( array $actions = null , $template_name = '' , $prefix = '' , array & $action_links = array (),
2011-06-02 22:03:34 +02:00
$max_length = self :: DEFAULT_MAX_MENU_LENGTH , array $default_attrs = null )
2011-04-16 23:45:13 +02:00
{
2011-04-30 10:17:14 +02:00
//echo "<p>".__METHOD__."(\$actions, '$template_name', '$prefix', \$action_links, $max_length) \$actions="; _debug_array($actions);
2011-04-16 23:45:13 +02:00
// default icons for some common actions
static $default_icons = array (
'view' => 'view' ,
'edit' => 'edit' ,
2011-04-19 11:44:25 +02:00
'open' => 'edit' , // does edit if possible, otherwise view
2011-04-16 23:45:13 +02:00
'add' => 'new' ,
2011-06-02 22:03:34 +02:00
'new' => 'new' ,
2011-04-16 23:45:13 +02:00
'delete' => 'delete' ,
'cat' => 'attach' , // add as category icon to api
'document' => 'etemplate/merge' ,
2011-05-06 20:10:50 +02:00
'print' => 'print' ,
'copy' => 'copy' ,
2011-06-02 22:03:34 +02:00
'move' => 'move' ,
2011-04-16 23:45:13 +02:00
);
2011-04-26 21:50:03 +02:00
$first_level = ! $action_links ; // add all first level actions
2011-04-16 23:45:13 +02:00
//echo "actions="; _debug_array($actions);
$egw_actions = array ();
2011-04-30 10:17:14 +02:00
$n = 1 ;
2011-04-16 23:45:13 +02:00
foreach (( array ) $actions as $id => $action )
{
// in case it's only selectbox id => label pairs
if ( ! is_array ( $action )) $action = array ( 'caption' => $action );
2011-06-02 22:03:34 +02:00
if ( $default_attrs ) $action += $default_attrs ;
2011-04-30 10:17:14 +02:00
if ( ! $first_level && $n == $max_length && count ( $actions ) > $max_length )
{
$id = 'more_' . count ( $actions ); // we need a new unique id
$action = array (
'caption' => 'More' ,
'prefix' => $prefix ,
// display rest of actions incl. current one as children
'children' => array_slice ( $actions , $max_length - 1 , count ( $actions ) - $max_length + 1 , true ),
);
//echo "*** Inserting id=$prefix$id"; _debug_array($action);
// we break at end of foreach loop, as rest of actions is already dealt with
// by putting them as children
}
2011-04-16 23:45:13 +02:00
$action [ 'id' ] = $prefix . $id ;
2011-06-02 22:03:34 +02:00
// set certain enable functions
2011-04-27 18:54:35 +02:00
foreach ( array (
'enableClass' => 'javaScript:nm_enableClass' ,
'disableClass' => 'javaScript:nm_not_disableClass' ,
2011-06-02 22:03:34 +02:00
'enableId' => 'javaScript:nm_enableId' ,
2011-04-27 18:54:35 +02:00
) as $attr => $check )
{
if ( isset ( $action [ $attr ]) && ! isset ( $action [ 'enabled' ]))
{
$action [ 'enabled' ] = $check ;
}
}
2011-05-31 21:49:19 +02:00
// add all first level popup actions plus ones with enabled = 'javaScript:...' to action_links
if (( ! isset ( $action [ 'type' ]) || in_array ( $action [ 'type' ], array ( 'popup' , 'drag' ))) && // popup is the default
( $first_level || substr ( $action [ 'enabled' ], 0 , 11 ) == 'javaScript:' ))
2011-04-26 21:50:03 +02:00
{
$action_links [] = $action [ 'id' ];
}
2011-04-16 23:45:13 +02:00
// set default icon, if no other is specified
if ( ! isset ( $action [ 'icon' ]) && isset ( $default_icons [ $id ]))
{
$action [ 'icon' ] = $default_icons [ $id ];
}
// use common eTemplate image semantics
if ( ! isset ( $action [ 'iconUrl' ]) && ! empty ( $action [ 'icon' ]))
{
list ( $app , $img ) = explode ( '/' , $action [ 'icon' ], 2 );
if ( ! $app || ! $img || ! is_dir ( EGW_SERVER_ROOT . '/' . $app ) || strpos ( $img , '/' ) !== false )
{
$img = $action [ 'icon' ];
list ( $app ) = explode ( '.' , $template_name );
}
$action [ 'iconUrl' ] = common :: find_image ( $app , $img );
unset ( $action [ 'icon' ]); // no need to submit it
}
// translate labels
2011-04-17 20:53:45 +02:00
if ( ! $action [ 'no_lang' ])
{
$action [ 'caption' ] = lang ( $action [ 'caption' ]);
if ( $action [ 'hint' ]) $action [ 'hint' ] = lang ( $action [ 'hint' ]);
}
2011-04-16 23:45:13 +02:00
unset ( $action [ 'no_lang' ]);
2011-04-17 20:53:45 +02:00
foreach ( array ( 'confirm' , 'confirm_multiple' ) as $confirm )
2011-04-16 23:45:13 +02:00
{
2011-04-17 20:53:45 +02:00
if ( isset ( $action [ $confirm ]))
{
$action [ $confirm ] = lang ( $action [ $confirm ]) . ( substr ( $action [ $confirm ], - 1 ) != '?' ? '?' : '' );
}
2011-04-16 23:45:13 +02:00
}
2011-06-02 22:03:34 +02:00
// add sub-menues
if ( $action [ 'children' ])
{
static $inherit_attrs = array ( 'url' , 'popup' , 'nm_action' , 'onExecute' , 'type' , 'egw_open' );
$action [ 'children' ] = self :: egw_actions ( $action [ 'children' ], $template_name , $action [ 'prefix' ], $action_links , $max_length ,
array_intersect_key ( $action , array_flip ( $inherit_attrs )));
unset ( $action [ 'prefix' ]);
$action = array_diff_key ( $action , array_flip ( $inherit_attrs ));
}
2011-04-16 23:45:13 +02:00
// link or popup action
if ( $action [ 'url' ])
{
2011-06-02 22:03:34 +02:00
$action [ 'url' ] = egw :: link ( '/index.php' , str_replace ( '$action' , $id , $action [ 'url' ]));
2011-04-16 23:45:13 +02:00
if ( $action [ 'popup' ])
{
list ( $action [ 'data' ][ 'width' ], $action [ 'data' ][ 'height' ]) = explode ( 'x' , $action [ 'popup' ]);
unset ( $action [ 'popup' ]);
$action [ 'data' ][ 'nm_action' ] = 'popup' ;
}
else
{
$action [ 'data' ][ 'nm_action' ] = 'location' ;
}
}
2011-06-02 22:03:34 +02:00
if ( $action [ 'egw_open' ])
2011-04-16 23:45:13 +02:00
{
2011-06-02 22:03:34 +02:00
$action [ 'data' ][ 'nm_action' ] = 'egw_open' ;
2011-04-16 23:45:13 +02:00
}
static $egw_action_supported = array ( // attributes supported by egw_action
'id' , 'caption' , 'iconUrl' , 'type' , 'default' , 'onExecute' , 'group' ,
'enabled' , 'allowOnMultiple' , 'hideOnDisabled' , 'data' , 'children' ,
2011-05-31 22:42:34 +02:00
'hint' , 'checkbox' , 'checked' , 'radioGroup' , 'acceptedTypes' , 'dragType' ,
2011-04-16 23:45:13 +02:00
);
// add all not egw_action supported attributes to data
$action [ 'data' ] = array_merge ( array_diff_key ( $action , array_flip ( $egw_action_supported )),( array ) $action [ 'data' ]);
if ( ! $action [ 'data' ]) unset ( $action [ 'data' ]);
// only add egw_action attributes
$egw_actions [] = array_intersect_key ( $action , array_flip ( $egw_action_supported ));
2011-04-30 10:17:14 +02:00
if ( ! $first_level && $n ++ == $max_length ) break ;
2011-04-16 23:45:13 +02:00
}
//echo "egw_actions="; _debug_array($egw_actions);
return $egw_actions ;
}
/**
2011-05-01 11:11:45 +02:00
* Action with submenu for categories
2011-04-16 23:45:13 +02:00
*
2011-05-01 11:11:45 +02:00
* Automatic switch to hierarchical display , if more then $max_cats_flat = 14 cats found .
2011-04-16 23:45:13 +02:00
*
* @ param string $app
* @ param int $group = 0 see self :: egw_actions
* @ param string $caption = 'Change category'
* @ param string $prefix = 'cat_' prefix category id to get action id
2011-05-01 11:11:45 +02:00
* @ param boolean $globals = true application global categories too
* @ param int $parent_id = 0 only returns cats of a certain parent
* @ param int $max_cats_flat = self :: DEFAULT_MAX_MENU_LENGTH use hierarchical display if more cats
2011-04-16 23:45:13 +02:00
* @ return array like self :: egw_actions
*/
2011-05-01 11:11:45 +02:00
public static function category_action ( $app , $group = 0 , $caption = 'Change category' ,
$prefix = 'cat_' , $globals = true , $parent_id = 0 , $max_cats_flat = self :: DEFAULT_MAX_MENU_LENGTH )
2011-04-16 23:45:13 +02:00
{
$cat = new categories ( null , $app );
2011-05-01 11:11:45 +02:00
$cats = $cat -> return_sorted_array ( $start = 0 , $limit = false , $query = '' , $sort = 'ASC' , $order = 'cat_name' , $globals , $parent_id , $unserialize_data = true );
2011-04-16 23:45:13 +02:00
2011-05-01 11:11:45 +02:00
// if more then max_length cats, switch automatically to hierarchical display
if ( count ( $cats ) > $max_cats_flat )
{
2011-06-01 18:23:04 +02:00
$cat_actions = self :: category_hierarchy ( $cats , $prefix , $parent_id );
2011-04-16 23:45:13 +02:00
}
2011-05-01 11:11:45 +02:00
else // flat, indented categories
{
$cat_actions = array ();
foreach (( array ) $cats as $cat )
{
$name = str_repeat ( ' ' , 2 * $cat [ 'level' ]) . stripslashes ( $cat [ 'name' ]);
if ( categories :: is_global ( $cat )) $name .= ' ♦' ;
2011-04-16 23:45:13 +02:00
2011-05-01 11:11:45 +02:00
$cat_actions [ $cat [ 'id' ]] = array (
'caption' => $name ,
'no_lang' => true ,
);
// add category icon
if ( $cat [ 'data' ][ 'icon' ] && file_exists ( EGW_SERVER_ROOT . '/phpgwapi/images/' . basename ( $cat [ 'data' ][ 'icon' ])))
{
$cat_actions [ $cat [ 'id' ]][ 'iconUrl' ] = $GLOBALS [ 'egw_info' ][ 'server' ][ 'webserver_url' ] . '/phpgwapi/images/' . $cat [ 'data' ][ 'icon' ];
}
}
}
2011-04-16 23:45:13 +02:00
return array (
'caption' => $caption ,
'children' => $cat_actions ,
'enabled' => ( boolean ) $cat_actions ,
'group' => $group ,
'prefix' => $prefix ,
);
}
2011-05-01 11:11:45 +02:00
/**
* Return one level of the category hierarchy
*
* @ param array $cats = null all cats if already read
* @ param string $prefix = 'cat_' prefix category id to get action id
* @ param int $parent_id = 0 only returns cats of a certain parent
* @ return array
*/
private static function category_hierarchy ( array $cats , $prefix , $parent_id = 0 )
{
$cat_actions = array ();
foreach ( $cats as $key => $cat )
{
// current hierarchy level
if ( $cat [ 'parent' ] == $parent_id )
{
$name = stripslashes ( $cat [ 'name' ]);
if ( categories :: is_global ( $cat )) $name .= ' ♦' ;
$cat_actions [ $cat [ 'id' ]] = array (
'caption' => $name ,
'no_lang' => true ,
2011-06-01 18:23:04 +02:00
'prefix' => $prefix ,
2011-05-01 11:11:45 +02:00
);
// add category icon
if ( $cat [ 'data' ][ 'icon' ] && file_exists ( EGW_SERVER_ROOT . '/phpgwapi/images/' . basename ( $cat [ 'data' ][ 'icon' ])))
{
$cat_actions [ $cat [ 'id' ]][ 'iconUrl' ] = $GLOBALS [ 'egw_info' ][ 'server' ][ 'webserver_url' ] . '/phpgwapi/images/' . $cat [ 'data' ][ 'icon' ];
}
unset ( $cats [ $key ]);
}
// direct children
elseif ( isset ( $cat_actions [ $cat [ 'parent' ]]))
{
$cat_actions [ 'sub_' . $cat [ 'parent' ]] = $cat_actions [ $cat [ 'parent' ]];
// have to add category itself to children, to be able to select it!
$cat_actions [ $cat [ 'parent' ]][ 'group' ] = - 1 ; // own group on top
$cat_actions [ 'sub_' . $cat [ 'parent' ]][ 'children' ] = array (
$cat [ 'parent' ] => $cat_actions [ $cat [ 'parent' ]],
) + self :: category_hierarchy ( $cats , $prefix , $cat [ 'parent' ]);
unset ( $cat_actions [ $cat [ 'parent' ]]);
}
}
return $cat_actions ;
}
2011-05-02 15:34:59 +02:00
/**
* Return HTML to initialise actions , if called without arguments only CSS and JS get loaded
*
* Gets called from etemplate :: show_grid () and addressbook_ui :: view ( without arguments ) .
*
* @ param array $actions = null
* @ param array $action_links = null
2011-06-02 16:49:56 +02:00
* @ param string $template_name = '' name of the template , used as default for app name of images - e . g . infolog . index . rows
2011-05-02 15:34:59 +02:00
* @ param string $prefix = 'egw_' JS action objects generated for this widget are prefixed with given prefix
*/
public static function init_egw_actions ( array $actions = null , $action_links = null , $template_name = '' , $prefix = 'egw_' )
{
// Load some JS files needed for the egw_action framework
egw_framework :: includeCSS ( '/phpgwapi/js/egw_action/test/skins/dhtmlxmenu_egw.css' );
egw_framework :: validate_file ( 'dhtmlxtree' , 'dhtmlxMenu/codebase/dhtmlxcommon' );
egw_framework :: validate_file ( 'dhtmlxtree' , 'dhtmlxMenu/codebase/dhtmlxmenu' );
egw_framework :: validate_file ( 'dhtmlxtree' , 'dhtmlxMenu/codebase/ext/dhtmlxmenu_ext' );
egw_framework :: validate_file ( 'egw_action' , 'egw_action' );
egw_framework :: validate_file ( 'egw_action' , 'egw_action_common' );
egw_framework :: validate_file ( 'egw_action' , 'egw_action_popup' );
egw_framework :: validate_file ( 'egw_action' , 'egw_menu' );
egw_framework :: validate_file ( 'egw_action' , 'egw_menu_dhtmlx' );
2011-06-12 18:43:43 +02:00
egw_framework :: validate_file ( 'egw_action' , 'egw_keymanager' );
2011-05-02 15:34:59 +02:00
egw_framework :: validate_file ( '.' , 'nextmatch_action' , 'etemplate' );
2011-05-27 12:28:30 +02:00
if ( ! is_array ( $action_links )) $action_links = array ();
2011-05-02 15:34:59 +02:00
2011-06-02 16:49:56 +02:00
$app = $GLOBALS [ 'egw_info' ][ 'flags' ][ 'currentapp' ];
$enc_actions = self :: egw_actions ( $actions , $template_name , '' , $action_links );
2011-05-27 12:28:30 +02:00
return '
2011-05-02 15:34:59 +02:00
< script type = " text/javascript " >
$ ( document ) . ready ( function () {
// Initialize the action manager and add some actions to it
2011-06-02 16:49:56 +02:00
var actionManager = egw_getActionManager ( " '. $app .' " );
var objectManager = egw_getObjectManager ( " '. $app .' " );
2011-05-02 15:34:59 +02:00
2011-06-02 16:49:56 +02:00
// Create anonymous object and action container for this etemplate
var actionCntr = actionManager . addAction ( " actionManager " , " '. $template_name .' " );
var objectCntr = objectManager . addObject ( new egwActionObjectManager ( " '. $template_name .' " , actionCntr ));
actionCntr . updateActions ( '.json_encode($enc_actions).' );
actionCntr . setDefaultExecute ( " javaScript:nm_action " );
actionCntr . etemplate_var_prefix = " '.etemplate:: $name_vars .' " ;
actionCntr . etemplate_form = document . forms . '.etemplate::$name_form.' ;
2011-05-02 15:34:59 +02:00
var actionLinks = [ " '.implode(' " , " ', $action_links ).' " ];
// Create a new action object for each table row
2011-06-02 16:49:56 +02:00
var divObj = document . getElementById ( " '. $template_name .' " );
$ ( " table.egwGridView_grid>tbody>tr " , divObj ) . each ( function ( index , elem )
2011-05-02 15:34:59 +02:00
{
// Create a new action object
2011-06-02 16:49:56 +02:00
if ( elem . id ) {
var obj = objectCntr . addObject ( elem . id , new nextmatchRowAOI ( elem ));
2011-05-02 15:34:59 +02:00
2011-06-02 16:49:56 +02:00
obj . updateActionLinks ( actionLinks );
}
2011-05-02 15:34:59 +02:00
});
});
</ script > ' ;
}
2009-10-11 14:37:48 +02:00
/**
* Calling our callback
2010-10-26 15:29:09 +02:00
*
2010-01-12 00:37:39 +01:00
* Signature of get_rows callback is either :
* a ) int get_rows ( $query , & $rows , & $readonlys )
* b ) int get_rows ( & $query , & $rows , & $readonlys )
2010-10-26 15:29:09 +02:00
*
2010-01-12 00:37:39 +01:00
* If get_rows is called static ( and php >= 5.2 . 3 ), it is always b ) independent on how it ' s defined !
2009-10-11 14:37:48 +02:00
*
* @ param array & $value
* @ param array & $rows = null
* @ param array & $readonlys = null
* @ param object $obj = null ( internal )
* @ param string | array $method = null ( internal )
* @ return int | boolean total items found of false on error ( $value [ 'get_rows' ] not callable )
*/
private static function call_get_rows ( array & $value , array & $rows = null , array & $readonlys = null , $obj = null , $method = null )
{
if ( is_null ( $method )) $method = $value [ 'get_rows' ];
if ( is_null ( $obj ))
{
// allow static callbacks
if ( strpos ( $method , '::' ) !== false )
{
2010-01-12 00:37:39 +01:00
list ( $class , $method ) = explode ( '::' , $method );
// workaround for php < 5.2.3: do NOT call it static, but allow application code to specify static callbacks
if ( version_compare ( PHP_VERSION , '5.2.3' , '>=' ))
{
$method = array ( $class , $method );
unset ( $class );
}
2009-10-11 14:37:48 +02:00
}
else
{
list ( $app , $class , $method ) = explode ( '.' , $value [ 'get_rows' ]);
}
if ( $class )
{
if ( ! $app && ! is_object ( $GLOBALS [ $class ]))
{
$GLOBALS [ $class ] = new $class ();
}
if ( is_object ( $GLOBALS [ $class ])) // use existing instance (put there by a previous CreateObject)
{
$obj = $GLOBALS [ $class ];
}
else
{
$obj = CreateObject ( $app . '.' . $class );
}
}
}
2010-01-12 00:37:39 +01:00
if ( is_callable ( $method )) // php5.2.3+ static call (value is always a var param!)
2009-10-11 14:37:48 +02:00
{
2010-01-12 00:37:39 +01:00
$total = call_user_func_array ( $method , array ( & $value , & $rows , & $readonlys ));
2009-10-11 14:37:48 +02:00
}
elseif ( is_object ( $obj ) && method_exists ( $obj , $method ))
{
if ( ! is_array ( $readonlys )) $readonlys = array ();
$total = $obj -> $method ( $value , $rows , $readonlys );
}
else
{
$total = false ; // method not callable
}
if ( $method && $total && $value [ 'start' ] >= $total )
{
$value [ 'start' ] = 0 ;
$total = self :: call_get_rows ( $value , $rows , $readonlys , $obj , $method );
}
2009-11-28 12:07:31 +01:00
// otherwise we get stoped by max_excutiontime
if ( $total > 200 ) @ set_time_limit ( 0 );
2009-10-12 11:08:11 +02:00
//error_log($value['get_rows'].'() returning '.array2string($total).', method = '.array2string($method).', value = '.array2string($value));
2009-10-11 14:37:48 +02:00
return $total ;
}
2008-06-22 09:16:40 +02:00
/**
* Preprocess for the custom fields header
*
* @ param array & $cell
*/
2009-10-11 14:37:48 +02:00
private function pre_process_cf_header ( array & $cell , etemplate $tmpl , & $value , $nm_global )
2008-06-22 09:16:40 +02:00
{
//echo __CLASS__.'::'.__METHOD__."() selectcols=$this->selectcols\n";
if ( is_null ( $this -> cfs ))
{
list ( $app ) = explode ( '.' , $tmpl -> name );
$this -> cfs = config :: get_customfields ( $app );
2008-01-19 06:36:20 +01:00
}
2009-09-23 17:27:33 +02:00
// Needed for custom fields linking to other apps
$cell_name = $cell [ 'name' ];
2008-06-22 09:16:40 +02:00
$cell [ 'type' ] = 'vbox' ;
$cell [ 'name' ] = '' ;
$cell [ 'size' ] = '0,,0,0' ;
2008-06-11 06:05:10 +02:00
2008-06-22 09:16:40 +02:00
if ( $this -> selectcols )
2007-03-07 13:30:17 +01:00
{
2010-10-26 15:29:09 +02:00
foreach ( is_array ( $this -> selectcols ) ? $this -> selectcols : explode ( ',' , $this -> selectcols ) as $col )
2007-03-07 13:30:17 +01:00
{
2008-06-22 09:16:40 +02:00
if ( $col [ 0 ] == self :: CF_PREFIX ) $allowed [] = $col ;
2007-03-07 13:30:17 +01:00
}
2008-06-22 09:16:40 +02:00
}
foreach ( $this -> cfs as $name => $field )
{
if ( ! $allowed || in_array ( self :: CF_PREFIX . $name , $allowed ))
2007-03-07 13:30:17 +01:00
{
2008-06-22 09:16:40 +02:00
if ( $field [ 'type' ] == 'select' )
2007-03-07 13:30:17 +01:00
{
2008-06-22 09:16:40 +02:00
$header =& etemplate :: empty_cell ( 'nextmatch-filterheader' , self :: CF_PREFIX . $name , array (
'sel_options' => $field [ 'values' ],
'size' => $field [ 'label' ],
'no_lang' => True ,
2008-10-05 18:56:37 +02:00
'readonly' => $cell [ 'readonly' ],
2008-06-22 09:16:40 +02:00
));
2007-03-07 13:30:17 +01:00
}
2009-09-25 09:59:34 +02:00
elseif ( $GLOBALS [ 'egw_info' ][ 'apps' ][ $field [ 'type' ]])
2009-09-23 17:27:33 +02:00
{
$header =& etemplate :: empty_cell ( 'link-entry' , $cell_name . '[' . self :: CF_PREFIX . $name . ']' , array (
'label' => $field [ 'label' ],
'size' => $field [ 'type' ],
'readonly' => $cell [ 'readonly' ],
'onchange' => 1
));
etemplate :: add_child ( $cell , $header );
unset ( $header );
2009-09-25 16:17:41 +02:00
$header =& etemplate :: empty_cell ( 'label' , '' );
2009-09-23 17:27:33 +02:00
$value [ self :: CF_PREFIX . $name ] = $field [ 'type' ] . ':' . $nm_global [ 'col_filter' ][ self :: CF_PREFIX . $name ];
}
2008-06-22 09:16:40 +02:00
else
2008-01-19 06:36:20 +01:00
{
2008-06-22 09:16:40 +02:00
$header =& etemplate :: empty_cell ( 'nextmatch-sortheader' , self :: CF_PREFIX . $name , array (
'label' => $field [ 'label' ],
2008-10-05 18:56:37 +02:00
'readonly' => $cell [ 'readonly' ],
2008-06-22 09:16:40 +02:00
));
2008-01-19 06:36:20 +01:00
}
2008-06-22 09:16:40 +02:00
etemplate :: add_child ( $cell , $header );
unset ( $header );
2007-03-07 13:30:17 +01:00
}
}
2009-06-30 20:21:15 +02:00
// do we have more then 5 cf's to display --> limit header height to 5 lines plus vertical scrollbar
$num = ! $allowed ? count ( $this -> cfs ) : count ( $allowed );
if ( $num > 5 )
{
$vbox = $cell ;
$cell = etemplate :: empty_cell ( 'box' , '' , array (
'size' => '0,,0,0' ,
'span' => ',cf_header_height_limit' ,
));
etemplate :: add_child ( $cell , $vbox );
}
2008-06-22 09:16:40 +02:00
return false ; // no extra label
}
2007-03-07 13:30:17 +01:00
2008-06-22 09:16:40 +02:00
/**
* Extract the column names and labels from the template
*
* @ param etemplate & $tmpl
* @ param array & $cols here we add the column - name /- label
* @ param array & $name2col
* @ param array $content nextmatch content , to be able to resolve labels with @ name
* @ param array $selectcols selected colums
* @ return int columns found , count ( $cols )
*/
2009-10-11 14:37:48 +02:00
private function cols_from_tpl ( etemplate $tmpl , & $cols , & $name2col , & $content , $selectcols )
2008-06-22 09:16:40 +02:00
{
//_debug_array($cols);
// fetching column-names & -labels from the template
$cols [ '__content__' ] =& $content ;
2009-10-11 14:37:48 +02:00
$tmpl -> widget_tree_walk ( array ( $this , 'cols_from_tpl_walker' ), $cols );
2008-06-22 09:16:40 +02:00
unset ( $cols [ '__content__' ]);
2009-08-25 15:14:00 +02:00
$col2names = is_array ( $cols [ 'col2names' ]) ? $cols [ 'col2names' ] : array (); unset ( $cols [ 'col2names' ]);
//_debug_array($col2names);
2008-06-22 09:16:40 +02:00
//_debug_array($cols);
foreach ( $cols as $name => $label )
2007-03-07 13:30:17 +01:00
{
2008-06-22 09:16:40 +02:00
if ( ! $label ) unset ( $cols [ $name ]);
}
//_debug_array($cols);
$cols2 = $cols ;
$cols = array ();
foreach ( $cols2 as $name => $label )
{
$col = $name ;
// and replace the column letter then with the name concatinated by an underscore
if ( is_array ( $col2names [ $name ]))
2007-03-07 13:30:17 +01:00
{
2008-06-22 09:16:40 +02:00
$name = implode ( '_' , $col2names [ $name ]);
$name2col [ $name ] = $col ;
2007-03-07 13:30:17 +01:00
}
2008-06-22 09:16:40 +02:00
$cols [ $name ] = $label ;
2007-03-07 13:30:17 +01:00
2008-06-22 09:16:40 +02:00
// we are behind the column of a custom fields header --> add the individual fields
if ( $name == $this -> cf_header && ( ! $selectcols ||
in_array ( $this -> cf_header , explode ( ',' , $selectcols ))))
2007-03-07 13:30:17 +01:00
{
2008-06-22 09:16:40 +02:00
$cols [ $name ] .= ':' ;
list ( $app ) = explode ( '.' , $tmpl -> name );
if (( $this -> cfs = config :: get_customfields ( $app )))
2007-03-07 13:30:17 +01:00
{
2008-06-22 09:16:40 +02:00
foreach ( $this -> cfs as $name => $field )
{
$cols [ self :: CF_PREFIX . $name ] = '- ' . $field [ 'label' ];
}
2007-03-07 13:30:17 +01:00
}
2008-06-22 09:16:40 +02:00
else
2007-03-07 13:30:17 +01:00
{
2008-06-22 09:16:40 +02:00
unset ( $cols [ $name ]); // no cf's defined -> no header
2007-03-07 13:30:17 +01:00
}
}
}
2008-06-22 09:16:40 +02:00
//_debug_array($cols);
return count ( $cols );
}
/**
* Extract the column names and labels from the template ( callback for etemplate :: widget_tree_walk ())
*
* @ param array & $widget
* @ param array & $cols here we add the column - name /- label
* @ param string $path
*/
2009-10-11 14:37:48 +02:00
function cols_from_tpl_walker ( & $widget , & $cols , $path )
2008-06-22 09:16:40 +02:00
{
list ( $type , $subtype ) = explode ( '-' , $widget [ 'type' ]);
2002-09-17 23:31:45 +02:00
2008-06-22 09:16:40 +02:00
if ( $subtype == 'customfields' )
2002-09-17 23:31:45 +02:00
{
2008-06-22 09:16:40 +02:00
if ( ! $widget [ 'name' ]) $widget [ 'name' ] = 'customfields' ;
if ( ! $widget [ 'label' ]) $widget [ 'label' ] = 'Custom fields' ;
$this -> cf_header = $widget [ 'name' ];
}
if ( $type != 'nextmatch' || ! $subtype || ! $widget [ 'name' ] || $widget [ 'disabled' ])
{
return ;
}
$options = explode ( ',' , $widget [ 'size' ]);
if ( ! ( $label = $widget [ 'label' ]) || in_array ( $subtype , array ( 'header' , 'sortheader' )) && $options [ 1 ])
{
if ( in_array ( $subtype , array ( 'customfilter' , 'sortheader' ))) $subtype = array_shift ( $options );
2007-06-21 11:59:53 +02:00
2008-06-22 09:16:40 +02:00
$label = $options [ 0 ];
2009-09-28 14:52:50 +02:00
// some widgets have label as second option (column name with _ as first), not a perfect detection ...
if ( strpos ( $label , '_' ) !== false && ! empty ( $options [ 1 ])) $label = $options [ 1 ];
2008-06-22 09:16:40 +02:00
}
list (,, $col , $sub ) = explode ( '/' , $path );
$row = ( int ) $col ;
$col = substr ( $col , $row > 9 ? 2 : 1 );
2009-09-28 14:52:50 +02:00
if (( $label [ 0 ] == '@' || strchr ( $lable , '$' ) !== false ) && is_array ( $cols [ '__content__' ]))
2008-06-22 09:16:40 +02:00
{
$label = etemplate :: expand_name ( $label , $col , $row , '' , '' , $cols [ '__content__' ]);
}
if ( ! isset ( $cols [ $widget [ 'name' ]]) && $label )
{
$label = substr ( $label , - 3 ) == '...' ? lang ( substr ( $label , 0 , - 3 )) : lang ( $label );
2007-06-21 11:59:53 +02:00
2009-08-25 16:40:28 +02:00
if ( empty ( $label ) || strpos ( $cols [ $col ], $label ) === false )
2003-12-09 01:03:41 +01:00
{
2008-06-22 09:16:40 +02:00
$cols [ $col ] .= ( $cols [ $col ] ? ', ' : '' ) . $label ;
}
}
2009-08-25 15:14:00 +02:00
$cols [ 'col2names' ][ $col ][] = $widget [ 'name' ];
2008-06-11 06:05:10 +02:00
2008-06-22 09:16:40 +02:00
//echo "<p>$path: $widget[name] $label</p>\n";
}
2003-12-09 01:03:41 +01:00
2008-06-22 09:16:40 +02:00
/**
* postprocessing method , called after the submission of the form
*
* It has to copy the allowed / valid data from $value_in to $value , otherwise the widget
* will return no data ( if it has a preprocessing method ) . The framework insures that
* the post - processing of all contained widget has been done before .
*
* Only used by select - dow so far
*
* @ param string $name form - name of the widget
* @ param mixed & $value the extension returns here it 's input, if there' s any
* @ param mixed & $extension_data persistent storage between calls or pre - and post - process
* @ param boolean & $loop can be set to true to request a re - submision of the form / dialog
2009-03-26 15:25:41 +01:00
* @ param etemplate & $tmpl the eTemplate the widget belongs too
2008-06-22 09:16:40 +02:00
* @ param mixed & value_in the posted values ( already striped of magic - quotes )
* @ return boolean true if $value has valid content , on false no content will be returned !
*/
2009-10-11 14:37:48 +02:00
public function post_process ( $name , & $value , & $extension_data , & $loop , etemplate & $tmpl , $value_in )
2008-06-22 09:16:40 +02:00
{
$nm_global =& self :: get_nm_global ( $name , $extension_data [ 'type' ]);
2008-06-11 06:05:10 +02:00
2008-06-22 09:16:40 +02:00
if ( $this -> debug ) { echo " <p>nextmatch_widget.post_process(type=' $extension_data[type] ', name=' $name ',value_in= " . print_r ( $value_in , true ) . " ,order=' $nm_global[order] '): value = " ; _debug_array ( $value ); }
2002-09-17 23:31:45 +02:00
2008-06-22 09:16:40 +02:00
switch ( $extension_data [ 'type' ])
{
case 'nextmatch' :
break ;
2002-10-16 17:22:06 +02:00
2008-06-22 09:16:40 +02:00
case 'nextmatch-sortheader' :
if ( $value_in )
2003-06-29 19:00:05 +02:00
{
2008-06-22 09:16:40 +02:00
$nm_global [ 'order' ] = self :: last_part ( $name );
$nm_global [ 'default_sort' ] = $extension_data [ 'default_sort' ];
2003-06-29 19:00:05 +02:00
}
2008-06-22 09:16:40 +02:00
return False ; // dont report value back, as it's in the wrong location (rows)
2009-09-23 17:27:33 +02:00
case 'nextmatch-customfields' :
2009-10-11 14:37:48 +02:00
$this -> post_process_cf_header ( $value , $extension_data , $tmpl , $value_in , $nm_global );
2009-09-29 14:04:27 +02:00
return False ; // dont report value back, as it's in the wrong location (rows)
2009-09-25 09:59:34 +02:00
case 'link-entry' : // allways return app:id, if an entry got selected, otherwise null
if ( is_array ( $value_in ) && ! empty ( $value_in [ 'id' ]))
{
$value_in = ( isset ( $value_in [ 'app' ]) ? $value_in [ 'app' ] : $extension_data [ 'app' ]) . ':' . $value_in [ 'id' ];
}
else
{
$value_in = null ;
}
// fall through
2008-06-22 09:16:40 +02:00
default :
case 'select-account' : // used by nextmatch-accountfilter
case 'nextmatch-filterheader' :
if (( string ) $value_in != ( string ) $extension_data [ 'old_value' ])
2007-03-07 13:30:17 +01:00
{
2008-06-22 09:16:40 +02:00
if ( $this -> debug ) echo " <p>setting nm_global[filter][ " . self :: last_part ( $name ) . " ]=' $value_in ' (was ' $extension_data[old_value] ')</p> \n " ;
$nm_global [ 'filter' ][ self :: last_part ( $name )] = $value_in ;
2007-03-07 13:30:17 +01:00
}
2008-06-22 09:16:40 +02:00
return False ; // dont report value back, as it's in the wrong location (rows)
2008-06-11 06:05:10 +02:00
2008-06-22 09:16:40 +02:00
case 'nextmatch-header' :
return False ; // nothing to report
}
$old_value = $extension_data ;
if ( $this -> debug ) { echo " old_value= " ; _debug_array ( $old_value ); }
$value [ 'start' ] = $old_value [ 'start' ]; // need to be set, to be reported back
$value [ 'return' ] = $old_value [ 'return' ];
if ( is_array ( $value [ 'bottom' ])) // we have a second bottom-bar
{
$inputs = array ( 'search' , 'cat_id' , 'filter' , 'filter2' , 'num_rows' );
foreach ( $inputs as $name )
2002-09-17 23:31:45 +02:00
{
2008-06-22 09:16:40 +02:00
if ( isset ( $value [ 'bottom' ][ $name ]) && $value [ $name ] == $old_value [ $name ])
2007-06-21 11:59:53 +02:00
{
2008-06-22 09:16:40 +02:00
if ( $this -> debug ) echo " value[ $name ] overwritten by bottom-value[ $name ]=' " . $value [ 'bottom' ][ $name ] . " ', old_value[ $name ]=' " . $old_value [ $name ] . " '<br> \n " ;
$value [ $name ] = $value [ 'bottom' ][ $name ];
2007-06-21 11:59:53 +02:00
}
2002-09-17 23:31:45 +02:00
}
2008-06-22 09:16:40 +02:00
$buttons = array ( 'start_search' , 'first' , 'left' , 'right' , 'last' , 'export' );
foreach ( $buttons as $name )
2002-09-17 23:31:45 +02:00
{
2008-06-22 09:16:40 +02:00
if ( isset ( $value [ 'bottom' ][ $name ]) && $value [ 'bottom' ][ $name ])
{
$value [ $name ] = $value [ 'bottom' ][ $name ];
}
2002-09-17 23:31:45 +02:00
}
2008-06-22 09:16:40 +02:00
if ( $value [ 'bottom' ][ 'savecols' ])
2002-09-17 23:31:45 +02:00
{
2008-06-22 09:16:40 +02:00
$value [ 'selectcols' ] = $value [ 'bottom' ][ 'selectcols' ];
$value [ 'default_prefs' ] = $value [ 'bottom' ][ 'default_prefs' ];
2002-09-17 23:31:45 +02:00
}
2008-06-22 09:16:40 +02:00
unset ( $value [ 'bottom' ]);
}
if ( isset ( $old_value [ 'num_rows' ]) && ! is_null ( $value [ 'num_rows' ]) && $value [ 'num_rows' ] != $old_value [ 'num_rows' ])
{
if ( $this -> debug ) echo " <p>nextmatch_widget::post_process() num_rows changed { $old_value [ 'num_rows' ] } --> { $value [ 'num_rows' ] } ==> looping</p> \n " ;
$loop = true ; // num_rows changed
}
// num_rows: use old value in extension data, if $value['num_rows'] is not set because nm-header is not shown
$value [ 'num_rows' ] = isset ( $value [ 'num_rows' ]) ? ( int ) $value [ 'num_rows' ] : ( int ) $extension_data [ 'num_rows' ];
$max = $value [ 'num_rows' ] ? $value [ 'num_rows' ] : ( int ) $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'common' ][ 'maxmatchs' ];
if ( strpos ( $value [ 'search' ], 'xjxquery' )) {
// We deal with advancedsearch
$aXml = $value [ 'search' ];
$value [ 'search' ] = '' ;
$aXml = str_replace ( " <xjxquery><q> " , " & " , $aXml );
$aXml = str_replace ( " </q></xjxquery> " , " " , $aXml );
$value [ 'advsearch_cont' ] = explode ( '&' , $aXml );
//$GLOBALS['egw']->boetemplate = &CreateObject('etemplate.boetemplate');
//print_r($GLOBALS['egw']->boetemplate->get_array($value['advsearch_cont'],'exec[]',true));
//$value['advsearch_cont'] = preg_replace("/exec\[ (.*)/",'$1',$value['advsearch_cont']);
//$value['advsearch_cont'] = preg_replace("\]",'',$value['advsearch_cont']);
//print_r($value['advsearch_cont']);
}
if ( $value [ 'start_search' ] || $value [ 'search' ] != $old_value [ 'search' ] ||
isset ( $value [ 'cat_id' ]) && $value [ 'cat_id' ] != $old_value [ 'cat_id' ] ||
isset ( $value [ 'filter' ]) && $value [ 'filter' ] != $old_value [ 'filter' ] ||
isset ( $value [ 'filter2' ]) && $value [ 'filter2' ] != $old_value [ 'filter2' ])
{
if ( $this -> debug )
2002-09-17 23:31:45 +02:00
{
2008-06-22 09:16:40 +02:00
echo " <p>search=' $old_value[search] '->' $value[search] ', filter=' $old_value[filter] '->' $value[filter] ', filter2=' $old_value[filter2] '->' $value[filter2] '<br> " ;
echo " new filter --> loop</p> " ;
echo " value = " ; _debug_array ( $value );
echo " old_value = " ; _debug_array ( $old_value );
2002-09-22 11:30:29 +02:00
}
2008-06-22 09:16:40 +02:00
$loop = True ;
}
elseif ( $value [ 'first' ] || $value [ 'left' ] && $old_value [ 'start' ] < $max )
{
$value [ 'start' ] = 0 ;
unset ( $value [ 'first' ]);
$loop = True ;
}
elseif ( $value [ 'left' ])
{
$value [ 'start' ] = $old_value [ 'start' ] - $max ;
unset ( $value [ 'left' ]);
$loop = True ;
}
elseif ( $value [ 'right' ])
{
$value [ 'start' ] = $old_value [ 'start' ] + $max ;
unset ( $value [ 'right' ]);
$loop = True ;
}
elseif ( $value [ 'last' ])
{
$value [ 'start' ] = ( int ) (( $old_value [ 'total' ] - 1 ) / $max ) * $max ;
unset ( $value [ 'last' ]);
$loop = True ;
}
elseif ( $nm_global [ 'order' ])
{
$value [ 'order' ] = $nm_global [ 'order' ];
if ( $old_value [ 'order' ] != $value [ 'order' ])
2002-09-22 11:30:29 +02:00
{
2008-06-22 09:16:40 +02:00
$value [ 'sort' ] = $nm_global [ 'default_sort' ];
2002-09-17 23:31:45 +02:00
}
2008-06-22 09:16:40 +02:00
else
2003-12-09 01:03:41 +01:00
{
2008-06-22 09:16:40 +02:00
$value [ 'sort' ] = $old_value [ 'sort' ] != 'DESC' ? 'DESC' : 'ASC' ;
2003-12-09 01:03:41 +01:00
}
2008-06-22 09:16:40 +02:00
if ( $this -> debug ) echo " <p>old_value= $old_value[order] / $old_value[sort] ==> $value[order] / $value[sort] </p> \n " ;
$loop = True ;
}
elseif ( $nm_global [ 'filter' ])
{
if ( ! is_array ( $value [ 'col_filter' ])) $value [ 'col_filter' ] = array ();
2005-07-14 08:47:14 +02:00
2008-06-22 09:16:40 +02:00
$value [ 'col_filter' ] += $nm_global [ 'filter' ];
$loop = True ;
}
elseif ( isset ( $value [ 'searchletter' ]))
{
list ( $value [ 'searchletter' ]) = @ each ( $value [ 'searchletter' ]);
if ( $value [ 'searchletter' ] === 'all' ) $value [ 'searchletter' ] = false ;
$loop = True ;
}
if ( $value [ 'savecols' ])
{
$name = is_object ( $extension_data [ 'template' ]) ? $extension_data [ 'template' ] -> name : $extension_data [ 'template' ];
list ( $app ) = explode ( '.' , $name );
if ( isset ( $extension_data [ 'columnselection_pref' ])) $name = $extension_data [ 'columnselection_pref' ];
$pref = ! $GLOBALS [ 'egw_info' ][ 'user' ][ 'apps' ][ 'admin' ] && $value [ 'default_prefs' ] ? 'default' : 'user' ;
$GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ] = $GLOBALS [ 'egw' ] -> preferences -> add ( $app , 'nextmatch-' . $name , is_array ( $value [ 'selectcols' ]) ?
implode ( ',' , $value [ 'selectcols' ]) : $value [ 'selectcols' ], $pref );
$GLOBALS [ 'egw' ] -> preferences -> save_repository ( false , $pref );
$loop = True ;
}
if ( $value [ 'export' ])
{
2009-10-11 14:37:48 +02:00
self :: csv_export ( $extension_data );
2008-06-22 09:16:40 +02:00
}
2011-04-16 23:45:13 +02:00
// allows return selected as array
2011-05-06 20:10:50 +02:00
$value [ $extension_data [ 'action_var' ]] = $value [ 'nm_action' ]; //unset($value['nm_action']);
2011-04-16 23:45:13 +02:00
$value [ 'selected' ] = $value [ 'selected' ] === '' ? array () : boetemplate :: csv_split ( $value [ 'selected' ]);
2011-04-27 09:49:46 +02:00
$checkboxes = $value [ 'checkboxes' ];
$value [ 'checkboxes' ] = array ();
foreach ( explode ( ';' , $checkboxes ) as $data )
{
list ( $name , $checked ) = explode ( ':' , $data );
if ( $name ) $value [ 'checkboxes' ][ $name ] = ( boolean ) $checked ;
}
$value [ 'select_all' ] =& $value [ 'checkboxes' ][ 'select_all' ];
2011-04-16 23:45:13 +02:00
2008-06-22 09:16:40 +02:00
return True ;
}
2009-09-23 17:27:33 +02:00
/**
* Postprocess for the custom fields header to do filtering on custom fields that are links to other applications
*
* @ param array & $cell
*/
2009-10-11 14:37:48 +02:00
private function post_process_cf_header ( & $value , $extension_data , $tmpl , $value_in , & $nm_global )
2009-09-23 17:27:33 +02:00
{
if ( is_null ( $this -> cfs ))
{
list ( $app ) = explode ( '.' , $tmpl -> name );
$this -> cfs = config :: get_customfields ( $app );
}
foreach ( $this -> cfs as $name => $field ) {
if ( $GLOBALS [ 'egw_info' ][ 'apps' ][ $field [ 'type' ]]) {
2010-02-22 23:46:17 +01:00
if ( is_array ( $value_in [ self :: CF_PREFIX . $name ])) {
list ( $old_app , $old_id ) = explode ( ':' , $extension_data [ 'old_value' ][ self :: CF_PREFIX . $name ]);
if ( $value_in [ self :: CF_PREFIX . $name ][ 'id' ] != '' && $value_in [ self :: CF_PREFIX . $name ][ 'id' ] != $old_id ) {
$nm_global [ 'filter' ][ self :: CF_PREFIX . $name ] = $value_in [ self :: CF_PREFIX . $name ][ 'id' ];
}
2010-10-26 15:29:09 +02:00
}
2010-02-22 23:46:17 +01:00
elseif (( string ) $value_in [ self :: CF_PREFIX . $name ] != ( string ) $extension_data [ 'old_value' ][ self :: CF_PREFIX . $name ])
2009-09-23 17:27:33 +02:00
{
$nm_global [ 'filter' ][ self :: CF_PREFIX . $name ] = $value_in [ self :: CF_PREFIX . $name ][ 'id' ];
}
$value = $value_in ;
}
}
}
2008-06-22 09:16:40 +02:00
/**
* Export the list as csv file download
*
2009-10-11 14:37:48 +02:00
* @ param array $value array ( 'get_rows' => $method ), further values see nextmatch widget $query parameter
* @ param string $separator = ';'
2009-03-26 15:25:41 +01:00
* @ return boolean false = error , eg . get_rows callback does not exits , true = nothing to export , otherwise we do NOT return !
2008-06-22 09:16:40 +02:00
*/
2009-10-11 14:37:48 +02:00
static public function csv_export ( & $value , $separator = ';' )
2008-06-22 09:16:40 +02:00
{
if ( ! isset ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'apps' ][ 'admin' ]))
{
$export_limit = $GLOBALS [ 'egw_info' ][ 'server' ][ 'export_limit' ];
2009-10-11 14:37:48 +02:00
//if (isset($value['export_limit'])) $export_limit = $value['export_limit'];
2008-06-22 09:16:40 +02:00
}
2009-10-11 14:37:48 +02:00
$charset = $charset_out = translation :: charset ();
2008-06-22 09:16:40 +02:00
if ( isset ( $value [ 'csv_charset' ]))
{
2009-10-11 14:37:48 +02:00
$charset_out = $value [ 'csv_charset' ];
2008-06-22 09:16:40 +02:00
}
elseif ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'common' ][ 'csv_charset' ])
{
2009-10-11 14:37:48 +02:00
$charset_out = $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'common' ][ 'csv_charset' ];
2008-06-22 09:16:40 +02:00
}
$backup_start = $value [ 'start' ];
$backup_num_rows = $value [ 'num_rows' ];
2008-06-11 06:05:10 +02:00
2008-06-22 09:16:40 +02:00
$value [ 'start' ] = 0 ;
$value [ 'num_rows' ] = 500 ;
$value [ 'csv_export' ] = true ; // so get_rows method _can_ produce different content or not store state in the session
do
2007-09-22 16:58:10 +02:00
{
2009-10-11 14:37:48 +02:00
if ( ! ( $total = self :: call_get_rows ( $value , $rows )))
2007-09-22 16:58:10 +02:00
{
2008-06-22 09:16:40 +02:00
break ; // nothing to export
2007-09-22 16:58:10 +02:00
}
2008-06-22 09:16:40 +02:00
if ( $export_limit && ( ! is_numeric ( $export_limit ) || $export_limit < $total ))
2007-09-22 16:58:10 +02:00
{
2009-03-16 13:58:24 +01:00
etemplate :: set_validation_error ( $name , lang ( 'You are not allowed to export more then %1 entries!' , $export_limit ));
2007-09-22 16:58:10 +02:00
return false ;
}
2008-06-22 09:16:40 +02:00
if ( ! isset ( $value [ 'no_csv_support' ])) $value [ 'no_csv_support' ] = ! is_array ( $value [ 'csv_fields' ]);
//echo "<p>start=$value[start], num_rows=$value[num_rows]: total=$total, count(\$rows)=".count($rows)."</p>\n";
if ( ! $value [ 'start' ]) // send the neccessary headers
2007-09-22 16:58:10 +02:00
{
2009-11-14 08:17:19 +01:00
// skip empty data row(s) used to adjust to number of header-lines
foreach ( $rows as $row0 )
{
if ( is_array ( $row0 ) && count ( $row0 ) > 1 ) break ;
}
$fp = self :: csv_open ( $row0 , $value [ 'csv_fields' ], $app , $charset_out , $charset , $separator );
2007-09-22 16:58:10 +02:00
}
2008-06-22 09:16:40 +02:00
foreach ( $rows as $key => $row )
2007-09-22 16:58:10 +02:00
{
2009-10-11 14:37:48 +02:00
if ( ! is_numeric ( $key ) || ! $row ) continue ; // not a real rows
fwrite ( $fp , self :: csv_encode ( $row , $value [ 'csv_fields' ], true , $rows [ 'sel_options' ], $charset_out , $charset , $separator ) . " \n " );
2007-09-22 16:58:10 +02:00
}
2008-06-22 09:16:40 +02:00
$value [ 'start' ] += $value [ 'num_rows' ];
2007-09-22 16:58:10 +02:00
2008-06-22 09:16:40 +02:00
@ set_time_limit ( 10 ); // 10 more seconds
}
while ( $total > $value [ 'start' ]);
2007-09-22 16:58:10 +02:00
2008-06-22 09:16:40 +02:00
unset ( $value [ 'csv_export' ]);
$value [ 'start' ] = $backup_start ;
$value [ 'num_rows' ] = $backup_num_rows ;
if ( $value [ 'no_csv_support' ]) // we need to call the get_rows method in case start&num_rows are stored in the session
{
2009-10-11 14:37:48 +02:00
self :: call_get_rows ( $value );
2007-09-22 16:58:10 +02:00
}
2008-06-22 09:16:40 +02:00
if ( $fp )
{
fclose ( $fp );
2009-10-11 14:37:48 +02:00
common :: egw_exit ();
2008-06-22 09:16:40 +02:00
}
return true ;
}
2008-06-11 06:05:10 +02:00
2008-06-22 09:16:40 +02:00
/**
* Opens the csv output ( download ) and writes the header line
*
* @ param array $row0 first row to guess the available fields
* @ param array $fields name => label or name => array ( 'lable' => label , 'type' => type ) pairs
* @ param string $app app - name
2009-10-27 15:49:23 +01:00
* @ param string $charset_out = null output charset
* @ param string $charset data charset
* @ param string $separator = ';'
2008-06-22 09:16:40 +02:00
* @ return FILE
*/
2009-10-27 15:49:23 +01:00
private static function csv_open ( $row0 , & $fields , $app , $charset_out = null , $charset = null , $separator = ';' )
2008-06-22 09:16:40 +02:00
{
if ( ! is_array ( $fields ) || ! count ( $fields ))
2007-09-22 16:58:10 +02:00
{
2009-10-11 14:37:48 +02:00
$fields = self :: autodetect_fields ( $row0 , $app );
2008-06-22 09:16:40 +02:00
}
2009-10-11 14:37:48 +02:00
html :: content_header ( 'export.csv' , 'text/comma-separated-values' );
2008-06-22 09:16:40 +02:00
//echo "<pre>";
2008-06-11 06:05:10 +02:00
2008-06-22 09:16:40 +02:00
if (( $fp = fopen ( 'php://output' , 'w' )))
{
$labels = array ();
foreach ( $fields as $field => $label )
2007-09-22 16:58:10 +02:00
{
2008-06-22 09:16:40 +02:00
if ( is_array ( $label )) $label = $label [ 'label' ];
$labels [ $field ] = $label ? $label : $field ;
2007-09-22 16:58:10 +02:00
}
2009-10-27 15:49:23 +01:00
fwrite ( $fp , self :: csv_encode ( $labels , $fields , false , null , $charset_out , $charset , $separator ) . " \n " );
2007-09-22 16:58:10 +02:00
}
2008-06-22 09:16:40 +02:00
return $fp ;
}
2008-06-11 06:05:10 +02:00
2008-06-22 09:16:40 +02:00
/**
* CSV encode a single row , including some basic type conversation
*
* @ param array $data
* @ param array $fields
* @ param boolean $use_type = true
2009-03-26 15:25:41 +01:00
* @ param array $extra_sel_options = null
2009-10-11 14:37:48 +02:00
* @ param string $charset_out = null output charset
* @ param string $charset data charset
* @ param string $separator = ';'
2008-06-22 09:16:40 +02:00
* @ return string
*/
2009-10-11 14:37:48 +02:00
private static function csv_encode ( $data , $fields , $use_type = true , $extra_sel_options = null , $charset_out = null , $charset = null , $separator = ';' )
2008-06-22 09:16:40 +02:00
{
2009-03-26 15:25:41 +01:00
$sel_options =& boetemplate :: $request -> sel_options ;
2008-06-22 09:16:40 +02:00
$out = array ();
foreach ( $fields as $field => $label )
{
$value = ( array ) $data [ $field ];
2009-03-26 15:25:41 +01:00
if ( $use_type && is_array ( $label ) && in_array ( $label [ 'type' ], array ( 'select-account' , 'select-cat' , 'date-time' , 'date' , 'select' , 'int' , 'float' )))
2008-06-22 09:16:40 +02:00
{
foreach ( $value as $key => $val )
2007-09-22 16:58:10 +02:00
{
2008-06-22 09:16:40 +02:00
switch ( $label [ 'type' ])
2007-09-22 16:58:10 +02:00
{
2008-06-22 09:16:40 +02:00
case 'select-account' :
2009-10-11 14:37:48 +02:00
if ( $val ) $value [ $key ] = common :: grab_owner_name ( $val );
2008-06-22 09:16:40 +02:00
break ;
case 'select-cat' :
if ( $val )
{
2009-09-18 16:09:11 +02:00
$cats = array ();
foreach ( is_array ( $val ) ? $val : explode ( ',' , $val ) as $cat_id )
{
$cats [] = $GLOBALS [ 'egw' ] -> categories -> id2name ( $cat_id );
}
$value [ $key ] = implode ( '; ' , $cats );
2008-06-22 09:16:40 +02:00
}
break ;
case 'date-time' :
case 'date' :
2009-10-11 14:37:48 +02:00
if ( $val )
{
try {
$value [ $key ] = egw_time :: to ( $val , $label [ 'type' ] == 'date' ? true : '' );
}
catch ( Exception $e ) {
// ignore conversation errors, leave value unchanged (might be a wrongly as date(time) detected field
}
}
2008-06-22 09:16:40 +02:00
break ;
2009-03-26 15:25:41 +01:00
case 'select' :
if ( isset ( $sel_options [ $field ]))
{
if ( $val ) $value [ $key ] = lang ( $sel_options [ $field ][ $val ]);
}
elseif ( is_array ( $extra_sel_options ) && isset ( $extra_sel_options [ $field ]))
{
if ( $val ) $value [ $key ] = lang ( $extra_sel_options [ $field ][ $val ]);
}
break ;
case 'int' : // size: [min],[max],[len],[precission/sprint format]
case 'float' :
list (,,, $pre ) = explode ( ',' , $label [ 'size' ]);
if (( $label [ 'type' ] == 'float' || ! is_numeric ( $pre )) && $val && $pre )
{
$val = str_replace ( array ( ' ' , ',' ), array ( '' , '.' ), $val );
$value [ $key ] = is_numeric ( $pre ) ? round ( $value , $pre ) : sprintf ( $pre , $value );
}
2007-09-22 16:58:10 +02:00
}
}
}
2008-06-22 09:16:40 +02:00
$value = implode ( ', ' , $value );
2008-06-11 06:05:10 +02:00
2009-10-11 14:37:48 +02:00
if ( strpos ( $value , $separator ) !== false || strpos ( $value , " \n " ) !== false || strpos ( $value , " \r " ) !== false )
2007-09-22 16:58:10 +02:00
{
2011-01-24 17:43:15 +01:00
$value = '"' . str_replace ( array ( '\\' , '"' ,), array ( '\\\\' , '""' ), $value ) . '"' ;
2010-12-17 17:59:02 +01:00
$value = str_replace ( " \r \n " , " \n " , $value ); // to avoid early linebreak by Excel
2007-09-22 16:58:10 +02:00
}
2008-06-22 09:16:40 +02:00
$out [] = $value ;
2007-09-22 16:58:10 +02:00
}
2009-10-11 14:37:48 +02:00
$out = implode ( $separator , $out );
2008-06-11 06:05:10 +02:00
2009-10-11 14:37:48 +02:00
if ( $charset_out && $charset != $charset_out )
2007-09-22 16:58:10 +02:00
{
2009-10-11 14:37:48 +02:00
$out = translation :: convert ( $out , $charset , $charset_out );
2008-06-22 09:16:40 +02:00
}
return $out ;
}
2008-06-11 06:05:10 +02:00
2008-06-22 09:16:40 +02:00
/**
* Try to autodetect the fields from the first data - row and the app - name
*
* @ param array $row0 first data - row
* @ param string $app
*/
2009-10-11 14:37:48 +02:00
private static function autodetect_fields ( $row0 , $app )
2008-06-22 09:16:40 +02:00
{
$fields = array_combine ( array_keys ( $row0 ), array_keys ( $row0 ));
foreach ( $fields as $name => $label )
{
// try to guess field-type from the fieldname
if ( preg_match ( '/(modified|created|start|end)/' , $name ) && strpos ( $name , 'by' ) === false &&
( ! $row0 [ $name ] || is_numeric ( $row0 [ $name ]))) // only use for real timestamps
2007-09-22 16:58:10 +02:00
{
2008-06-22 09:16:40 +02:00
$fields [ $name ] = array ( 'label' => $label , 'type' => 'date-time' );
}
elseif ( preg_match ( '/(cat_id|category|cat)/' , $name ))
{
$fields [ $name ] = array ( 'label' => $label , 'type' => 'select-cat' );
}
elseif ( preg_match ( '/(owner|creator|modifier|assigned|by|coordinator|responsible)/' , $name ))
{
$fields [ $name ] = array ( 'label' => $label , 'type' => 'select-account' );
2007-09-22 16:58:10 +02:00
}
2008-06-22 09:16:40 +02:00
elseif ( preg_match ( '/(jpeg|photo)/' , $name ))
2007-09-22 16:58:10 +02:00
{
2008-06-22 09:16:40 +02:00
unset ( $fields [ $name ]);
}
}
if ( $app )
{
2009-10-11 14:37:48 +02:00
$customfields = config :: get_customfields ( $app );
2008-06-11 06:05:10 +02:00
2008-06-22 09:16:40 +02:00
if ( is_array ( $customfields ))
{
foreach ( $customfields as $name => $data )
2007-09-22 16:58:10 +02:00
{
2008-06-22 09:16:40 +02:00
$fields [ '#' . $name ] = array (
'label' => $data [ 'label' ],
'type' => $data [ 'type' ],
);
2007-09-22 16:58:10 +02:00
}
}
}
2008-06-22 09:16:40 +02:00
//_debug_array($fields);
return $fields ;
2002-10-16 16:11:36 +02:00
}
2008-06-22 09:16:40 +02:00
}