Merge branch 'master' into web-components

This commit is contained in:
nathan 2021-11-18 10:28:30 -07:00
commit b91e1d8a5f
45 changed files with 235 additions and 137 deletions

View File

@ -103,7 +103,7 @@ class addressbook_import_contacts_csv extends importexport_basic_import_csv {
if(!in_array($contact_owner, array_keys($this->bocontacts->get_addressbooks(Api\Acl::ADD)))) if(!in_array($contact_owner, array_keys($this->bocontacts->get_addressbooks(Api\Acl::ADD))))
{ {
$this->warnings[0] = lang("Unable to import into %1, using %2", $this->warnings[0] = lang("Unable to import into %1, using %2",
$contact_owner . ' ('.Api\Accounts::username($contact_owner) . ')', $contact_owner . ' (' . (is_numeric($contact_owner) ? Api\Accounts::username($contact_owner) : $contact_owner) . ')',
Api\Accounts::username($this->user) Api\Accounts::username($this->user)
); );
$contact_owner = 'personal'; $contact_owner = 'personal';

View File

@ -108,13 +108,13 @@ class addressbook_wizard_import_contacts_csv extends importexport_wizard_basic_i
{ {
$content['msg'] = $this->steps['wizard_step60']; $content['msg'] = $this->steps['wizard_step60'];
$content['step'] = 'wizard_step60'; $content['step'] = 'wizard_step60';
if(!array_key_exists($content['contact_owner']) && $content['plugin_options']) { if(!array_key_exists('contact_owner', $content) && $content['plugin_options']) {
$content['contact_owner'] = $content['plugin_options']['contact_owner']; $content['contact_owner'] = $content['plugin_options']['contact_owner'];
} }
if(!array_key_exists($content['owner_from_csv']) && $content['plugin_options']) { if(!array_key_exists('owner_from_csv', $content) && $content['plugin_options']) {
$content['owner_from_csv'] = $content['plugin_options']['owner_from_csv']; $content['owner_from_csv'] = $content['plugin_options']['owner_from_csv'];
} }
if(!array_key_exists($content['change_owner']) && $content['plugin_options']) { if(!array_key_exists('change_owner', $content) && $content['plugin_options']) {
$content['change_owner'] = $content['plugin_options']['change_owner']; $content['change_owner'] = $content['plugin_options']['change_owner'];
} }

View File

@ -392,7 +392,7 @@ class admin_acl
} }
if (!(int)$rights) if (!(int)$rights)
{ {
if (count($ids) > 1) if (count((array)$ids) > 1)
{ {
$msg = lang('%1 ACL entries deleted.', count($ids)); $msg = lang('%1 ACL entries deleted.', count($ids));
} }

View File

@ -1257,7 +1257,7 @@ abstract class admin_cmd
*/ */
static function display_account($account) static function display_account($account)
{ {
$id = is_numeric($account) ? $account : $GLOBALS['egw']->accounts->id2name($account); $id = is_numeric($account) ? $account : Api\Accounts::getInstance()->name2id($account);
return $account.' ('.Api\Accounts::username($id).')'; return $account.' ('.Api\Accounts::username($id).')';
} }

View File

@ -42,7 +42,7 @@
<menupopup type="select-account" id="${row}[owner]" readonly="true" options="All users,groups"/> <menupopup type="select-account" id="${row}[owner]" readonly="true" options="All users,groups"/>
</menulist> </menulist>
<checkbox align="center" id="${row}[access]" readonly="true" options="private,public"/> <checkbox align="center" id="${row}[access]" readonly="true" options="private,public"/>
<image align="center" label="{$row_cont[data][icon]}" src="${row}[icon_url]"/> <image align="center" label="{$row_cont['data']['icon']}" src="${row}[icon_url]"/>
<description id="${row}[data][color]"/> <description id="${row}[data][color]"/>
<date-time id="${row}[last_mod]" readonly="true"/> <date-time id="${row}[last_mod]" readonly="true"/>
<description align="center" id="${row}[subs]"/> <description align="center" id="${row}[subs]"/>

View File

@ -386,6 +386,7 @@ export class et2_tree extends et2_inputWidget
f(data, f); f(data, f);
options = data; options = data;
} }
this.input.deleteChildItems("0");
// if no options given, but autoloading url, use that to load initial nodes // if no options given, but autoloading url, use that to load initial nodes
if (typeof options.id == 'undefined' && this.input.XMLsource) if (typeof options.id == 'undefined' && this.input.XMLsource)
this.input.loadJSON(this.input.XMLsource); this.input.loadJSON(this.input.XMLsource);

View File

@ -1199,7 +1199,7 @@ class Sql extends Api\Storage
unset($this->data['etag']); unset($this->data['etag']);
if (!($err = parent::save(array('contact_etag=contact_etag+1'),array('contact_etag' => $etag)))) if (!($err = parent::save(array('contact_etag=contact_etag+1'),array('contact_etag' => $etag))))
{ {
$this->data['etag'] = $etag+1; $this->data['etag'] = (int)$etag+1;
} }
else else
{ {

View File

@ -123,6 +123,7 @@ class Widget
// read all attributes // read all attributes
$this->set_attrs($reader); $this->set_attrs($reader);
libxml_clear_errors();
while(($ok=$reader->read()) && $reader->depth > $depth) while(($ok=$reader->read()) && $reader->depth > $depth)
{ {
if ($reader->nodeType == XMLReader::ELEMENT && $reader->depth > $depth) if ($reader->nodeType == XMLReader::ELEMENT && $reader->depth > $depth)

View File

@ -373,7 +373,7 @@ class Link extends Etemplate\Widget
$already = self::get_array($validated,$form_name); $already = self::get_array($validated,$form_name);
if($already != null) if($already != null)
{ {
$value = array_merge($value,$already); $value = array_merge((array)$value, $already);
} }
// Automatically do link if user selected entry but didn't click 'Link' button // Automatically do link if user selected entry but didn't click 'Link' button
$link = self::get_array($content, self::form_name($cname, $this->id . '_link_entry')); $link = self::get_array($content, self::form_name($cname, $this->id . '_link_entry'));

View File

@ -176,7 +176,7 @@ class Vfs extends File
self::get_vfs_path($data['to_app'].':'.$data['to_id'])).'/', $file); self::get_vfs_path($data['to_app'].':'.$data['to_id'])).'/', $file);
// store temp. vfs-path like links to be able to move it to correct location after entry is stored // store temp. vfs-path like links to be able to move it to correct location after entry is stored
if (!$data['to_id'] || is_array($data['to_id'])) if (is_array($data) && (empty($data['to_id']) || is_array($data['to_id'])))
{ {
Api\Link::link($data['to_app'], $data['to_id'], Api\Link::VFS_APPNAME, array( Api\Link::link($data['to_app'], $data['to_id'], Api\Link::VFS_APPNAME, array(
'name' => $file['name'], 'name' => $file['name'],

View File

@ -1271,7 +1271,7 @@ class Link extends Link\Storage
if (file_exists($entry_dir) || ($Ok = mkdir($entry_dir,0,true))) if (file_exists($entry_dir) || ($Ok = mkdir($entry_dir,0,true)))
{ {
$Ok = Vfs::copy_uploaded($file, $p=Vfs::parse_url($entry_dir, PHP_URL_PATH), $comment, false); // no is_uploaded_file() check! $Ok = Vfs::copy_uploaded($file, $p=Vfs::parse_url($entry_dir, PHP_URL_PATH), $comment, false); // no is_uploaded_file() check!
if (!$Ok) error_log(__METHOD__."('$app', '$id', ".array2string($file).", '$comment') called Vfs::copy_uploaded('$file[tmp_name]', '$p', '$comment', false)=".array2string($Ok)); if (!$Ok) error_log(__METHOD__."('$app', '$id', ".array2string($file).", '$comment') called Vfs::copy_uploaded(".json_encode($file).", '$p', '$comment', false)=".array2string($Ok));
} }
else else
{ {

View File

@ -171,7 +171,7 @@ class Storage
// catch Illegal mix of collations (ascii_general_ci,IMPLICIT) and (utf8_general_ci,COERCIBLE) for operation '=' (1267) // catch Illegal mix of collations (ascii_general_ci,IMPLICIT) and (utf8_general_ci,COERCIBLE) for operation '=' (1267)
// caused by non-ascii chars compared with ascii field uid // caused by non-ascii chars compared with ascii field uid
catch(Api\Db\Exception $e) { catch(Api\Db\Exception $e) {
_egw_log_exception($e); //_egw_log_exception($e);
} }
return is_array($id) ? $links : ($links[$id] ?? []); return is_array($id) ? $links : ($links[$id] ?? []);
} }

View File

@ -5574,12 +5574,12 @@ class Mail
* @param string/int $_partID = '' , the partID, may be omitted * @param string/int $_partID = '' , the partID, may be omitted
* @param string $_folder folder to work on * @param string $_folder folder to work on
* @param boolean $_stream =false true: return a stream, false: return string, stream suppresses any caching * @param boolean $_stream =false true: return a stream, false: return string, stream suppresses any caching
* @return string the message body * @return ?string the message body or null if $_uid or $_partID not found
*/ */
function getMessageRawBody($_uid, $_partID = '', $_folder='', $_stream=false) function getMessageRawBody($_uid, $_partID = '', $_folder='', $_stream=false)
{ {
static $rawBody; static $rawBody;
$body = []; $body = null;
if (empty($_folder)) $_folder = $this->sessionData['mailbox']?: $this->icServer->getCurrentMailbox(); if (empty($_folder)) $_folder = $this->sessionData['mailbox']?: $this->icServer->getCurrentMailbox();
$_uid = !(is_object($_uid) || is_array($_uid)) ? (array)$_uid : $_uid; $_uid = !(is_object($_uid) || is_array($_uid)) ? (array)$_uid : $_uid;
@ -7405,6 +7405,12 @@ class Mail
if (self::$debug) error_log(__METHOD__.__LINE__.array2string($identity)); if (self::$debug) error_log(__METHOD__.__LINE__.array2string($identity));
$headers = $this->getMessageHeader($uid, '', 'object', true, $_folder); $headers = $this->getMessageHeader($uid, '', 'object', true, $_folder);
// check we have an email to send the mdn to (otherwise Horde_Mime_Mdn throws a RuntimeException)
if (empty($mdn_to = $headers[strtoupper('Disposition-Notification-To')]) || strpos($mdn_to, '@') === false)
{
return false;
}
// Override Horde's translation with our own // Override Horde's translation with our own
Horde_Translation::setHandler('Horde_Mime', new Horde_Translation_Handler_Gettext('Horde_Mime', EGW_SERVER_ROOT.'/api/lang/locale')); Horde_Translation::setHandler('Horde_Mime', new Horde_Translation_Handler_Gettext('Horde_Mime', EGW_SERVER_ROOT.'/api/lang/locale'));
Preferences::setlocale(); Preferences::setlocale();

View File

@ -687,7 +687,7 @@ class Imap extends Horde_Imap_Client_Socket implements Imap\PushIface
'MAILBOX' => $k, 'MAILBOX' => $k,
'ATTRIBUTES' => $box['attributes'], 'ATTRIBUTES' => $box['attributes'],
'delimiter' => $box['delimiter'] ? $box['delimiter'] : $this->getDelimiter('personal'), 'delimiter' => $box['delimiter'] ? $box['delimiter'] : $this->getDelimiter('personal'),
'SUBSCRIBED' => in_array(self::SUBSCRIBED_ATTRIBUTE, $box['attributes']), 'SUBSCRIBED' => in_array(self::SUBSCRIBED_ATTRIBUTE, $box['attributes'] ?? []),
]; ];
} }
} }

View File

@ -158,7 +158,7 @@ class Script
// <crnl>s will be encoded as \\n. undo this. // <crnl>s will be encoded as \\n. undo this.
$rule['action_arg'] = preg_replace("/\\\\n/","\r\n",$rule['action_arg']); $rule['action_arg'] = preg_replace("/\\\\n/","\r\n",$rule['action_arg']);
$rule['action_arg'] = stripslashes($rule['action_arg']); $rule['action_arg'] = stripslashes($rule['action_arg']);
$rule['flg'] = $bits[8]; // bitwise flag $rule['flg'] = $bits[8] = (int)$bits[8]; // bitwise flag
$rule['field'] = stripslashes($bits[9]); $rule['field'] = stripslashes($bits[9]);
$rule['field_val'] = stripslashes($bits[10]); $rule['field_val'] = stripslashes($bits[10]);
$rule['size'] = $bits[11]; $rule['size'] = $bits[11];

View File

@ -1752,7 +1752,7 @@ abstract class Merge
break; break;
default: default:
$app = str_replace('_merge', '', get_class($this)); $app = str_replace('_merge', '', get_class($this));
if(!in_array($app, array_keys($GLOBALS['egw_info']['apps']))) if(!in_array($app, array_keys($GLOBALS['egw_info']['apps'] ?? [])))
{ {
$app = false; $app = false;
} }

View File

@ -1482,7 +1482,7 @@ class Vfs extends Vfs\Base
$comment = self::find_prop($props,'comment'); $comment = self::find_prop($props,'comment');
if($comment) if($comment)
{ {
$zip->setCommentName($_name, $comment); $zip->setCommentName($_name, $comment['val']);
} }
} }
unset($props); unset($props);
@ -1909,6 +1909,10 @@ class Vfs extends Vfs\Base
*/ */
static public function copy_files(array $src, $dst, &$errs=null, array &$copied=null) static public function copy_files(array $src, $dst, &$errs=null, array &$copied=null)
{ {
if (!is_array($copied))
{
$copied = [];
}
if (self::is_dir($dst)) if (self::is_dir($dst))
{ {
foreach ($src as $file) foreach ($src as $file)

View File

@ -278,7 +278,7 @@ class Base
{ {
if(self::LOG_LEVEL > 1) if(self::LOG_LEVEL > 1)
{ {
error_log(__METHOD__ . "('$path') = '" . self::$resolve_url_cache[$path] . "' (from cache)"); error_log(__METHOD__ . "('$path') = '" . print_r(self::$resolve_url_cache[$path], true) . "' (from cache)");
} }
$mounted = self::$resolve_url_cache[$path]['mounted']; $mounted = self::$resolve_url_cache[$path]['mounted'];
return self::$resolve_url_cache[$path]['url']; return self::$resolve_url_cache[$path]['url'];
@ -704,6 +704,10 @@ class Base
else else
{ {
$time = null; $time = null;
if (in_array($name, ['readlink']))
{
return $name($url);
}
return $name($url, $time); return $name($url, $time);
} }
} }

View File

@ -645,7 +645,7 @@ class calendar_boupdate extends calendar_bo
$msg_is_response = $msg_type == MSG_REJECTED || $msg_type == MSG_ACCEPTED || $msg_type == MSG_TENTATIVE || $msg_type == MSG_DELEGATED; $msg_is_response = $msg_type == MSG_REJECTED || $msg_type == MSG_ACCEPTED || $msg_type == MSG_TENTATIVE || $msg_type == MSG_DELEGATED;
// Check if user is not participating, and does not want notifications // Check if user is not participating, and does not want notifications
if($msg_is_response && !$part_prefs['calendar']['receive_not_participating'] && !array_key_exists($userid, $old_event['participants'])) if ($msg_is_response && !$part_prefs['calendar']['receive_not_participating'] && !array_key_exists($userid, $old_event['participants'] ?? []))
{ {
return false; return false;
} }
@ -1591,7 +1591,7 @@ class calendar_boupdate extends calendar_bo
$save_event[$ts] = $save_event[$ts]->format('ts'); $save_event[$ts] = $save_event[$ts]->format('ts');
} }
} }
$tracking->track($save_event, $old_event); $tracking->track($save_event, $old_event ?: null);
return $cal_id; return $cal_id;
} }
@ -1832,7 +1832,7 @@ class calendar_boupdate extends calendar_bo
if (($event = $this->read($cal_id, $recur_date, $ignore_acl, 'server'))) if (($event = $this->read($cal_id, $recur_date, $ignore_acl, 'server')))
{ {
$tracking = new calendar_tracking($this); $tracking = new calendar_tracking($this);
$tracking->track($event, $old_event); $tracking->track($event, $old_event ?: null);
} }
// notify the link-class about the update, as other apps may be subscribed to it // notify the link-class about the update, as other apps may be subscribed to it
Link::notify_update('calendar', $cal_id, $event, "update"); Link::notify_update('calendar', $cal_id, $event, "update");
@ -2077,7 +2077,7 @@ class calendar_boupdate extends calendar_bo
'data' => $event['description'] 'data' => $event['description']
); );
foreach(explode(',',$event['category']) as $cat_id) foreach(is_array($event['category']) ? $event['category'] : explode(',', $event['category']) as $cat_id)
{ {
$cat_string[] = stripslashes(Api\Categories::id2name($cat_id)); $cat_string[] = stripslashes(Api\Categories::id2name($cat_id));
} }

View File

@ -1226,7 +1226,7 @@ class calendar_groupdav extends Api\CalDAV\Handler
$xml->startDocument('1.0', 'UTF-8'); $xml->startDocument('1.0', 'UTF-8');
$xml->startElementNs('C', 'schedule-response', Api\CalDAV::CALDAV); $xml->startElementNs('C', 'schedule-response', Api\CalDAV::CALDAV);
foreach(array_keys($event['participants']) as $uid) foreach(array_keys($event['participants'] ?? []) as $uid)
{ {
$xml->startElementNs('C', 'response', null); $xml->startElementNs('C', 'response', null);

View File

@ -240,7 +240,7 @@ class calendar_hooks
$freebusy_help .= ' ' . $freebusy_url; $freebusy_help .= ' ' . $freebusy_url;
// Timezone for file exports // Timezone for file exports
$export_tzs = array('0' => lang('Use Event TZ')); $export_tzs = array(['value' => '0', 'label' => lang('Use Event TZ')]);
$export_tzs += Api\DateTime::getTimezones(); $export_tzs += Api\DateTime::getTimezones();
} }
$link_title_options = calendar_bo::get_link_options(); $link_title_options = calendar_bo::get_link_options();

View File

@ -761,7 +761,7 @@ class calendar_merge extends Api\Storage\Merge
if((strpos($content, '$$range') !== false || strpos($content, '{{range') !== false) && is_array($ids)) if((strpos($content, '$$range') !== false || strpos($content, '{{range') !== false) && is_array($ids))
{ {
// Merging into a template that uses range - need ranges, got events // Merging into a template that uses range - need ranges, got events
if(is_array($ids) && (is_array($ids[0]) && $ids[0]['id'] || is_string($ids[0]))) if (is_array($ids) && (is_array($ids[0]) && isset($ids[0]['id']) || is_string($ids[0])))
{ {
// Passed an array of events, to be handled like a date range // Passed an array of events, to be handled like a date range
$events = $ids; $events = $ids;
@ -777,7 +777,7 @@ class calendar_merge extends Api\Storage\Merge
else if((strpos($content, '$$pagerepeat') !== false || strpos($content, '{{pagerepeat') !== false) else if((strpos($content, '$$pagerepeat') !== false || strpos($content, '{{pagerepeat') !== false)
&& ((strpos($content, '$$range') === false && strpos($content, '{{range') === false))) && ((strpos($content, '$$range') === false && strpos($content, '{{range') === false)))
{ {
if(is_array($ids) && $ids[0] && !$ids[0]['id']) if (is_array($ids) && !(is_array($ids[0]) && isset($ids[0]['id']) || is_string($ids[0])))
{ {
foreach($ids as $range) foreach($ids as $range)
{ {
@ -888,9 +888,9 @@ class calendar_merge extends Api\Storage\Merge
*/ */
protected function normalize_event_id($id) protected function normalize_event_id($id)
{ {
if(is_string($id) || is_array($id) && $id['id'] && !$id['start']) if(is_string($id) || is_array($id) && !empty($id['id']) && empty($id['start']))
{ {
if(strpos($id, ':')) if (is_string($id) && strpos($id, ':'))
{ {
$_id = $id; $_id = $id;
$id = array(); $id = array();

View File

@ -678,7 +678,7 @@ class calendar_ui
'filter' => $this->cal_prefs['saved_states']['status_filter'], 'filter' => $this->cal_prefs['saved_states']['status_filter'],
'num_rows' => 1 'num_rows' => 1
); );
$filter_match = count($this->bo->search($filter_check, $this->bo->so->cal_table.".cal_id = {$event['id']}")) > 0; $filter_match = (bool)$this->bo->search($filter_check, $this->bo->so->cal_table.".cal_id = {$event['id']}");
} }
if(!$event || !$filter_match) if(!$event || !$filter_match)

View File

@ -3247,7 +3247,7 @@ class calendar_uiforms extends calendar_ui
else else
{ {
// Group membership // Group membership
foreach(array_keys($event['participants']) as $id) foreach(array_keys($event['participants'] ?? []) as $id)
{ {
if($GLOBALS['egw']->accounts->get_type($id) == 'g' && in_array($uid,$GLOBALS['egw']->accounts->members($id,true))) if($GLOBALS['egw']->accounts->get_type($id) == 'g' && in_array($uid,$GLOBALS['egw']->accounts->members($id,true)))
{ {

View File

@ -345,7 +345,7 @@ class calendar_uiviews extends calendar_ui
'caption' => 'Weekend', 'caption' => 'Weekend',
'icon' => '7_day_view', 'icon' => '7_day_view',
'checkbox' => true, 'checkbox' => true,
'checked' => $this->cal_prefs['saved_states']['weekend'], 'checked' => is_array($this->cal_prefs['saved_states']) ? $this->cal_prefs['saved_states']['weekend']:false,
'group' => $group, 'group' => $group,
'onExecute' => 'javaScript:app.calendar.toolbar_action', 'onExecute' => 'javaScript:app.calendar.toolbar_action',
'hint' => 'Toggle weekend', 'hint' => 'Toggle weekend',
@ -734,7 +734,7 @@ class calendar_uiviews extends calendar_ui
$days == 1 // Showing just 1 day $days == 1 // Showing just 1 day
) )
{ {
$content['view'][] = (array)$this->tagWholeDayOnTop($this->bo->search($search_params)) + $content['view'][] = $this->tagWholeDayOnTop($this->bo->search($search_params)) +
array('owner' => $users); array('owner' => $users);
} }
else else
@ -1055,6 +1055,10 @@ class calendar_uiviews extends calendar_ui
} }
} }
} }
else
{
$dayEvents = []; // search returns false or null for nothing found!
}
return $dayEvents; return $dayEvents;
} }
} }

View File

@ -58,6 +58,14 @@
"type": "vcs", "type": "vcs",
"url": "https://github.com/egroupware/Imap_Client" "url": "https://github.com/egroupware/Imap_Client"
}, },
{
"type": "vcs",
"url": "https://github.com/egroupware/Util"
},
{
"type": "vcs",
"url": "https://github.com/egroupware/Stream_Wrapper"
},
{ {
"type": "vcs", "type": "vcs",
"url": "https://github.com/IMSGlobal/lti-1-3-php-library" "url": "https://github.com/IMSGlobal/lti-1-3-php-library"
@ -105,8 +113,10 @@
"egroupware/rocketchat": "self.version", "egroupware/rocketchat": "self.version",
"egroupware/smallpart": "self.version", "egroupware/smallpart": "self.version",
"egroupware/status": "self.version", "egroupware/status": "self.version",
"egroupware/stream-wrapper": "^2.1.5",
"egroupware/swoolepush": "self.version", "egroupware/swoolepush": "self.version",
"egroupware/tracker": "self.version", "egroupware/tracker": "self.version",
"egroupware/util": "^2.6.2",
"egroupware/webdav": "^v0.3.2", "egroupware/webdav": "^v0.3.2",
"egroupware/z-push-dev": "^2.5", "egroupware/z-push-dev": "^2.5",
"giggsey/libphonenumber-for-php": "^8.12", "giggsey/libphonenumber-for-php": "^8.12",

172
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "de144b0b6d74a96e1d0fa87a0ece2b7c", "content-hash": "beeabf26cf7c537dea75e236217d44c6",
"packages": [ "packages": [
{ {
"name": "adldap2/adldap2", "name": "adldap2/adldap2",
@ -1673,6 +1673,55 @@
"homepage": "https://www.egroupware.org/", "homepage": "https://www.egroupware.org/",
"time": "2021-07-02T11:13:29+00:00" "time": "2021-07-02T11:13:29+00:00"
}, },
{
"name": "egroupware/stream-wrapper",
"version": "2.1.5",
"source": {
"type": "git",
"url": "https://github.com/EGroupware/Stream_Wrapper.git",
"reference": "e274502d91c9615ac1c6e1ee133940a7252d0884"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/EGroupware/Stream_Wrapper/zipball/e274502d91c9615ac1c6e1ee133940a7252d0884",
"reference": "e274502d91c9615ac1c6e1ee133940a7252d0884",
"shasum": ""
},
"require": {
"php": "^5.3 || ^7 || ^8"
},
"replace": {
"pear-horde/horde_stream_wrapper": "2.*",
"pear-pear.horde.org/horde_stream_wrapper": "2.*"
},
"type": "library",
"autoload": {
"psr-0": {
"Horde_Stream_Wrapper": "lib/"
}
},
"license": [
"BSD-2-Clause"
],
"authors": [
{
"name": "Chuck Hagenbuch",
"email": "chuck@horde.org",
"role": "lead"
},
{
"name": "Michael Slusarz",
"email": "slusarz@horde.org",
"role": "lead"
}
],
"description": "PHP stream wrappers library",
"homepage": "https://www.horde.org/libraries/Horde_Stream_Wrapper",
"support": {
"source": "https://github.com/EGroupware/Stream_Wrapper/tree/2.1.5"
},
"time": "2018-01-25T00:00:00+00:00"
},
{ {
"name": "egroupware/swoolepush", "name": "egroupware/swoolepush",
"version": "dev-master", "version": "dev-master",
@ -1755,6 +1804,72 @@
"homepage": "https://www.egroupware.org/", "homepage": "https://www.egroupware.org/",
"time": "2021-08-31T17:21:00+00:00" "time": "2021-08-31T17:21:00+00:00"
}, },
{
"name": "egroupware/util",
"version": "2.6.2",
"source": {
"type": "git",
"url": "https://github.com/EGroupware/Util.git",
"reference": "c45016747dfdbb004891fd72f0ff41982aeb45c0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/EGroupware/Util/zipball/c45016747dfdbb004891fd72f0ff41982aeb45c0",
"reference": "c45016747dfdbb004891fd72f0ff41982aeb45c0",
"shasum": ""
},
"require": {
"ext-dom": "*",
"php": "^5.3 || ^7 || ^8"
},
"replace": {
"pear-horde/horde_util": "2.*",
"pear-pear.horde.org/horde_util": "*"
},
"suggest": {
"ext-ctype": "*",
"ext-filter": "*",
"ext-iconv": "*",
"ext-intl": "*",
"ext-json": "*",
"ext-mbstring": "*",
"ext-xml": "*",
"pear-pear.horde.org/Horde_Imap_Client": "^2",
"pear-pear.horde.org/Horde_Test": "^2.1"
},
"type": "library",
"autoload": {
"classmap": [
"lib/"
]
},
"license": [
"LGPL-2.1"
],
"authors": [
{
"name": "Chuck Hagenbuch",
"email": "chuck@horde.org",
"role": "lead"
},
{
"name": "Jan Schneider",
"email": "jan@horde.org",
"role": "lead"
},
{
"name": "Michael Slusarz",
"email": "slusarz@horde.org",
"role": "developer"
}
],
"description": "Utility library",
"homepage": "https://www.horde.org/libraries/Horde_Util",
"support": {
"source": "https://github.com/EGroupware/Util/tree/2.6.2"
},
"time": "2017-11-11T00:00:00+00:00"
},
{ {
"name": "egroupware/webdav", "name": "egroupware/webdav",
"version": "0.3.2", "version": "0.3.2",
@ -3322,33 +3437,6 @@
], ],
"description": "A collection of various stream filters." "description": "A collection of various stream filters."
}, },
{
"name": "pear-pear.horde.org/Horde_Stream_Wrapper",
"version": "2.1.4",
"dist": {
"type": "file",
"url": "https://pear.horde.org/get/Horde_Stream_Wrapper-2.1.4.tgz"
},
"require": {
"php": "<8.0.0.0"
},
"replace": {
"pear-horde/horde_stream_wrapper": "== 2.1.4.0"
},
"type": "pear-library",
"autoload": {
"classmap": [
""
]
},
"include-path": [
"/"
],
"license": [
"BSD-2-Clause"
],
"description": "A collection of stream wrappers."
},
{ {
"name": "pear-pear.horde.org/Horde_Support", "name": "pear-pear.horde.org/Horde_Support",
"version": "2.2.0", "version": "2.2.0",
@ -3491,34 +3579,6 @@
], ],
"description": "This class represents a single URL and provides methods for manipulating URLs." "description": "This class represents a single URL and provides methods for manipulating URLs."
}, },
{
"name": "pear-pear.horde.org/Horde_Util",
"version": "2.5.9",
"dist": {
"type": "file",
"url": "https://pear.horde.org/get/Horde_Util-2.5.9.tgz"
},
"require": {
"ext-dom": "*",
"php": "<8.0.0.0"
},
"replace": {
"pear-horde/horde_util": "== 2.5.9.0"
},
"type": "pear-library",
"autoload": {
"classmap": [
""
]
},
"include-path": [
"/"
],
"license": [
"LGPL-2.1"
],
"description": "A library that provides functionality useful for all kind of applications."
},
{ {
"name": "pear/archive_tar", "name": "pear/archive_tar",
"version": "1.4.14", "version": "1.4.14",

View File

@ -80,6 +80,7 @@ function usage($error_msg='')
exit; exit;
} }
$long = $numeric = $recursive = $perms = $all = $inode = false; $long = $numeric = $recursive = $perms = $all = $inode = false;
$domain = 'default';
$args = $_SERVER['argv']; $args = $_SERVER['argv'];
$cmd = basename(array_shift($args),'.php'); $cmd = basename(array_shift($args),'.php');
if ($args[0][0] != '-' && $args[0][0] != '/' && strpos($args[0],'://') === false) if ($args[0][0] != '-' && $args[0][0] != '/' && strpos($args[0],'://') === false)
@ -186,7 +187,7 @@ while(!is_null($option = array_shift($args)))
} }
if ($user && $passwd) if ($user && $passwd)
{ {
load_egw($user,$passwd,$domain ? $domain : 'default'); load_egw($user, $passwd, $domain);
} }
$argc = count($argv); $argc = count($argv);

View File

@ -94,7 +94,7 @@ class home_favorite_portlet extends home_portlet
$need_reload = true; $need_reload = true;
} }
$this->favorite = $context['favorite']; $this->favorite = (array)$context['favorite'];
$this->title = lang($context['appname']) . ': ' . $this->favorite['name']; $this->title = lang($context['appname']) . ': ' . $this->favorite['name'];
$this->context = $context; $this->context = $context;
if($this->favorite) if($this->favorite)

View File

@ -116,7 +116,7 @@ class importexport_admin_prefs_sidebox_hooks
),false)."','_blank',850,440,'yes')", ),false)."','_blank',850,440,'yes')",
'icon' => 'import', 'icon' => 'import',
'app' => 'importexport', 'app' => 'importexport',
'text' => in_array($appname, array('sitemgr')) || count($plugins[$appname]['import']) > 1 ? 'Import' : 'Import CSV' 'text' => in_array($appname, array('sitemgr')) || count($plugins[$appname]['import'] ?? []) > 1 ? 'Import' : 'Import CSV'
); );
if($GLOBALS['egw_info']['flags']['disable_importexport']['import']) { if($GLOBALS['egw_info']['flags']['disable_importexport']['import']) {
$file['Import CSV']['link'] = ''; $file['Import CSV']['link'] = '';
@ -133,7 +133,7 @@ class importexport_admin_prefs_sidebox_hooks
),false)."','_blank',850,440,'yes')", ),false)."','_blank',850,440,'yes')",
'icon' => 'export', 'icon' => 'export',
'app' => 'importexport', 'app' => 'importexport',
'text' => in_array($appname, array('sitemgr')) || count($plugins[$appname]['export']) > 1 ? 'Export' : 'Export CSV' 'text' => in_array($appname, array('sitemgr')) || count($plugins[$appname]['export'] ?? []) > 1 ? 'Export' : 'Export CSV'
); );
if($GLOBALS['egw_info']['flags']['disable_importexport']['export']) { if($GLOBALS['egw_info']['flags']['disable_importexport']['export']) {
$file['Export CSV']['link'] = ''; $file['Export CSV']['link'] = '';

View File

@ -457,7 +457,13 @@ abstract class importexport_basic_import_csv implements importexport_iface_impor
rewind($stream); rewind($stream);
// Set up result // Set up result
$rows = array('h1'=>array(),'f1'=>array(),'.h1'=>'class=th'); $rows = array('h1' => array(), 'f1' => array(), '.h1' => 'class=th');
// Check for no results
if(!count($this->preview_records) || !is_object($this->preview_records[0]))
{
return Api\Html::table($rows);
}
// Load labels for app // Load labels for app
$record_class = get_class($this->preview_records[0]); $record_class = get_class($this->preview_records[0]);
@ -467,16 +473,23 @@ abstract class importexport_basic_import_csv implements importexport_iface_impor
$plugin = get_called_class(); $plugin = get_called_class();
$wizard_name = $definition->application . '_wizard_' . str_replace($definition->application . '_', '', $plugin); $wizard_name = $definition->application . '_wizard_' . str_replace($definition->application . '_', '', $plugin);
try { try
{
$wizard = new $wizard_name; $wizard = new $wizard_name;
$fields = $wizard->get_import_fields(); $fields = $wizard->get_import_fields();
foreach($labels as $field => &$label) foreach($labels as $field => &$label)
{ {
if($fields[$field]) $label = $fields[$field]; if($fields[$field])
{
$label = $fields[$field];
} }
} catch (Exception $e) { }
}
catch (Exception $e)
{
Api\Translation::add_app($definition->application); Api\Translation::add_app($definition->application);
foreach($labels as $field => &$label) { foreach($labels as $field => &$label)
{
$label = lang($label); $label = lang($label);
} }
} }

View File

@ -288,7 +288,7 @@ class importexport_export_csv implements importexport_iface_export_record
break; break;
default: default:
list($type) = explode('-',$c_field['type'],2); list($type) = explode('-',$c_field['type'],2);
if(in_array($type, array_keys($GLOBALS['egw_info']['apps']))) { if (in_array($type, array_keys($GLOBALS['egw_info']['apps'] ?? []))) {
$fields['links'][] = $name; $fields['links'][] = $name;
$links[$name] = $c_field['type']; $links[$name] = $c_field['type'];
} }

View File

@ -371,12 +371,12 @@ class importexport_helper_functions {
$val $val
); );
} }
$val = preg_replace_callback( "/($c_functions)\(([^)]*)\)/i", array( self, 'c2_dispatcher') , $val ); $val = preg_replace_callback( "/($c_functions)\(([^)]*)\)/i", array(__CLASS__, 'c2_dispatcher') , $val );
break; break;
} }
} }
// clean each field // clean each field
$val = preg_replace_callback("/(\|T\{|\|TC\{|\|TCnCL\{|\|INE\{)(.*)\}/", array( self, 'strclean'), $val ); $val = preg_replace_callback("/(\|T\{|\|TC\{|\|TCnCL\{|\|INE\{)(.*)\}/", array(__CLASS__, 'strclean'), $val );
$_record[$idx] = $val; $_record[$idx] = $val;
} }

View File

@ -83,12 +83,12 @@ class importexport_import_csv implements importexport_iface_import_record { //,
* @param string $_resource resource containing data. May be each valid php-stream * @param string $_resource resource containing data. May be each valid php-stream
* @param array $_options options for the resource array with keys: charset and fieldsep * @param array $_options options for the resource array with keys: charset and fieldsep
*/ */
public function __construct( $_resource, array $_options ) { public function __construct( $_resource, array $_options )
{
$this->resource = $_resource; $this->resource = $_resource;
$this->csv_fieldsep = $_options['fieldsep']; $this->csv_fieldsep = $_options['fieldsep'][0];
if($_options['charset'] == 'user') $_options['charset'] = $GLOBALS['egw_info']['user']['preferences']['common']['csv_charset']; if($_options['charset'] == 'user') $_options['charset'] = $GLOBALS['egw_info']['user']['preferences']['common']['csv_charset'];
$this->csv_charset = $_options['charset']; $this->csv_charset = $_options['charset'];
return;
} // end of member function __construct } // end of member function __construct
/** /**

View File

@ -62,7 +62,7 @@ use EGroupware\Api\Etemplate;
{ {
Api\Translation::add_app($appname); Api\Translation::add_app($appname);
} }
if($content['import'] && $definition) { if ($content['import'] && $definition && !empty($content['file']) && file_exists($content['file']['tmp_name'])) {
try { try {
if($content['dry-run']) { if($content['dry-run']) {
// Set this so plugin doesn't do any data changes // Set this so plugin doesn't do any data changes
@ -132,7 +132,7 @@ use EGroupware\Api\Etemplate;
} }
if(count($check_message)) if(count($check_message))
{ {
$this->message .= implode($check_message, "<br />\n") . "<br />\n"; $this->message .= implode("<br />\n", $check_message) . "<br />\n";
} }
if($content['dry-run']) if($content['dry-run'])
{ {
@ -200,7 +200,7 @@ use EGroupware\Api\Etemplate;
} }
elseif($content['cancel']) elseif($content['cancel'])
{ {
egw_framework::set_onload('window.close();'); Api\Framework::window_close();
} }
elseif ($GLOBALS['egw_info']['user']['apps']['admin']) elseif ($GLOBALS['egw_info']['user']['apps']['admin'])
{ {
@ -358,7 +358,7 @@ use EGroupware\Api\Etemplate;
foreach($plugin->get_errors() as $record => $message) { foreach($plugin->get_errors() as $record => $message) {
$this->message .= "<br />\n$record: $message"; $this->message .= "<br />\n$record: $message";
} }
if($count != $total_processed) $this->message .= "<br />\n".lang('Some records may not have been imported'); $this->message .= "<br />\n".lang('Some records may not have been imported');
$this->message .= "<br />\n"; $this->message .= "<br />\n";
} }
return '<div class="header">' . lang('Preview') . ' - ' . $plugin->get_name() . '</div>' . $preview; return '<div class="header">' . lang('Preview') . ' - ' . $plugin->get_name() . '</div>' . $preview;
@ -489,17 +489,18 @@ use EGroupware\Api\Etemplate;
if($dst_file) if($dst_file)
{ {
// Still have uploaded file, jump there // Still have uploaded file, jump there
Api\Cache::setSession($definition->application,'csvfile',$dst_file); Api\Cache::setSession($definition->application, 'csvfile', $dst_file);
$edit_link['step'] = 'wizard_step30'; $edit_link['step'] = 'wizard_step30';
} }
$edit_link = Egw::link('/index.php',$edit_link); $edit_link = Egw::link('/index.php', $edit_link);
$edit_link = "javascript:egw_openWindowCentered2('$edit_link','_blank',500,500,'yes')"; $edit_link = "javascript:egw_openWindowCentered2('$edit_link','_blank',500,500,'yes')";
$actions[] = lang('Edit definition <a href="%1">%2</a> to match your file', $actions[] = lang('Edit definition <a href="%1">%2</a> to match your file',
$edit_link, $definition->name ); $edit_link, $definition->name
);
} }
$actions[] = lang('Edit your file to match the definition:') . ' ' $actions[] = lang('Edit your file to match the definition:') . ' '
. implode(array_map('lang',array_intersect_key($options['csv_fields'],$options['field_mapping'])),', '); . implode(', ', array_map('lang', array_intersect_key($options['csv_fields'], $options['field_mapping'])));
$message[] = "\n<li>".implode($actions,"\n<li>"); $message[] = "\n<li>" . implode("\n<li>", $actions);
} }
} }
return $ok; return $ok;

View File

@ -426,7 +426,7 @@ class importexport_wizard_basic_import_csv
$sel_options['action'] = $this->actions; $sel_options['action'] = $this->actions;
// Make at least 1 (empty) conditions // Make at least 1 (empty) conditions
$j = count($content['conditions']); $j = count($content['conditions'] ?? []);
while ($j < 1) while ($j < 1)
{ {
$content['conditions'][] = array( $content['conditions'][] = array(

View File

@ -801,7 +801,7 @@ class infolog_so
} }
$sortbycf=''; $sortbycf='';
if (!empty($query['order']) && (preg_match('/^[a-z_0-9, ]+$/i',$query['order']) || stripos($query['order'],'#')!==FALSE ) && if (!empty($query['order']) && (preg_match('/^[a-z_0-9, ]+$/i',$query['order']) || stripos($query['order'],'#')!==FALSE ) &&
(empty($query['sort']) || preg_match('/^(DESC|ASC)$/i',$query['sort']))) (empty($query['sort']) || is_string($query['sort']) && preg_match('/^(DESC|ASC)$/i',$query['sort'])))
{ {
$order = array(); $order = array();
foreach(explode(',',$query['order']) as $val) foreach(explode(',',$query['order']) as $val)

View File

@ -355,7 +355,8 @@ class infolog_ui
} }
// Querying for a particular ID. If linked is a list of IDs, reset the linked or we won't find the ID we want. // Querying for a particular ID. If linked is a list of IDs, reset the linked or we won't find the ID we want.
if($query['col_filter']['info_id'] && $link_filters['linked'] && !$link_filters['linked']['app']) if (!empty($query['col_filter']['info_id']) && !empty($link_filters['linked']) &&
(!is_array($link_filters['linked']) || empty($link_filters['linked']['app'])))
{ {
unset($links['linked']); unset($links['linked']);
unset($link_filters['linked']); unset($link_filters['linked']);

View File

@ -284,7 +284,7 @@ else
error_log('login::'.__LINE__.' User '. $login. ' authenticated with an unsave password'.' '.$unsave_msg); error_log('login::'.__LINE__.' User '. $login. ' authenticated with an unsave password'.' '.$unsave_msg);
$message = lang('eGroupWare checked your password for safetyness. You have to change your password for the following reason:')."\n"; $message = lang('eGroupWare checked your password for safetyness. You have to change your password for the following reason:')."\n";
Egw::redirect_link('/index.php', array( Egw::redirect_link('/index.php', array(
'menuaction' => 'preferences.uipassword.change', 'menuaction' => 'preferences.preferences_password.change',
'message' => $message . $unsave_msg, 'message' => $message . $unsave_msg,
'cd' => 'yes', 'cd' => 'yes',
)); ));

View File

@ -205,11 +205,7 @@ class mail_acl
} }
else else
{ {
$button = @key($content['button']); $button = !empty ($content['grid']['delete']) ? 'delete' : @key((array)$content['button']);
if (!empty ($content['grid']['delete']))
{
$button = 'delete';
}
$data = $content; $data = $content;
$data['mailbox'] = self::_extract_mailbox($content['mailbox'], $acc_id); $data['mailbox'] = self::_extract_mailbox($content['mailbox'], $acc_id);
switch ($button) switch ($button)

View File

@ -414,8 +414,7 @@ class mail_integration {
$replace = array(); $replace = array();
foreach($links as $link) foreach($links as $link)
{ {
$matches = null; if (is_array($link) && is_array($link['id']) && !empty($link['id']['egw_data']) && strpos($html, self::INLINE_PREFIX . $link['id']['egw_data']) !== false)
if (is_array($link) && $link['id']['egw_data'] && strpos($html, self::INLINE_PREFIX . $link['id']['egw_data']) !== false)
{ {
$replace[self::INLINE_PREFIX. $link['id']['egw_data']] = $replace[self::INLINE_PREFIX. $link['id']['egw_data']] =
Api\Egw::link(Api\Vfs::download_url(Api\Link::vfs_path($app, $id, Api\Vfs::basename($link['id']['name'])))); Api\Egw::link(Api\Vfs::download_url(Api\Link::vfs_path($app, $id, Api\Vfs::basename($link['id']['name']))));

View File

@ -2757,10 +2757,7 @@ $filter['before']= date("d-M-Y", $cutoffdate2);
{ {
//error_log(__METHOD__."about to call calendar_ical"); //error_log(__METHOD__."about to call calendar_ical");
$calendar_ical = new calendar_ical(); $calendar_ical = new calendar_ical();
$eventid = $calendar_ical->search($attachment['attachment'],-1); $event = $calendar_ical->importVCal($attachment['attachment'],-1,null,true,0,'',null,$attachment['charset']);
//error_log(__METHOD__.array2string($eventid));
if (!$eventid) $eventid = -1;
$event = $calendar_ical->importVCal($attachment['attachment'],(is_array($eventid)?$eventid[0]:$eventid),null,true,0,'',null,$attachment['charset']);
//error_log(__METHOD__.$event); //error_log(__METHOD__.$event);
if ((int)$event > 0) if ((int)$event > 0)
{ {
@ -2965,7 +2962,7 @@ $filter['before']= date("d-M-Y", $cutoffdate2);
$file = $tmp_file; $file = $tmp_file;
} }
if (!($fp = Vfs::fopen($file,'wb')) || !fwrite($fp,$message)) if (!is_string($message) || !($fp = Vfs::fopen($file,'wb')) || !fwrite($fp,$message))
{ {
$res['msg'] = lang('Error saving %1!',$file); $res['msg'] = lang('Error saving %1!',$file);
$res['success'] = false; $res['success'] = false;

View File

@ -877,7 +877,7 @@ class mail_zpush implements activesync_plugin_write, activesync_plugin_sendmail,
$bodypreference = $contentparameters->GetBodyPreference(); /* fmbiete's contribution r1528, ZP-320 */ $bodypreference = $contentparameters->GetBodyPreference(); /* fmbiete's contribution r1528, ZP-320 */
// fix for z-push bug returning additional bodypreference type 4, even if only 1 is requested and mimessupport = 0 // fix for z-push bug returning additional bodypreference type 4, even if only 1 is requested and mimessupport = 0
if (!$mimesupport && ($key = array_search('4', $bodypreference))) unset($bodypreference[$key]); if (!$mimesupport && $bodypreference !== false && ($key = array_search('4', $bodypreference))) unset($bodypreference[$key]);
//$this->debugLevel=4; //$this->debugLevel=4;
if (!isset($this->mail)) $this->mail = Mail::getInstance(false,self::$profileID,true,false,true); if (!isset($this->mail)) $this->mail = Mail::getInstance(false,self::$profileID,true,false,true);
@ -1633,7 +1633,7 @@ class mail_zpush implements activesync_plugin_write, activesync_plugin_sendmail,
//ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.array2string($rv_messages)); //ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.array2string($rv_messages));
$list=array(); $list=array();
$cnt = count($rv_messages['header']); $cnt = count($rv_messages['header'] ?? []);
//$list['status'] = 1; //$list['status'] = 1;
$list['searchtotal'] = $cnt; $list['searchtotal'] = $cnt;
$list["range"] = $_searchquery->GetSearchRange(); $list["range"] = $_searchquery->GetSearchRange();

View File

@ -47,10 +47,10 @@ class preferences_categories_ui extends admin_categories {
* *
* @param array $content * @param array $content
*/ */
public function index(array $content=null) public function index(array $content = null, $msg = '')
{ {
$GLOBALS['egw_info']['flags']['currentapp'] = $_GET['cats_app']; $GLOBALS['egw_info']['flags']['currentapp'] = $_GET['cats_app'];
parent::index($content); parent::index($content, $msg);
} }
} }

View File

@ -7,8 +7,8 @@
* @package timesheet * @package timesheet
* @copyright (c) 2005-16 by Ralf Becker <RalfBecker-AT-outdoor-training.de> * @copyright (c) 2005-16 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/ */
use EGroupware\Api\Link; use EGroupware\Api\Link;
use EGroupware\Api\Acl; use EGroupware\Api\Acl;
@ -24,7 +24,7 @@ class timesheet_datasource extends datasource
*/ */
function __construct() function __construct()
{ {
parent::__construct(TIMESHEET_APP); parent::__construct('timesheet');
$this->valid = PM_REAL_START|PM_REAL_END|PM_USED_TIME|PM_USED_BUDGET|PM_USED_QUANTITY| $this->valid = PM_REAL_START|PM_REAL_END|PM_USED_TIME|PM_USED_BUDGET|PM_USED_QUANTITY|
PM_PRICELIST_ID|PM_UNITPRICE|PM_RESOURCES|PM_DETAILS|PM_COMPLETION|PM_CAT_ID; PM_PRICELIST_ID|PM_UNITPRICE|PM_RESOURCES|PM_DETAILS|PM_COMPLETION|PM_CAT_ID;
@ -62,7 +62,7 @@ class timesheet_datasource extends datasource
'pl_id' => $data['pl_id'], 'pl_id' => $data['pl_id'],
'pe_unitprice' => $data['ts_unitprice'], 'pe_unitprice' => $data['ts_unitprice'],
'pe_used_quantity' => $data['ts_quantity'], 'pe_used_quantity' => $data['ts_quantity'],
'pe_used_budget' => $data['ts_quantity'] * (float)$data['ts_unitprice'], 'pe_used_budget' => (float)$data['ts_quantity'] * (float)$data['ts_unitprice'],
'pe_completion' => 100, 'pe_completion' => 100,
'cat_id' => $data['cat_id'], 'cat_id' => $data['cat_id'],
); );