Merge branch 'master' into web-components

This commit is contained in:
nathan 2021-11-24 09:42:28 -07:00
commit da6ff9d639
18 changed files with 200 additions and 230 deletions

View File

@ -557,7 +557,7 @@ class addressbook_zpush implements activesync_plugin_write, activesync_plugin_se
case 'cat_id': case 'cat_id':
// for existing entries in all-in-one addressbook, remove addressbook name as category // for existing entries in all-in-one addressbook, remove addressbook name as category
if ($contact && $GLOBALS['egw_info']['user']['preferences']['activesync']['addressbook-all-in-one'] && if ($contact && $GLOBALS['egw_info']['user']['preferences']['activesync']['addressbook-all-in-one'] && is_array($message->$key) &&
($k=array_search($this->get_addressbooks($contact['owner'].($contact['private']?'p':''), false, true),$message->$key))) ($k=array_search($this->get_addressbooks($contact['owner'].($contact['private']?'p':''), false, true),$message->$key)))
{ {
unset($message->categories[$k]); unset($message->categories[$k]);

View File

@ -513,7 +513,7 @@ class Sql extends Api\Storage
$shared_sql = $this->table_name.'.contact_id IN (SELECT contact_id FROM '.self::SHARED_TABLE.' WHERE '. $shared_sql = $this->table_name.'.contact_id IN (SELECT contact_id FROM '.self::SHARED_TABLE.' WHERE '.
// $filter[tid] === null is used by sync-collection report, in which case we need to return deleted shares, to remove them from devices // $filter[tid] === null is used by sync-collection report, in which case we need to return deleted shares, to remove them from devices
(array_key_exists('tid', $filter) && !isset($filter['tid']) ? '' : 'shared_deleted IS NULL AND '). (array_key_exists('tid', $filter) && !isset($filter['tid']) ? '' : 'shared_deleted IS NULL AND ').
$this->db->expression(self::SHARED_TABLE, ['shared_with' => $filter['owner'] ?? array_keys($this->grants)]).')'; $this->db->expression(self::SHARED_TABLE, ['shared_with' => $filter['owner'] ?? array_keys($this->grants ?? [0])]).')';
} }
// add filter for read ACL in sql, if user is NOT the owner of the addressbook // add filter for read ACL in sql, if user is NOT the owner of the addressbook

View File

@ -281,7 +281,7 @@ class Account implements \ArrayAccess
public static function ssl2secure($ssl) public static function ssl2secure($ssl)
{ {
$secure = false; $secure = false;
switch($ssl & ~self::SSL_VERIFY) switch((int)$ssl & ~self::SSL_VERIFY)
{ {
case self::SSL_STARTTLS: case self::SSL_STARTTLS:
$secure = 'tls'; // Horde uses 'tls' for STARTTLS, not ssl connection with tls version >= 1 and no sslv2/3 $secure = 'tls'; // Horde uses 'tls' for STARTTLS, not ssl connection with tls version >= 1 and no sslv2/3

View File

@ -492,7 +492,7 @@ class Base
$this->init($keys); $this->init($keys);
$this->data2db(); $this->data2db();
$query = false; $query = [];
foreach ($this->db_key_cols as $db_col => $col) foreach ($this->db_key_cols as $db_col => $col)
{ {
if ($this->data[$col] != '') if ($this->data[$col] != '')
@ -513,7 +513,7 @@ class Base
$q = array(); $q = array();
foreach($col as $db_c => $c) foreach($col as $db_c => $c)
{ {
if ($this->data[$col] == '') if ($this->data[$c] == '')
{ {
$q = null; $q = null;
break; break;

View File

@ -875,18 +875,20 @@ class calendar_boupdate extends calendar_bo
$disinvited = $msg_type == MSG_DISINVITE ? array_keys($to_notify) : array(); $disinvited = $msg_type == MSG_DISINVITE ? array_keys($to_notify) : array();
$owner = $old_event ? $old_event['owner'] : $new_event['owner']; $owner = $old_event ? $old_event['owner'] : $new_event['owner'];
if ($owner && !isset($to_notify[$owner]) && $msg_type != MSG_ALARM) if($owner && !isset($to_notify[$owner]) && $msg_type != MSG_ALARM)
{ {
$to_notify[$owner] = 'OCHAIR'; // always include the event-owner $to_notify[$owner] = 'OCHAIR'; // always include the event-owner
} }
// ignore events in the past (give a tolerance of 10 seconds for the script) // ignore events in the past (give a tolerance of 10 seconds for the script)
if($old_event && $this->date2ts($old_event['start']) < ($this->now_su - 10)) if($new_event && $this->date2ts($new_event['start']) < ($this->now_su - 10) ||
!$new_event && $old_event && $this->date2ts($old_event['start']) < ($this->now_su - 10)
)
{ {
return False; return False;
} }
// check if default timezone is set correctly to server-timezone (ical-parser messes with it!!!) // check if default timezone is set correctly to server-timezone (ical-parser messes with it!!!)
if ($GLOBALS['egw_info']['server']['server_timezone'] && ($tz = date_default_timezone_get()) != $GLOBALS['egw_info']['server']['server_timezone']) if($GLOBALS['egw_info']['server']['server_timezone'] && ($tz = date_default_timezone_get()) != $GLOBALS['egw_info']['server']['server_timezone'])
{ {
$restore_tz = $tz; $restore_tz = $tz;
date_default_timezone_set($GLOBALS['egw_info']['server']['server_timezone']); date_default_timezone_set($GLOBALS['egw_info']['server']['server_timezone']);
@ -1746,7 +1748,7 @@ class calendar_boupdate extends calendar_bo
static $memberships=null; static $memberships=null;
if (is_null($memberships)) if (is_null($memberships))
{ {
$memberships = $GLOBALS['egw']->accounts->memberships($user,true); $memberships = $GLOBALS['egw']->accounts->memberships($user,true) ?: [];
$memberships[] = $user; $memberships[] = $user;
} }
foreach($cat_rights as $uid => $value) foreach($cat_rights as $uid => $value)

View File

@ -129,6 +129,106 @@ class calendar_merge extends Api\Storage\Merge
return parent::merge_string($content, $ids, $err, $mimetype, $fix, $charset); return parent::merge_string($content, $ids, $err, $mimetype, $fix, $charset);
} }
public static function merge_entries(array $ids = null, \EGroupware\Api\Storage\Merge &$document_merge = null, $pdf = null)
{
$document_merge = new calendar_merge();
if(is_null(($ids)))
{
$ids = json_decode($_REQUEST['id'], true);
}
// Try to make time span into appropriate ranges to match
$template = $ids['view'] ?: '';
if(stripos($_REQUEST['document'], 'month') !== false || stripos($_REQUEST['document'], lang('month')) !== false)
{
$template = 'month';
}
if(stripos($_REQUEST['document'], 'week') !== false || stripos($_REQUEST['document'], lang('week')) !== false)
{
$template = 'week';
}
//error_log("Detected template $template");
$date = $ids['date'];
$first = $ids['first'];
$last = $ids['last'];
// Pull dates from session if they're not in the request
if(!array_key_exists('first', $ids))
{
$ui = new calendar_ui();
$date = $ui->date;
$first = $ui->first;
$last = $ui->last;
}
switch($template)
{
case 'month':
// Trim to _only_ the month, do not pad to week start / end
$time = new Api\DateTime($date);
$timespan = array(array(
'start' => Api\DateTime::to($time->format('Y-m-01 00:00:00'), 'ts'),
'end' => Api\DateTime::to($time->format('Y-m-t 23:59:59'), 'ts')
));
break;
case 'week':
$timespan = array();
$start = new Api\DateTime($first);
$end = new Api\DateTime($last);
$t = clone $start;
$t->modify('+1 week')->modify('-1 second');
if($t < $end)
{
do
{
$timespan[] = array(
'start' => $start->format('ts'),
'end' => $t->format('ts')
);
$start->modify('+1 week');
$t->modify('+1 week');
}
while($start < $end);
break;
}
// Fall through
default:
$timespan = array(array(
'start' => $first,
'end' => $last
));
}
// Add path into document
static::check_document($_REQUEST['document'], $GLOBALS['egw_info']['user']['preferences']['calendar']['document_dir']);
return \EGroupware\Api\Storage\Merge::merge_entries(array_key_exists('0', $ids) ? $ids : $timespan, $document_merge);
}
public function get_filename_placeholders($document, $ids)
{
$placeholders = parent::get_filename_placeholders($document, $ids);
$request = json_decode($_REQUEST['id'], true) ?: [];
$template = $ids['view'] ?: '';
if(stripos($document, 'month') !== false || stripos($document, lang('month')) !== false)
{
$template = 'month';
}
if(stripos($document, 'week') !== false || stripos($document, lang('week')) !== false)
{
$template = 'week';
}
$placeholders['$$span$$'] = lang($template);
$placeholders['$$first$$'] = Api\DateTime::to($ids['first'] ?: $request['first'], true);
$placeholders['$$last$$'] = Api\DateTime::to($ids['last'] ?: $request['last'], true);
$placeholders['$$date$$'] = Api\DateTime::to($ids['date'] ?: $request['date'], true);
return $placeholders;
}
/** /**
* Get replacements * Get replacements
* *
@ -182,7 +282,7 @@ class calendar_merge extends Api\Storage\Merge
{ {
foreach(self::$range_tags as $key => $format) foreach(self::$range_tags as $key => $format)
{ {
$value = date($format, $key == 'end' ? $id['end'] : $id['start']); $value = Api\DateTime::to($key == 'end' ? $id['end'] : $id['start'], $format);
if($key == 'month') $value = lang($value); if($key == 'month') $value = lang($value);
$values["$\$range/$key$$"] = $value; $values["$\$range/$key$$"] = $value;
} }
@ -440,8 +540,11 @@ class calendar_merge extends Api\Storage\Merge
} }
} }
$_date = $date['start'] ? $date['start'] : $date; $_date = new Api\DateTime(['start'] ? $date['start'] : $date);
if($days[date('Ymd',$_date)][$plugin]) return $days[date('Ymd',$_date)][$plugin][$n]; if($days[$_date->format('Ymd')][$plugin])
{
return $days[$_date->format('Ymd')][$plugin][$n];
}
$events = $this->bo->search($this->query + array( $events = $this->bo->search($this->query + array(
'start' => $date['end'] ? $date['start'] : mktime(0, 0, 0, date('m', $_date), date('d', $_date), date('Y', $_date)), 'start' => $date['end'] ? $date['start'] : mktime(0, 0, 0, date('m', $_date), date('d', $_date), date('Y', $_date)),
@ -481,7 +584,7 @@ class calendar_merge extends Api\Storage\Merge
$replacements['$$calendar_endtime$$'] = date($time_format, $day == date('Ymd', $event['end']) ? $event['end'] : mktime(23, 59, 59, 0, 0, 0)); $replacements['$$calendar_endtime$$'] = date($time_format, $day == date('Ymd', $event['end']) ? $event['end'] : mktime(23, 59, 59, 0, 0, 0));
} }
$days[date('Ymd', $_date)][$dow][] = $replacements; $days[$_date->format('Ymd')][$dow][] = $replacements;
} }
if(strpos($repeat, 'day/date') !== false || strpos($repeat, 'day/name') !== false) if(strpos($repeat, 'day/date') !== false || strpos($repeat, 'day/name') !== false)
{ {
@ -489,24 +592,24 @@ class calendar_merge extends Api\Storage\Merge
'$$day/date$$' => date($GLOBALS['egw_info']['user']['preferences']['common']['dateformat'], strtotime($day)), '$$day/date$$' => date($GLOBALS['egw_info']['user']['preferences']['common']['dateformat'], strtotime($day)),
'$$day/name$$' => lang(date('l', strtotime($day))) '$$day/name$$' => lang(date('l', strtotime($day)))
); );
if(!is_array($days[date('Ymd', $_date)][date('l', strtotime($day))])) if(!is_array($days[$_date->format('Ymd')][date('l', strtotime($day))]))
{ {
$blank = $this->calendar_replacements(array()); $blank = $this->calendar_replacements(array());
foreach($blank as &$value) foreach($blank as &$value)
{ {
$value = ''; $value = '';
} }
$days[date('Ymd', $_date)][date('l', strtotime($day))][] = $blank; $days[$_date->format('Ymd')][date('l', strtotime($day))][] = $blank;
} }
$days[date('Ymd', $_date)][date('l', strtotime($day))][0] += $date_marker; $days[$_date->format('Ymd')][date('l', strtotime($day))][0] += $date_marker;
} }
// Add in birthdays // Add in birthdays
if(strpos($repeat, 'day/birthdays') !== false) if(strpos($repeat, 'day/birthdays') !== false)
{ {
$days[date('Ymd', $_date)][date('l', strtotime($day))][0]['$$day/birthdays$$'] = $this->get_birthdays($day); $days[$_date->format('Ymd')][date('l', strtotime($day))][0]['$$day/birthdays$$'] = $this->get_birthdays($day);
} }
} }
return $days[date('Ymd', $_date)][$plugin][0]; return $days[$_date->format('Ymd')][$plugin][0];
} }
/** /**

View File

@ -534,28 +534,6 @@ class calendar_ui
*/ */
function sidebox_etemplate($content = array()) function sidebox_etemplate($content = array())
{ {
if($content['merge'])
{
// View from sidebox is JSON encoded
$this->manage_states(array_merge($content,(array)json_decode($content['view'],true)));
if($content['first'])
{
$this->first = Api\DateTime::to($content['first'],'ts');
}
if($content['last'])
{
$this->last = new Api\DateTime($content['last']);
$this->last->setTime(23, 59, 59);
$this->last = $this->last->format('ts');
}
$_GET['merge'] = $content['merge'];
if($this->merge())
{
Framework::redirect('/index.php');//, array('menuaction' => 'calendar.calendar_uiviews.index'));
return false;
}
}
Etemplate::reset_request(); Etemplate::reset_request();
$sidebox = new Etemplate('calendar.sidebox'); $sidebox = new Etemplate('calendar.sidebox');
@ -857,129 +835,4 @@ class calendar_ui
} }
} }
} }
/**
* Merge calendar events into a document
*
* Checks $_GET['merge'] for the document, and $timespan for the date range.
* If timespan is not provided, we try to guess based on the document name.
*
* @param Array $timespan
*
* @return boolean stop execution
*/
public function merge($timespan = array())
{
// Merge print
if($_GET['merge'])
{
if(!$timespan)
{
// Try to make time span into appropriate ranges to match
if(stripos($_GET['merge'],'month') !== false || stripos($_GET['merge'],lang('month')) !== false) $template = 'month';
if(stripos($_GET['merge'],'week') !== false || stripos($_GET['merge'],lang('week')) !== false) $template = 'week';
//error_log("Detected template $template");
switch ($template)
{
case 'month':
// Trim to _only_ the month, do not pad to week start / end
$time = new Api\DateTime($this->date);
$timespan = array(array(
'start' => Api\DateTime::to($time->format('Y-m-01 00:00:00'),'ts'),
'end' => Api\DateTime::to($time->format('Y-m-t 23:59:59'),'ts')
));
break;
case 'week':
$timespan = array();
$start = new Api\DateTime($this->first);
$t = clone $start;
$t->modify('+1 week')->modify('-1 second');
if($t->format('ts') < Api\DateTime::to($this->last,'ts'))
{
do
{
$timespan[] = array(
'start' => $start->format('ts'),
'end' => $t->format('ts')
);
$start->modify('+1 week');
$t->modify('+1 week');
} while( $start->format('ts') < $this->last);
break;
}
// Fall through
default:
$timespan = array(array(
'start' => is_array($this->first) ? $this->bo->date2ts($this->first) : $this->first,
'end' => is_array($this->last) ? $this->bo->date2ts($this->last) : $this->last
));
}
}
$document = $_GET['merge'];
$merge = new calendar_merge();
if($error = $merge->check_document($document, $GLOBALS['egw_info']['user']['preferences']['calendar']['document_dir']))
{
return $error;
}
if(!$error && $this->merge_collabora($document, $timespan))
{
return false;
}
else if (!$error)
{
//error_log($_GET['merge'] . ' Timespan: ');foreach($timespan as $t) error_log(Api\DateTime::to($t['start']) . ' - ' . Api\DateTime::to($t['end']));
$error = $merge->download($document, $timespan, '', $GLOBALS['egw_info']['user']['preferences']['calendar']['document_dir']);
}
// Here? Doesn't actually give the message
Framework::refresh_opener($error, 'calendar');
}
unset($_GET['merge']);
if($error)
{
// This doesn't give message either, but at least it doesn't give a blank screen
Framework::redirect_link('/index.php', array(
'msg' => $error,
'cd' => 'yes'
));
}
}
protected function merge_collabora($document, $timespan)
{
$file = Api\Vfs::stat($document);
if(!$file['mime'])
{
$file['mime'] = Api\Vfs::mime_content_type($document);
}
$editable_mimes = array();
try
{
if (class_exists('EGroupware\\collabora\\Bo') &&
$GLOBALS['egw_info']['user']['apps']['collabora'] &&
$discovery = \EGroupware\collabora\Bo::discover()
)
{
$editable_mimes = $discovery;
}
}
catch (\Exception $e)
{
return false;
}
$timespan = json_encode($timespan);
if ($editable_mimes[$file['mime']])
{
Framework::popup(Framework::link('/index.php', array(
'menuaction' => 'collabora.EGroupware\\collabora\\Ui.merge_edit',
'document' => $document,
'merge' => 'calendar_merge',
'id' => $timespan
)),'_blank',false);
return true;
}
return false;
}
} }

View File

@ -191,23 +191,6 @@ class calendar_uiviews extends calendar_ui
*/ */
function index($content=array()) function index($content=array())
{ {
if($content['merge'])
{
// View from sidebox is JSON encoded
$this->manage_states(array_merge($content,json_decode($content['view'],true)));
if($content['first'])
{
$this->first = Api\DateTime::to($content['first'],'ts');
}
if($content['last'])
{
$this->last = Api\DateTime::to($content['last'],'ts');
}
$_GET['merge'] = $content['merge'];
$this->merge();
return;
}
// handle views in other files // handle views in other files
if (!isset($this->public_functions[$this->view]) && $this->view !== 'listview') if (!isset($this->public_functions[$this->view]) && $this->view !== 'listview')
{ {
@ -499,16 +482,6 @@ class calendar_uiviews extends calendar_ui
$this->last = $this->bo->date2ts($this->last); $this->last = $this->bo->date2ts($this->last);
} }
$merge = $this->merge();
if($merge)
{
Egw::redirect_link('/index.php',array(
'menuaction' => 'calendar.calendar_uiviews.index',
'msg' => $merge,
'ajax' => 'true'
));
}
$search_params = $this->search_params; $search_params = $this->search_params;
$search_params['daywise'] = false; $search_params['daywise'] = false;
$search_params['start'] = $this->first; $search_params['start'] = $this->first;

View File

@ -2533,7 +2533,7 @@ export class CalendarApp extends EgwApp
} }
if(action && selected) if(action && selected)
{ {
action.execute(selected); super.merge(action, selected);
} }
} }
else else
@ -2541,16 +2541,23 @@ export class CalendarApp extends EgwApp
// Set the hidden inputs to the current time span & submit // Set the hidden inputs to the current time span & submit
widget.getRoot().getWidgetById('first').set_value(app.calendar.state.first); widget.getRoot().getWidgetById('first').set_value(app.calendar.state.first);
widget.getRoot().getWidgetById('last').set_value(app.calendar.state.last); widget.getRoot().getWidgetById('last').set_value(app.calendar.state.last);
if(widget.getRoot().getArrayMgr('content').getEntry('collabora_enabled'))
{ let vars = {
widget.getInstanceManager().submit(); menuaction: 'calendar.calendar_merge.merge_entries',
} document: widget.getValue(),
else merge: 'calendar_merge',
{ pdf: false,
widget.getInstanceManager().postSubmit(); select_all: false,
window.setTimeout(function() {widget.set_value('');},100); id: JSON.stringify({
} first: app.calendar.state.first,
last: app.calendar.state.last,
date: app.calendar.state.first,
view: app.calendar.state.view
})
};
egw.open_link(egw.link('/index.php', vars), '_blank');
} }
widget.set_value('');
return false; return false;
} }

View File

@ -160,7 +160,7 @@ class filemanager_admin extends filemanager_ui
{ {
throw new Api\Exception\WrongUserinput(lang('SMB, WebDAVs and VFS require a username!')); throw new Api\Exception\WrongUserinput(lang('SMB, WebDAVs and VFS require a username!'));
} }
$url .= $content['mounts']['url']['user'] === '$user' ? '$user' : urlencode(trim($content['mounts']['url']['user'])); $url .= str_replace(urlencode('$user'), '$user', urlencode(trim($content['mounts']['url']['user'])));
if (!empty($content['mounts']['url']['pass'])) if (!empty($content['mounts']['url']['pass']))
{ {
$url .= ':' . ($content['mounts']['url']['pass'] === '$pass' ? '$pass' : urlencode(trim($content['mounts']['url']['pass']))); $url .= ':' . ($content['mounts']['url']['pass'] === '$pass' ? '$pass' : urlencode(trim($content['mounts']['url']['pass'])));
@ -174,7 +174,7 @@ class filemanager_admin extends filemanager_ui
{ {
throw new Api\Exception\WrongUserinput(lang('Versioning requires EGroupware EPL')); throw new Api\Exception\WrongUserinput(lang('Versioning requires EGroupware EPL'));
} }
elseif (!Vfs::file_exists(Vfs::decodePath($path)) || Vfs::file_exists($path) && !Vfs::is_dir($path)) elseif (Vfs::file_exists($path) && !Vfs::is_dir($path))
{ {
throw new Api\Exception\WrongUserinput(lang('Path %1 not found or not a directory!', $path)); throw new Api\Exception\WrongUserinput(lang('Path %1 not found or not a directory!', $path));
} }
@ -186,7 +186,8 @@ class filemanager_admin extends filemanager_ui
else else
{ {
$msg = Vfs::mount($url, $path, true) ? $msg = Vfs::mount($url, $path, true) ?
lang('Successful mounted %1 on %2.', $url, $path) : lang('Error mounting %1 on %2!', $url, $path); lang('Successful mounted %1 on %2.', str_replace('%5C', '\\', $url), $path) :
lang('Error mounting %1 on %2!', str_replace('%5C', '\\', $url), $path);
} }
} }
if ($content['allow_delete_versions'] != $GLOBALS['egw_info']['server']['allow_delete_versions']) if ($content['allow_delete_versions'] != $GLOBALS['egw_info']['server']['allow_delete_versions'])
@ -297,7 +298,8 @@ class filemanager_admin extends filemanager_ui
{ {
$content['mounts'][$n++] = array( $content['mounts'][$n++] = array(
'path' => $path, 'path' => $path,
'url' => preg_replace('#://([^:@/]+):((?!\$pass)[^@/]+)@#', '://$1:****@', $url), 'url' => preg_replace('#://([^:@/]+):((?!\$pass)[^@/]+)@#', '://$1:****@',
str_replace('%5c', '\\', $url)),
); );
$readonlys["disable[$path]"] = !$this->versioning || !Vfs::$is_root || $readonlys["disable[$path]"] = !$this->versioning || !Vfs::$is_root ||
Vfs::parse_url($url,PHP_URL_SCHEME) != $this->versioning; Vfs::parse_url($url,PHP_URL_SCHEME) != $this->versioning;

View File

@ -193,10 +193,12 @@ class importexport_definition implements importexport_iface_egw_record {
* *
* @param array $options * @param array $options
*/ */
private function set_options(array $_plugin_options) { private function set_options(array $_plugin_options)
{
// Check conditions // Check conditions
foreach ( (Array)$_plugin_options['conditions'] as $key => $condition ) { foreach ((array)$_plugin_options['conditions'] as $key => $condition)
if(!$condition['string'] && array_key_exists($key, $_plugin_options['conditions'])) {
if (is_array($condition) && empty($condition['string']) && array_key_exists($key, $_plugin_options['conditions']))
{ {
unset($_plugin_options['conditions'][$key]); unset($_plugin_options['conditions'][$key]);
} }

View File

@ -818,9 +818,9 @@ class importexport_definitions_ui
if($this->can_edit($content)) if($this->can_edit($content))
{ {
$content['owner'] = $content['just_me'] || !$GLOBALS['egw']->acl->check('share_definitions', Acl::READ,'importexport') ? $content['owner'] = $content['just_me'] || !$GLOBALS['egw']->acl->check('share_definitions', Acl::READ,'importexport') ?
($content['owner'] ? $content['owner'] : $GLOBALS['egw_info']['user']['account_id']) : ($content['owner'] ?: $GLOBALS['egw_info']['user']['account_id']) :
null; null;
$content['allowed_users'] = $content['just_me'] ? '' : ($content['all_users'] ? 'all' : implode(',',$content['allowed_users'])); $content['allowed_users'] = $content['just_me'] ? '' : ($content['all_users'] ? 'all' : implode(',', (array)$content['allowed_users']));
unset($content['just_me']); unset($content['just_me']);
} }

View File

@ -317,7 +317,7 @@ class importexport_schedule_ui
'method' => 'HEAD', 'method' => 'HEAD',
'ignore_errors' => 1 'ignore_errors' => 1
))); )));
$headers = get_headers($data['target'],1); $headers = get_headers($data['target'],1) ?: [];
// Reset... // Reset...
stream_context_set_default(array('http'=>array( stream_context_set_default(array('http'=>array(
@ -551,7 +551,7 @@ class importexport_schedule_ui
foreach($po->get_warnings() as $record => $msg) foreach($po->get_warnings() as $record => $msg)
{ {
$data['warnings'][$target][] = "#$record: $msg"; $data['warnings'][$target][] = "#$record: $msg";
$buffer += "$record\t$msg\n"; $buffer .= "$record\t$msg\n";
} }
error_log($buffer); error_log($buffer);
} }
@ -561,7 +561,7 @@ class importexport_schedule_ui
foreach($po->get_errors() as $record => $error) foreach($po->get_errors() as $record => $error)
{ {
$data['errors'][$target][] = "#$record: $error"; $data['errors'][$target][] = "#$record: $error";
$buffer += "$record\t$error\n"; $buffer .= "$record\t$error\n";
} }
error_log($buffer); error_log($buffer);
} }

View File

@ -961,6 +961,10 @@ class infolog_bo
$check_modified = $values['info_datemodified'] && !$xmlrpc ? $to_write['info_datemodified'] : false; $check_modified = $values['info_datemodified'] && !$xmlrpc ? $to_write['info_datemodified'] : false;
$values['info_datemodified'] = $this->user_time_now; $values['info_datemodified'] = $this->user_time_now;
$to_write['info_datemodified'] = $this->now; $to_write['info_datemodified'] = $this->now;
if ($check_modified && isset($values['info_etag']))
{
++$values['info_etag'];
}
} }
if ($touch_modified || !$values['info_modifier']) if ($touch_modified || !$values['info_modifier'])
{ {
@ -979,7 +983,7 @@ class infolog_bo
if (($info_id = $this->so->write($to_write, $check_modified, $purge_cfs, !isset($old)))) if (($info_id = $this->so->write($to_write, $check_modified, $purge_cfs, !isset($old))))
{ {
if(!isset($values['info_type']) || $status_only || empty($values['caldav_url'])) if(!isset($values['info_type']) || $status_only || empty($values['caldav_name']))
{ {
$values = $this->read($info_id, true, 'server', $ignore_acl); $values = $this->read($info_id, true, 'server', $ignore_acl);
} }
@ -1006,12 +1010,13 @@ class infolog_bo
$values['link_to']['to_id'] = $info_id; $values['link_to']['to_id'] = $info_id;
} }
} }
if($values['info_id'] && $info_id !== false) if ($values['info_id'] && $info_id)
{ {
$this->write_check_links($to_write); $this->write_check_links($to_write);
if(!$values['info_link_id'] || $values['info_link_id'] != $to_write['info_link_id']) if(!$values['info_link_id'] || $values['info_link_id'] != $to_write['info_link_id'])
{ {
// Just got a link ID, need to save it // Just got a link ID, need to save it
unset($to_write['info_etag']); // we must not increment it again
$this->so->write($to_write); $this->so->write($to_write);
$values['info_link_id'] = $to_write['info_link_id']; $values['info_link_id'] = $to_write['info_link_id'];
$values['info_contact'] = $to_write['info_contact']; $values['info_contact'] = $to_write['info_contact'];
@ -1396,6 +1401,7 @@ class infolog_bo
*/ */
function import_mail($_addresses,$_subject,$_message,$_attachments,$_date) function import_mail($_addresses,$_subject,$_message,$_attachments,$_date)
{ {
$names = $emails = [];
foreach($_addresses as $address) foreach($_addresses as $address)
{ {
$names[] = $address['name']; $names[] = $address['name'];
@ -1407,7 +1413,7 @@ class infolog_bo
$info = array( $info = array(
'info_id' => 0, 'info_id' => 0,
'info_type' => $type, 'info_type' => $type,
'info_from' => implode(', ',$names) . implode(', ', $emails), 'info_from' => implode(', ', $names) . implode(', ', $emails),
'info_subject' => $_subject, 'info_subject' => $_subject,
'info_des' => $_message, 'info_des' => $_message,
'info_startdate' => Api\DateTime::server2user($_date), 'info_startdate' => Api\DateTime::server2user($_date),

View File

@ -584,7 +584,19 @@ class infolog_so
if (($this->data['info_id'] = $info_id) && !$force_insert) if (($this->data['info_id'] = $info_id) && !$force_insert)
{ {
$where = array('info_id' => $info_id); $where = array('info_id' => $info_id);
if ($check_modified) $where['info_datemodified'] = $check_modified; if ($check_modified)
{
$where['info_datemodified'] = $check_modified;
// also check etag, if we got it
if (isset($values['info_etag']))
{
$where['info_etag'] = $values['info_etag'];
}
unset($to_write['info_etag']);
// and increment it
$to_write[] = 'info_etag=info_etag+1';
}
if (!$this->db->update($this->info_table,$to_write,$where,__LINE__,__FILE__)) if (!$this->db->update($this->info_table,$to_write,$where,__LINE__,__FILE__))
{ {
//error_log("### soinfolog::write(".print_r($to_write,true).") where=".print_r($where,true)." returning false"); //error_log("### soinfolog::write(".print_r($to_write,true).") where=".print_r($where,true)." returning false");

View File

@ -173,7 +173,12 @@ class DoubleLinkPMTest extends \EGroupware\Api\EtemplateTest
$this->makeProject('3'); $this->makeProject('3');
$new_project = $this->pm_id[2]; $new_project = $this->pm_id[2];
// Sleep for a bit to make the modified time different, or it will fail
sleep(1);
// Fake opening the edit dialog, important not to pass an array to accurately copy normal behaviour // Fake opening the edit dialog, important not to pass an array to accurately copy normal behaviour
// New BO to make sure we get a clean load, no caching
$this->ui->bo = $this->bo = new \infolog_bo();
$this->ui->edit($this->info_id); $this->ui->edit($this->info_id);
$content = self::$mocked_exec_result; $content = self::$mocked_exec_result;

View File

@ -2077,7 +2077,7 @@ class mail_compose
{ {
//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); $eventid = $calendar_ical->iCalSearch($attachment['attachment'],-1);
//error_log(__METHOD__.array2string($eventid)); //error_log(__METHOD__.array2string($eventid));
if (!$eventid) $eventid = -1; if (!$eventid) $eventid = -1;
$event = $calendar_ical->importVCal($attachment['attachment'],(is_array($eventid)?$eventid[0]:$eventid),null,true); $event = $calendar_ical->importVCal($attachment['attachment'],(is_array($eventid)?$eventid[0]:$eventid),null,true);

View File

@ -16,7 +16,8 @@ import {et2_createWidget} from "../../api/js/etemplate/et2_core_widget";
import {et2_dialog} from "../../api/js/etemplate/et2_widget_dialog"; import {et2_dialog} from "../../api/js/etemplate/et2_widget_dialog";
import {et2_button} from "../../api/js/etemplate/et2_widget_button"; import {et2_button} from "../../api/js/etemplate/et2_widget_button";
import {egw_getObjectManager} from '../../api/js/egw_action/egw_action.js'; import {egw_getObjectManager} from '../../api/js/egw_action/egw_action.js';
import {egwIsMobile} from "../../api/js/egw_action/egw_action_common.js"; import {egwIsMobile, egwSetBit} from "../../api/js/egw_action/egw_action_common.js";
import {EGW_AO_FLAG_DEFAULT_FOCUS} from "../../api/js/egw_action/egw_action_constants.js";
import {egw_keycode_translation_function, egw_keycode_makeValid} from "../../api/js/egw_action/egw_keymanager.js"; import {egw_keycode_translation_function, egw_keycode_makeValid} from "../../api/js/egw_action/egw_keymanager.js";
/* required dependency, commented out because no module, but egw:uses is no longer parsed /* required dependency, commented out because no module, but egw:uses is no longer parsed
*/ */
@ -198,7 +199,7 @@ app.classes.mail = AppJS.extend(
break; break;
case 'mail.index': case 'mail.index':
var self = this; var self = this;
jQuery('iframe#mail-index_messageIFRAME').on('load', function() jQuery('iframe#mail-index_messageIFRAME').on('load', function ()
{ {
// decrypt preview body if mailvelope is available // decrypt preview body if mailvelope is available
self.mailvelopeAvailable(self.mailvelopeDisplay); self.mailvelopeAvailable(self.mailvelopeDisplay);
@ -207,20 +208,24 @@ app.classes.mail = AppJS.extend(
var nm = this.et2.getWidgetById(this.nm_index); var nm = this.et2.getWidgetById(this.nm_index);
this.mail_isMainWindow = true; this.mail_isMainWindow = true;
// Stop list from focussing next row on keypress
egw_getObjectManager('nm').flags = egwSetBit(egw_getObjectManager('nm').flags, EGW_AO_FLAG_DEFAULT_FOCUS, false);
// Set preview pane state // Set preview pane state
this.mail_disablePreviewArea(!this.getPreviewPaneState()); this.mail_disablePreviewArea(!this.getPreviewPaneState());
//Get initial folder status //Get initial folder status
this.mail_refreshFolderStatus(undefined,undefined,false); this.mail_refreshFolderStatus(undefined, undefined, false);
// Bind to nextmatch refresh to update folder status // Bind to nextmatch refresh to update folder status
if(nm != null && (typeof jQuery._data(nm).events=='undefined'||typeof jQuery._data(nm).events.refresh == 'undefined')) if (nm != null && (typeof jQuery._data(nm).events == 'undefined' || typeof jQuery._data(nm).events.refresh == 'undefined'))
{ {
var self = this; var self = this;
jQuery(nm).on('refresh',function(_event, _widget, _row_id, _type) { jQuery(nm).on('refresh', function (_event, _widget, _row_id, _type)
{
if (!self.push_active[_widget.settings.foldertree.split("::")[0]]) if (!self.push_active[_widget.settings.foldertree.split("::")[0]])
{ {
self.mail_refreshFolderStatus.call(self,undefined,undefined,false); self.mail_refreshFolderStatus.call(self, undefined, undefined, false);
} }
}); });
} }
@ -2189,11 +2194,11 @@ app.classes.mail = AppJS.extend(
} }
// Tell server // Tell server
egw.json('mail.mail_ui.ajax_deleteMessages',[_msg,(typeof _action == 'undefined'?'no':_action)]) egw.json('mail.mail_ui.ajax_deleteMessages', [_msg, (typeof _action == 'undefined' ? 'no' : _action)])
.sendRequest(true); .sendRequest(true);
if (_msg['all']) this.egw.refresh(this.egw.lang("deleted %1 messages in %2",(_msg['all']?egw.lang('all'):_msg['msg'].length),(displayname?displayname:egw.lang('current folder'))),'mail');//,ids,'delete'); if (_msg['all']) this.egw.refresh(this.egw.lang("deleted %1 messages in %2",(_msg['all']?egw.lang('all'):_msg['msg'].length),(displayname?displayname:egw.lang('current folder'))),'mail');//,ids,'delete');
this.egw.message(this.egw.lang("deleted %1 messages in %2",(_msg['all']?egw.lang('all'):_msg['msg'].length),(displayname?displayname:egw.lang('current Folder'))), 'success'); this.egw.message(this.egw.lang("deleted %1 messages in %2", (_msg['all'] ? egw.lang('all') : _msg['msg'].length), (displayname ? displayname : egw.lang('current Folder'))), 'success');
}, },
/** /**
@ -2214,7 +2219,7 @@ app.classes.mail = AppJS.extend(
{ {
for (var i = 0; i < _msg['msg'].length; i++) for (var i = 0; i < _msg['msg'].length; i++)
{ {
this.egw.refresh(_msg['egw_message'], 'mail', _msg['msg'][i].replace(/mail::/,''), 'delete'); this.egw.refresh(_msg['egw_message'], 'mail', _msg['msg'][i].replace(/mail::/, ''), 'delete');
} }
// Nextmatch automatically selects the next row and calls preview. // Nextmatch automatically selects the next row and calls preview.