Merge remote-tracking branch 'origin/master'

This commit is contained in:
Milan 2023-07-20 16:40:50 +02:00
commit c170c44168
18 changed files with 166 additions and 128 deletions

View File

@ -56,7 +56,7 @@ class addressbook_wizard_import_contacts_csv extends importexport_wizard_basic_i
unset($this->mapping_fields['jpegphoto']); // can't cvs import that unset($this->mapping_fields['jpegphoto']); // can't cvs import that
// Add in special handled fields // Add in special handled fields
$this->mapping_fields[lang('Special')] = addressbook_import_contacts_csv::$special_fields; $this->mapping_fields += addressbook_import_contacts_csv::$special_fields;
// Actions // Actions
$this->actions = array( $this->actions = array(

View File

@ -2336,8 +2336,9 @@ class CalDAV extends HTTP_WebDAV_Server
$this->store_request = $_SERVER['REQUEST_METHOD'] != 'POST' || $this->store_request = $_SERVER['REQUEST_METHOD'] != 'POST' ||
!self::isFileUpload() || !self::isFileUpload() ||
substr($_SERVER['CONTENT_TYPE'], 0, 5) == 'text/'; substr($_SERVER['CONTENT_TYPE'], 0, 5) == 'text/';
ob_start();
} }
// unconditionally start output-buffering to fix problems with huge multiget reports from TB110 AB
ob_start();
parent::ServeRequest($prefix); parent::ServeRequest($prefix);
if (self::$request_starttime) self::log_request(); if (self::$request_starttime) self::log_request();

View File

@ -78,13 +78,13 @@ class Contact extends Entry
'__default__' => array( '__default__' => array(
'options' => array( 'options' => array(
'bday' => array('type' => 'date', 'options' => 'Y-m-d'), 'bday' => array('type' => 'date', 'options' => 'Y-m-d'),
'owner' => array('type' => 'select-account', 'options' => ''), 'owner' => array('type' => 'select-account'),
'modifier' => array('type' => 'select-account', 'options' => ''), 'modifier' => array('type' => 'select-account'),
'creator' => array('type' => 'select-account', 'options' => ''), 'creator' => array('type' => 'select-account'),
'modifed' => array('type' => 'date-time', 'options' => ''), 'modifed' => array('type' => 'date-time'),
'created' => array('type' => 'date-time', 'options' => ''), 'created' => array('type' => 'date-time'),
'cat_id' => array('type' => 'select-cat', 'options' => ''), 'cat_id' => array('type' => 'select-cat'),
'__default__' => array('type' => 'label', 'options' => ''), '__default__' => array('type' => 'label'),
), ),
'noLang' => true, 'noLang' => true,
), ),

View File

@ -69,7 +69,7 @@ abstract class Entry extends Transformer
$form_name = self::form_name($cname, $this->id); $form_name = self::form_name($cname, $this->id);
$prefixed_id = (substr($this->id, 0, 1) == self::ID_PREFIX ? $this->id : self::ID_PREFIX . $this->id); $prefixed_id = (substr($this->id, 0, 1) == self::ID_PREFIX ? $this->id : self::ID_PREFIX . $this->id);
$data_id = $attrs['value'] ? self::form_name($cname, $attrs['value']) : self::form_name($cname, $prefixed_id); $data_id = !empty($attrs['value']) ? self::form_name($cname, $attrs['value']) : self::form_name($cname, $prefixed_id);
// No need to proceed // No need to proceed
if(!$data_id) return; if(!$data_id) return;
@ -151,9 +151,9 @@ abstract class Entry extends Transformer
$value =& $data; $value =& $data;
if(!is_array($value)) return $value; if(!is_array($value)) return $value;
foreach(array($attrs['field']) + explode(':',$attrs['alternate_fields']) as $field) foreach(array($attrs['field']) + explode(':', ($attrs['alternate_fields'] ?? '')) as $field)
{ {
if($value[$field]) if(!empty($value[$field]))
{ {
return $value[$field]; return $value[$field];
} }
@ -175,12 +175,12 @@ abstract class Entry extends Transformer
*/ */
protected function customfield($attrs, &$data) protected function customfield($attrs, &$data)
{ {
list($app, $type) = explode('-',$attrs['type']); list($app, $type) = explode('-', $attrs['type']);
$data_id = $attrs['value'] ?: $attrs['id']; $data_id = isset($attrs['value']) ? $attrs['value'] : $attrs['id'];
$id = is_array($data) ? static::get_array($data, $data_id) : $data; $id = is_array($data) ? static::get_array($data, $data_id) : $data;
if(!$app || !$type || !$GLOBALS['egw_info']['apps'][$app] || !$id || if(!$app || !$type || !isset($GLOBALS['egw_info']['apps'][$app]) || !$id ||
// Simple CF, already there // Simple CF, already there
$data[$attrs['field']] isset($data[$attrs['field']])
) )
{ {
return; return;
@ -194,7 +194,7 @@ abstract class Entry extends Transformer
$merge = new $classname(); $merge = new $classname();
$replacement_field = '$$'.$attrs['field'].'$$'; $replacement_field = '$$'.$attrs['field'].'$$';
$replacements = $merge->get_app_replacements($app, $id, $replacement_field); $replacements = $merge->get_app_replacements($app, $id, $replacement_field);
$data[$attrs['field']] = $replacements[$replacement_field]; $data[$attrs['field']] = $replacements[$replacement_field] ?? '';
} }
catch(\Exception $e) catch(\Exception $e)
{ {
@ -212,7 +212,7 @@ abstract class Entry extends Transformer
protected function regex($attrs, &$data) protected function regex($attrs, &$data)
{ {
$value =& $this->get_data_field($attrs, $data); $value =& $this->get_data_field($attrs, $data);
if(!$attrs['regex'] || !$value) if(!isset($attrs['regex']) || !$value)
{ {
return; return;
} }

View File

@ -219,15 +219,18 @@ abstract class Transformer extends Etemplate\Widget
elseif(is_array($action)) elseif(is_array($action))
{ {
// case matches --> run all actions // case matches --> run all actions
if (isset($action[$attrs[$attr]]) || !isset($action[$attrs[$attr]]) && isset($action['__default__'])) if(isset($attrs[$attr]) && isset($action[$attrs[$attr]]) || (!isset($attrs[$attr]) || !isset($action[$attrs[$attr]])) && isset($action['__default__']))
{ {
$actions = isset($action[$attrs[$attr]]) ? $action[$attrs[$attr]] : $action['__default__']; $actions = isset($attrs[$attr]) && isset($action[$attrs[$attr]]) ? $action[$attrs[$attr]] : $action['__default__'];
if(!is_array($actions)) if(!is_array($actions))
{ {
$attrs[$attr] = $actions; $attrs[$attr] = $actions;
$actions = array($attr => $actions); $actions = array($attr => $actions);
} }
if (self::DEBUG) error_log(__METHOD__."(attr='$attr', action=".array2string($action).") attrs['$attr']=='{$attrs[$attr]}' --> running actions"); if(self::DEBUG)
{
error_log(__METHOD__ . "(attr='$attr', action=" . array2string($action) . ") attrs['$attr']=='{$attrs[$attr]}' --> running actions");
}
foreach($actions as $attr => $action) foreach($actions as $attr => $action)
{ {
$this->action($attr, $action, $attrs); $this->action($attr, $action, $attrs);

View File

@ -331,6 +331,7 @@ class IncludeMgr
// we will do no further processing but just include the file // we will do no further processing but just include the file
// XXX: Is this case still used? If yes, it will not work with // XXX: Is this case still used? If yes, it will not work with
// adding the ctime to all js files... // adding the ctime to all js files...
$args = '';
if (is_array($file)) if (is_array($file))
{ {
foreach($file as $name => $val) foreach($file as $name => $val)

View File

@ -60,10 +60,10 @@ class Http
static function schema() static function schema()
{ {
return !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || return !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ||
$_SERVER['SERVER_PORT'] == 443 || $_SERVER['SERVER_PORT'] == 443 ||
!empty($GLOBALS['egw_info']['server']['enforce_ssl']) || !empty($GLOBALS['egw_info']['server']['enforce_ssl']) ||
$_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https' ? isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https' ?
'https' : 'http'; 'https' : 'http';
} }
/** /**

View File

@ -1683,13 +1683,14 @@ abstract class Merge
{ {
if(str_starts_with($cf_sub, '#')) if(str_starts_with($cf_sub, '#'))
{ {
$expand_sub_cfs[$cf[$index]] .= '$$' . $cf_sub . '$$ '; $expand_sub_cfs[$cf[$index]] = (isset($expand_sub_cfs[$cf[$index]]) ? $expand_sub_cfs[$cf[$index]] : '') .
'$$' . $cf_sub . '$$ ';
} }
} }
foreach($cf as $index => $field) foreach($cf as $index => $field)
{ {
if($cfs[$field]) if(isset($cfs[$field]))
{ {
if(in_array($cfs[$field]['type'], array_keys($GLOBALS['egw_info']['apps']))) if(in_array($cfs[$field]['type'], array_keys($GLOBALS['egw_info']['apps'])))
{ {
@ -1719,18 +1720,18 @@ abstract class Merge
} }
// Get replacements for that application // Get replacements for that application
if(!$app_replacements[$field]) if(!isset($app_replacements[$field]))
{ {
// If we send the real content it can result in infinite loop of lookups // If we send the real content it can result in infinite loop of lookups
// so we send only the used fields // so we send only the used fields
$content = $expand_sub_cfs[$field] ?? $matches[0][$index]; $content = $expand_sub_cfs[$field] ?? $matches[0][$index];
$app_replacements[$field] = $this->get_app_replacements($field_app, $values['#' . $field], $content); $app_replacements[$field] = $this->get_app_replacements($field_app, $values['#' . $field], $content);
} }
$replacements[$placeholders[$index]] = $app_replacements[$field]['$$' . $sub[$index] . '$$']; $replacements[$placeholders[$index]] = $app_replacements[$field]['$$' . $sub[$index] . '$$'] ?? '';
} }
else else
{ {
if($cfs[$field]['type'] == 'date' || $cfs[$field]['type'] == 'date-time') if(isset($cfs[$field]) && ($cfs[$field]['type'] == 'date' || $cfs[$field]['type'] == 'date-time'))
{ {
$this->date_fields[] = '#' . $field; $this->date_fields[] = '#' . $field;
} }

View File

@ -1450,6 +1450,19 @@ div.et2_vfsPath li img {
visibility: visible; visibility: visible;
} }
/* Category indents in select options */
sl-menu-item.cat_level1::part(label) {
padding-left: var(--sl-spacing-medium, 1em);
}
sl-menu-item.cat_level2::part(label) {
padding-left: calc(2 * var(--sl-spacing-medium, 1em));
}
sl-menu-item.cat_level3::part(label) {
padding-left: calc(3 * var(--sl-spacing-medium, 1em));
}
.egw_tooltip { .egw_tooltip {
position: fixed; position: fixed;
border: 1px solid #897f51; border: 1px solid #897f51;

View File

@ -134,7 +134,7 @@ class calendar_holidays
{ {
ksort($data); ksort($data);
} }
error_log(__METHOD__."('$country', $year, $end_year) took ". number_format(microtime(true)-$starttime, 3).'s to fetch '.count(call_user_func_array('array_merge', $years)).' events'); //error_log(__METHOD__."('$country', $year, $end_year) took ". number_format(microtime(true)-$starttime, 3).'s to fetch '.count(call_user_func_array('array_merge', $years)).' events');
unset($starttime); unset($starttime);
return $until_year ? $years : $years[$year]; return $until_year ? $years : $years[$year];

View File

@ -51,7 +51,7 @@ export class CalendarOwner extends Et2StaticSelectMixin(Et2Select)
// Start fetch of users // Start fetch of users
const type = this.egw().preference('account_selection', 'common'); const type = this.egw().preference('account_selection', 'common');
if(!type || type == "none" || type == "selectbox") if(!type || type == "none")
{ {
return; return;
} }
@ -65,6 +65,7 @@ export class CalendarOwner extends Et2StaticSelectMixin(Et2Select)
else else
{ {
fetch.push(this.egw().accounts('accounts').then(options => {this.static_options = this.static_options.concat(cleanSelectOptions(options))})); fetch.push(this.egw().accounts('accounts').then(options => {this.static_options = this.static_options.concat(cleanSelectOptions(options))}));
fetch.push(this.egw().accounts('groups').then(options => {this.static_options = this.static_options.concat(cleanSelectOptions(options))}));
} }
this.fetchComplete = Promise.all(fetch) this.fetchComplete = Promise.all(fetch)
.then(() => this._renderOptions()); .then(() => this._renderOptions());

View File

@ -4010,8 +4010,17 @@ export class CalendarApp extends EgwApp
var all_loaded = this.sidebox_et2 !== null; var all_loaded = this.sidebox_et2 !== null;
// Avoid home portlets using our templates, and get them right // Avoid home portlets using our templates, and get them right
if(_et2.uniqueId.indexOf('portlet') === 0) return; if(_et2.uniqueId.indexOf('portlet') === 0)
if(_et2.uniqueId === 'calendar-add') return; {
return;
}
// Skip templates not involved in main view
if(['calendar.conflicts', 'calendar.add'].includes(_name))
{
return;
}
// Flag to make sure we don't hide non-view templates // Flag to make sure we don't hide non-view templates
var view_et2 = false; var view_et2 = false;

View File

@ -540,7 +540,7 @@ export class et2_calendar_view extends et2_valueWidget
{ {
// Create event // Create event
this._drag_create_event() this._drag_create_event()
}, 100); }, 500);
} }
/** /**

View File

@ -115,48 +115,39 @@
</grid> </grid>
</template> </template>
<template id="filemanager.file.eacl" template="" lang="" group="0" version="1.9.001"> <template id="filemanager.file.eacl" template="" lang="" group="0" version="1.9.001">
<grid width="100%" spacing="10"> <et2-vbox class="full-height">
<columns> <groupbox style="flex-grow: 1">
<column width="120"/> <caption label="Extended access control list"/>
<column/> <grid width="100%" overflow="auto" id="eacl">
<column/> <columns>
</columns> <column width="80"/>
<rows> <column width="80"/>
<row valign="top" height="200"> <column width="20%"/>
<groupbox span="all"> <column width="16"/>
<caption label="Extended access control list"/> </columns>
<grid width="100%" overflow="auto" id="eacl"> <rows>
<columns> <row class="th">
<column width="80"/> <et2-description value="Owner"></et2-description>
<column width="80"/> <et2-description value="Rights"></et2-description>
<column width="20%"/> <et2-description value="Inherited"></et2-description>
<column width="16"/> <et2-description></et2-description>
</columns> </row>
<rows> <row class="row" disabled="!@1">
<row class="th"> <et2-select-account id="${row}[owner]" readonly="true"></et2-select-account>
<et2-description value="Owner"></et2-description> <et2-select id="${row}[rights]" readonly="true"></et2-select>
<et2-description value="Rights"></et2-description> <et2-description id="${row}[path]"></et2-description>
<et2-description value="Inherited"></et2-description> <et2-button label="Delete" id="delete[$row_cont[ino]-$row_cont[owner]]" onclick="et2_dialog.confirm(widget,'Delete this extended ACL?','Delete')" image="delete"></et2-button>
<et2-description></et2-description> </row>
</row> </rows>
<row class="row" disabled="!@1"> </grid>
<et2-select-account id="${row}[owner]" readonly="true"></et2-select-account> </groupbox>
<et2-select id="${row}[rights]" readonly="true"></et2-select> <et2-hbox valign="bottom" disabled="!@is_owner">
<et2-description id="${row}[path]"></et2-description> <et2-select-account class="filemanager-file_filemanager-file-eaclowner eaclAccount" label="Owner" id="eacl_owner" emptyLabel="select one" accountType="both" placement="top"></et2-select-account>
<et2-button label="Delete" id="delete[$row_cont[ino]-$row_cont[owner]]" onclick="et2_dialog.confirm(widget,'Delete this extended ACL?','Delete')" image="delete"></et2-button> <et2-select class="eaclRights" statustext="You can only grant additional rights, you can NOT take rights away!" label="Rights" id="eacl[rights]"></et2-select>
</row> <et2-button label="Add" id="button[eacl]" image="add"></et2-button>
</rows> </et2-hbox>
</grid> </et2-vbox>
</groupbox> </template>
</row>
<row valign="bottom" disabled="!@is_owner">
<et2-select-account class="filemanager-file_filemanager-file-eaclowner eaclAccount" label="Owner" id="eacl_owner" emptyLabel="select one" accountType="both"></et2-select-account>
<et2-select class="eaclRights" statustext="You can only grant additional rights, you can NOT take rights away!" label="Rights" id="eacl[rights]"></et2-select>
<et2-button label="Add" id="button[eacl]" image="add"></et2-button>
</row>
</rows>
</grid>
</template>
<template id="filemanager.file.preview" template="" lang="" group="0" version="1.5.001"> <template id="filemanager.file.preview" template="" lang="" group="0" version="1.5.001">
<grid width="100%" spacing="10" overflow="auto"> <grid width="100%" spacing="10" overflow="auto">
<columns> <columns>
@ -259,48 +250,49 @@
<et2-button align="right" statustext="Enter setup user and password to get root rights" label="Superuser" id="sudouser" onclick="jQuery('.superuser').css('display','inline'); document.getElementById(form::name('sudo[user]')).focus();" image="superuser" noSubmit="true"></et2-button> <et2-button align="right" statustext="Enter setup user and password to get root rights" label="Superuser" id="sudouser" onclick="jQuery('.superuser').css('display','inline'); document.getElementById(form::name('sudo[user]')).focus();" image="superuser" noSubmit="true"></et2-button>
</et2-hbox> </et2-hbox>
</row> </row>
<row> <row>
<groupbox class="superuser"> <groupbox class="superuser">
<caption label="Enter setup user and password"/> <caption label="Enter setup user and password"/>
<grid> <grid>
<columns> <columns>
<column/> <column/>
<column/> <column/>
</columns> </columns>
<rows> <rows>
<row> <row>
<et2-description for="sudo[user]" value="User"></et2-description> <et2-description for="sudo[user]" value="User"></et2-description>
<et2-textbox id="sudo[user]"></et2-textbox> <et2-textbox id="sudo[user]"></et2-textbox>
</row> </row>
<row> <row>
<et2-description for="sudo[passwd]" value="Password"></et2-description> <et2-description for="sudo[passwd]" value="Password"></et2-description>
<et2-password id="sudo[passwd]" autocomplete="on"></et2-password> <et2-password id="sudo[passwd]" autocomplete="on"></et2-password>
</row> </row>
<row> <row>
<et2-description></et2-description> <et2-description></et2-description>
<et2-hbox> <et2-hbox>
<et2-button label="Submit" id="button[setup]"></et2-button> <et2-button label="Submit" id="button[setup]"></et2-button>
<et2-button label="Cancel" onclick="jQuery('.superuser').hide();" noSubmit="true"></et2-button> <et2-button label="Cancel" onclick="jQuery('.superuser').hide();" noSubmit="true"></et2-button>
</et2-hbox> </et2-hbox>
</row> </row>
</rows> </rows>
</grid> </grid>
</groupbox> </groupbox>
</row> </row>
</rows> </rows>
</grid> </grid>
<styles> <styles>
.eaclAccount select,.eaclRights select { width: 160px; } .full-height { height: 100% }
.superuser { .full-height > fieldset { flex-grow: 1; }
position: absolute; .superuser {
top: 130px; position: absolute;
left: 120px; top: 130px;
width: 200px; left: 120px;
background-color: white; width: 200px;
z-index: 1; background-color: white;
display: none; z-index: 1;
} display: none;
</styles> }
</template> </styles>
</template>
</overlay> </overlay>

View File

@ -351,7 +351,24 @@ class importexport_export_csv implements importexport_iface_export_record
$names = array(); $names = array();
foreach($record->$name as $_name) foreach($record->$name as $_name)
{ {
$option = $selects[$name][$_name]; $select_options = $selects[$name] ?? [];
$option = '';
foreach($select_options as $key => $select_option)
{
if(is_array($select_option) && isset($select_option['value']) && $select_option['value'] == $name && isset($select_option['label']))
{
$option = $select_option['label'];
break;
}
else
{
if($key == $_name && !is_array($select_option))
{
$option = $select_option;
break;
}
}
}
$names[] = lang(is_array($option) && $option['label'] ? $option['label'] : $option); $names[] = lang(is_array($option) && $option['label'] ? $option['label'] : $option);
} }
$record->$name = implode(', ', $names); $record->$name = implode(', ', $names);

View File

@ -357,9 +357,9 @@ class mail_integration {
foreach($mailcontent['attachments'] as $key => $attachment) foreach($mailcontent['attachments'] as $key => $attachment)
{ {
$data_attachments[$key] = array( $data_attachments[$key] = array(
'name' => $mailcontent['attachments'][$key]['name'], 'name' => $attachment['filename'] ?? $mailcontent['attachments'][$key]['name'],
'type' => $mailcontent['attachments'][$key]['type'], 'type' => $mailcontent['attachments'][$key]['type'],
'size' => $mailcontent['attachments'][$key]['size'], 'size' => $mailcontent['attachments'][$key]['size'],
'tmp_name' => $mailcontent['attachments'][$key]['tmp_name'] 'tmp_name' => $mailcontent['attachments'][$key]['tmp_name']
); );
if ($uid && !$mailcontent['attachments'][$key]['add_raw']) if ($uid && !$mailcontent['attachments'][$key]['add_raw'])

View File

@ -1207,13 +1207,13 @@ app.classes.mail = AppJS.extend(
}, },
{ {
id: 'saveOneToVfs', id: 'saveOneToVfs',
label: 'Save in Filemanager', label: 'Save to Filemanager',
icon: 'filemanager/navbar', icon: 'filemanager/navbar',
value: 'saveOneToVfs' value: 'saveOneToVfs'
}, },
{ {
id: 'saveAllToVfs', id: 'saveAllToVfs',
label: 'Save all to Filemanager', label: 'Save all attachments to Filemanager',
icon: 'mail/save_all', icon: 'mail/save_all',
value: 'saveAllToVfs' value: 'saveAllToVfs'
}, },

View File

@ -502,7 +502,7 @@ rule with priority mail en rule with priority
rules mail en rules rules mail en rules
s/mime encryption failed because no certificate has been found for sender address: %1 mail en S/MIME Encryption failed because no certificate has been found for sender address: %1 s/mime encryption failed because no certificate has been found for sender address: %1 mail en S/MIME Encryption failed because no certificate has been found for sender address: %1
save all mail en Save all save all mail en Save all
save all attachments to filemanager mail en Save all attachments to filemanager save all attachments to filemanager mail en Save all to filemanager
save as calendar mail en Save as Calendar save as calendar mail en Save as Calendar
save as default mail en save as default save as default mail en save as default
save as draft mail en Save as Draft save as draft mail en Save as Draft
@ -518,7 +518,7 @@ save message to disk mail en Save Message to disk
save of message %1 failed. could not save message to folder %2 due to: %3 mail en Save of message %1 failed. Could not save message to folder %2 due to: %3 save of message %1 failed. could not save message to folder %2 due to: %3 mail en Save of message %1 failed. Could not save message to folder %2 due to: %3
save the drafted message as eml file into vfs mail en Save the drafted message as eml file into VFS save the drafted message as eml file into vfs mail en Save the drafted message as eml file into VFS
save to disk mail en Save to disk save to disk mail en Save to disk
save to filemanager mail en Save to filemanager save to filemanager mail en Save in filemanager
save: mail en Save: save: mail en Save:
saves subscription changes mail en Save subscription changes saves subscription changes mail en Save subscription changes
saves this acl mail en Save this ACL saves this acl mail en Save this ACL