forked from extern/egroupware
0a91bc5aa9
Filterable fields are attempted to be autodetected by using the exportable fields. Records can be filtered by fields with type select,select-cat,select-account,date,date-time (according to egw_record class) only at this time. Filters are saved in the definition and used with scheduled exports. They are also available to the user for modification in the export dialog.
715 lines
25 KiB
PHP
Executable File
715 lines
25 KiB
PHP
Executable File
<?php
|
|
/**
|
|
* eGroupWare
|
|
*
|
|
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
|
* @package importexport
|
|
* @link http://www.egroupware.org
|
|
* @author Cornelius Weiss <nelius@cwtech.de>
|
|
* @copyright Cornelius Weiss <nelius@cwtech.de>
|
|
* @version $Id$
|
|
*/
|
|
|
|
/**
|
|
* class importexport_helper_functions (only static methods)
|
|
* use importexport_helper_functions::method
|
|
*/
|
|
class importexport_helper_functions {
|
|
|
|
/**
|
|
* Plugins are scanned and cached for all instances using this source path for given time (in seconds)
|
|
*/
|
|
const CACHE_EXPIRATION = 3600;
|
|
|
|
/**
|
|
* Make no changes (automatic category creation)
|
|
*/
|
|
public static $dry_run = false;
|
|
|
|
/**
|
|
* Relative date ranges for filtering
|
|
*/
|
|
public static $relative_dates = array( // Start: year,month,day,week, End: year,month,day,week
|
|
'Today' => array(0,0,0,0, 0,0,1,0),
|
|
'Yesterday' => array(0,0,-1,0, 0,0,0,0),
|
|
'This week' => array(0,0,0,0, 0,0,0,1),
|
|
'Last week' => array(0,0,0,-1, 0,0,0,0),
|
|
'This month' => array(0,0,0,0, 0,1,0,0),
|
|
'Last month' => array(0,-1,0,0, 0,0,0,0),
|
|
'Last 3 months' => array(0,-3,0,0, 0,1,0,0),
|
|
'This quarter'=> array(0,0,0,0, 0,0,0,0), // Just a marker, needs special handling
|
|
'Last quarter'=> array(0,-4,0,0, 0,-4,0,0), // Just a marker
|
|
'This year' => array(0,0,0,0, 1,0,0,0),
|
|
'Last year' => array(-1,0,0,0, 0,0,0,0),
|
|
'2 years ago' => array(-2,0,0,0, -1,0,0,0),
|
|
'3 years ago' => array(-3,0,0,0, -2,0,0,0),
|
|
);
|
|
|
|
/**
|
|
* Files known to cause problems, and will be skipped in a plugin scan
|
|
* If you put appname => true, the whole app will be skipped.
|
|
*/
|
|
protected static $blacklist_files = array(
|
|
'news_admin' => array(
|
|
'class.news_admin_import.inc.php',
|
|
),
|
|
);
|
|
|
|
/**
|
|
* Class used to provide extra conversion functions
|
|
*
|
|
* Passed in as a param to conversion()
|
|
*/
|
|
protected static $cclass = null;
|
|
|
|
/**
|
|
* nothing to construct here, only static functions!
|
|
*/
|
|
|
|
/**
|
|
* Converts a custom time string to to unix timestamp
|
|
* The format of the time string is given by the argument $_format
|
|
* which takes the same parameters as the php date() function.
|
|
*
|
|
* @abstract supportet formatstrings: d,m,y,Y,H,h,i,s,O,a,A
|
|
* If timestring is empty, php strtotime is used.
|
|
* @param string $_string time string to convert
|
|
* @param string $_format format of time string e.g.: d.m.Y H:i
|
|
* @param int $_is_dst is day light saving time? 0 = no, 1 = yes, -1 = system default
|
|
*/
|
|
public static function custom_strtotime( $_string, $_format='', $_is_dst = -1) {
|
|
if ( empty( $_format ) ) return strtotime( $_string );
|
|
$fparams = explode( ',', chunk_split( $_format, 1, ',' ) );
|
|
$spos = 0;
|
|
foreach ( $fparams as $fparam ) {
|
|
|
|
switch ( $fparam ) {
|
|
case 'd': (int)$day = substr( $_string, $spos, 2 ); $spos += 2; break;
|
|
case 'm': (int)$mon = substr( $_string, $spos, 2 ); $spos += 2; break;
|
|
case 'y': (int)$year = substr( $_string, $spos, 2 ); $spos += 2; break;
|
|
case 'Y': (int)$year = substr( $_string, $spos, 4 ); $spos += 4; break;
|
|
case 'H': (int)$hour = substr( $_string, $spos, 2 ); $spos += 2; break;
|
|
case 'h': (int)$hour = substr( $_string, $spos, 2 ); $spos += 2; break;
|
|
case 'i': (int)$min = substr( $_string, $spos, 2 ); $spos += 2; break;
|
|
case 's': (int)$sec = substr( $_string, $spos, 2 ); $spos += 2; break;
|
|
case 'O': (int)$offset = $year = substr( $_string, $spos, 5 ); $spos += 5; break;
|
|
case 'a': (int)$hour = $fparam == 'am' ? $hour : $hour + 12; break;
|
|
case 'A': (int)$hour = $fparam == 'AM' ? $hour : $hour + 12; break;
|
|
default: $spos++; // seperator
|
|
}
|
|
}
|
|
|
|
print_debug("hour:$hour; min:$min; sec:$sec; mon:$mon; day:$day; year:$year;\n");
|
|
$timestamp = mktime($hour, $min, $sec, $mon, $day, $year, $_is_dst);
|
|
|
|
// offset given?
|
|
if ( isset( $offset ) && strlen( $offset == 5 ) ) {
|
|
$operator = $offset{0};
|
|
$ohour = 60 * 60 * (int)substr( $offset, 1, 2 );
|
|
$omin = 60 * (int)substr( $offset, 3, 2 );
|
|
if ( $operator == '+' ) $timestamp += $ohour + $omin;
|
|
else $timestamp -= $ohour + $omin;
|
|
}
|
|
return $timestamp;
|
|
}
|
|
/**
|
|
* converts accound_lid to account_id
|
|
*
|
|
* @param mixed $_account_lid comma seperated list or array with lids
|
|
* @return mixed comma seperated list or array with ids
|
|
*/
|
|
public static function account_name2id( &$_account_lids ) {
|
|
$account_lids = is_array( $_account_lids ) ? $_account_lids : explode( ',', $_account_lids );
|
|
$skip = false;
|
|
foreach ( $account_lids as $key => $account_lid ) {
|
|
if($skip) {
|
|
unset($account_lids[$key]);
|
|
$skip = false;
|
|
continue;
|
|
}
|
|
$account_lid = trim($account_lid);
|
|
|
|
// Handle any IDs that slip in
|
|
if(is_numeric($account_lid) && $GLOBALS['egw']->accounts->id2name($account_lid)) {
|
|
unset($account_lids[$key]);
|
|
$account_ids[] = (int)$account_lid;
|
|
continue;
|
|
}
|
|
// Handle users listed as Lastname, Firstname instead of login ID
|
|
// Do this first, in case their first name matches a username
|
|
if ( $account_lids[$key+1][0] == ' ' && $account_id = $GLOBALS['egw']->accounts->name2id( trim($account_lids[$key+1]).' ' .$account_lid, 'account_fullname')) {
|
|
$account_ids[] = $account_id;
|
|
unset($account_lids[$key]);
|
|
$skip = true; // Skip the next one, it's the first name
|
|
continue ;
|
|
}
|
|
if ( $account_id = $GLOBALS['egw']->accounts->name2id( $account_lid )) {
|
|
$account_ids[] = $account_id;
|
|
unset($account_lids[$key]);
|
|
continue;
|
|
}
|
|
if ( $account_id = $GLOBALS['egw']->accounts->name2id( $account_lid, 'account_fullname' )) {
|
|
$account_ids[] = $account_id;
|
|
unset($account_lids[$key]);
|
|
continue;
|
|
}
|
|
|
|
// Handle groups listed as Group, <name>
|
|
if ( $account_lid[0] == ' ' && $account_id = $GLOBALS['egw']->accounts->name2id( trim($account_lid))) {
|
|
$account_ids[] = $account_id;
|
|
unset($account_lids[$key-1]);
|
|
unset($account_lids[$key]);
|
|
}
|
|
}
|
|
$_account_lids = (is_array($_account_lids) ? $account_lids : implode(',',$account_lids));
|
|
return is_array( $_account_lids ) ? $account_ids : implode( ',', (array)$account_ids );
|
|
|
|
} // end of member function account_lid2id
|
|
|
|
/**
|
|
* converts account_ids to account_lids
|
|
*
|
|
* @param mixed $_account_ids comma seperated list or array with ids
|
|
* @return mixed comma seperated list or array with lids
|
|
*/
|
|
public static function account_id2name( $_account_id ) {
|
|
$account_ids = is_array( $_account_id ) ? $_account_id : explode( ',', $_account_id );
|
|
foreach ( $account_ids as $account_id ) {
|
|
if ( $account_lid = $GLOBALS['egw']->accounts->id2name( $account_id )) {
|
|
$account_lids[] = $account_lid;
|
|
}
|
|
}
|
|
return is_array( $_account_id ) ? $account_lids : implode( ',', (array)$account_lids );
|
|
} // end of member function account_id2lid
|
|
|
|
/**
|
|
* converts cat_id to a cat_name
|
|
*
|
|
* @param mixed _cat_ids comma seperated list or array
|
|
* @return mixed comma seperated list or array with cat_names
|
|
*/
|
|
public static function cat_id2name( $_cat_ids ) {
|
|
$cat_ids = is_array( $_cat_ids ) ? $_cat_ids : explode( ',', $_cat_ids );
|
|
foreach ( $cat_ids as $cat_id ) {
|
|
$cat_names[] = categories::id2name( (int)$cat_id );
|
|
}
|
|
return is_array( $_cat_ids ) ? $cat_names : implode(',',(array)$cat_names);
|
|
} // end of member function category_id2name
|
|
|
|
/**
|
|
* converts cat_name to a cat_id.
|
|
* If a cat isn't found, it will be created.
|
|
*
|
|
* @param mixed $_cat_names comma seperated list or array.
|
|
* @param int $parent Optional parent ID to use for new categories
|
|
* @return mixed comma seperated list or array with cat_ids
|
|
*/
|
|
public static function cat_name2id( $_cat_names, $parent = 0 ) {
|
|
$cats = new categories(); // uses current user and app (egw_info[flags][currentapp])
|
|
|
|
$cat_names = is_array( $_cat_names ) ? $_cat_names : explode( ',', $_cat_names );
|
|
foreach ( $cat_names as $cat_name ) {
|
|
$cat_name = trim($cat_name);
|
|
if ( $cat_name == '' ) continue;
|
|
if ( ( $cat_id = $cats->name2id( $cat_name ) ) == 0 && !self::$dry_run) {
|
|
$cat_id = $cats->add( array(
|
|
'name' => $cat_name,
|
|
'parent' => $parent,
|
|
'access' => 'public',
|
|
'descr' => $cat_name. ' ('. lang('Automatically created by importexport'). ')'
|
|
));
|
|
}
|
|
$cat_ids[] = $cat_id;
|
|
}
|
|
return is_array( $_cat_names ) ? $cat_ids : implode( ',', (array)$cat_ids );
|
|
|
|
} // end of member function category_name2id
|
|
|
|
/**
|
|
* conversion
|
|
*
|
|
* Conversions enable you to change / adapt the content of each _record field for your needs.
|
|
* General syntax is: pattern1 |> replacement1 || ... || patternN |> replacementN
|
|
* If the pattern-part of a pair is ommited it will match everything ('^.*$'), which
|
|
* is only usefull for the last pair, as they are worked from left to right.
|
|
* Example: 1|>private||public
|
|
* This will translate a '1' in the _record field to 'privat' and everything else to 'public'.
|
|
*
|
|
* In addintion to the fields assign by the pattern of the reg.exp.
|
|
* you can use all other _record fields, with the syntax |[FIELDINDEX].
|
|
* Example:
|
|
* Your record is:
|
|
* array( 0 => Company, 1 => NFamily, 2 => NGiven
|
|
* Your conversion string for field 0 (Company):
|
|
* .+|>|[0]: |[1], |[2]|||[1], |[2]
|
|
* This constructs something like
|
|
* Company: FamilyName, GivenName or FamilyName, GivenName if 'Company' is empty.
|
|
*
|
|
* Moreover the following helper functions can be used:
|
|
* cat(Cat1,...,CatN) returns a (','-separated) list with the cat_id's. If a
|
|
* category isn't found, it will be automaticaly added.
|
|
*
|
|
* account(name) returns an account ID, if found in the system
|
|
* list(sep, data, index) lets you explode a field on sep, then select just one part (index)
|
|
*
|
|
* Patterns as well as the replacement can be regular expressions (the replacement is done
|
|
* via str_replace).
|
|
*
|
|
* @param array _record reference with record to do the conversion with
|
|
* @param array _conversion array with conversion description
|
|
* @param object &$cclass calling class to process the '@ evals'
|
|
* @return bool
|
|
*/
|
|
public static function conversion( &$_record, $_conversion, &$_cclass = null ) {
|
|
if (empty( $_conversion ) ) return $_record;
|
|
|
|
self::$cclass =& $_cclass;
|
|
|
|
$PSep = '||'; // Pattern-Separator, separats the pattern-replacement-pairs in conversion
|
|
$ASep = '|>'; // Assignment-Separator, separats pattern and replacesment
|
|
$CPre = '|['; $CPos = ']'; // |[_record-idx] is expanded to the corespondig value
|
|
$TPre = '|T{'; $TPos = '}'; // |{_record-idx} is trimmed
|
|
$CntlPre = '|TC{'; // Filter all cntl-chars \x01-\x1f and trim
|
|
$CntlnCLPre = '|TCnCL{'; // Like |C{ but allowes CR and LF
|
|
$INE = '|INE{'; // Only insert if stuff in ^^ is not empty
|
|
|
|
foreach ( $_conversion as $idx => $conversion_string ) {
|
|
if ( empty( $conversion_string ) ) continue;
|
|
|
|
// fetch patterns ($rvalues)
|
|
$pat_reps = explode( $PSep, stripslashes( $conversion_string ) );
|
|
foreach( $pat_reps as $k => $pat_rep ) {
|
|
list( $pattern, $replace ) = explode( $ASep, $pat_rep, 2 );
|
|
if( $replace == '' ) {
|
|
$replace = $pattern; $pattern = '^.*$';
|
|
}
|
|
$rvalues[$pattern] = $replace; // replace two with only one, added by the form
|
|
}
|
|
|
|
// conversion list may be longer than $_record aka (no_csv)
|
|
$val = array_key_exists( $idx, $_record ) ? $_record[$idx] : '';
|
|
|
|
$c_functions = array('cat', 'account', 'strtotime', 'list');
|
|
if($_cclass) {
|
|
// Add in additional methods
|
|
$reflection = new ReflectionClass(get_class($_cclass));
|
|
$methods = $reflection->getMethods(ReflectionMethod::IS_STATIC);
|
|
foreach($methods as $method) {
|
|
$c_functions[] = $method->name;
|
|
}
|
|
}
|
|
$c_functions = implode('|', $c_functions);
|
|
foreach ( $rvalues as $pattern => $replace ) {
|
|
if( preg_match('/'. (string)$pattern.'/', $val) ) {
|
|
|
|
$val = preg_replace( '/'.(string)$pattern.'/', $replace, (string)$val );
|
|
|
|
$reg = '/\|\[([a-zA-Z_0-9]+)\]/';
|
|
while( preg_match( $reg, $val, $vars ) ) {
|
|
// expand all _record fields
|
|
$val = str_replace(
|
|
$CPre . $vars[1] . $CPos,
|
|
$_record[array_search($vars[1], array_keys($_record))],
|
|
$val
|
|
);
|
|
}
|
|
$val = preg_replace_callback( "/($c_functions)\(([^)]*)\)/i", array( self, 'c2_dispatcher') , $val );
|
|
break;
|
|
}
|
|
}
|
|
// clean each field
|
|
$val = preg_replace_callback("/(\|T\{|\|TC\{|\|TCnCL\{|\|INE\{)(.*)\}/", array( self, 'strclean'), $val );
|
|
|
|
$_record[$idx] = $val;
|
|
}
|
|
return $_record;
|
|
} // end of member function conversion
|
|
|
|
/**
|
|
* callback for preg_replace_callback from self::conversion.
|
|
* This function gets called when 2nd level conversions are made,
|
|
* like the cat() and account() statements in the conversions.
|
|
*
|
|
* @param array $_matches
|
|
*/
|
|
private static function c2_dispatcher( $_matches ) {
|
|
$action = &$_matches[1]; // cat or account ...
|
|
$data = &$_matches[2]; // datas for action
|
|
|
|
switch ( $action ) {
|
|
case 'strtotime' :
|
|
list( $string, $format ) = explode( ',', $data );
|
|
return self::custom_strtotime( trim( $string ), trim( $format ) );
|
|
case 'list':
|
|
list( $split, $data, $index) = explode(',',$data);
|
|
$exploded = explode($split, $data);
|
|
// 1 based indexing for user ease
|
|
return $exploded[$index - 1];
|
|
default :
|
|
if(self::$cclass && method_exists(self::$cclass, $action)) {
|
|
$class = get_class(self::$cclass);
|
|
return call_user_func("$class::$action", $data);
|
|
}
|
|
$method = (string)$action. ( is_int( $data ) ? '_id2name' : '_name2id' );
|
|
if(self::$cclass && method_exists(self::$cclass, $method)) {
|
|
$class = get_class(self::$cclass);
|
|
return call_user_func("$class::$action", $data);
|
|
} else {
|
|
return self::$method( $data );
|
|
}
|
|
}
|
|
}
|
|
|
|
private static function strclean( $_matches ) {
|
|
switch( $_matches[1] ) {
|
|
case '|T{' : return trim( $_matches[2] );
|
|
case '|TC{' : return trim( preg_replace( '/[\x01-\x1F]+/', '', $_matches[2] ) );
|
|
case '|TCnCL{' : return trim( preg_replace( '/[\x01-\x09\x11\x12\x14-\x1F]+/', '', $_matches[2] ) );
|
|
case '|INE{' : return preg_match( '/\^.+\^/', $_matches[2] ) ? $_matches[2] : '';
|
|
default:
|
|
throw new Exception('Error in conversion string! "'. substr( $_matches[1], 0, -1 ). '" is not valid!');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* returns a list of importexport plugins
|
|
*
|
|
* @param string $_tpye {import | export | all}
|
|
* @param string $_appname {<appname> | all}
|
|
* @return array(<appname> => array( <type> => array(<plugin> => <title>)))
|
|
*/
|
|
public static function get_plugins( $_appname = 'all', $_type = 'all' ) {
|
|
$plugins = egw_cache::getTree(
|
|
__CLASS__,
|
|
'plugins',
|
|
array('importexport_helper_functions','_get_plugins'),
|
|
array(array_keys($GLOBALS['egw_info']['apps']), array('import', 'export')),
|
|
self::CACHE_EXPIRATION
|
|
);
|
|
$appnames = $_appname == 'all' ? array_keys($GLOBALS['egw_info']['apps']) : (array)$_appname;
|
|
$types = $_type == 'all' ? array('import','export') : (array)$_type;
|
|
|
|
// Testing: comment out egw_cache call, use this
|
|
//$plugins = self::_get_plugins($appnames, $types);
|
|
foreach($plugins as $appname => $_types) {
|
|
if(!in_array($appname, $appnames)) unset($plugins[$appname]);
|
|
}
|
|
foreach($plugins as $appname => $types) {
|
|
$plugins[$appname] = array_intersect_key($plugins[$appname], $types);
|
|
}
|
|
return $plugins;
|
|
}
|
|
|
|
public static function _get_plugins(Array $appnames, Array $types) {
|
|
$plugins = array();
|
|
foreach ($appnames as $appname) {
|
|
if(array_key_exists($appname, self::$blacklist_files) && self::$blacklist_files[$appname] === true) continue;
|
|
|
|
$appdir = EGW_INCLUDE_ROOT. "/$appname/inc";
|
|
if(!is_dir($appdir)) continue;
|
|
$d = dir($appdir);
|
|
|
|
// step through each file in appdir
|
|
while (false !== ($entry = $d->read())) {
|
|
// Blacklisted?
|
|
if(is_array(self::$blacklist_files[$appname]) && in_array($entry, self::$blacklist_files[$appname])) continue;
|
|
if (!preg_match('/^class\.([^.]+)\.inc\.php$/', $entry, $matches)) continue;
|
|
$classname = $matches[1];
|
|
$file = $appdir. '/'. $entry;
|
|
|
|
foreach ($types as $type) {
|
|
if( !is_file($file) || strpos($entry, $type) === false || strpos($entry,'wizard') !== false) continue;
|
|
require_once($file);
|
|
$reflectionClass = new ReflectionClass($classname);
|
|
if($reflectionClass->IsInstantiable() &&
|
|
$reflectionClass->implementsInterface('importexport_iface_'.$type.'_plugin')) {
|
|
try {
|
|
$plugin_object = new $classname;
|
|
}
|
|
catch (Exception $exception) {
|
|
continue;
|
|
}
|
|
$plugins[$appname][$type][$classname] = $plugin_object->get_name();
|
|
unset ($plugin_object);
|
|
}
|
|
}
|
|
}
|
|
$d->close();
|
|
|
|
$config = config::read('importexport');
|
|
if($config['update'] == 'auto') {
|
|
self::load_defaults($appname);
|
|
}
|
|
}
|
|
//error_log(__CLASS__.__FUNCTION__.print_r($plugins,true));
|
|
return $plugins;
|
|
}
|
|
|
|
/**
|
|
* returns list of apps which have plugins of given type.
|
|
*
|
|
* @param string $_type
|
|
* @return array $num => $appname
|
|
*/
|
|
public static function get_apps($_type, $ignore_acl = false) {
|
|
$apps = array_keys(self::get_plugins('all',$_type));
|
|
if($ignore_acl) return $apps;
|
|
|
|
foreach($apps as $key => $app) {
|
|
if(!self::has_definitions($app, $_type)) unset($apps[$key]);
|
|
}
|
|
return $apps;
|
|
}
|
|
|
|
public static function load_defaults($appname) {
|
|
// Check for new definitions to import from $appname/setup/*.xml
|
|
$appdir = EGW_INCLUDE_ROOT. "/$appname/setup";
|
|
if(!is_dir($appdir)) continue;
|
|
$d = dir($appdir);
|
|
|
|
// step through each file in app's setup
|
|
while (false !== ($entry = $d->read())) {
|
|
$file = $appdir. '/'. $entry;
|
|
list( $filename, $extension) = explode('.',$entry);
|
|
if ( $extension != 'xml' ) continue;
|
|
try {
|
|
// import will skip invalid files
|
|
importexport_definitions_bo::import( $file );
|
|
} catch (Exception $e) {
|
|
error_log(__CLASS__.__FUNCTION__. " import $appname definitions: " . $e->getMessage());
|
|
}
|
|
}
|
|
$d->close();
|
|
}
|
|
|
|
public static function guess_filetype( $_file ) {
|
|
|
|
}
|
|
|
|
/**
|
|
* returns if the given app has importexport definitions for the current user
|
|
*
|
|
* @param string $_appname {<appname> | all}
|
|
* @param string $_type {import | export | all}
|
|
* @return boolean
|
|
*/
|
|
public static function has_definitions( $_appname = 'all', $_type = 'all' ) {
|
|
$definitions = egw_cache::getSession(
|
|
__CLASS__,
|
|
'has_definitions',
|
|
array('importexport_helper_functions','_has_definitions'),
|
|
array(array_keys($GLOBALS['egw_info']['apps']), array('import', 'export')),
|
|
self::CACHE_EXPIRATION
|
|
);
|
|
$appnames = $_appname == 'all' ? array_keys($GLOBALS['egw_info']['apps']) : (array)$_appname;
|
|
$types = $_type == 'all' ? array('import','export') : (array)$_type;
|
|
|
|
// Testing: Comment out cache call above, use this
|
|
//$definitions = self::_has_definitions($appnames, $types);
|
|
|
|
foreach($definitions as $appname => $_types) {
|
|
if(!in_array($appname, $appnames)) unset($definitions[$appname]);
|
|
}
|
|
foreach($definitions as $appname => $_types) {
|
|
$definitions[$appname] = array_intersect_key($definitions[$appname], array_flip($types));
|
|
}
|
|
return count($definitions[$appname]) > 0;
|
|
}
|
|
|
|
// egw_cache needs this public
|
|
public static function _has_definitions(Array $appnames, Array $types) {
|
|
$def = new importexport_definitions_bo(array('application'=>$appnames, 'type' => $types));
|
|
$list = array();
|
|
foreach((array)$def->get_definitions() as $id) {
|
|
// Need to instanciate it to check, but if the user doesn't have permission, it throws an exception
|
|
try {
|
|
$definition = new importexport_definition($id);
|
|
if($def->is_permitted($definition->get_record_array())) {
|
|
$list[$definition->application][$definition->type][] = $id;
|
|
}
|
|
} catch (Exception $e) {
|
|
// That one doesn't work, keep going
|
|
}
|
|
$definition = null;
|
|
}
|
|
return $list;
|
|
}
|
|
|
|
/**
|
|
* Get a list of filterable fields, and options for those fields
|
|
*
|
|
* It tries to automatically pull filterable fields from the list of fields in the wizard,
|
|
* and sets widget properties. The plugin can edit / set the fields by implementing
|
|
* get_filter_fields(Array &$fields).
|
|
*
|
|
* Currently only supports select,select-cat,select-account,date,date-time
|
|
*
|
|
* @param $app_name String name of app
|
|
* @param $plugin_name Name of the plugin
|
|
*
|
|
* @return Array ([fieldname] => array(widget settings), ...)
|
|
*/
|
|
public static function get_filter_fields($app_name, $plugin_name, $wizard_plugin = null, $record_classname = null)
|
|
{
|
|
$fields = array();
|
|
try {
|
|
if($record_classname == null) $record_classname = $app_name . '_egw_record';
|
|
if(!class_exists($record_classname)) throw new Exception('Bad class name ' . $record_classname);
|
|
|
|
$plugin = is_object($plugin_name) ? $plugin_name : new $plugin_name();
|
|
$plugin_name = get_class($plugin);
|
|
|
|
if(!$wizard_plugin)
|
|
{
|
|
$wizard_name = $app_name . '_wizard_' . str_replace($app_name . '_', '', $plugin_name);
|
|
if(!class_exists($wizard_name)) throw new Exception('Bad wizard name ' . $wizard_name);
|
|
$wizard_plugin = new $wizard_name;
|
|
}
|
|
}
|
|
catch (Exception $e)
|
|
{
|
|
error_log($e->getMessage());
|
|
return array();
|
|
}
|
|
|
|
// Get field -> label map and initialize fields using wizard field order
|
|
$fields = $export_fields = $wizard_plugin->get_export_fields();
|
|
|
|
foreach($record_classname::$types as $type => $type_fields)
|
|
{
|
|
// Only these for now, until filter methods for others are figured out
|
|
if(!in_array($type, array('select','select-cat','select-account','date','date-time'))) continue;
|
|
foreach($type_fields as $field_name)
|
|
{
|
|
$fields[$field_name] = array(
|
|
'name' => $field_name,
|
|
'label' => $export_fields[$field_name] ? $export_fields[$field_name] : $field_name,
|
|
'type' => $type
|
|
);
|
|
}
|
|
}
|
|
// Add custom fields
|
|
$custom = config::get_customfields($app_name);
|
|
foreach($custom as $field_name => $settings)
|
|
{
|
|
$settings['name'] = '#'.$field_name;
|
|
$fields['#'.$field_name] = $settings;
|
|
}
|
|
|
|
foreach($fields as $field_name => &$settings) {
|
|
// Can't really filter on these (or at least no generic, sane way figured out yet)
|
|
if(!is_array($settings) || in_array($settings['type'], array('text','button', 'label','url','url-email','url-phone','htmlarea')))
|
|
{
|
|
unset($fields[$field_name]);
|
|
continue;
|
|
}
|
|
if($settings['type'] == 'radio') $settings['type'] = 'select';
|
|
switch($settings['type'])
|
|
{
|
|
case 'checkbox':
|
|
// This isn't quite right - there's only 2 options and you can select both
|
|
$settings['type'] = 'select-bool';
|
|
$settings['rows'] = 1;
|
|
$settings['enhance'] = true;
|
|
break;
|
|
case 'select-cat':
|
|
$settings['rows'] = "5,,,$app_name";
|
|
$settings['enhance'] = true;
|
|
break;
|
|
case 'select-account':
|
|
$settings['rows'] = '5,both';
|
|
$settings['enhance'] = true;
|
|
break;
|
|
case 'select':
|
|
$settings['rows'] = 5;
|
|
$settings['enhance'] = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(method_exists($plugin, 'get_filter_fields'))
|
|
{
|
|
$plugin->get_filter_fields($fields);
|
|
}
|
|
return $fields;
|
|
}
|
|
|
|
/**
|
|
* Parse a relative date (Yesterday) into absolute (2012-12-31) date
|
|
*
|
|
* @param $value String description of date matching $relative_dates
|
|
*
|
|
* @return Array([from] => timestamp, [to]=> timestamp), inclusive
|
|
*/
|
|
public static function date_rel2abs($value)
|
|
{
|
|
if(is_array($value))
|
|
{
|
|
$abs = array();
|
|
foreach($value as $key => $val)
|
|
{
|
|
$abs[$key] = self::date_rel2abs($val);
|
|
}
|
|
return $abs;
|
|
}
|
|
if($date = self::$relative_dates[$value])
|
|
{
|
|
$year = (int) date('Y');
|
|
$month = (int) date('m');
|
|
$day = (int) date('d');
|
|
$today = mktime(0,0,0,date('m'),date('d'),date('Y'));
|
|
|
|
list($syear,$smonth,$sday,$sweek,$eyear,$emonth,$eday,$eweek) = $date;
|
|
|
|
if(stripos($value, 'quarter') !== false)
|
|
{
|
|
// Handle quarters
|
|
$start = mktime(0,0,0,((int)floor(($smonth+$month) / 3.1)) * 3 + 1, 1, $year);
|
|
$end = mktime(0,0,0,((int)floor(($emonth+$month) / 3.1)+1) * 3 + 1, 1, $year);
|
|
}
|
|
elseif ($syear || $eyear)
|
|
{
|
|
$start = mktime(0,0,0,1,1,$syear+$year);
|
|
$end = mktime(0,0,0,1,1,$eyear+$year);
|
|
}
|
|
elseif ($smonth || $emonth)
|
|
{
|
|
$start = mktime(0,0,0,$smonth+$month,1,$year);
|
|
$end = mktime(0,0,0,$emonth+$month,1,$year);
|
|
}
|
|
elseif ($sday || $eday)
|
|
{
|
|
$start = mktime(0,0,0,$month,$sday+$day,$year);
|
|
$end = mktime(0,0,0,$month,$eday+$day,$year);
|
|
}
|
|
elseif ($sweek || $eweek)
|
|
{
|
|
$wday = (int) date('w'); // 0=sun, ..., 6=sat
|
|
switch($GLOBALS['egw_info']['user']['preferences']['calendar']['weekdaystarts'])
|
|
{
|
|
case 'Sunday':
|
|
$weekstart = $today - $wday * 24*60*60;
|
|
break;
|
|
case 'Saturday':
|
|
$weekstart = $today - (6-$wday) * 24*60*60;
|
|
break;
|
|
case 'Moday':
|
|
default:
|
|
$weekstart = $today - ($wday ? $wday-1 : 6) * 24*60*60;
|
|
break;
|
|
}
|
|
$start = $weekstart + $sweek*7*24*60*60;
|
|
$end = $weekstart + $eweek*7*24*60*60;
|
|
}
|
|
$end_param = $end - 24*60*60;
|
|
|
|
// Take 1 second off end to provide an inclusive range.for filtering
|
|
$end -= 1;
|
|
|
|
//echo __METHOD__."($value,$start,$end) today=".date('l, Y-m-d H:i',$today)." ==> <br />".date('l, Y-m-d H:i:s',$start)." <= date <= ".date('l, Y-m-d H:i:s',$end)."</p>\n";
|
|
return array('from' => $start, 'to' => $end);
|
|
}
|
|
return null;
|
|
}
|
|
} // end of importexport_helper_functions
|