mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-12-22 06:30:59 +01:00
Merge branch 'master' into web-components
This commit is contained in:
commit
fbbc466c78
@ -696,10 +696,16 @@ class addressbook_groupdav extends Api\CalDAV\Handler
|
||||
{
|
||||
trim($attribute);
|
||||
list($key, $value) = explode('=', $attribute);
|
||||
// check if value is enclosed in quotes
|
||||
if (in_array($value[0], ['"', "'"], true) && $value[0] === substr($value, -1))
|
||||
{
|
||||
$value = substr($value,1,-1);
|
||||
}
|
||||
switch (strtolower($key))
|
||||
{
|
||||
case 'charset':
|
||||
$charset = strtoupper(substr($value,1,-1));
|
||||
$charset = strtoupper($value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -291,41 +291,8 @@ class addressbook_hooks
|
||||
|
||||
if ($GLOBALS['egw_info']['user']['apps']['filemanager'])
|
||||
{
|
||||
$settings['default_document'] = array(
|
||||
'type' => 'vfs_file',
|
||||
'size' => 60,
|
||||
'label' => 'Default document to insert contacts',
|
||||
'name' => 'default_document',
|
||||
'help' => lang('If you specify a document (full vfs path) here, %1 displays an extra document icon for each entry. That icon allows to download the specified document with the data inserted.', lang('addressbook')).' '.
|
||||
lang('The document can contain placeholder like {{%1}}, to be replaced with the data.','n_fn').' '.
|
||||
lang('The following document-types are supported:'). implode(',',Api\Storage\Merge::get_file_extensions()),
|
||||
'run_lang' => false,
|
||||
'xmlrpc' => True,
|
||||
'admin' => False,
|
||||
);
|
||||
$settings['document_dir'] = array(
|
||||
'type' => 'vfs_dirs',
|
||||
'size' => 60,
|
||||
'label' => 'Directory with documents to insert contacts',
|
||||
'name' => 'document_dir',
|
||||
'help' => lang('If you specify a directory (full vfs path) here, %1 displays an action for each document. That action allows to download the specified document with the data inserted.', lang('addressbook')) . ' ' .
|
||||
lang('The document can contain placeholder like {{%1}}, to be replaced with the data.', 'n_fn') . ' ' .
|
||||
lang('The following document-types are supported:') . implode(',', Api\Storage\Merge::get_file_extensions()),
|
||||
'run_lang' => false,
|
||||
'xmlrpc' => True,
|
||||
'admin' => False,
|
||||
'default' => '/templates/addressbook',
|
||||
);
|
||||
$settings[Api\Storage\Merge::PREF_DOCUMENT_FILENAME] = array(
|
||||
'type' => 'taglist',
|
||||
'label' => 'Document download filename',
|
||||
'name' => 'document_download_name',
|
||||
'values' => Api\Storage\Merge::DOCUMENT_FILENAME_OPTIONS,
|
||||
'help' => 'Choose the default filename for downloaded documents.',
|
||||
'xmlrpc' => True,
|
||||
'admin' => False,
|
||||
'default' => 'document',
|
||||
);
|
||||
$merge = new Api\Contacts\Merge();
|
||||
$settings += $merge->merge_preferences();
|
||||
}
|
||||
|
||||
if ($GLOBALS['egw_info']['user']['apps']['felamimail'] || $GLOBALS['egw_info']['user']['apps']['mail'])
|
||||
|
@ -273,7 +273,7 @@ class admin_acl
|
||||
{
|
||||
$rows['sel_options']['filter2'][] = array(
|
||||
'value' => $appname,
|
||||
'label' => lang(Api\Link::get_registry($appname, 'entries')) ?? lang($appname)
|
||||
'label' => lang(Api\Link::get_registry($appname, 'entries') ?: $appname)
|
||||
);
|
||||
}
|
||||
usort($rows['sel_options']['filter2'], function($a,$b) {
|
||||
|
@ -87,7 +87,7 @@ class admin_cmd_category extends admin_cmd
|
||||
unset($set['old_parent'], $set['base_url'], $set['last_mod'], $set['all_cats'], $set['no_private']);
|
||||
foreach($set as $key => $value)
|
||||
{
|
||||
if(array_key_exists($key, $old) && $old[$key] == $value)
|
||||
if ($old && array_key_exists($key, $old) && $old[$key] == $value)
|
||||
{
|
||||
unset($set[$key]);
|
||||
unset($old[$key]);
|
||||
|
@ -128,10 +128,10 @@ class admin_customfields
|
||||
public function index($content = array())
|
||||
{
|
||||
// determine appname
|
||||
$this->appname = $this->appname ? $this->appname : ($_GET['appname'] ? $_GET['appname'] : ($content['appname'] ? $content['appname'] : false));
|
||||
$this->appname = $this->appname ?: (!empty($_GET['appname']) ? $_GET['appname'] : (!empty($content['appname']) ? $content['appname'] : false));
|
||||
if(!$this->appname) die(lang('Error! No appname found'));
|
||||
|
||||
$this->use_private = !isset($_GET['use_private']) || (boolean)$_GET['use_private'] || $content['use_private'];
|
||||
$this->use_private = !empty($_GET['use_private']) && $_GET['use_private'] !== 'undefined' || !empty($content['use_private']);
|
||||
|
||||
// Read fields, constructor doesn't always know appname
|
||||
$this->fields = Api\Storage\Customfields::get($this->appname,true);
|
||||
@ -323,10 +323,10 @@ class admin_customfields
|
||||
*/
|
||||
function edit($content = null)
|
||||
{
|
||||
$cf_id = $_GET['cf_id'] ? (int)$_GET['cf_id'] : (int)$content['cf_id'];
|
||||
$cf_id = isset($_GET['cf_id']) ? (int)$_GET['cf_id'] : (int)$content['cf_id'];
|
||||
|
||||
// determine appname
|
||||
$this->appname = $this->appname ? $this->appname : ($_GET['appname'] ? $_GET['appname'] : ($content['cf_app'] ? $content['cf_app'] : false));
|
||||
$this->appname = $this->appname ?: (isset($_GET['appname']) ? $_GET['appname'] : (!empty($content['cf_app']) ? $content['cf_app'] : false));
|
||||
if(!$this->appname)
|
||||
{
|
||||
if($cf_id && $this->so)
|
||||
@ -339,7 +339,7 @@ class admin_customfields
|
||||
{
|
||||
die(lang('Error! No appname found'));
|
||||
}
|
||||
$this->use_private = !isset($_GET['use_private']) || (boolean)$_GET['use_private'] || $content['use_private'];
|
||||
$this->use_private = !isset($_GET['use_private']) || (boolean)$_GET['use_private'] || !empty($content['use_private']);
|
||||
|
||||
// Read fields, constructor doesn't always know appname
|
||||
$this->fields = Api\Storage\Customfields::get($this->appname,true);
|
||||
@ -347,7 +347,7 @@ class admin_customfields
|
||||
// Update based on info returned from template
|
||||
if (is_array($content))
|
||||
{
|
||||
$action = @key($content['button']);
|
||||
$action = key($content['button'] ?? []);
|
||||
switch($action)
|
||||
{
|
||||
case 'delete':
|
||||
@ -422,7 +422,7 @@ class admin_customfields
|
||||
}
|
||||
else
|
||||
{
|
||||
$content['use_private'] = !isset($_GET['use_private']) || (boolean)$_GET['use_private'];
|
||||
$content['use_private'] = !empty($_GET['use_private']) && $_GET['use_private'] !== 'undefined';
|
||||
}
|
||||
|
||||
|
||||
@ -474,11 +474,11 @@ class admin_customfields
|
||||
// Show sub-type row, and get types
|
||||
if($this->manage_content_types)
|
||||
{
|
||||
if(count($this->content_types) == 0)
|
||||
if(empty($this->content_types))
|
||||
{
|
||||
$this->content_types = Api\Config::get_content_types($this->appname);
|
||||
}
|
||||
if (count($this->content_types)==0)
|
||||
if (empty($this->content_types))
|
||||
{
|
||||
// if you define your default types of your app with the search_link hook, they are available here, if no types were found
|
||||
$this->content_types = (array)Api\Link::get_registry($this->appname, 'default_types');
|
||||
@ -592,7 +592,7 @@ class admin_customfields
|
||||
*/
|
||||
function create_field(&$content)
|
||||
{
|
||||
$new_name = trim($content['fields'][count($content['fields'])-1]['name']);
|
||||
$new_name = trim($content['fields'][count((array)$content['fields'])-1]['name']);
|
||||
if (empty($new_name) || isset($this->fields[$new_name]))
|
||||
{
|
||||
$content['error_msg'] .= empty($new_name) ?
|
||||
@ -601,7 +601,7 @@ class admin_customfields
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->fields[$new_name] = $content['fields'][count($content['fields'])-1];
|
||||
$this->fields[$new_name] = $content['fields'][count((array)$content['fields'])-1];
|
||||
if(!$this->fields[$new_name]['label']) $this->fields[$new_name]['label'] = $this->fields[$new_name]['name'];
|
||||
$this->save_repository();
|
||||
}
|
||||
|
@ -1258,7 +1258,7 @@ class admin_mail
|
||||
if ($content['ident_id'] != $content['old_ident_id'] &&
|
||||
($content['old_ident_id'] || $content['ident_id'] != $content['std_ident_id']))
|
||||
{
|
||||
if ($content['ident_id'] > 0)
|
||||
if ((int)$content['ident_id'] > 0)
|
||||
{
|
||||
$identity = Mail\Account::read_identity($content['ident_id'], false, $content['called_for']);
|
||||
unset($identity['account_id']);
|
||||
@ -1285,7 +1285,7 @@ class admin_mail
|
||||
{
|
||||
$sel_options['ident_email_alias'] = array_merge(
|
||||
array('' => $content['mailLocalAddress'].' ('.lang('Default').')'),
|
||||
array_combine($content['mailAlternateAddress'], $content['mailAlternateAddress']));
|
||||
array_combine($content['mailAlternateAddress'] ?? [], $content['mailAlternateAddress'] ?? []));
|
||||
// if admin explicitly set a non-alias, we need to add it to aliases to keep it after storing signature by user
|
||||
if ($content['ident_email'] !== $content['mailLocalAddress'] && !isset($sel_options['ident_email_alias'][$content['ident_email']]))
|
||||
{
|
||||
|
@ -44,7 +44,7 @@
|
||||
</row>
|
||||
<row>
|
||||
<description value="Category owner" for="owner"/>
|
||||
<taglist statustext="Limit global category to members of a certain group" id="owner" needed="1" height="190" class="et2_fullWidth" rows="4" />
|
||||
<taglist-account statustext="Limit global category to members of a certain group" id="owner" needed="1" class="et2_fullWidth" />
|
||||
</row>
|
||||
|
||||
</rows>
|
||||
|
@ -227,8 +227,9 @@ export class et2_placeholder_select extends et2_inputWidget
|
||||
app.onchange = (node, widget) =>
|
||||
{
|
||||
preview.set_value("");
|
||||
if(['user'].indexOf(widget.get_value()) >= 0)
|
||||
if(['user', 'filemanager'].indexOf(widget.get_value()) >= 0)
|
||||
{
|
||||
// These ones don't let you select an entry for preview (they don't work)
|
||||
entry.set_disabled(true);
|
||||
entry.app_select.val('user');
|
||||
entry.set_value({app: 'user', id: '', query: ''});
|
||||
@ -338,7 +339,7 @@ export class et2_placeholder_select extends et2_inputWidget
|
||||
{
|
||||
continue;
|
||||
}
|
||||
options[key].push({
|
||||
options[this.egw().lang(key)].push({
|
||||
value: key + '-' + sub,
|
||||
label: this.egw().lang(sub)
|
||||
});
|
||||
|
@ -50,6 +50,22 @@ export class et2_toolbar extends et2_DOMWidget implements et2_IInput
|
||||
"type": "string",
|
||||
"default": "more",
|
||||
"description": "Define a style for list header (more ...), which can get short 3dots with no caption or bigger button with caption more ..."
|
||||
},
|
||||
"preference_id": {
|
||||
"name": "Preference id",
|
||||
"type": "string",
|
||||
"default": false,
|
||||
"description": "Define a custom preference id for saving the toolbar preferences." +
|
||||
"This is useful when you have the same toolbar and you use it in a pop up but also in a tab, which have different dom ids" +
|
||||
"When not set it defaults to the dom id of the form."
|
||||
},
|
||||
"preference_app": {
|
||||
"name": "Preference application",
|
||||
"type": "string",
|
||||
"default": false,
|
||||
"description": "Define a custom preference application for saving the toolbar preferences." +
|
||||
"This is useful when you have the same toolbar and you use it in a pop up but also in a tab, wich have different application names" +
|
||||
"When not set it defaults to the result of this.egw().app_name();"
|
||||
}
|
||||
};
|
||||
|
||||
@ -94,6 +110,13 @@ export class et2_toolbar extends et2_DOMWidget implements et2_IInput
|
||||
// Set proper id and dom_id for the widget
|
||||
this.set_id(this.id);
|
||||
|
||||
if(!this.options.preference_id){
|
||||
this.options.preference_id = this.dom_id;
|
||||
}
|
||||
|
||||
if(!this.options.preference_app){
|
||||
this.options.preference_app = this.egw().app_name();
|
||||
}
|
||||
|
||||
this.actionbox = jQuery(document.createElement('div'))
|
||||
.addClass("et2_toolbar_more")
|
||||
@ -232,7 +255,7 @@ export class et2_toolbar extends et2_DOMWidget implements et2_IInput
|
||||
{
|
||||
this.actionbox.find('.toolbar-admin-pref').click(function(e){
|
||||
e.stopImmediatePropagation();
|
||||
egw.json('EGroupware\\Api\\Etemplate\\Widget\\Toolbar::ajax_get_default_prefs', [egw.app_name(), that.dom_id], function(_prefs){
|
||||
egw.json('EGroupware\\Api\\Etemplate\\Widget\\Toolbar::ajax_get_default_prefs', [that.options.preference_app, that.options.preference_id], function(_prefs){
|
||||
let prefs = [];
|
||||
for (let p in _prefs)
|
||||
{
|
||||
@ -242,7 +265,8 @@ export class et2_toolbar extends et2_DOMWidget implements et2_IInput
|
||||
}).sendRequest(true);
|
||||
});
|
||||
}
|
||||
let pref = (!egwIsMobile())? egw.preference(this.dom_id, this.egw().app_name()): undefined;
|
||||
|
||||
let pref = (!egwIsMobile())? egw.preference(this.options.preference_id, this.options.preference_app): undefined;
|
||||
if (pref && !jQuery.isArray(pref)) this.preference = pref;
|
||||
|
||||
//Set the default actions for the first time
|
||||
@ -461,7 +485,7 @@ export class et2_toolbar extends et2_DOMWidget implements et2_IInput
|
||||
if (that.actionlist.find(".ui-draggable").length == 0)
|
||||
{
|
||||
that.preference = {};
|
||||
egw.set_preference(that.egw().app_name(),that.dom_id,that.preference);
|
||||
egw.set_preference(that.options.preference_app,that.options.preference_id,that.preference);
|
||||
}
|
||||
},
|
||||
tolerance:"touch"
|
||||
@ -525,7 +549,7 @@ export class et2_toolbar extends et2_DOMWidget implements et2_IInput
|
||||
{
|
||||
this.preference[_action] = _state;
|
||||
if (egwIsMobile()) return;
|
||||
egw.set_preference(this.egw().app_name(),this.dom_id,this.preference);
|
||||
egw.set_preference(this.options.preference_app,this.options.preference_id,this.preference);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -537,7 +561,7 @@ export class et2_toolbar extends et2_DOMWidget implements et2_IInput
|
||||
{
|
||||
let button_options = {
|
||||
};
|
||||
let button = jQuery(document.createElement('button'))
|
||||
let button = jQuery(document.createElement('button'))
|
||||
.addClass("et2_button et2_button_text et2_button_with_image")
|
||||
.attr('id', this.id+'-'+action.id)
|
||||
.attr('type', 'button')
|
||||
@ -773,7 +797,7 @@ export class et2_toolbar extends et2_DOMWidget implements et2_IInput
|
||||
id:child,
|
||||
value: child,
|
||||
label: _actions[key]['children'][child]['caption'],
|
||||
app: egw.app_name(),
|
||||
app: self.options.preference_app,
|
||||
icon: _actions[key]['children'][child]['iconUrl']
|
||||
});
|
||||
}
|
||||
@ -784,7 +808,7 @@ export class et2_toolbar extends et2_DOMWidget implements et2_IInput
|
||||
id:key,
|
||||
value: key,
|
||||
label: _actions[key]['caption'],
|
||||
app: egw.app_name(),
|
||||
app: self.options.preference_app,
|
||||
icon: _actions[key]['iconUrl']
|
||||
});
|
||||
}
|
||||
@ -808,12 +832,12 @@ export class et2_toolbar extends et2_DOMWidget implements et2_IInput
|
||||
_value.actions = pref;
|
||||
}
|
||||
egw.json('EGroupware\\Api\\Etemplate\\Widget\\Toolbar::ajax_setAdminSettings',
|
||||
[_value, self.dom_id, egw.app_name()],function(_result){
|
||||
[_value, self.options.preference_id, self.options.preference_app],function(_result){
|
||||
egw.message(_result);
|
||||
}).sendRequest(true);
|
||||
}
|
||||
},
|
||||
title: egw.lang('admin settings for %1', this.dom_id),
|
||||
title: egw.lang('admin settings for %1', this.options.preference_id),
|
||||
buttons: buttons,
|
||||
minWidth: 600,
|
||||
minHeight: 300,
|
||||
|
@ -744,20 +744,24 @@ export abstract class EgwApp
|
||||
// Find what we need
|
||||
let nm = null;
|
||||
let action = _action;
|
||||
let as_pdf = false;
|
||||
let as_pdf = null;
|
||||
|
||||
// Find Select all
|
||||
while(nm == null && action != null)
|
||||
while(nm == null && action.parent != null)
|
||||
{
|
||||
if(action.data != null && action.data.nextmatch)
|
||||
{
|
||||
nm = action.data.nextmatch;
|
||||
}
|
||||
if(as_pdf === null && action.getActionById('as_pdf') !== null)
|
||||
{
|
||||
as_pdf = action.getActionById('as_pdf').checked;
|
||||
}
|
||||
action = action.parent;
|
||||
}
|
||||
let all = nm?.getSelection().all || false;
|
||||
|
||||
as_pdf = action.getActionById('as_pdf')?.checked || false;
|
||||
as_pdf = as_pdf || false;
|
||||
|
||||
// Get list of entry IDs
|
||||
let ids = [];
|
||||
|
@ -274,6 +274,7 @@ choose a background style. common de Wählen Sie einen Hintergrundstil.
|
||||
choose a text color for the icons common de Wählen Sie eine Textfarbe für die Symbole
|
||||
choose file... common de Dateien wählen...
|
||||
choose the category common de Kategorie auswählen
|
||||
choose the default filename for merged documents. preferences de Wählen Sie den Standard-Dateinamen für zusammengeführte Platzhalter-Dokumente.
|
||||
choose the parent category common de Wählen der übergeordneten Kategorie
|
||||
choose time common de Uhrzeit auswählen
|
||||
chosen parent category no longer exists common de Die ausgewählte Elternkategorie existiert nicht (mehr).
|
||||
@ -378,6 +379,7 @@ december common de Dezember
|
||||
deck common de Deck (intern)
|
||||
default common de Vorgabe
|
||||
default category common de Standard-Kategorie
|
||||
default document to insert entries preferences de Standarddokument für Einfügen in Dokument
|
||||
default height for the windows common de Vorgabewert für Höhe des Fensters
|
||||
default visible actions common de standardmäßig sichtbare Aktionen
|
||||
default width for the windows common de Vorgabewert für Breite des Fensters
|
||||
@ -417,6 +419,8 @@ diable the execution a bugfixscript for internet explorer 5.5 and higher to show
|
||||
direction left to right common de Richtung von links nach rechts
|
||||
directory common de Verzeichnis
|
||||
directory does not exist, is not readable by the webserver or is not relative to the document root! common de Verzeichnis existiert nicht, ist nicht vom Webserver lesbar oder ist nicht entsprechend zur Dokumentroot!
|
||||
directory for storing merged documents preferences de Verzeichnis für zusammengeführte Platzhalter-Dokumente
|
||||
directory with documents to insert entries preferences de Vorlagen-Verzeichnis für Einfügen in Dokument
|
||||
disable internet explorer png-image-bugfix common de Internet Explorer PNG-Bilder-Bugfix abschalten
|
||||
disable slider effects common de Schwebeeffekte des Navigationsmenüs abschalten
|
||||
disable the animated slider effects when showing or hiding menus in the page? opera and konqueror users will probably must want this. common de Die animierten Schwebeeffekte beim Anzeigen oder Verstecken des Navigationsmenüs in der Seite abschalten? Benutzer von Opera oder Konquerer müssen diese Funktion abschalten.
|
||||
@ -1500,6 +1504,7 @@ western sahara common de WEST SAHARA
|
||||
what color should all the blank space on the desktop have common de Welche Farbe soll der freie Platz auf der Arbeitsfläche haben
|
||||
what happens with overflowing content: visible (default), hidden, scroll, auto (browser decides) common de was passiert mit überbreitem Inhalt: sichtbar (standard), versteckt, rollend, automatisch (der Browser entscheidet)
|
||||
what style would you like the image to have? common de Welchen Stil soll das Bild haben?
|
||||
when you merge entries into documents, they will be stored here. If no directory is provided, they will be stored in your home directory (%1) preferences de Wenn Sie Einträge mit Platzhalter-Dokumenten zusammenführen, werden diese hier gespeichert. Wenn Sie kein Verzeichnis angeben, werden diese in Ihrem Homeverzeichnis gespeichert (%1)
|
||||
when you say yes the home and logout buttons are presented as applications in the main top applcation bar. common de Wenn Sie dies aktivieren, werden die Start und Abmelde Symbole als Anwendungen im oberen Anwendungsbalken angezeigt.
|
||||
where and how will the egroupware links like preferences, about and logout be displayed. common de Wo und wie werden die EGroupware Verknüpfungen wie Einstellungen, Über ..., und Abmelden angezeigt.
|
||||
which groups common de Welche Gruppen
|
||||
|
@ -274,6 +274,7 @@ choose a background style. common en Choose a background style
|
||||
choose a text color for the icons common en Choose a text color for the icons
|
||||
choose file... common en Choose file...
|
||||
choose the category common en Choose the category
|
||||
choose the default filename for merged documents. preferences en Choose the default filename for merged documents.
|
||||
choose the parent category common en Choose the parent category
|
||||
choose time common en Choose Time
|
||||
chosen parent category no longer exists common en Chosen parent category no longer exists
|
||||
@ -378,6 +379,7 @@ december common en December
|
||||
deck common en Deck (internal)
|
||||
default common en Default
|
||||
default category common en Default category
|
||||
default document to insert entries preferences en Default document to insert entries
|
||||
default height for the windows common en Default height for the windows
|
||||
default visible actions common en Default visible actions
|
||||
default width for the windows common en Default width for the windows
|
||||
@ -417,6 +419,8 @@ diable the execution a bugfixscript for internet explorer 5.5 and higher to show
|
||||
direction left to right common en Direction left to right
|
||||
directory common en Directory
|
||||
directory does not exist, is not readable by the webserver or is not relative to the document root! common en Directory does not exist, is not readable by the web server or is not relative to the document root!
|
||||
directory for storing merged documents preferences en Directory for storing merged documents
|
||||
directory with documents to insert entries preferences en Directory with documents to insert entries
|
||||
disable internet explorer png-image-bugfix common en Disable Internet Explorer png image bugfix
|
||||
disable slider effects common en Disable slider effects
|
||||
disable the animated slider effects when showing or hiding menus in the page? opera and konqueror users will probably must want this. common en Disable the animated slider effects when showing or hiding menus in the page.
|
||||
@ -857,6 +861,7 @@ maybe common en Maybe
|
||||
mayotte common en MAYOTTE
|
||||
medium common en Medium
|
||||
menu common en Menu
|
||||
merged document filename preferences en Merged document filename
|
||||
message common en Message
|
||||
message ... common en Message ...
|
||||
message prepared for sending. common en Message prepared for sending.
|
||||
@ -1501,6 +1506,7 @@ western sahara common en WESTERN SAHARA
|
||||
what color should all the blank space on the desktop have common en What color should all the blank space on the desktop have?
|
||||
what happens with overflowing content: visible (default), hidden, scroll, auto (browser decides) common en What happens with overflowing content: visible (default), hidden, scroll, auto (browser decides)
|
||||
what style would you like the image to have? common en Image style
|
||||
when you merge entries into documents, they will be stored here. If no directory is provided, they will be stored in your home directory (%1) preferences en When you merge entries into documents, they will be stored here. If no directory is provided, they will be stored in your home directory (%1)
|
||||
when you say yes the home and logout buttons are presented as applications in the main top applcation bar. common en If you say yes, the Home and Log out buttons are presented as applications in the main top application bar.
|
||||
where and how will the egroupware links like preferences, about and logout be displayed. common en Where and how will the EGroupware links like Preferences, About and Log out be displayed.
|
||||
which groups common en Which groups
|
||||
|
@ -468,7 +468,7 @@ class Accounts
|
||||
$data = self::cache_read($id);
|
||||
|
||||
// add default description for Admins and Default group
|
||||
if ($data['account_type'] === 'g')
|
||||
if ($data && $data['account_type'] === 'g')
|
||||
{
|
||||
self::add_default_group_description($data);
|
||||
}
|
||||
@ -595,11 +595,15 @@ class Accounts
|
||||
/**
|
||||
* Return formatted username for a given account_id
|
||||
*
|
||||
* @param int $account_id account id
|
||||
* @param ?int $account_id account id, default current user
|
||||
* @return string full name of user or "#$account_id" if user not found
|
||||
*/
|
||||
static function username(int $account_id)
|
||||
static function username(int $account_id=null)
|
||||
{
|
||||
if (empty($account_id))
|
||||
{
|
||||
$account_id = $GLOBALS['egw_info']['user']['account_id'];
|
||||
}
|
||||
if (!($account = self::cache_read($account_id)))
|
||||
{
|
||||
return '#'.$account_id;
|
||||
@ -985,7 +989,7 @@ class Accounts
|
||||
$ret = $just_id && $data['memberships'] ? array_keys($data['memberships']) : $data['memberships'];
|
||||
}
|
||||
//error_log(__METHOD__."($account_id, $just_id) data=".array2string($data)." returning ".array2string($ret));
|
||||
return $ret;
|
||||
return $ret ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -15,8 +15,7 @@
|
||||
namespace EGroupware\Api;
|
||||
|
||||
// allow to set an application depending authentication type (eg. for syncml, groupdav, ...)
|
||||
if (isset($GLOBALS['egw_info']['server']['auth_type_'.$GLOBALS['egw_info']['flags']['currentapp']]) &&
|
||||
$GLOBALS['egw_info']['server']['auth_type_'.$GLOBALS['egw_info']['flags']['currentapp']])
|
||||
if (!empty($GLOBALS['egw_info']['server']['auth_type_'.$GLOBALS['egw_info']['flags']['currentapp']]))
|
||||
{
|
||||
$GLOBALS['egw_info']['server']['auth_type'] = $GLOBALS['egw_info']['server']['auth_type_'.$GLOBALS['egw_info']['flags']['currentapp']];
|
||||
}
|
||||
@ -223,11 +222,11 @@ class Auth
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (is_null($passwordAgeBorder) && $GLOBALS['egw_info']['server']['change_pwd_every_x_days'])
|
||||
if (is_null($passwordAgeBorder) && !empty($GLOBALS['egw_info']['server']['change_pwd_every_x_days']))
|
||||
{
|
||||
$passwordAgeBorder = (DateTime::to('now','ts')-($GLOBALS['egw_info']['server']['change_pwd_every_x_days']*86400));
|
||||
}
|
||||
if (is_null($daysLeftUntilChangeReq) && $GLOBALS['egw_info']['server']['warn_about_upcoming_pwd_change'])
|
||||
if (is_null($daysLeftUntilChangeReq) && !empty($GLOBALS['egw_info']['server']['warn_about_upcoming_pwd_change']))
|
||||
{
|
||||
// maxage - passwordage = days left until change is required
|
||||
$daysLeftUntilChangeReq = ($GLOBALS['egw_info']['server']['change_pwd_every_x_days'] - ((DateTime::to('now','ts')-($alpwchange_val?$alpwchange_val:0))/86400));
|
||||
@ -235,9 +234,9 @@ class Auth
|
||||
if ($alpwchange_val == 0 || // admin requested password change
|
||||
$passwordAgeBorder > $alpwchange_val || // change password every N days policy requests change
|
||||
// user should be warned N days in advance about change and is not yet
|
||||
$GLOBALS['egw_info']['server']['change_pwd_every_x_days'] &&
|
||||
$GLOBALS['egw_info']['user']['apps']['preferences'] &&
|
||||
$GLOBALS['egw_info']['server']['warn_about_upcoming_pwd_change'] &&
|
||||
!empty($GLOBALS['egw_info']['server']['change_pwd_every_x_days']) &&
|
||||
!empty($GLOBALS['egw_info']['user']['apps']['preferences']) &&
|
||||
!empty($GLOBALS['egw_info']['server']['warn_about_upcoming_pwd_change']) &&
|
||||
$GLOBALS['egw_info']['server']['warn_about_upcoming_pwd_change'] > $daysLeftUntilChangeReq &&
|
||||
$UserKnowsAboutPwdChange !== true)
|
||||
{
|
||||
@ -255,8 +254,8 @@ class Auth
|
||||
else
|
||||
{
|
||||
// login page does not inform user about passwords about to expire
|
||||
if ($GLOBALS['egw_info']['flags']['currentapp'] != 'login' &&
|
||||
($GLOBALS['egw_info']['flags']['currentapp'] != 'home' ||
|
||||
if ($GLOBALS['egw_info']['flags']['currentapp'] !== 'login' &&
|
||||
($GLOBALS['egw_info']['flags']['currentapp'] !== 'home' ||
|
||||
strpos($_SERVER['SCRIPT_NAME'], '/home/') !== false))
|
||||
{
|
||||
$UserKnowsAboutPwdChange = true;
|
||||
|
@ -853,7 +853,7 @@ class Categories
|
||||
|
||||
if (is_null(self::$cache)) self::init_cache();
|
||||
|
||||
$cat = self::$cache[$cat_id];
|
||||
$cat = self::$cache[$cat_id] ?? null;
|
||||
if ($item == 'path')
|
||||
{
|
||||
if ($cat['parent'])
|
||||
@ -864,7 +864,7 @@ class Categories
|
||||
}
|
||||
if ($item == 'data')
|
||||
{
|
||||
return $cat['data'] ? json_php_unserialize($cat['data'], true) : array();
|
||||
return !empty($cat['data']) ? json_php_unserialize($cat['data'], true) : array();
|
||||
}
|
||||
elseif ($cat[$item])
|
||||
{
|
||||
|
@ -210,7 +210,7 @@ class Config
|
||||
{
|
||||
self::init_static();
|
||||
}
|
||||
return (array)self::$configs[$app];
|
||||
return self::$configs[$app] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -238,7 +238,7 @@ class Config
|
||||
{
|
||||
$config = self::read($app);
|
||||
|
||||
return is_array($config['types']) ? $config['types'] : array();
|
||||
return !empty($config['types']) && is_array($config['types']) ? $config['types'] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -202,9 +202,9 @@ class Contacts extends Contacts\Storage
|
||||
$this->prefs['hide_accounts'] = '0';
|
||||
}
|
||||
// get the default addressbook from the users prefs
|
||||
$this->default_addressbook = $GLOBALS['egw_info']['user']['preferences']['addressbook']['add_default'] ?
|
||||
$this->default_addressbook = !empty($GLOBALS['egw_info']['user']['preferences']['addressbook']['add_default']) ?
|
||||
(int)$GLOBALS['egw_info']['user']['preferences']['addressbook']['add_default'] : $this->user;
|
||||
$this->default_private = substr($GLOBALS['egw_info']['user']['preferences']['addressbook']['add_default'],-1) == 'p';
|
||||
$this->default_private = substr($GLOBALS['egw_info']['user']['preferences']['addressbook']['add_default'] ?? '',-1) == 'p';
|
||||
if ($this->default_addressbook > 0 && $this->default_addressbook != $this->user &&
|
||||
($this->default_private ||
|
||||
$this->default_addressbook == (int)$GLOBALS['egw']->preferences->forced['addressbook']['add_default'] ||
|
||||
@ -312,14 +312,14 @@ class Contacts extends Contacts\Storage
|
||||
'adr_two_countryname' => lang('country').' ('.lang('business').')',
|
||||
);
|
||||
//_debug_array($this->contact_fields);
|
||||
$this->own_account_acl = $GLOBALS['egw_info']['server']['own_account_acl'];
|
||||
$this->own_account_acl = $GLOBALS['egw_info']['server']['own_account_acl'] ?? null;
|
||||
if (!is_array($this->own_account_acl)) $this->own_account_acl = json_php_unserialize($this->own_account_acl, true);
|
||||
// we have only one acl (n_fn) for the whole name, as not all backends store every part in an own field
|
||||
if ($this->own_account_acl && in_array('n_fn',$this->own_account_acl))
|
||||
{
|
||||
$this->own_account_acl = array_merge($this->own_account_acl,array('n_prefix','n_given','n_middle','n_family','n_suffix'));
|
||||
}
|
||||
if ($GLOBALS['egw_info']['server']['org_fileds_to_update'])
|
||||
if (!empty($GLOBALS['egw_info']['server']['org_fileds_to_update']))
|
||||
{
|
||||
$this->org_fields = $GLOBALS['egw_info']['server']['org_fileds_to_update'];
|
||||
if (!is_array($this->org_fields)) $this->org_fields = unserialize($this->org_fields);
|
||||
@ -337,7 +337,7 @@ class Contacts extends Contacts\Storage
|
||||
}
|
||||
$this->categories = new Categories($this->user,'addressbook');
|
||||
|
||||
$this->delete_history = $GLOBALS['egw_info']['server']['history'];
|
||||
$this->delete_history = $GLOBALS['egw_info']['server']['history'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -155,119 +155,6 @@ class Merge extends Api\Storage\Merge
|
||||
return $replacements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate table with replacements for the preferences
|
||||
*
|
||||
*/
|
||||
public function show_replacements()
|
||||
{
|
||||
$GLOBALS['egw_info']['flags']['app_header'] = lang('Addressbook').' - '.lang('Replacements for inserting contacts into documents');
|
||||
$GLOBALS['egw_info']['flags']['nonavbar'] = (bool)$_GET['nonavbar'];
|
||||
|
||||
ob_start();
|
||||
echo "<table width='90%' align='center'>\n";
|
||||
echo '<tr><td colspan="4"><h3>'.lang('Contact fields:')."</h3></td></tr>";
|
||||
|
||||
$n = 0;
|
||||
foreach($this->contacts->contact_fields as $name => $label)
|
||||
{
|
||||
if (in_array($name,array('tid','label','geo'))) continue; // dont show them, as they are not used in the UI atm.
|
||||
|
||||
if (in_array($name,array('email','org_name','tel_work','url')) && $n&1) // main values, which should be in the first column
|
||||
{
|
||||
echo "</tr>\n";
|
||||
$n++;
|
||||
}
|
||||
if (!($n&1)) echo '<tr>';
|
||||
echo '<td>{{'.$name.'}}</td><td>'.$label.'</td>';
|
||||
if($name == 'cat_id')
|
||||
{
|
||||
if ($n&1) echo "</tr>\n";
|
||||
echo '<td>{{categories}}</td><td>'.lang('Category path').'</td>';
|
||||
$n++;
|
||||
}
|
||||
if ($n&1) echo "</tr>\n";
|
||||
$n++;
|
||||
}
|
||||
|
||||
echo '<tr><td colspan="4"><h3>'.lang('Custom fields').":</h3></td></tr>";
|
||||
foreach($this->contacts->customfields as $name => $field)
|
||||
{
|
||||
echo '<tr><td>{{#'.$name.'}}</td><td colspan="3">'.$field['label']."</td></tr>\n";
|
||||
}
|
||||
|
||||
echo '<tr><td colspan="4"><h3>'.lang('General fields:')."</h3></td></tr>";
|
||||
foreach(array(
|
||||
'link' => lang('HTML link to the current record'),
|
||||
'links' => lang('Titles of any entries linked to the current record, excluding attached files'),
|
||||
'attachments' => lang('List of files linked to the current record'),
|
||||
'links_attachments' => lang('Links and attached files'),
|
||||
'links/[appname]' => lang('Links to specified application. Example: {{links/infolog}}'),
|
||||
'date' => lang('Date'),
|
||||
'user/n_fn' => lang('Name of current user, all other contact fields are valid too'),
|
||||
'user/account_lid' => lang('Username'),
|
||||
'pagerepeat' => lang('For serial letter use this tag. Put the content, you want to repeat between two Tags.'),
|
||||
'label' => lang('Use this tag for addresslabels. Put the content, you want to repeat, between two tags.'),
|
||||
'labelplacement' => lang('Tag to mark positions for address labels'),
|
||||
'IF fieldname' => lang('Example {{IF n_prefix~Mr~Hello Mr.~Hello Ms.}} - search the field "n_prefix", for "Mr", if found, write Hello Mr., else write Hello Ms.'),
|
||||
'NELF' => lang('Example {{NELF role}} - if field role is not empty, you will get a new line with the value of field role'),
|
||||
'NENVLF' => lang('Example {{NENVLF role}} - if field role is not empty, set a LF without any value of the field'),
|
||||
'LETTERPREFIX' => lang('Example {{LETTERPREFIX}} - Gives a letter prefix without double spaces, if the title is empty for example'),
|
||||
'LETTERPREFIXCUSTOM' => lang('Example {{LETTERPREFIXCUSTOM n_prefix title n_family}} - Example: Mr Dr. James Miller'),
|
||||
) as $name => $label)
|
||||
{
|
||||
echo '<tr><td>{{'.$name.'}}</td><td colspan="3">'.$label."</td></tr>\n";
|
||||
}
|
||||
|
||||
echo '<tr><td colspan="4"><h3>'.lang('EPL Only').":</h3></td></tr>";
|
||||
echo '<tr><td>{{share}}</td><td colspan="3">'.lang('Public sharing URL')."</td></tr>\n";
|
||||
|
||||
Api\Translation::add_app('calendar');
|
||||
echo '<tr><td colspan="4"><h3>'.lang('Calendar fields:')." # = 1, 2, ..., 20, -1</h3></td></tr>";
|
||||
foreach(array(
|
||||
'title' => lang('Title'),
|
||||
'description' => lang('Description'),
|
||||
'participants' => lang('Participants'),
|
||||
'location' => lang('Location'),
|
||||
'start' => lang('Start').': '.lang('Date').'+'.lang('Time'),
|
||||
'startday' => lang('Start').': '.lang('Weekday'),
|
||||
'startdate'=> lang('Start').': '.lang('Date'),
|
||||
'starttime'=> lang('Start').': '.lang('Time'),
|
||||
'end' => lang('End').': '.lang('Date').'+'.lang('Time'),
|
||||
'endday' => lang('End').': '.lang('Weekday'),
|
||||
'enddate' => lang('End').': '.lang('Date'),
|
||||
'endtime' => lang('End').': '.lang('Time'),
|
||||
'duration' => lang('Duration'),
|
||||
'category' => lang('Category'),
|
||||
'priority' => lang('Priority'),
|
||||
'updated' => lang('Updated'),
|
||||
'recur_type' => lang('Repetition'),
|
||||
'access' => lang('Access').': '.lang('public').', '.lang('private'),
|
||||
'owner' => lang('Owner'),
|
||||
) as $name => $label)
|
||||
{
|
||||
if(in_array($name, array('start',
|
||||
'end')) && $n & 1) // main values, which should be in the first column
|
||||
{
|
||||
echo "</tr>\n";
|
||||
$n++;
|
||||
}
|
||||
if(!($n & 1))
|
||||
{
|
||||
echo '<tr>';
|
||||
}
|
||||
echo '<td>{{calendar/#/' . $name . '}}</td><td>' . $label . '</td>';
|
||||
if($n & 1)
|
||||
{
|
||||
echo "</tr>\n";
|
||||
}
|
||||
$n++;
|
||||
}
|
||||
echo "</table>\n";
|
||||
|
||||
$GLOBALS['egw']->framework->render(ob_get_clean());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of placeholders provided.
|
||||
*
|
||||
@ -352,10 +239,58 @@ class Merge extends Api\Storage\Merge
|
||||
'label' => "Formatted private address"
|
||||
];
|
||||
|
||||
$placeholders['EPL only'][] = [
|
||||
'value' => $this->prefix($prefix, 'share', '{'),
|
||||
'label' => 'Public sharing URL'
|
||||
];
|
||||
|
||||
$this->add_customfield_placeholders($placeholders, $prefix);
|
||||
|
||||
// Don't add any linked placeholders if we're not at the top level
|
||||
// This avoids potential recursion
|
||||
if(!$prefix)
|
||||
{
|
||||
$this->add_calendar_placeholders($placeholders, $prefix);
|
||||
}
|
||||
|
||||
return $placeholders;
|
||||
}
|
||||
|
||||
protected function add_calendar_placeholders(&$placeholders, $prefix)
|
||||
{
|
||||
Api\Translation::add_app('calendar');
|
||||
|
||||
// NB: The -1 is actually ‑1, a non-breaking hyphen to avoid UI issues where we split on -
|
||||
$group = lang('Calendar fields:') . " # = 1, 2, ..., 20, ‑1";
|
||||
foreach(array(
|
||||
'title' => lang('Title'),
|
||||
'description' => lang('Description'),
|
||||
'participants' => lang('Participants'),
|
||||
'location' => lang('Location'),
|
||||
'start' => lang('Start') . ': ' . lang('Date') . '+' . lang('Time'),
|
||||
'startday' => lang('Start') . ': ' . lang('Weekday'),
|
||||
'startdate' => lang('Start') . ': ' . lang('Date'),
|
||||
'starttime' => lang('Start') . ': ' . lang('Time'),
|
||||
'end' => lang('End') . ': ' . lang('Date') . '+' . lang('Time'),
|
||||
'endday' => lang('End') . ': ' . lang('Weekday'),
|
||||
'enddate' => lang('End') . ': ' . lang('Date'),
|
||||
'endtime' => lang('End') . ': ' . lang('Time'),
|
||||
'duration' => lang('Duration'),
|
||||
'category' => lang('Category'),
|
||||
'priority' => lang('Priority'),
|
||||
'updated' => lang('Updated'),
|
||||
'recur_type' => lang('Repetition'),
|
||||
'access' => lang('Access') . ': ' . lang('public') . ', ' . lang('private'),
|
||||
'owner' => lang('Owner'),
|
||||
) as $name => $label)
|
||||
{
|
||||
$placeholders[$group][] = array(
|
||||
'value' => $this->prefix(($prefix ? $prefix . '/' : '') . 'calendar/#', $name, '{'),
|
||||
'label' => $label
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get insert-in-document action with optional default document on top
|
||||
*
|
||||
|
@ -77,15 +77,15 @@ class Sql extends Api\Storage
|
||||
// Get custom fields from addressbook instead of api
|
||||
$this->customfields = Api\Storage\Customfields::get('addressbook');
|
||||
|
||||
if ($GLOBALS['egw_info']['server']['account_repository'])
|
||||
if (!empty($GLOBALS['egw_info']['server']['account_repository']))
|
||||
{
|
||||
$this->account_repository = $GLOBALS['egw_info']['server']['account_repository'];
|
||||
}
|
||||
elseif ($GLOBALS['egw_info']['server']['auth_type'])
|
||||
elseif (!empty($GLOBALS['egw_info']['server']['auth_type']))
|
||||
{
|
||||
$this->account_repository = $GLOBALS['egw_info']['server']['auth_type'];
|
||||
}
|
||||
if ($GLOBALS['egw_info']['server']['contact_repository'])
|
||||
if (!empty($GLOBALS['egw_info']['server']['contact_repository']))
|
||||
{
|
||||
$this->contact_repository = $GLOBALS['egw_info']['server']['contact_repository'];
|
||||
}
|
||||
@ -742,7 +742,7 @@ class Sql extends Api\Storage
|
||||
$cat_filter = array();
|
||||
foreach(is_array($cats) ? $cats : (is_numeric($cats) ? array($cats) : explode(',',$cats)) as $cat)
|
||||
{
|
||||
if (is_numeric($cat)) $cat_filter[] = $this->db->concat("','",cat_id,"','")." LIKE '%,$cat,%'";
|
||||
if (is_numeric($cat)) $cat_filter[] = $this->db->concat("','", 'cat_id', "','")." LIKE '%,$cat,%'";
|
||||
}
|
||||
return $cat_filter;
|
||||
}
|
||||
|
@ -256,7 +256,7 @@ class Storage
|
||||
}
|
||||
$this->customfields = Api\Storage\Customfields::get('addressbook');
|
||||
// contacts backend (contacts in LDAP require accounts in LDAP!)
|
||||
if($GLOBALS['egw_info']['server']['contact_repository'] == 'ldap' && $this->account_repository == 'ldap')
|
||||
if (($GLOBALS['egw_info']['server']['contact_repository']??null) === 'ldap' && $this->account_repository === 'ldap')
|
||||
{
|
||||
$this->contact_repository = 'ldap';
|
||||
$this->somain = new Ldap();
|
||||
@ -264,7 +264,7 @@ class Storage
|
||||
}
|
||||
else // sql or sql->ldap
|
||||
{
|
||||
if ($GLOBALS['egw_info']['server']['contact_repository'] == 'sql-ldap')
|
||||
if (($GLOBALS['egw_info']['server']['contact_repository']??null) === 'sql-ldap')
|
||||
{
|
||||
$this->contact_repository = 'sql-ldap';
|
||||
}
|
||||
@ -347,9 +347,9 @@ class Storage
|
||||
if ($user)
|
||||
{
|
||||
// contacts backend (contacts in LDAP require accounts in LDAP!)
|
||||
if($GLOBALS['egw_info']['server']['contact_repository'] == 'ldap' && $this->account_repository == 'ldap')
|
||||
if(($GLOBALS['egw_info']['server']['contact_repository']??null) === 'ldap' && $this->account_repository === 'ldap')
|
||||
{
|
||||
// static grants from ldap: all rights for the own personal addressbook and the group ones of the meberships
|
||||
// static grants from ldap: all rights for the own personal addressbook and the group ones of the memberships
|
||||
$grants = array($user => ~0);
|
||||
foreach($GLOBALS['egw']->accounts->memberships($user,true) as $gid)
|
||||
{
|
||||
@ -415,9 +415,9 @@ class Storage
|
||||
*/
|
||||
function allow_account_edit($user=null)
|
||||
{
|
||||
return $GLOBALS['egw_info']['server']['allow_account_edit'] &&
|
||||
return !empty($GLOBALS['egw_info']['server']['allow_account_edit']) &&
|
||||
array_intersect($GLOBALS['egw_info']['server']['allow_account_edit'],
|
||||
$GLOBALS['egw']->accounts->memberships($user ? $user : $this->user, true));
|
||||
$GLOBALS['egw']->accounts->memberships($user ?: $this->user, true));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -591,7 +591,7 @@ class Db
|
||||
$this->setupType = $this->Type;
|
||||
$this->Type = 'mysql';
|
||||
}
|
||||
if ($new_connection)
|
||||
if (!empty($new_connection))
|
||||
{
|
||||
foreach(get_included_files() as $file)
|
||||
{
|
||||
@ -1599,7 +1599,7 @@ class Db
|
||||
{
|
||||
return $array;
|
||||
}
|
||||
if (!$column_definitions)
|
||||
if (empty($column_definitions))
|
||||
{
|
||||
$column_definitions = $this->column_definitions;
|
||||
}
|
||||
|
@ -125,7 +125,7 @@ class Pdo
|
||||
// Exception reveals password, so we ignore the exception and connect again without pw, to get the right exception without pw
|
||||
self::$pdo = new \PDO($dsn,$egw_db->User,'$egw_db->Password');
|
||||
}
|
||||
if ($query)
|
||||
if (!empty($query))
|
||||
{
|
||||
self::$pdo->exec($query);
|
||||
}
|
||||
|
@ -117,22 +117,22 @@ class Etemplate extends Etemplate\Widget\Template
|
||||
|
||||
foreach(count(array_filter(array_keys($extras), 'is_int')) ? $extras : array($extras) as $extra)
|
||||
{
|
||||
if ($extra['data'] && is_array($extra['data']))
|
||||
if (!empty($extra['data']) && is_array($extra['data']))
|
||||
{
|
||||
$content = array_merge($content, $extra['data']);
|
||||
}
|
||||
|
||||
if ($extra['preserve'] && is_array($extra['preserve']))
|
||||
if (!empty($extra['preserve']) && is_array($extra['preserve']))
|
||||
{
|
||||
$preserv = array_merge($preserv, $extra['preserve']);
|
||||
}
|
||||
|
||||
if ($extra['readonlys'] && is_array($extra['readonlys']))
|
||||
if (!empty($extra['readonlys']) && is_array($extra['readonlys']))
|
||||
{
|
||||
$readonlys = array_merge($readonlys, $extra['readonlys']);
|
||||
}
|
||||
|
||||
if ($extra['sel_options'] && is_array($extra['sel_options']))
|
||||
if (!empty($extra['sel_options']) && is_array($extra['sel_options']))
|
||||
{
|
||||
$sel_options = array_merge($sel_options, $extra['sel_options']);
|
||||
}
|
||||
@ -177,7 +177,7 @@ class Etemplate extends Etemplate\Widget\Template
|
||||
}
|
||||
|
||||
// some apps (eg. InfoLog) set app_header only in get_rows depending on filter settings
|
||||
self::$request->app_header = $GLOBALS['egw_info']['flags']['app_header'];
|
||||
self::$request->app_header = $GLOBALS['egw_info']['flags']['app_header'] ?? null;
|
||||
|
||||
// compile required translations translations
|
||||
$currentapp = $GLOBALS['egw_info']['flags']['currentapp'];
|
||||
@ -209,7 +209,7 @@ class Etemplate extends Etemplate\Widget\Template
|
||||
'currentapp' => $currentapp,
|
||||
);
|
||||
|
||||
if($data['content']['nm']['rows'] && is_array($data['content']['nm']['rows']))
|
||||
if (!empty($data['content']['nm']['rows']) && is_array($data['content']['nm']['rows']))
|
||||
{
|
||||
// Deep copy rows so we don't lose them when request is set to null
|
||||
// (some content by reference)
|
||||
@ -420,7 +420,7 @@ class Etemplate extends Etemplate\Widget\Template
|
||||
}
|
||||
|
||||
$tcontent = is_array($content) ? $content :
|
||||
self::complete_array_merge(self::$request->preserv, $validated);
|
||||
self::complete_array_merge(self::$request->preserv ?? [], $validated);
|
||||
|
||||
$hook_data = Hooks::process(
|
||||
array(
|
||||
|
@ -428,9 +428,9 @@ class Request
|
||||
* @param string $var
|
||||
* @param mixed $val
|
||||
*/
|
||||
public function __set($var,$val)
|
||||
public function __set($var, $val)
|
||||
{
|
||||
if ($this->data[$var] !== $val)
|
||||
if (!isset($this->data[$var]) || $this->data[$var] !== $val)
|
||||
{
|
||||
$this->data[$var] = $val;
|
||||
//error_log(__METHOD__."('$var', ...) data of id=$this->id changed ...");
|
||||
|
@ -97,9 +97,10 @@ class Files extends Etemplate\Request
|
||||
* Factory method to get a new request object or the one for an existing request
|
||||
*
|
||||
* @param string $id =null
|
||||
* @return ?Etemplate\Request|false the object or false if $id is not found
|
||||
* @param bool $handle_not_found =true true: handle not found by trying to redirect, false: just return null
|
||||
* @return Request|null null if Request not found and $handle_not_found === false
|
||||
*/
|
||||
static function read($id=null)
|
||||
public static function read($id=null, $handle_not_found=true)
|
||||
{
|
||||
$request = new Files($id);
|
||||
|
||||
|
@ -83,9 +83,10 @@ class Session extends Etemplate\Request
|
||||
* Factory method to get a new request object or the one for an existing request
|
||||
*
|
||||
* @param string $id =null
|
||||
* @return ?Etemplate\request|false the object or false if $id is not found
|
||||
* @param bool $handle_not_found =true true: handle not found by trying to redirect, false: just return null
|
||||
* @return Request|null null if Request not found and $handle_not_found === false
|
||||
*/
|
||||
static function read($id=null)
|
||||
public static function read($id=null, $handle_not_found=true)
|
||||
{
|
||||
$request = new Session($id);
|
||||
|
||||
|
@ -553,12 +553,21 @@ class Widget
|
||||
$method = new ReflectionMethod($this, $method_name);
|
||||
foreach($method->getParameters() as $index => $param)
|
||||
{
|
||||
if(!$param->isOptional() && !array_key_exists($index,$params))
|
||||
if(!$param->isOptional() && !array_key_exists($index, $params))
|
||||
{
|
||||
error_log("Missing required parameter {$param->getPosition()}: {$param->getName()}");
|
||||
$call = false;
|
||||
}
|
||||
if($param->isArray() && !is_array($params[$index]))
|
||||
// Check to see if method wants an array, and we're providing it
|
||||
$paramType = $param->getType();
|
||||
if(!$paramType)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
$types = $paramType instanceof \ReflectionUnionType
|
||||
? $paramType->getTypes()
|
||||
: [$paramType];
|
||||
if(in_array('array', array_map(fn(\ReflectionNamedType $t) => $t->getName(), $types)) && !is_array($params[$index]))
|
||||
{
|
||||
error_log("$method_name expects an array for {$param->getPosition()}: {$param->getName()}");
|
||||
$params[$index] = (array)$params[$index];
|
||||
@ -1044,6 +1053,10 @@ class Widget
|
||||
*/
|
||||
public static function &setElementAttribute($name,$attr,$val)
|
||||
{
|
||||
if (!isset(self::$request))
|
||||
{
|
||||
throw new \Exception(__METHOD__."('$name', '$attr', ".json_encode($val)." called before instanciating Api\Etemplate!");
|
||||
}
|
||||
//error_log(__METHOD__."('$name', '$attr', ...) request=".get_class(self::$request).", response=".get_class(self::$response).function_backtrace());
|
||||
$ref =& self::$request->modifications[$name][$attr];
|
||||
if(self::$request && self::$response)
|
||||
|
@ -50,7 +50,7 @@ class Box extends Etemplate\Widget
|
||||
$old_expand = $params[1];
|
||||
|
||||
if ($this->id && $this->type != 'groupbox') $cname = self::form_name($cname, $this->id, $params[1]);
|
||||
if (!empty($expand['cname']) && $expand['cname'] !== $cname && trim($cname))
|
||||
if (($expand['cname'] ?? null) !== $cname && trim($cname))
|
||||
{
|
||||
$expand['cont'] =& self::get_array(self::$request->content, $cname);
|
||||
$expand['cname'] = $cname;
|
||||
|
@ -98,7 +98,7 @@ class Customfields extends Transformer
|
||||
$form_name = self::form_name($cname, $this->id, $expand);
|
||||
|
||||
// Store properties at top level, so all customfield widgets can share
|
||||
if($this->attrs['app'])
|
||||
if (!empty($this->attrs['app']))
|
||||
{
|
||||
$app = $this->attrs['app'];
|
||||
}
|
||||
@ -141,12 +141,12 @@ class Customfields extends Transformer
|
||||
// app changed
|
||||
$customfields = Api\Storage\Customfields::get($app);
|
||||
}
|
||||
if($this->attrs['customfields'])
|
||||
if (!empty($this->attrs['customfields']))
|
||||
{
|
||||
$customfields = $this->attrs['customfields'];
|
||||
}
|
||||
// Filter fields
|
||||
if($this->attrs['field-names'])
|
||||
if (!empty($this->attrs['field-names']))
|
||||
{
|
||||
$fields_name = explode(',', $this->attrs['field-names']);
|
||||
foreach($fields_name as &$f)
|
||||
@ -162,8 +162,8 @@ class Customfields extends Transformer
|
||||
|
||||
$fields = $customfields;
|
||||
|
||||
$use_private = self::expand_name($this->attrs['use-private'],0,0,'','',self::$cont);
|
||||
$this->attrs['sub-type'] = self::expand_name($this->attrs['sub-type'],0,0,'','',self::$cont);
|
||||
$use_private = self::expand_name($this->attrs['use-private'] ?? null,0,0,'','',self::$cont);
|
||||
$this->attrs['sub-type'] = self::expand_name($this->attrs['sub-type'] ?? null,0,0,'','',self::$cont);
|
||||
|
||||
foreach((array)$fields as $key => $field)
|
||||
{
|
||||
@ -174,7 +174,7 @@ class Customfields extends Transformer
|
||||
}
|
||||
|
||||
// Remove filtered fields
|
||||
if($field_filters && in_array($key, $negate_fields) && in_array($key, $field_filters))
|
||||
if (!empty($field_filters) && in_array($key, $negate_fields) && in_array($key, $field_filters))
|
||||
{
|
||||
unset($fields[$key]);
|
||||
}
|
||||
@ -284,7 +284,7 @@ class Customfields extends Transformer
|
||||
|
||||
$type = $field['type'];
|
||||
// Link-tos needs to change from appname to link-to
|
||||
if($link_types[$field['type']])
|
||||
if (!empty($link_types[$field['type']]))
|
||||
{
|
||||
if($type == 'filemanager')
|
||||
{
|
||||
@ -314,8 +314,8 @@ class Customfields extends Transformer
|
||||
{
|
||||
$widget->attrs['data_format'] = $type == 'date' ? 'Y-m-d' : 'Y-m-d H:i:s';
|
||||
}
|
||||
if($field['values']['min']) $widget->attrs['min'] = $field['values']['min'];
|
||||
if($field['values']['max']) $widget->attrs['min'] = $field['values']['max'];
|
||||
if (isset($field['values']['min'])) $widget->attrs['min'] = $field['values']['min'];
|
||||
if (isset($field['values']['max'])) $widget->attrs['min'] = $field['values']['max'];
|
||||
break;
|
||||
|
||||
case 'vfs-upload':
|
||||
@ -355,7 +355,7 @@ class Customfields extends Transformer
|
||||
$field['values'] = Api\Storage\Customfields::get_options_from_file($field['values']['@']);
|
||||
}
|
||||
// keep extra values set by app code, eg. addressbook advanced search
|
||||
if (is_array(self::$request->sel_options[self::$prefix.$fname]))
|
||||
if (!empty(self::$request->sel_options[self::$prefix.$fname]) && is_array(self::$request->sel_options[self::$prefix.$fname]))
|
||||
{
|
||||
self::$request->sel_options[self::$prefix.$fname] += (array)$field['values'];
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ class Description extends Etemplate\Widget
|
||||
*/
|
||||
public function beforeSendToClient($cname, array $expand=null)
|
||||
{
|
||||
if ($this->attrs['activate_links'])
|
||||
if (!empty($this->attrs['activate_links']))
|
||||
{
|
||||
$form_name = self::form_name($cname, $this->id, $expand);
|
||||
$value =& self::get_array(self::$request->content, $form_name);
|
||||
|
@ -88,8 +88,11 @@ class Grid extends Box
|
||||
return false; // return
|
||||
}
|
||||
|
||||
if ($this->id && $this->type !== 'row') $cname = self::form_name($cname, $this->id, $expand);
|
||||
if (!empty($expand['cname']) && $expand['cname'] !== $cname && $cname)
|
||||
if($this->id && $this->type !== 'row')
|
||||
{
|
||||
$cname = self::form_name($cname, $this->id, $expand);
|
||||
}
|
||||
if($cname && (!empty($expand['cname']) && $expand['cname'] !== $cname || !$expand['cname']))
|
||||
{
|
||||
$expand['cont'] =& self::get_array(self::$request->content, $cname);
|
||||
$expand['cname'] = $cname;
|
||||
|
@ -36,17 +36,17 @@ class Image extends Etemplate\Widget
|
||||
|
||||
$image = $value != '' ? $value : $this->attrs['src'];
|
||||
|
||||
if (is_string($image)) list($app,$img) = explode('/',$image,2);
|
||||
if (!$app || !$img || !is_dir(EGW_SERVER_ROOT.'/'.$app) || strpos($img,'/')!==false)
|
||||
if (is_string($image)) list($app,$img) = explode('/',$image,2)+[null,null];
|
||||
if (empty($app) || empty($img) || !is_dir(EGW_SERVER_ROOT.'/'.$app) || strpos($img,'/')!==false)
|
||||
{
|
||||
$img = $image;
|
||||
list($app) = explode('.',$form_name);
|
||||
}
|
||||
$src = Api\Image::find($app, $img);
|
||||
if(!$this->id)
|
||||
/*if(!$this->id)
|
||||
{
|
||||
// self::setElementAttribute($this->attrs['src'], 'id', $this->attrs['src']);
|
||||
}
|
||||
}*/
|
||||
self::setElementAttribute($this->attrs['src'], 'src', $src);
|
||||
}
|
||||
}
|
||||
|
@ -130,13 +130,13 @@ class Nextmatch extends Etemplate\Widget
|
||||
$send_value = $value;
|
||||
|
||||
list($app) = explode('.',$value['get_rows']);
|
||||
if(!$GLOBALS['egw_info']['apps'][$app])
|
||||
if (empty($GLOBALS['egw_info']['apps'][$app]))
|
||||
{
|
||||
list($app) = explode('.',$this->attrs['template']);
|
||||
}
|
||||
|
||||
// Check for a favorite in URL
|
||||
if($_GET['favorite'] && $value['favorites'])
|
||||
if (!empty($_GET['favorite']) && !empty($value['favorites']))
|
||||
{
|
||||
$safe_name = preg_replace('/[^A-Za-z0-9-_]/','_',strip_tags($_GET['favorite']));
|
||||
$pref_name = "favorite_" .$safe_name;
|
||||
@ -210,7 +210,7 @@ class Nextmatch extends Etemplate\Widget
|
||||
}
|
||||
|
||||
// Favorite group for admins
|
||||
if($GLOBALS['egw_info']['apps']['admin'] && $value['favorites'])
|
||||
if (!empty($GLOBALS['egw_info']['apps']['admin']) && !empty($value['favorites']))
|
||||
{
|
||||
self::$request->sel_options[$form_name]['favorite']['group'] = array('all' => lang('All users')) +
|
||||
Select::typeOptions('select-account',',groups');
|
||||
@ -894,7 +894,7 @@ class Nextmatch extends Etemplate\Widget
|
||||
if ($default_attrs) $action += $default_attrs;
|
||||
|
||||
// Add 'Select All' after first group
|
||||
if ($first_level && $group !== false && $action['group'] != $group && empty($egw_actions[$prefix.'select_all']))
|
||||
if ($first_level && $group !== false && ($action['group']??null) != $group && empty($egw_actions[$prefix.'select_all']))
|
||||
{
|
||||
|
||||
$egw_actions[$prefix.'select_all'] = array(
|
||||
|
@ -49,7 +49,8 @@ class Password extends Etemplate\Widget\Textbox
|
||||
{
|
||||
$form_name = self::form_name($cname, $this->id, $expand);
|
||||
$value =& self::get_array(self::$request->content, $form_name);
|
||||
$plaintext = !in_array(self::expand_name($this->attrs['plaintext'],$expand['c'], $expand['row'], $expand['c_'], $expand['row_'], $expand['cont']),
|
||||
$plaintext = !empty($this->attrs['plaintext']) && !in_array(
|
||||
self::expand_name($this->attrs['plaintext'], $expand['c'] ?? null, $expand['row'] ?? null, $expand['c_'] ?? null, $expand['row_'] ?? null, $expand['cont']),
|
||||
['false', '0']);
|
||||
|
||||
if (!empty($value))
|
||||
|
@ -64,9 +64,13 @@ class Placeholder extends Etemplate\Widget
|
||||
|
||||
if(is_null($apps))
|
||||
{
|
||||
$apps = ['addressbook', 'user', 'general'] +
|
||||
$apps = array_merge(
|
||||
['addressbook', 'user', 'general'],
|
||||
// We use linking for preview, so limit to apps that support links
|
||||
array_keys(Api\Link::app_list('query'));
|
||||
array_keys(Api\Link::app_list('query')),
|
||||
// Filemanager doesn't support links, but add it anyway
|
||||
['filemanager']
|
||||
);
|
||||
}
|
||||
|
||||
foreach($apps as $appname)
|
||||
@ -86,6 +90,8 @@ class Placeholder extends Etemplate\Widget
|
||||
// Looks like app doesn't support merging
|
||||
continue 2;
|
||||
}
|
||||
|
||||
Api\Translation::load_app($appname, $GLOBALS['egw_info']['user']['preferences']['common']['lang']);
|
||||
$list = method_exists($merge, 'get_placeholder_list') ? $merge->get_placeholder_list() : [];
|
||||
break;
|
||||
}
|
||||
|
@ -109,14 +109,14 @@ class Select extends Etemplate\Widget
|
||||
{
|
||||
parent::set_attrs($xml, $cloned);
|
||||
|
||||
if ($this->attrs['multiple'] !== 'dynamic')
|
||||
if (!isset($this->attrs['multiple']) || $this->attrs['multiple'] !== 'dynamic')
|
||||
{
|
||||
$this->attrs['multiple'] = !isset($this->attrs['multiple']) ? false :
|
||||
!(!$this->attrs['multiple'] || $this->attrs['multiple'] === 'false');
|
||||
}
|
||||
|
||||
// set attrs[multiple] from attrs[options], unset options only if it just contains number or rows
|
||||
if ($this->attrs['options'] > 1)
|
||||
if (isset($this->attrs['options']) && $this->attrs['options'] > 1)
|
||||
{
|
||||
$this->attrs['multiple'] = (int)$this->attrs['options'];
|
||||
if ((string)$this->attrs['multiple'] == $this->attrs['options'])
|
||||
@ -124,7 +124,7 @@ class Select extends Etemplate\Widget
|
||||
unset($this->attrs['options']);
|
||||
}
|
||||
}
|
||||
elseif($this->attrs['rows'] > 1)
|
||||
elseif(isset($this->attrs['rows']) && $this->attrs['rows'] > 1)
|
||||
{
|
||||
$this->attrs['multiple'] = true;
|
||||
}
|
||||
@ -311,8 +311,8 @@ class Select extends Etemplate\Widget
|
||||
{
|
||||
$form_name = self::form_name($cname, $this->id, $expand);
|
||||
}
|
||||
if (!is_array(self::$request->sel_options[$form_name])) self::$request->sel_options[$form_name] = array();
|
||||
$type = $this->attrs['type'] ? $this->attrs['type'] : $this->type;
|
||||
if (empty(self::$request->sel_options[$form_name]) || !is_array(self::$request->sel_options[$form_name])) self::$request->sel_options[$form_name] = [];
|
||||
$type = $this->attrs['type'] ?? $this->type;
|
||||
if ($type != 'select' && $type != 'menupopup')
|
||||
{
|
||||
// Check selection preference, we may be able to skip reading some data
|
||||
@ -335,8 +335,8 @@ class Select extends Etemplate\Widget
|
||||
if (!isset($form_names_done[$form_name]) &&
|
||||
($type_options = self::typeOptions($this,
|
||||
// typeOptions thinks # of rows is the first thing in options
|
||||
($this->attrs['rows'] && strpos($this->attrs['options'], $this->attrs['rows']) !== 0 ? $this->attrs['rows'].','.$this->attrs['options'] : $this->attrs['options']),
|
||||
$no_lang, $this->attrs['readonly'], self::get_array(self::$request->content, $form_name), $form_name)))
|
||||
(!empty($this->attrs['rows']) && !empty($this->attrs['options']) && strpos($this->attrs['options'], $this->attrs['rows']) !== 0 ? $this->attrs['rows'].','.$this->attrs['options'] : $this->attrs['options']),
|
||||
$no_lang, $this->attrs['readonly'] ?? false, self::get_array(self::$request->content, $form_name), $form_name)))
|
||||
{
|
||||
self::fix_encoded_options($type_options);
|
||||
|
||||
@ -356,7 +356,7 @@ class Select extends Etemplate\Widget
|
||||
$options = (isset(self::$request->sel_options[$form_name]) ? $form_name : $this->id);
|
||||
if(is_array(self::$request->sel_options[$options]))
|
||||
{
|
||||
if(in_array($this->attrs['type'], self::$cached_types) && !isset($form_names_done[$options]))
|
||||
if (isset($this->attrs['type']) && in_array($this->attrs['type'], self::$cached_types) && !isset($form_names_done[$options]))
|
||||
{
|
||||
// Fix any custom options from application
|
||||
self::fix_encoded_options(self::$request->sel_options[$options],true);
|
||||
@ -561,7 +561,7 @@ class Select extends Etemplate\Widget
|
||||
$field = self::expand_name($field, 0, 0,'','',self::$cont);
|
||||
}
|
||||
|
||||
list($rows,$type,$type2,$type3,$type4,$type5) = $legacy_options;
|
||||
list($rows,$type,$type2,$type3,$type4,$type5) = $legacy_options+[null,null,null,null,null,null];
|
||||
$no_lang = false;
|
||||
$options = array();
|
||||
switch ($widget_type)
|
||||
@ -644,7 +644,7 @@ class Select extends Etemplate\Widget
|
||||
// These are extra info for easy dealing with categories
|
||||
// client side, without extra loading
|
||||
'main' => (int)$cat['main'],
|
||||
'children' => $cat['children'],
|
||||
'children' => $cat['children'] ?? null,
|
||||
//add different class per level to allow different styling for each category level:
|
||||
'class' => "cat_level". $cat['level']
|
||||
);
|
||||
@ -839,7 +839,7 @@ class Select extends Etemplate\Widget
|
||||
}
|
||||
foreach((array)$options as $right => $name)
|
||||
{
|
||||
if(!!($value & $right))
|
||||
if (!!((int)$value & (int)$right))
|
||||
{
|
||||
$new_value[] = $right;
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ class Template extends Etemplate\Widget
|
||||
list($name) = explode('?', $_name); // remove optional cache-buster
|
||||
if (isset(self::$cache[$name]) || !($path = self::relPath($name, $template_set, $version, $load_via)))
|
||||
{
|
||||
if ((!$path || self::read($load_via, $template_set)) && isset(self::$cache[$name]))
|
||||
if ((empty($path) || self::read($load_via, $template_set)) && isset(self::$cache[$name]))
|
||||
{
|
||||
//error_log(__METHOD__."('$name', '$template_set', '$version', '$load_via') read from cache");
|
||||
return self::$cache[$name];
|
||||
@ -146,7 +146,7 @@ class Template extends Etemplate\Widget
|
||||
{
|
||||
static $prefixes = null;
|
||||
unset($version); // not used currently
|
||||
list($app, $rest) = explode('.', $load_via ?: $name, 2);
|
||||
list($app, $rest) = explode('.', $load_via ?: $name, 2)+[null,null];
|
||||
|
||||
if (empty($template_set))
|
||||
{
|
||||
@ -184,7 +184,7 @@ class Template extends Etemplate\Widget
|
||||
$path = $prefix.$path;
|
||||
}
|
||||
//error_log(__METHOD__."('$name', '$template_set') returning ".array2string($path));
|
||||
return $path;
|
||||
return $path ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -230,16 +230,16 @@ class Template extends Etemplate\Widget
|
||||
{
|
||||
$cname =& $params[0];
|
||||
$old_cname = $params[0];
|
||||
if ($this->attrs['content']) $cname = self::form_name($cname, $this->attrs['content'], $params[1]);
|
||||
if (!empty($this->attrs['content'])) $cname = self::form_name($cname, $this->attrs['content'], $params[1]);
|
||||
|
||||
// Check for template from content, and run over it
|
||||
// templates included via template tag have their name to load them from in attribute "template"
|
||||
$expand_name = self::expand_name($this->id ? $this->id : $this->attrs['template'], '','','','',self::$request->content);
|
||||
$expand_name = self::expand_name($this->id ?: $this->attrs['template'], '','','','',self::$request->content);
|
||||
if(!$expand_name && $this->id && $this->attrs['template'])
|
||||
{
|
||||
$expand_name = $this->attrs['template'];
|
||||
}
|
||||
if($this->original_name)
|
||||
if (!empty($this->original_name))
|
||||
{
|
||||
$expand_name = self::expand_name($this->original_name, '','','','',self::$request->content);
|
||||
}
|
||||
|
@ -62,14 +62,14 @@ class Textbox extends Etemplate\Widget
|
||||
parent::set_attrs($xml, $cloned);
|
||||
|
||||
// Legacy handling only
|
||||
// A negative size triggered the HTML readonly attibute, but not etemplate readonly,
|
||||
// A negative size triggered the HTML readonly attribute, but not etemplate readonly,
|
||||
// so you got an input element, but it was not editable.
|
||||
if ($this->attrs['size'] < 0)
|
||||
if (isset($this->attrs['size']) && $this->attrs['size'] < 0)
|
||||
{
|
||||
self::setElementAttribute($this->id, 'size', abs($this->attrs['size']));
|
||||
self::$request->readonlys[$this->id] = false;
|
||||
self::setElementAttribute($this->id, 'readonly', true);
|
||||
trigger_error("Using a negative size to set textbox readonly. " .$this, E_USER_DEPRECATED);
|
||||
//trigger_error("Using a negative size to set textbox readonly. " .$this, E_USER_DEPRECATED);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
@ -143,7 +143,7 @@ class Tree extends Etemplate\Widget
|
||||
parent::set_attrs($xml, $cloned);
|
||||
|
||||
// set attrs[multiple] from attrs[options]
|
||||
if ($this->attrs['options'] > 1)
|
||||
if (isset($this->attrs['options']) && (int)$this->attrs['options'] > 1)
|
||||
{
|
||||
self::setElementAttribute($this->id, 'multiple', true);
|
||||
}
|
||||
@ -297,21 +297,21 @@ class Tree extends Etemplate\Widget
|
||||
{
|
||||
$form_name = self::form_name($cname, $this->id);
|
||||
|
||||
if (($templated_path = self::templateImagePath($this->attrs['image_path'])) != $this->attrs['image_path'])
|
||||
if (($templated_path = self::templateImagePath($this->attrs['image_path'] ?? null)) !== ($this->attrs['image_path'] ?? null))
|
||||
{
|
||||
self::setElementAttribute($form_name, 'image_path', $this->attrs['image_path'] = $templated_path);
|
||||
//error_log(__METHOD__."() setting templated image-path for $form_name: $templated_path");
|
||||
}
|
||||
|
||||
if (!is_array(self::$request->sel_options[$form_name])) self::$request->sel_options[$form_name] = array();
|
||||
if ($this->attrs['type'])
|
||||
if (empty(self::$request->sel_options[$form_name])) self::$request->sel_options[$form_name] = [];
|
||||
if (!empty($this->attrs['type']))
|
||||
{
|
||||
// += to keep further options set by app code
|
||||
self::$request->sel_options[$form_name] += self::typeOptions($this->attrs['type'], $this->attrs['options'],
|
||||
$no_lang, $this->attrs['readonly'], self::get_array(self::$request->content, $form_name), $form_name);
|
||||
self::$request->sel_options[$form_name] += self::typeOptions($this->attrs['type'], $this->attrs['options'] ?? null,
|
||||
$no_lang, $this->attrs['readonly'] ?? null, self::get_array(self::$request->content, $form_name), $form_name);
|
||||
|
||||
// if no_lang was modified, forward modification to the client
|
||||
if ($no_lang != $this->attr['no_lang'])
|
||||
if (!isset($this->attr['no_lang']) || $no_lang != $this->attr['no_lang'])
|
||||
{
|
||||
self::setElementAttribute($form_name, 'no_lang', $no_lang);
|
||||
}
|
||||
@ -440,7 +440,7 @@ class Tree extends Etemplate\Widget
|
||||
*/
|
||||
public static function typeOptions($widget_type, $legacy_options, &$no_lang=false, $readonly=false, $value=null, $form_name=null)
|
||||
{
|
||||
list($rows,$type,$type2,$type3) = explode(',',$legacy_options);
|
||||
list($rows,$type,$type2,$type3) = explode(',', $legacy_options)+[null,null,null,null];
|
||||
|
||||
$no_lang = false;
|
||||
$options = array();
|
||||
|
@ -37,10 +37,10 @@ class Vfs extends File
|
||||
*/
|
||||
public function beforeSendToClient($cname, $expand = array())
|
||||
{
|
||||
if($this->type == 'vfs-upload' || $this->attrs['type'] == 'vfs-upload')
|
||||
if ($this->type === 'vfs-upload' || !empty($this->attrs['type']) && $this->attrs['type'] === 'vfs-upload')
|
||||
{
|
||||
$form_name = self::form_name($cname, $this->id, $expand ? $expand : array('cont'=>self::$request->content));
|
||||
if($this->attrs['path'])
|
||||
if (!empty($this->attrs['path']))
|
||||
{
|
||||
$path = self::expand_name($this->attrs['path'],$expand['c'], $expand['row'], $expand['c_'], $expand['row_'], $expand['cont']);
|
||||
}
|
||||
@ -226,7 +226,7 @@ class Vfs extends File
|
||||
foreach($links as $link)
|
||||
{
|
||||
$matches = null;
|
||||
if (is_array($link) && preg_match('|^'.preg_quote(Api\Vfs::PREFIX,'|').'('.preg_quote(self::get_temp_dir($app, ''), '|').'[^/]+)/|', $link['id']['tmp_name'], $matches))
|
||||
if (is_array($link) && !empty($link['id']['tmp_name']) && preg_match('|^'.preg_quote(Api\Vfs::PREFIX,'|').'('.preg_quote(self::get_temp_dir($app, ''), '|').'[^/]+)/|', $link['id']['tmp_name'], $matches))
|
||||
{
|
||||
$replace[substr($link['id']['tmp_name'], strlen(Api\Vfs::PREFIX))] =
|
||||
Api\Link::vfs_path($app, $id, Api\Vfs::basename($link['id']['tmp_name']), true);
|
||||
|
@ -2,12 +2,12 @@
|
||||
/**
|
||||
* EGroupware - Framework for Ajax based templates: jdots & Pixelegg
|
||||
*
|
||||
* @link http://www.stylite.de
|
||||
* @link https://www.egroupware.org
|
||||
* @package api
|
||||
* @subpackage framework
|
||||
* @author Andreas Stöckel <as@stylite.de>
|
||||
* @author Ralf Becker <rb@stylite.de>
|
||||
* @author Nathan Gray <ng@stylite.de>
|
||||
* @author Andreas Stöckel
|
||||
* @author Ralf Becker <rb@egroupware.org>
|
||||
* @author Nathan Gray <ng@egroupware.org>
|
||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||
*/
|
||||
|
||||
@ -16,7 +16,7 @@ namespace EGroupware\Api\Framework;
|
||||
use EGroupware\Api;
|
||||
|
||||
/**
|
||||
* Stylite jdots template
|
||||
* Framework for Ajax based templates
|
||||
*/
|
||||
abstract class Ajax extends Api\Framework
|
||||
{
|
||||
@ -88,13 +88,13 @@ abstract class Ajax extends Api\Framework
|
||||
$width = self::DEFAULT_SIDEBAR_WIDTH;
|
||||
|
||||
//Check whether the width had been stored explicitly for the jdots template, use that value
|
||||
if ($GLOBALS['egw_info']['user']['preferences'][$app]['jdotssideboxwidth'])
|
||||
if (!empty($GLOBALS['egw_info']['user']['preferences'][$app]['jdotssideboxwidth']))
|
||||
{
|
||||
$width = (int)$GLOBALS['egw_info']['user']['preferences'][$app]['jdotssideboxwidth'];
|
||||
// error_log(__METHOD__.__LINE__."($app):$width --> reading jdotssideboxwidth");
|
||||
}
|
||||
//Otherwise use the legacy "idotssideboxwidth" value
|
||||
else if ($GLOBALS['egw_info']['user']['preferences'][$app]['idotssideboxwidth'])
|
||||
elseif (!empty($GLOBALS['egw_info']['user']['preferences'][$app]['idotssideboxwidth']))
|
||||
{
|
||||
$width = (int)$GLOBALS['egw_info']['user']['preferences'][$app]['idotssideboxwidth'];
|
||||
// error_log(__METHOD__.__LINE__."($app):$width --> reading idotssideboxwidth");
|
||||
@ -249,7 +249,7 @@ abstract class Ajax extends Api\Framework
|
||||
{
|
||||
if (empty($GLOBALS['egw_info']['flags']['java_script'])) $GLOBALS['egw_info']['flags']['java_script']='';
|
||||
// eT2 sets $GLOBALS['egw_info']['flags']['nonavbar'] === 'popup' for popups, Etemplate::exec($outputmode === 2)
|
||||
$extra['check-framework'] = $_GET['cd'] !== 'no' && $GLOBALS['egw_info']['flags']['nonavbar'] !== 'popup';
|
||||
$extra['check-framework'] = (!isset($_GET['cd']) || $_GET['cd'] !== 'no') && $GLOBALS['egw_info']['flags']['nonavbar'] !== 'popup';
|
||||
}
|
||||
}
|
||||
|
||||
@ -1047,16 +1047,16 @@ abstract class Ajax extends Api\Framework
|
||||
if (self::$footer_done) return; // prevent (multiple) footers
|
||||
self::$footer_done = true;
|
||||
|
||||
if (!isset($GLOBALS['egw_info']['flags']['nofooter']) || !$GLOBALS['egw_info']['flags']['nofooter'])
|
||||
if (empty($GLOBALS['egw_info']['flags']['nofooter']))
|
||||
{
|
||||
if ($no_framework && $GLOBALS['egw_info']['user']['preferences']['common']['show_generation_time'])
|
||||
if ($no_framework && !empty($GLOBALS['egw_info']['user']['preferences']['common']['show_generation_time']))
|
||||
{
|
||||
$vars = $this->_get_footer();
|
||||
$footer = "\n".$vars['page_generation_time']."\n";
|
||||
}
|
||||
}
|
||||
return $footer.
|
||||
$GLOBALS['egw_info']['flags']['need_footer']."\n". // eg. javascript, which need to be at the end of the page
|
||||
return ($footer??'').
|
||||
($GLOBALS['egw_info']['flags']['need_footer']??'')."\n". // eg. javascript, which need to be at the end of the page
|
||||
"</body>\n</html>\n";
|
||||
}
|
||||
|
||||
|
@ -49,12 +49,12 @@ class Bundle
|
||||
unset($GLOBALS['egw_info']['server']['debug_minify']);
|
||||
|
||||
$file2bundle = array();
|
||||
if ($GLOBALS['egw_info']['server']['debug_minify'] !== 'True')
|
||||
if (!isset($GLOBALS['egw_info']['server']['debug_minify']) || $GLOBALS['egw_info']['server']['debug_minify'] !== 'True')
|
||||
{
|
||||
// get used bundles and cache them on tree-level for 2h
|
||||
//$bundles = self::all(); Cache::setTree(__CLASS__, 'bundles', $bundles, 7200);
|
||||
$bundles = Cache::getTree(__CLASS__, 'bundles', array(__CLASS__, 'all'), array(), 7200);
|
||||
$bundles_ts = $bundles['.ts'];
|
||||
$bundles_ts = $bundles['.ts'] ?? null;
|
||||
unset($bundles['.ts']);
|
||||
foreach($bundles as $name => $files)
|
||||
{
|
||||
@ -83,13 +83,13 @@ class Bundle
|
||||
|
||||
if (!isset($to_include[$file]))
|
||||
{
|
||||
if (($bundle = $file2bundle[$file]))
|
||||
if (($bundle = $file2bundle[$file] ?? false))
|
||||
{
|
||||
//error_log(__METHOD__."() requiring bundle $bundle for $file");
|
||||
if (!in_array($bundle, $included_bundles))
|
||||
{
|
||||
$included_bundles[] = $bundle;
|
||||
$minurl = self::$bundle2minurl[$bundle];
|
||||
$minurl = self::$bundle2minurl[$bundle] ?? null;
|
||||
if (!isset($minurl) && isset($GLOBALS['egw_info']['apps'][$bundle]))
|
||||
{
|
||||
$minurl = '/'.$bundle.'/js/app.min.js';
|
||||
@ -108,10 +108,10 @@ class Bundle
|
||||
else
|
||||
{
|
||||
unset($query);
|
||||
list($path, $query) = explode('?', $file, 2);
|
||||
list($path, $query) = explode('?', $file, 2)+[null,null];
|
||||
$mod = filemtime(EGW_SERVER_ROOT.$path);
|
||||
// check if we have a more recent minified version of the file and use it
|
||||
if ($GLOBALS['egw_info']['server']['debug_minify'] !== 'True' &&
|
||||
if ((!isset($GLOBALS['egw_info']['server']['debug_minify']) || $GLOBALS['egw_info']['server']['debug_minify'] !== 'True') &&
|
||||
substr($path, -3) == '.js' && file_exists(EGW_SERVER_ROOT.($min_path = substr($path, 0, -3).'.min.js')) &&
|
||||
(($min_mod = filemtime(EGW_SERVER_ROOT.$min_path)) >= $mod))
|
||||
{
|
||||
@ -148,7 +148,7 @@ class Bundle
|
||||
*/
|
||||
protected static function urls(array $js_includes, &$max_modified=null, $minurl=null)
|
||||
{
|
||||
$debug_minify = $GLOBALS['egw_info']['server']['debug_minify'] === 'True';
|
||||
$debug_minify = !empty($GLOBALS['egw_info']['server']['debug_minify']) && $GLOBALS['egw_info']['server']['debug_minify'] === 'True';
|
||||
// ignore not existing minurl
|
||||
if (!empty($minurl) && !file_exists(EGW_SERVER_ROOT.$minurl)) $minurl = null;
|
||||
$to_include_first = $to_include = $to_minify = array();
|
||||
@ -158,7 +158,7 @@ class Bundle
|
||||
{
|
||||
if ($path == '/api/js/jsapi/egw.js') continue; // Leave egw.js out of bundle
|
||||
unset($query);
|
||||
list($path,$query) = explode('?',$path,2);
|
||||
list($path,$query) = explode('?',$path,2)+[null,null];
|
||||
$mod = filemtime(EGW_SERVER_ROOT.$path);
|
||||
if ($mod > $max_modified) $max_modified = $mod;
|
||||
|
||||
|
@ -113,7 +113,7 @@ class CssIncludes
|
||||
{
|
||||
foreach(self::resolve_css_includes($path) as $path)
|
||||
{
|
||||
list($file,$query) = explode('?',$path,2);
|
||||
list($file,$query) = explode('?',$path,2)+[null,null];
|
||||
if (($mod = filemtime(EGW_SERVER_ROOT.$file)) > $max_modified) $max_modified = $mod;
|
||||
|
||||
// do NOT include app.css or categories.php, as it changes from app to app
|
||||
|
@ -100,6 +100,7 @@ class Html
|
||||
// use preg_replace_callback as we experienced problems with links such as <www.example.tld/pfad/zu/einer/pdf-Datei.pdf>
|
||||
$result4 = preg_replace_callback( $Expr, function ($match) {
|
||||
//error_log(__METHOD__.__LINE__.array2string($match));
|
||||
$match += [null,null,null,null];
|
||||
if ($match[4]==';' && (strlen($match[3])-4) >=0 && strpos($match[3],'>',strlen($match[3])-4)!==false)
|
||||
{
|
||||
$match[3] = substr($match[3],0,strpos($match[3],'>',strlen($match[3])-4));
|
||||
@ -111,7 +112,7 @@ class Html
|
||||
$match[4] = ">";
|
||||
}
|
||||
//error_log(__METHOD__.__LINE__.array2string($match));
|
||||
return $match[1]."<a href=\"http://www".$match[2].$match[3]."\" target=\"_blank\">"."www".$match[2].$match[3]."</a>".$match[4];
|
||||
return $match[1]."<a href=\"https://www".$match[2].$match[3]."\" target=\"_blank\">"."www".$match[2].$match[3]."</a>".$match[4];
|
||||
}, $result3 );
|
||||
}
|
||||
return $result4;
|
||||
@ -755,7 +756,7 @@ tinymce.init({
|
||||
{
|
||||
parse_str($vars,$vars);
|
||||
}
|
||||
list($url,$v) = explode('?', $_url); // url may contain additional vars
|
||||
list($url,$v) = explode('?', $_url)+[null,null]; // url may contain additional vars
|
||||
if ($v)
|
||||
{
|
||||
parse_str($v,$v);
|
||||
|
@ -391,7 +391,7 @@ function hl_email_tag_transform($element, $attribute_array=0)
|
||||
// $GLOBALS['egw_info']['user']['preferences']['mail']['allowExternalIMGs'] ? '' : 'match' => '/^cid:.*/'),
|
||||
if (isset($attribute_array['src']))
|
||||
{
|
||||
if (!(strlen($attribute_array['src'])>4 && strlen($attribute_array['src'])<400))
|
||||
if (!(strlen($attribute_array['src'])>4 && strlen($attribute_array['src'])<800))
|
||||
{
|
||||
$attribute_array['alt']= $attribute_array['alt'].' [blocked (reason: url length):'.$attribute_array['src'].']';
|
||||
if (!isset($attribute_array['title'])) $attribute_array['title']=$attribute_array['alt'];
|
||||
|
@ -471,7 +471,7 @@ class Link extends Link\Storage
|
||||
*/
|
||||
static function temp_link_id($app,$id)
|
||||
{
|
||||
return $app.':'.(!in_array($app, array(self::VFS_APPNAME,self::VFS_LINK, self::DATA_APPNAME)) ? $id : $id['name']);
|
||||
return $app.':'.(!in_array($app, array(self::VFS_APPNAME,self::VFS_LINK, self::DATA_APPNAME)) || !is_array($id) ? $id : $id['name']);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -683,15 +683,15 @@ class Link extends Link\Storage
|
||||
{
|
||||
echo "<p>Link::unlink('$link_id','$app',".array2string($id).",'$owner','$app2','$id2', $hold_for_purge)</p>\n";
|
||||
}
|
||||
if ($link_id < 0) // vfs-link?
|
||||
if ((int)$link_id < 0) // vfs-link?
|
||||
{
|
||||
return self::delete_attached(-$link_id);
|
||||
}
|
||||
elseif ($app == self::VFS_APPNAME)
|
||||
elseif ($app === self::VFS_APPNAME)
|
||||
{
|
||||
return self::delete_attached($app2,$id2,$id);
|
||||
}
|
||||
elseif ($app2 == self::VFS_APPNAME)
|
||||
elseif ($app2 === self::VFS_APPNAME)
|
||||
{
|
||||
return self::delete_attached($app,$id,$id2);
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ class Storage
|
||||
{
|
||||
echo "<p>solink.get_links($app,".print_r($id,true).",$only_app,$order,$deleted)</p>\n";
|
||||
}
|
||||
if (($not_only = $only_app[0] == '!'))
|
||||
if (!empty($only_app) && ($not_only = $only_app[0] == '!'))
|
||||
{
|
||||
$only_app = substr($only_app,1);
|
||||
}
|
||||
@ -173,7 +173,7 @@ class Storage
|
||||
catch(Api\Db\Exception $e) {
|
||||
_egw_log_exception($e);
|
||||
}
|
||||
return is_array($id) ? $links : ($links[$id] ? $links[$id] : array());
|
||||
return is_array($id) ? $links : ($links[$id] ?? []);
|
||||
}
|
||||
|
||||
private static function _add2links($row,$left,$only_app,$not_only,array &$links)
|
||||
|
@ -414,14 +414,10 @@ class Mail
|
||||
{
|
||||
//error_log(__METHOD__." Session restore ".function_backtrace());
|
||||
$this->restoreSessionData();
|
||||
$lv_mailbox = $this->sessionData['mailbox'];
|
||||
$firstMessage = $this->sessionData['previewMessage'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->restoreSessionData();
|
||||
$lv_mailbox = $this->sessionData['mailbox'];
|
||||
$firstMessage = $this->sessionData['previewMessage'];
|
||||
$this->sessionData = array();
|
||||
}
|
||||
if (!$_reuseCache) $this->forcePrefReload($_profileID,!$_reuseCache);
|
||||
@ -1000,9 +996,7 @@ class Mail
|
||||
*/
|
||||
static function getTimeOut($_use='IMAP')
|
||||
{
|
||||
$timeout = $GLOBALS['egw_info']['user']['preferences']['mail']['connectionTimeout'];
|
||||
if (empty($timeout)) $timeout = ($_use=='SIEVE'?10:20); // this is the default value
|
||||
return $timeout;
|
||||
return $_use=='SIEVE' ? 10 : 20; // this is the default value
|
||||
}
|
||||
|
||||
/**
|
||||
@ -3211,7 +3205,7 @@ class Mail
|
||||
}
|
||||
//error_log(__METHOD__.__LINE__.array2string($autoFolderObjects));
|
||||
if (!$isGoogleMail) {
|
||||
$folders = array_merge($inboxFolderObject,$autoFolderObjects,(array)$inboxSubFolderObjects,(array)$folders,(array)$typeFolderObject['others'] ?? [],(array)$typeFolderObject['shared'] ?? []);
|
||||
$folders = array_merge($inboxFolderObject,$autoFolderObjects,(array)$inboxSubFolderObjects,(array)$folders,(array)($typeFolderObject['others'] ?? []),(array)($typeFolderObject['shared'] ?? []));
|
||||
} else {
|
||||
// avoid calling sortByAutoFolder as it is not regarding subfolders
|
||||
$gAutoFolderObjectsTmp = $googleAutoFolderObjects;
|
||||
@ -5589,10 +5583,10 @@ class Mail
|
||||
if (empty($_folder)) $_folder = $this->sessionData['mailbox']?: $this->icServer->getCurrentMailbox();
|
||||
$_uid = !(is_object($_uid) || is_array($_uid)) ? (array)$_uid : $_uid;
|
||||
|
||||
if (!$_stream && isset($rawBody[$this->icServer->ImapServerId][$_folder][$_uid[0]][(empty($_partID)?'NIL':$_partID)]))
|
||||
if (!$_stream && isset($rawBody[$this->icServer->ImapServerId][(string)$_folder][$_uid[0]][(empty($_partID)?'NIL':$_partID)]))
|
||||
{
|
||||
//error_log(__METHOD__.' ('.__LINE__.') '." Using Cache for raw Body $_uid, $_partID in Folder $_folder");
|
||||
return $rawBody[$this->icServer->ImapServerId][$_folder][$_uid[0]][(empty($_partID)?'NIL':$_partID)];
|
||||
return $rawBody[$this->icServer->ImapServerId][(string)$_folder][$_uid[0]][(empty($_partID)?'NIL':$_partID)];
|
||||
}
|
||||
|
||||
$uidsToFetch = new Horde_Imap_Client_Ids();
|
||||
@ -5629,7 +5623,7 @@ class Mail
|
||||
if (!$_stream)
|
||||
{
|
||||
//error_log(__METHOD__.' ('.__LINE__.') '."[{$this->icServer->ImapServerId}][$_folder][$_uid][".(empty($_partID)?'NIL':$_partID)."]");
|
||||
$rawBody[$this->icServer->ImapServerId][$_folder][$_uid[0]][(empty($_partID)?'NIL':$_partID)] = $body;
|
||||
$rawBody[$this->icServer->ImapServerId][(string)$_folder][$_uid[0]][(empty($_partID)?'NIL':$_partID)] = $body;
|
||||
}
|
||||
return $body;
|
||||
}
|
||||
@ -6775,6 +6769,8 @@ class Mail
|
||||
//error_log(__METHOD__."()");
|
||||
$imageC = 0;
|
||||
$images = null;
|
||||
$attachments = null;
|
||||
|
||||
if (preg_match_all("/(src|background)=\"(.*)\"/Ui", $_html2parse, $images) && isset($images[2]))
|
||||
{
|
||||
foreach($images[2] as $i => $url)
|
||||
@ -6782,18 +6778,18 @@ class Mail
|
||||
//$isData = false;
|
||||
$basedir = $data = '';
|
||||
$needTempFile = true;
|
||||
|
||||
$attachmentData = ['name' => '', 'type' => '', 'file' => '', 'tmp_name' => ''];
|
||||
try
|
||||
{
|
||||
// do not change urls for absolute images (thanks to corvuscorax)
|
||||
if (substr($url, 0, 5) !== 'data:')
|
||||
if (!str_starts_with($url, 'data:'))
|
||||
{
|
||||
$filename = basename($url); // need to resolve all sort of url
|
||||
$attachmentData['name'] = basename($url); // need to resolve all sort of url
|
||||
if (($directory = dirname($url)) == '.') $directory = '';
|
||||
$ext = pathinfo($filename, PATHINFO_EXTENSION);
|
||||
$mimeType = MimeMagic::ext2mime($ext);
|
||||
if ( strlen($directory) > 1 && substr($directory,-1) != '/') { $directory .= '/'; }
|
||||
$myUrl = $directory.$filename;
|
||||
$ext = pathinfo($attachmentData['name'], PATHINFO_EXTENSION);
|
||||
$attachmentData['type'] = MimeMagic::ext2mime($ext);
|
||||
if ( strlen($directory) > 1 && !str_ends_with($directory, '/')) { $directory .= '/'; }
|
||||
$myUrl = $directory.$attachmentData['name'];
|
||||
if ($myUrl[0]=='/') // local path -> we only allow path's that are available via http/https (or vfs)
|
||||
{
|
||||
$basedir = Framework::getUrl('/');
|
||||
@ -6801,7 +6797,7 @@ class Mail
|
||||
// use vfs instead of url containing webdav.php
|
||||
// ToDo: we should test if the webdav url is of our own scope, as we cannot handle foreign
|
||||
// webdav.php urls as vfs
|
||||
if (strpos($myUrl,'/webdav.php') !== false) // we have a webdav link, so we build a vfs/sqlfs link of it.
|
||||
if (str_contains($myUrl, '/webdav.php')) // we have a webdav link, so we build a vfs/sqlfs link of it.
|
||||
{
|
||||
Vfs::load_wrapper('vfs');
|
||||
list(,$myUrl) = explode('/webdav.php',$myUrl,2);
|
||||
@ -6811,7 +6807,7 @@ class Mail
|
||||
|
||||
// If it is an inline image url, we need to fetch the actuall attachment
|
||||
// content and later on to be able to store its content as temp file
|
||||
if (strpos($myUrl, '/index.php?menuaction=mail.mail_ui.displayImage') !== false && $mail_bo)
|
||||
if ($mail_bo && str_contains($myUrl, '/index.php?menuaction=mail.mail_ui.displayImage'))
|
||||
{
|
||||
$URI_params = array();
|
||||
// Strips the url and store it into a temp for further procss
|
||||
@ -6826,50 +6822,50 @@ class Mail
|
||||
if ($attachment)
|
||||
{
|
||||
$data = $attachment->getContents();
|
||||
$mimeType = $attachment->getType();
|
||||
$filename = $attachment->getDispositionParameter('filename');
|
||||
$attachmentData['type'] = $attachment->getType();
|
||||
$attachmentData['name'] = $attachment->getDispositionParameter('filename');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( strlen($basedir) > 1 && substr($basedir,-1) != '/' && $myUrl[0]!='/') { $basedir .= '/'; }
|
||||
if ($needTempFile && !$attachment && substr($myUrl,0,4) !== "http") $data = file_get_contents($basedir.urldecode($myUrl));
|
||||
if ( $myUrl[0]!='/' && strlen($basedir) > 1 && !str_ends_with($basedir, '/')) { $basedir .= '/'; }
|
||||
if ($needTempFile && !$attachment && !str_starts_with($myUrl, "http")) $data = file_get_contents($basedir.urldecode($myUrl));
|
||||
}
|
||||
if (substr($url,0,strlen('data:'))=='data:')
|
||||
if (str_starts_with($url, 'data:'))
|
||||
{
|
||||
//error_log(__METHOD__.' ('.__LINE__.') '.' -> '.$i.': '.array2string($images[$i]));
|
||||
// we only support base64 encoded data
|
||||
$tmp = substr($url,strlen('data:'));
|
||||
list($mimeType,$data_base64) = explode(';base64,',$tmp);
|
||||
list($attachmentData['type'],$data_base64) = explode(';base64,',$tmp);
|
||||
$data = base64_decode($data_base64);
|
||||
// FF currently does NOT add any mime-type
|
||||
if (strtolower(substr($mimeType, 0, 6)) != 'image/')
|
||||
if (strtolower(substr($attachmentData['type'], 0, 6)) != 'image/')
|
||||
{
|
||||
$mimeType = MimeMagic::analyze_data($data);
|
||||
$attachmentData['type'] = MimeMagic::analyze_data($data);
|
||||
}
|
||||
list($what,$exactly) = explode('/',$mimeType);
|
||||
list($what,$exactly) = explode('/',$attachmentData['type']);
|
||||
$needTempFile = true;
|
||||
$filename = ($what?$what:'data').$imageC++.'.'.$exactly;
|
||||
$attachmentData['name'] = ($what ?: 'data').$imageC++.'.'.$exactly;
|
||||
}
|
||||
if ($data || $needTempFile === false)
|
||||
{
|
||||
if ($needTempFile)
|
||||
{
|
||||
$attachment_file =tempnam($GLOBALS['egw_info']['server']['temp_dir'],$GLOBALS['egw_info']['flags']['currentapp']."_");
|
||||
$tmpfile = fopen($attachment_file,'w');
|
||||
$attachmentData['file'] =tempnam($GLOBALS['egw_info']['server']['temp_dir'],$GLOBALS['egw_info']['flags']['currentapp']."_");
|
||||
$tmpfile = fopen($attachmentData['file'],'w');
|
||||
fwrite($tmpfile,$data);
|
||||
fclose($tmpfile);
|
||||
}
|
||||
else
|
||||
{
|
||||
$attachment_file = $basedir.urldecode($myUrl);
|
||||
$attachmentData['file'] = $basedir.urldecode($myUrl);
|
||||
}
|
||||
// we use $attachment_file as base for cid instead of filename, as it may be image.png
|
||||
// we use $attachmentData['file'] as base for cid instead of filename, as it may be image.png
|
||||
// (or similar) in all cases (when cut&paste). This may lead to more attached files, in case
|
||||
// we use the same image multiple times, but, if we do this, we should try to detect that
|
||||
// on upload. filename itself is not sufficient to determine the sameness of images
|
||||
$cid = 'cid:' . md5($attachment_file);
|
||||
if ($_mailObject->AddEmbeddedImage($attachment_file, substr($cid, 4), urldecode($filename), $mimeType) !== null)
|
||||
$cid = 'cid:' . md5($attachmentData['file']);
|
||||
if ($_mailObject->AddEmbeddedImage($attachmentData['file'], substr($cid, 4), urldecode($attachmentData['file']), $attachmentData['type']) !== null)
|
||||
{
|
||||
//$_html2parse = preg_replace("/".$images[1][$i]."=\"".preg_quote($url, '/')."\"/Ui", $images[1][$i]."=\"".$cid."\"", $_html2parse);
|
||||
$_html2parse = str_replace($images[0][$i], $images[1][$i].'="'.$cid.'"', $_html2parse);
|
||||
@ -6882,12 +6878,8 @@ class Mail
|
||||
error_log("Error adding inline attachment. " . $e->getMessage());
|
||||
error_log($e->getTraceAsString());
|
||||
}
|
||||
$attachments [] = array(
|
||||
'name' => $filename,
|
||||
'type' => $mimeType,
|
||||
'file' => $attachment_file,
|
||||
'tmp_name' => $attachment_file
|
||||
);
|
||||
$attachmentData['tmp_name'] = $attachmentData['file'];
|
||||
$attachments [] = $attachmentData;
|
||||
}
|
||||
return is_array($attachments) ? $attachments : null;
|
||||
}
|
||||
|
@ -549,7 +549,7 @@ class Account implements \ArrayAccess
|
||||
$row = array_merge($row, Credentials::from_session($row));
|
||||
}
|
||||
// fill an empty ident_realname or ident_email of current user with data from user account
|
||||
if ($replace_placeholders && (!isset($user) || $user == $GLOBALS['egw_info']['user']['acount_id']))
|
||||
if ($replace_placeholders && (!isset($user) || $user == $GLOBALS['egw_info']['user']['account_id']))
|
||||
{
|
||||
if (empty($row['ident_realname'])) $row['ident_realname'] = $GLOBALS['egw_info']['user']['account_fullname'];
|
||||
if (empty($row['ident_email'])) $row['ident_email'] = $GLOBALS['egw_info']['user']['account_email'];
|
||||
@ -737,13 +737,13 @@ class Account implements \ArrayAccess
|
||||
|
||||
if (empty($data['ident_email']) && $is_current_user)
|
||||
{
|
||||
$data['ident_email'] = $GLOBALS['egw_info']['user']['account_email'];
|
||||
$data['ident_email'] = $GLOBALS['egw_info']['user']['account_email'] ?? null;
|
||||
}
|
||||
}
|
||||
if (empty($data['ident_realname']))
|
||||
{
|
||||
$data['ident_realname'] = $account->ident_realname || !$is_current_user ?
|
||||
$account->ident_realname : $GLOBALS['egw_info']['user']['account_fullname'];
|
||||
$account->ident_realname : ($GLOBALS['egw_info']['user']['account_fullname'] ?? null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1414,7 +1414,7 @@ class Account implements \ArrayAccess
|
||||
{
|
||||
// for current user prefer account with ident_email matching user email or domain
|
||||
// (this also helps notifications to account allowing to send with from address of current user / account_email)
|
||||
if ($only_current_user && $GLOBALS['egw_info']['user']['account_email'])
|
||||
if ($only_current_user && !empty($GLOBALS['egw_info']['user']['account_email']))
|
||||
{
|
||||
list(,$domain) = explode('@', $account_email = $GLOBALS['egw_info']['user']['account_email']);
|
||||
// empty ident_email will be replaced with account_email!
|
||||
|
@ -265,10 +265,10 @@ class Credentials
|
||||
throw new Api\Exception\WrongParameter("Unknown data[acc_imap_logintype]=".array2string($data['acc_imap_logintype']).'!');
|
||||
}
|
||||
$password = base64_decode(Api\Cache::getSession('phpgwapi', 'password'));
|
||||
$realname = !$set_identity || $data['ident_realname'] ? $data['ident_realname'] :
|
||||
$GLOBALS['egw_info']['user']['account_fullname'];
|
||||
$email = !$set_identity || $data['ident_email'] ? $data['ident_email'] :
|
||||
$GLOBALS['egw_info']['user']['account_email'];
|
||||
$realname = !$set_identity || !empty($data['ident_realname']) ? $data['ident_realname'] :
|
||||
($GLOBALS['egw_info']['user']['account_fullname'] ?? null);
|
||||
$email = !$set_identity || !empty($data['ident_email']) ? $data['ident_email'] :
|
||||
($GLOBALS['egw_info']['user']['account_email'] ?? null);
|
||||
|
||||
return array(
|
||||
'ident_realname' => $realname,
|
||||
|
@ -169,7 +169,7 @@ class Html
|
||||
if ($addbracesforendtag === true )
|
||||
{
|
||||
if (stripos($_body,'<'.$tag)!==false) $ct = preg_match_all('#<'.$tag.'(?:\s.*)?>(.+)</'.$endtag.'>#isU', $_body, $found);
|
||||
if ($ct>0)
|
||||
if (isset($ct) && $ct>0)
|
||||
{
|
||||
//error_log(__METHOD__.__LINE__.array2string($found[0]));
|
||||
// only replace what we have found
|
||||
@ -495,7 +495,7 @@ class Html
|
||||
$html = preg_replace('/&(?!#?[a-zA-Z0-9]+;)/', '&', $html);
|
||||
|
||||
$dom = new \DOMDocument('1.0','UTF-8');
|
||||
if(!$dom->loadHTML(
|
||||
if (!@$dom->loadHTML(
|
||||
'<?xml encoding="UTF-8">'. Api\Translation::convert($html,preg_match('/<meta[^>]+content="[^>"]+charset=([^;"]+)/i', $html, $matches) ? $matches[1] : false, 'utf8'),
|
||||
LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD | LIBXML_NOBLANKS
|
||||
))
|
||||
|
@ -360,9 +360,7 @@ class Imap extends Horde_Imap_Client_Socket implements Imap\PushIface
|
||||
*/
|
||||
static function getTimeOut($_use='IMAP')
|
||||
{
|
||||
$timeout = $GLOBALS['egw_info']['user']['preferences']['mail']['connectionTimeout'];
|
||||
if (empty($timeout) || !($timeout > 0)) $timeout = $_use == 'SIEVE' ? 10 : 20; // this is the default value
|
||||
return $timeout;
|
||||
return $_use == 'SIEVE' ? 10 : 20; // this is the default value
|
||||
}
|
||||
|
||||
/**
|
||||
@ -742,7 +740,7 @@ class Imap extends Horde_Imap_Client_Socket implements Imap\PushIface
|
||||
* @param string $returnAttributes true means return an assoc array containing mailbox names and mailbox attributes
|
||||
* false - the default - means return an array of mailboxes with only selected attributes like delimiter
|
||||
*
|
||||
* @return mixed array of mailboxes
|
||||
* @return ?array array of mailboxes or null
|
||||
*/
|
||||
function listSubscribedMailboxes($reference = '' , $restriction_search = 0, $returnAttributes = false)
|
||||
{
|
||||
@ -794,10 +792,10 @@ class Imap extends Horde_Imap_Client_Socket implements Imap\PushIface
|
||||
}
|
||||
else
|
||||
{
|
||||
$ret[$k]=array('MAILBOX'=>$k,'ATTRIBUTES'=>$box['attributes'],'delimiter'=>($box['delimiter']?$box['delimiter']:$this->getDelimiter('personal')),'SUBSCRIBED'=>true);
|
||||
$ret[$k]=array('MAILBOX'=>$k,'ATTRIBUTES'=>$box['attributes'],'delimiter'=>($box['delimiter']?:$this->getDelimiter('personal')),'SUBSCRIBED'=>true);
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
return $ret ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1376,6 +1374,7 @@ class Imap extends Horde_Imap_Client_Socket implements Imap\PushIface
|
||||
case 'retrieveRules':
|
||||
case 'getVacation':
|
||||
case 'setVacation':
|
||||
case 'getExtensions':
|
||||
if (is_null($this->sieve))
|
||||
{
|
||||
$this->sieve = new Sieve($this);
|
||||
|
@ -73,7 +73,7 @@ class Notifications
|
||||
$account_specific = 0;
|
||||
foreach($rows as $row)
|
||||
{
|
||||
if ($row['account_id'])
|
||||
if (!empty($row['account_id']))
|
||||
{
|
||||
$account_specific = $row['account_id'];
|
||||
}
|
||||
@ -82,7 +82,7 @@ class Notifications
|
||||
{
|
||||
self::$cache[$acc_id][$row['account_id']][] = $row['notif_folder'];
|
||||
} // make sure set the account_specific correctly when notify_folder gets removed
|
||||
elseif (!$row['account_id'] && !is_array($account_id) && is_array($rows[$account_id]))
|
||||
elseif (empty($row['account_id']) && !is_array($account_id) && is_array($rows[$account_id]))
|
||||
{
|
||||
$account_specific = $account_id;
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ class Script
|
||||
var $emailNotification; /* email notification settings. */
|
||||
var $pcount; /* highest priority value in ruleset. */
|
||||
var $errstr; /* error text. */
|
||||
var $extensions; /* contains extensions status*/
|
||||
/**
|
||||
* Body transform content types
|
||||
*
|
||||
@ -69,6 +70,21 @@ class Script
|
||||
$this->emailNotification = array(); // Added email notifications
|
||||
$this->pcount = 0;
|
||||
$this->errstr = '';
|
||||
$this->extensions = [];
|
||||
}
|
||||
|
||||
private function _setExtensionsStatus(Sieve $connection)
|
||||
{
|
||||
$this->extensions = [
|
||||
'vacation' => $connection->hasExtension('vacation'),
|
||||
'regex' => $connection->hasExtension('regex'),
|
||||
'enotify' => $connection->hasExtension('enotify'),
|
||||
'body' => $connection->hasExtension('body'),
|
||||
'variables' => $connection->hasExtension('variables'),
|
||||
'date' => $connection->hasExtension('date'),
|
||||
'imap4flags' => $connection->hasExtension('imap4flags'),
|
||||
'relational' => $connection->hasExtension('relational'),
|
||||
];
|
||||
}
|
||||
|
||||
// get sieve script rules for this user
|
||||
@ -86,6 +102,7 @@ class Script
|
||||
$anyofbit = 4;
|
||||
$keepbit = 8;
|
||||
$regexbit = 128;
|
||||
$this->_setExtensionsStatus($connection);
|
||||
|
||||
if (!isset($this->name)){
|
||||
$this->errstr = 'retrieveRules: no script name specified';
|
||||
@ -150,10 +167,10 @@ class Script
|
||||
$rule['anyof'] = ($bits[8] & $anyofbit);
|
||||
$rule['keep'] = ($bits[8] & $keepbit);
|
||||
$rule['regexp'] = ($bits[8] & $regexbit);
|
||||
$rule['bodytransform'] = ($bits[12]);
|
||||
$rule['field_bodytransform'] = ($bits[13]);
|
||||
$rule['ctype'] = ($bits[14]);
|
||||
$rule['field_ctype_val'] = ($bits[15]);
|
||||
$rule['bodytransform'] = ($bits[12]??null);
|
||||
$rule['field_bodytransform'] = ($bits[13]??null);
|
||||
$rule['ctype'] = ($bits[14]??null);
|
||||
$rule['field_ctype_val'] = ($bits[15]??null);
|
||||
$rule['unconditional'] = 0;
|
||||
if (!$rule['from'] && !$rule['to'] && !$rule['subject'] &&
|
||||
!$rule['field'] && !$rule['size'] && $rule['action']) {
|
||||
@ -188,7 +205,7 @@ class Script
|
||||
}
|
||||
$vacation['addresses'] = &$vaddresses;
|
||||
|
||||
$vacation['forwards'] = $bits[5];
|
||||
$vacation['forwards'] = $bits[5]??null;
|
||||
}
|
||||
break;
|
||||
case "notify":
|
||||
@ -236,7 +253,6 @@ class Script
|
||||
|
||||
$activerules = 0;
|
||||
$regexused = 0;
|
||||
$regexsupported = true;
|
||||
$rejectused = 0;
|
||||
$vacation_active = false;
|
||||
|
||||
@ -245,13 +261,10 @@ class Script
|
||||
|
||||
//include "$default->lib_dir/version.php";
|
||||
|
||||
// lets generate the main body of the script from our rules
|
||||
$enotify = $variables= $supportsbody = false;
|
||||
if ($connection->hasExtension('enotify')) $enotify = true;
|
||||
if ($connection->hasExtension('variables')) $variables = true;
|
||||
if ($connection->hasExtension('body')) $supportsbody = true;
|
||||
if (!$connection->hasExtension('vacation')) $this->vacation = false;
|
||||
if (!$connection->hasExtension('regex')) $regexsupported = false;
|
||||
// set extensions status
|
||||
$this->_setExtensionsStatus($connection);
|
||||
|
||||
if (!$this->extensions['vacation']) $this->vacation = false;
|
||||
|
||||
$newscriptbody = "";
|
||||
$continue = 1;
|
||||
@ -334,7 +347,7 @@ class Script
|
||||
$newruletext .= "size " . $xthan . $rule['size'] . "K";
|
||||
$started = 1;
|
||||
}
|
||||
if ($supportsbody){
|
||||
if ($this->extensions['body']){
|
||||
if (!empty($rule['field_bodytransform'])){
|
||||
if ($started) $newruletext .= ", ";
|
||||
$btransform = " :raw ";
|
||||
@ -379,6 +392,9 @@ class Script
|
||||
if (preg_match("/discard/i",$rule['action'])) {
|
||||
$newruletext .= "discard;";
|
||||
}
|
||||
if (preg_match("/flags/i",$rule['action'])) {
|
||||
$newruletext .= "addflag \"".$rule['action_arg']."\";";
|
||||
}
|
||||
if ($rule['keep']) $newruletext .= "\n\tkeep;";
|
||||
if (!$rule['unconditional']) $newruletext .= "\n}";
|
||||
|
||||
@ -417,7 +433,7 @@ class Script
|
||||
$vacation_active = true;
|
||||
if ($vacation['text'])
|
||||
{
|
||||
if ($regexsupported)
|
||||
if ($this->extensions['regex'])
|
||||
{
|
||||
$newscriptbody .= "if header :regex ".'"X-Spam-Status" '.'"\\\\bYES\\\\b"'."{\n\tstop;\n}\n"; //stop vacation reply if it is spam
|
||||
$regexused = 1;
|
||||
@ -441,17 +457,17 @@ class Script
|
||||
}
|
||||
$newscriptbody .= "\tkeep;\n}\n";
|
||||
}
|
||||
$newscriptbody .= "vacation :days " . $vacation['days'];
|
||||
$vac_rule = "vacation :days " . $vacation['days'];
|
||||
$first = 1;
|
||||
if (!empty($vacation['addresses'][0]))
|
||||
{
|
||||
$newscriptbody .= " :addresses [";
|
||||
$vac_rule .= " :addresses [";
|
||||
foreach ($vacation['addresses'] as $vaddress) {
|
||||
if (!$first) $newscriptbody .= ", ";
|
||||
$newscriptbody .= "\"" . trim($vaddress) . "\"";
|
||||
if (!$first) $vac_rule .= ", ";
|
||||
$vac_rule .= "\"" . trim($vaddress) . "\"";
|
||||
$first = 0;
|
||||
}
|
||||
$newscriptbody .= "] ";
|
||||
$vac_rule .= "] ";
|
||||
}
|
||||
$message = $vacation['text'];
|
||||
if ($vacation['start_date'] || $vacation['end_date'])
|
||||
@ -463,7 +479,20 @@ class Script
|
||||
date($format_date,$vacation['end_date']),
|
||||
),$message);
|
||||
}
|
||||
$newscriptbody .= " text:\n" . $message . "\n.\n;\n\n";
|
||||
$vac_rule .= " text:\n" . $message . "\n.\n;\n\n";
|
||||
if ($this->extensions['date'] && $vacation['start_date'] && $vacation['end_date'])
|
||||
{
|
||||
$newscriptbody .= "if allof (\n".
|
||||
"currentdate :value \"ge\" \"date\" \"". date('Y-m-d', $vacation['start_date']) ."\",\n".
|
||||
"currentdate :value \"le\" \"date\" \"". date('Y-m-d', $vacation['end_date']) ."\")\n".
|
||||
"{\n".
|
||||
$vac_rule."\n".
|
||||
"}\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$newscriptbody .= $vac_rule;
|
||||
}
|
||||
}
|
||||
|
||||
// update with any changes.
|
||||
@ -476,10 +505,10 @@ class Script
|
||||
|
||||
// format notification body
|
||||
$egw_site_title = $GLOBALS['egw_info']['server']['site_title'];
|
||||
if ($enotify==true)
|
||||
if ($this->extensions['enotify']==true)
|
||||
{
|
||||
$notification_body = lang("You have received a new message on the")." {$egw_site_title}";
|
||||
if ($variables)
|
||||
if ($this->extensions['variables'])
|
||||
{
|
||||
$notification_body .= ", ";
|
||||
$notification_body .= 'From: ${from}';
|
||||
@ -522,13 +551,18 @@ class Script
|
||||
|
||||
if ($activerules) {
|
||||
$newscripthead .= "require [\"fileinto\"";
|
||||
if ($regexsupported && $regexused) $newscripthead .= ",\"regex\"";
|
||||
if ($this->extensions['regex'] && $regexused) $newscripthead .= ",\"regex\"";
|
||||
if ($rejectused) $newscripthead .= ",\"reject\"";
|
||||
if ($this->vacation && $vacation_active) {
|
||||
$newscripthead .= ",\"vacation\"";
|
||||
}
|
||||
if ($supportsbody) $newscripthead .= ",\"body\"";
|
||||
if ($this->emailNotification && $this->emailNotification['status'] == 'on') $newscripthead .= ',"'.($enotify?'e':'').'notify"'.($variables?',"variables"':''); // Added email notifications
|
||||
if ($this->extensions['body']) $newscripthead .= ",\"body\"";
|
||||
if ($this->extensions['date']) $newscripthead .= ",\"date\"";
|
||||
if ($this->extensions['relational']) $newscripthead .= ",\"relational\"";
|
||||
if ($this->extensions['variables']) $newscripthead .= ",\"variables\"";
|
||||
if ($this->extensions['imap4flags']) $newscripthead .= ",\"imap4flags\"";
|
||||
|
||||
if ($this->emailNotification && $this->emailNotification['status'] == 'on') $newscripthead .= ',"'.($this->extensions['enotify']?'e':'').'notify"'.($this->extensions['variables']?',"variables"':''); // Added email notifications
|
||||
$newscripthead .= "];\n\n";
|
||||
} else {
|
||||
// no active rules, but might still have an active vacation rule
|
||||
@ -536,18 +570,21 @@ class Script
|
||||
if ($this->vacation && $vacation_active)
|
||||
{
|
||||
$newscripthead .= "require [\"vacation\"";
|
||||
if ($regexsupported && $regexused) $newscripthead .= ",\"regex\"";
|
||||
if ($this->extensions['regex'] && $regexused) $newscripthead .= ",\"regex\"";
|
||||
if ($this->extensions['date']) $newscripthead .= ",\"date\"";
|
||||
if ($this->extensions['relational']) $newscripthead .= ",\"relational\"";
|
||||
|
||||
$closeRequired=true;
|
||||
}
|
||||
if ($this->emailNotification && $this->emailNotification['status'] == 'on')
|
||||
{
|
||||
if ($this->vacation && $vacation_active)
|
||||
{
|
||||
$newscripthead .= ",\"".($enotify?'e':'')."notify\"".($variables?',"variables"':'')."];\n\n"; // Added email notifications
|
||||
$newscripthead .= ",\"".($this->extensions['enotify']?'e':'')."notify\"".($this->extensions['variables']?',"variables"':'')."];\n\n"; // Added email notifications
|
||||
}
|
||||
else
|
||||
{
|
||||
$newscripthead .= "require [\"".($enotify?'e':'')."notify\"".($variables?',"variables"':'')."];\n\n"; // Added email notifications
|
||||
$newscripthead .= "require [\"".($this->extensions['enotify']?'e':'')."notify\"".($this->extensions['variables']?',"variables"':'')."];\n\n"; // Added email notifications
|
||||
}
|
||||
}
|
||||
if ($closeRequired) $newscripthead .= "];\n\n";
|
||||
@ -570,7 +607,7 @@ class Script
|
||||
$newscriptfoot .= "#rule&&" . $rule['priority'] . "&&" . $rule['status'] . "&&" .
|
||||
addslashes($rule['from']) . "&&" . addslashes($rule['to']) . "&&" . addslashes($rule['subject']) . "&&" . $rule['action'] . "&&" .
|
||||
$rule['action_arg'] . "&&" . $rule['flg'] . "&&" . addslashes($rule['field']) . "&&" . addslashes($rule['field_val']) . "&&" . $rule['size'];
|
||||
if ($supportsbody && (!empty($rule['field_bodytransform']) || ($rule['ctype']!= '0' && !empty($rule['ctype'])))) $newscriptfoot .= "&&" . $rule['bodytransform'] . "&&" . $rule['field_bodytransform']. "&&" . $rule['ctype'] . "&&" . $rule['field_ctype_val'];
|
||||
if ($this->extensions['body'] && (!empty($rule['field_bodytransform']) || ($rule['ctype']!= '0' && !empty($rule['ctype'])))) $newscriptfoot .= "&&" . $rule['bodytransform'] . "&&" . $rule['field_bodytransform']. "&&" . $rule['ctype'] . "&&" . $rule['field_ctype_val'];
|
||||
$newscriptfoot .= "\n";
|
||||
$pcount = $pcount+2;
|
||||
//error_log(__CLASS__."::".__METHOD__.__LINE__.array2string($newscriptfoot));
|
||||
@ -616,7 +653,7 @@ class Script
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->errstr = 'updateScript: putscript failed: ' . $e->getMessage().($e->details?': '.$e->details:'');
|
||||
if ($regexused&&!$regexsupported) $this->errstr .= " REGEX is not an supported CAPABILITY";
|
||||
if ($regexused && !$this->extensions['regex']) $this->errstr .= " REGEX is not an supported CAPABILITY";
|
||||
error_log(__METHOD__.__LINE__.' # Error: ->'.$this->errstr);
|
||||
error_log(__METHOD__.__LINE__.' # ScriptName:'.$this->name.' Script:'.$newscript);
|
||||
error_log(__METHOD__.__LINE__.' # Instance='.$GLOBALS['egw_info']['user']['domain'].', User='.$GLOBALS['egw_info']['user']['account_lid']);
|
||||
|
@ -94,7 +94,7 @@ class Sql extends Mail\Smtp
|
||||
);
|
||||
}
|
||||
}
|
||||
if ($this->debug) error_log(__METHOD__."('$_accountName') returning ".array2string($emailAddresses));
|
||||
if (!empty($this->debug)) error_log(__METHOD__."('$_accountName') returning ".array2string($emailAddresses));
|
||||
|
||||
return $emailAddresses;
|
||||
}
|
||||
@ -191,7 +191,7 @@ class Sql extends Mail\Smtp
|
||||
case self::TYPE_MAILBOX:
|
||||
$userData['mailMessageStore'] = $row['mail_value'];
|
||||
//error_log(__METHOD__."('$user') row=".array2string($row).', enabled[$row[account_id]]='.array2string($enabled[$row['account_id']]).', forwardOnly[$row[account_id]]='.array2string($forwardOnly[$row['account_id']]));
|
||||
if ($row['account_id'] > 0 && $enabled[$row['account_id']] && !$forwardOnly[$row['account_id']])
|
||||
if ($row['account_id'] > 0 && !empty($enabled[$row['account_id']]) && empty($forwardOnly[$row['account_id']]))
|
||||
{
|
||||
$userData['uid'][] = $this->accounts->id2name($row['account_id'], 'account_lid');
|
||||
$userData['mailbox'][] = $row['mail_value'];
|
||||
@ -218,7 +218,7 @@ class Sql extends Mail\Smtp
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($this->debug) error_log(__METHOD__."('$user') returning ".array2string($userData));
|
||||
if (!empty($this->debug)) error_log(__METHOD__."('$user') returning ".array2string($userData));
|
||||
|
||||
return $userData;
|
||||
}
|
||||
@ -240,7 +240,7 @@ class Sql extends Mail\Smtp
|
||||
function setUserData($_uidnumber, array $_mailAlternateAddress, array $_mailForwardingAddress, $_deliveryMode,
|
||||
$_accountStatus, $_mailLocalAddress, $_quota, $_forwarding_only=false, $_setMailbox=null)
|
||||
{
|
||||
if ($this->debug) error_log(__METHOD__."($_uidnumber, ".array2string($_mailAlternateAddress).', '.array2string($_mailForwardingAddress).", '$_deliveryMode', '$_accountStatus', '$_mailLocalAddress', $_quota, forwarding_only=".array2string($_forwarding_only).') '.function_backtrace());
|
||||
if (!empty($this->debug)) error_log(__METHOD__."($_uidnumber, ".array2string($_mailAlternateAddress).', '.array2string($_mailForwardingAddress).", '$_deliveryMode', '$_accountStatus', '$_mailLocalAddress', $_quota, forwarding_only=".array2string($_forwarding_only).') '.function_backtrace());
|
||||
|
||||
if (!$_forwarding_only && $this->accounts->id2name($_uidnumber, 'account_email') !== $_mailLocalAddress)
|
||||
{
|
||||
|
@ -564,7 +564,7 @@ class Mailer extends Horde_Mime_Mail
|
||||
if (!isset($flowed)) $flowed = $this->_body && !in_array($this->_body->getType(), array('multipart/encrypted', 'multipart/signed'));
|
||||
|
||||
// check if flowed is disabled in mail site configuration
|
||||
if (($config = Config::read('mail')) && $config['disable_rfc3676_flowed'])
|
||||
if (($config = Config::read('mail')) && !empty($config['disable_rfc3676_flowed']))
|
||||
{
|
||||
$flowed = false;
|
||||
}
|
||||
@ -616,7 +616,7 @@ class Mailer extends Horde_Mime_Mail
|
||||
}
|
||||
|
||||
// log mails to file specified in $GLOBALS['egw_info']['server']['log_mail'] or error_log for true
|
||||
if ($GLOBALS['egw_info']['server']['log_mail'])
|
||||
if (!empty($GLOBALS['egw_info']['server']['log_mail']))
|
||||
{
|
||||
$msg = $GLOBALS['egw_info']['server']['log_mail'] !== true ? date('Y-m-d H:i:s')."\n" : '';
|
||||
$msg .= (!isset($e) ? 'Mail send' : 'Mail NOT send').
|
||||
@ -732,7 +732,7 @@ class Mailer extends Horde_Mime_Mail
|
||||
$recipients->add($h->getAddressList());
|
||||
}
|
||||
}
|
||||
if ($this->_bcc) {
|
||||
if (!empty($this->_bcc)) {
|
||||
$recipients->add($this->_bcc);
|
||||
}
|
||||
|
||||
|
@ -1538,7 +1538,7 @@ class Session
|
||||
}
|
||||
|
||||
// check if the url already contains a query and ensure that vars is an array and all strings are in extravars
|
||||
if (strpos($ret_url=$url, '?') !== false) list($ret_url,$othervars) = explode('?', $url, 2)+[null,null];
|
||||
list($ret_url,$othervars) = explode('?', $url, 2)+[null,null];
|
||||
if ($extravars && is_array($extravars))
|
||||
{
|
||||
$vars += $extravars;
|
||||
|
@ -674,7 +674,7 @@ class Storage extends Storage\Base
|
||||
elseif (is_string($name) && $val!=null && in_array($name, $this->db_cols))
|
||||
{
|
||||
$extra_columns = $this->db->get_table_definitions($this->app, $this->extra_table);
|
||||
if ($extra_columns['fd'][array_search($name, $this->db_cols)])
|
||||
if (!empty($extra_columns['fd'][array_search($name, $this->db_cols)]))
|
||||
{
|
||||
$filter[] = $this->db->expression($this->table_name,$this->table_name.'.',array(
|
||||
array_search($name, $this->db_cols) => $val,
|
||||
|
@ -1034,14 +1034,14 @@ class Base
|
||||
$this->total = $this->db->select($this->table_name,$colums,$query,__LINE__,__FILE__,false,$order_by,false,0,$join)->NumRows();
|
||||
}
|
||||
}
|
||||
$rs = $this->db->select($this->table_name,$mysql_calc_rows.$colums,$query,__LINE__,__FILE__,
|
||||
$rs = $this->db->select($this->table_name,($mysql_calc_rows??'').$colums,$query,__LINE__,__FILE__,
|
||||
$start,$order_by,$this->app,$num_rows,$join);
|
||||
if ($this->debug) error_log(__METHOD__."() ".$this->db->Query_ID->sql);
|
||||
$cols = $this->_get_columns($only_keys,$extra_cols);
|
||||
}
|
||||
if ((int) $this->debug >= 4) echo "<p>sql='{$this->db->Query_ID->sql}'</p>\n";
|
||||
|
||||
if ($mysql_calc_rows)
|
||||
if (!empty($mysql_calc_rows))
|
||||
{
|
||||
$this->total = $this->db->query('SELECT FOUND_ROWS()')->fetchColumn();
|
||||
}
|
||||
@ -1157,8 +1157,8 @@ class Base
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is_array($query) && $op != 'AND') $query = $this->db->column_data_implode(' '.$op.' ',$query);
|
||||
return $query;
|
||||
if (!empty($query) && is_array($query) && $op != 'AND') $query = $this->db->column_data_implode(' '.$op.' ',$query);
|
||||
return $query ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -190,7 +190,7 @@ class Customfields implements \IteratorAggregate
|
||||
/**
|
||||
* Format a single custom field value as string
|
||||
*
|
||||
* @param array $field field defintion incl. type
|
||||
* @param array $field field definition incl. type
|
||||
* @param string $value field value
|
||||
* @return string formatted value
|
||||
*/
|
||||
@ -204,7 +204,7 @@ class Customfields implements \IteratorAggregate
|
||||
$values = array();
|
||||
foreach($field['rows'] > 1 ? explode(',', $value) : (array) $value as $value)
|
||||
{
|
||||
$values[] = Api\Accounts::username($value);
|
||||
$values[] = is_numeric($value) ? Api\Accounts::username($value) : $value;
|
||||
}
|
||||
$value = implode(', ',$values);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -283,7 +283,7 @@ abstract class Tracking
|
||||
//error_log(__METHOD__."() $name: data['#$name']=".array2string($data['#'.$name]).", field[values]=".array2string($field['values']));
|
||||
$details['#'.$name] = array(
|
||||
'label' => $field['label'],
|
||||
'value' => Customfields::format($field, $data['#'.$name]),
|
||||
'value' => Customfields::format($field, $data['#'.$name] ?? null),
|
||||
);
|
||||
//error_log("--> details['#$name']=".array2string($details['#'.$name]));
|
||||
}
|
||||
@ -636,13 +636,13 @@ abstract class Tracking
|
||||
{
|
||||
//error_log(__METHOD__."() data[$this->assigned_field]=".print_r($data[$this->assigned_field],true).", old[$this->assigned_field]=".print_r($old[$this->assigned_field],true));
|
||||
$old_assignees = array();
|
||||
$assignees = $assigned ? $assigned : array();
|
||||
if ($data[$this->assigned_field]) // current assignments
|
||||
$assignees = $assigned ?? array();
|
||||
if (!empty($data[$this->assigned_field])) // current assignments
|
||||
{
|
||||
$assignees = is_array($data[$this->assigned_field]) ?
|
||||
$data[$this->assigned_field] : explode(',',$data[$this->assigned_field]);
|
||||
}
|
||||
if ($old && $old[$this->assigned_field])
|
||||
if ($old && !empty($old[$this->assigned_field]))
|
||||
{
|
||||
$old_assignees = is_array($old[$this->assigned_field]) ?
|
||||
$old[$this->assigned_field] : explode(',',$old[$this->assigned_field]);
|
||||
@ -1050,7 +1050,7 @@ abstract class Tracking
|
||||
// remove the session-id in the notification mail!
|
||||
$link = preg_replace('/(sessionid|kp3|domain)=[^&]+&?/','',$link);
|
||||
|
||||
if ($popup) $link .= '&nopopup=1';
|
||||
if (!empty($popup)) $link .= '&nopopup=1';
|
||||
}
|
||||
//error_log(__METHOD__."(..., $allow_popup, $receiver) returning ".array2string($allow_popup ? array($link,$popup) : $link));
|
||||
return $allow_popup ? array($link,$popup) : $link;
|
||||
@ -1123,22 +1123,22 @@ abstract class Tracking
|
||||
{
|
||||
// if there's no old entry, the entry is not modified by definition
|
||||
// if both values are '', 0 or null, we count them as equal too
|
||||
$modified = $old && $data[$name] != $old[$name] && !(!$data[$name] && !$old[$name]);
|
||||
$modified = $old && ($data[$name] ?? null) != ($old[$name] ?? null) && !(empty($data[$name]) && empty($old[$name]));
|
||||
//if ($modified) error_log("data[$name]=".print_r($data[$name],true).", old[$name]=".print_r($old[$name],true)." --> modified=".(int)$modified);
|
||||
if (empty($detail['value']) && !$modified) continue; // skip unchanged, empty values
|
||||
|
||||
$body .= $this->format_line($html_email,$detail['type'],$modified,
|
||||
$detail['label'] ? $detail['label'] : '', $detail['value']);
|
||||
$body .= $this->format_line($html_email, $detail['type'] ?? null, $modified,
|
||||
$detail['label'] ?? '', $detail['value']);
|
||||
}
|
||||
if ($html_email)
|
||||
{
|
||||
$body .= "</table>\n";
|
||||
}
|
||||
if(($sig = $this->get_signature($data,$old,$receiver)))
|
||||
if (($sig = $this->get_signature($data,$old,$receiver)))
|
||||
{
|
||||
$body .= ($html_email ? '<br />':'') . "\n$sig";
|
||||
}
|
||||
if (!$html_email && $data['tr_edit_mode'] == 'html')
|
||||
if (!$html_email && isset($data['tr_edit_mode']) && $data['tr_edit_mode'] === 'html')
|
||||
{
|
||||
$body = Api\Mail\Html::convertHTMLToText($body);
|
||||
}
|
||||
@ -1271,7 +1271,7 @@ abstract class Tracking
|
||||
$merge_class = $this->app.'_merge';
|
||||
$merge = new $merge_class();
|
||||
$error = null;
|
||||
$sig = $merge->merge_string($config['signature'], array($data[$this->id_field]), $error, 'text/html');
|
||||
$sig = $merge->merge_string($config['signature']??null, array($data[$this->id_field]), $error, 'text/html');
|
||||
if($error)
|
||||
{
|
||||
error_log($error);
|
||||
|
@ -335,21 +335,20 @@ class Base
|
||||
$url = str_replace($matches[0], $matches[1] . Vfs::concat($matches[2], substr($parts['path'], strlen($mounted))), $url);
|
||||
}
|
||||
|
||||
if($replace_user_pass_host)
|
||||
if ($replace_user_pass_host)
|
||||
{
|
||||
$url = str_replace(array('$user',
|
||||
'$pass',
|
||||
'$host',
|
||||
'$home'), array($parts['user'],
|
||||
$parts['pass'],
|
||||
$parts['host'],
|
||||
$parts['home']), $url);
|
||||
$url = strtr($url, [
|
||||
'$user' => $parts['user'],
|
||||
'$pass' => $parts['pass'],
|
||||
'$host' => $parts['host'],
|
||||
'$home' => $parts['home'],
|
||||
]);
|
||||
}
|
||||
if($parts['query'])
|
||||
if (isset($parts['query']))
|
||||
{
|
||||
$url .= '?' . $parts['query'];
|
||||
}
|
||||
if($parts['fragment'])
|
||||
if (isset($parts['fragment']))
|
||||
{
|
||||
$url .= '#' . $parts['fragment'];
|
||||
}
|
||||
@ -657,7 +656,7 @@ class Base
|
||||
return false;
|
||||
}
|
||||
$k = (string)Vfs::parse_url($url, PHP_URL_SCHEME);
|
||||
if(!(is_array($scheme2urls[$k])))
|
||||
if (!isset($scheme2urls[$k]))
|
||||
{
|
||||
$scheme2urls[$k] = array();
|
||||
}
|
||||
|
@ -798,11 +798,11 @@ class StreamWrapper extends Base implements StreamWrapperIface
|
||||
{
|
||||
$stat['url'] = $url;
|
||||
}
|
||||
if (($stat['mode'] & 0222) && self::url_is_readonly($stat['url']))
|
||||
if ($stat && ($stat['mode'] & 0222) && self::url_is_readonly($stat['url']))
|
||||
{
|
||||
$stat['mode'] &= ~0222;
|
||||
}
|
||||
if($stat['url'] && $query && strpos($stat['url'],'?'.$query)===false)
|
||||
if ($stat && $stat['url'] && $query && strpos($stat['url'],'?'.$query) === false)
|
||||
{
|
||||
$stat['url'] .= '?'.$query;
|
||||
}
|
||||
@ -998,7 +998,7 @@ class StreamWrapper extends Base implements StreamWrapperIface
|
||||
}
|
||||
else
|
||||
{
|
||||
$vfs_fstab = $GLOBALS['egw_info']['user']['preferences']['common']['vfs_fstab'];
|
||||
$vfs_fstab = $GLOBALS['egw_info']['user']['preferences']['common']['vfs_fstab'] ?? [];
|
||||
}
|
||||
if (!empty($vfs_fstab) && is_array($vfs_fstab))
|
||||
{
|
||||
|
@ -71,6 +71,7 @@ function _egw_log_exception($e,&$headline=null)
|
||||
{
|
||||
error_log($headline.($e instanceof egw_exception_warning ? ': ' : ' ('.get_class($e).'): ').
|
||||
$e->getMessage().(!empty($e->details) ? ': '.$e->details : ''));
|
||||
error_log('File: '.str_replace(EGW_SERVER_ROOT, '', $e->getFile()).', Line: '.$e->getLine());
|
||||
foreach($trace as $line)
|
||||
{
|
||||
error_log($line);
|
||||
@ -103,6 +104,7 @@ function egw_exception_handler($e)
|
||||
if(!isset($_SERVER['HTTP_HOST']) || $GLOBALS['egw_info']['flags']['no_exception_handler'] == 'cli')
|
||||
{
|
||||
echo ($headline ? $headline.': ' : '').$e->getMessage()."\n";
|
||||
echo $e->getFile().' ('.$e->getLine().")\n";
|
||||
if ($GLOBALS['egw_info']['server']['exception_show_trace'])
|
||||
{
|
||||
echo $e->getTraceAsString()."\n";
|
||||
@ -116,6 +118,8 @@ function egw_exception_handler($e)
|
||||
$message = '<h3>'.Api\Html::htmlspecialchars($headline)."</h3>\n".
|
||||
'<pre><b>'.Api\Html::htmlspecialchars($e->getMessage())."</b>\n\n";
|
||||
|
||||
echo $e->getFile().' ('.$e->getLine().")\n";
|
||||
|
||||
// only show trace (incl. function arguments) if explicitly enabled, eg. on a development system
|
||||
if ($GLOBALS['egw_info']['server']['exception_show_trace'])
|
||||
{
|
||||
@ -174,6 +178,7 @@ function egw_error_handler ($errno, $errstr, $errfile, $errline)
|
||||
{
|
||||
case E_RECOVERABLE_ERROR:
|
||||
case E_USER_ERROR:
|
||||
error_log(__METHOD__."($errno, '$errstr', '$errfile', $errline)");
|
||||
throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
|
||||
|
||||
case E_WARNING:
|
||||
|
@ -175,6 +175,8 @@ function php_safe_unserialize($str)
|
||||
*/
|
||||
function json_php_unserialize($str, $allow_not_serialized=false)
|
||||
{
|
||||
if (!isset($str)) return $str;
|
||||
|
||||
if ((in_array($str[0], array('a', 'i', 's', 'b', 'O', 'C')) && $str[1] == ':' || $str === 'N;') &&
|
||||
($arr = php_safe_unserialize($str)) !== false || $str === 'b:0;')
|
||||
{
|
||||
|
65
api/templates/default/show_replacements.xet
Normal file
65
api/templates/default/show_replacements.xet
Normal file
@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE overlay PUBLIC "-//EGroupware GmbH//eTemplate 2//EN" "http://www.egroupware.org/etemplate2.dtd">
|
||||
<!-- $Id$ -->
|
||||
<overlay>
|
||||
<template id="api.show_replacements.placeholder_list">
|
||||
<description id="title" class="title"/>
|
||||
<grid id="placeholders" width="100%">
|
||||
<columns>
|
||||
<column width="30%"/>
|
||||
<column/>
|
||||
</columns>
|
||||
<rows>
|
||||
<row>
|
||||
<description id="${row}[value]"/>
|
||||
<description id="${row}[label]"/>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
</template>
|
||||
<template id="api.show_replacements" template="" lang="" group="0" version="21.1.001">
|
||||
<vbox>
|
||||
<description value="Placeholders" class="group title"/>
|
||||
<box id="placeholders">
|
||||
<box id="${row}">
|
||||
<template template="api.show_replacements.placeholder_list"/>
|
||||
</box>
|
||||
</box>
|
||||
<template template="@extra_template"/>
|
||||
<details title="Common">
|
||||
<description value="Common" class="group title"/>
|
||||
<box id="common">
|
||||
<box id="${row}">
|
||||
<template template="api.show_replacements.placeholder_list"/>
|
||||
</box>
|
||||
</box>
|
||||
</details>
|
||||
<details title="Current user">
|
||||
<description value="Current user" class="group title"/>
|
||||
<box id="user">
|
||||
<box id="${row}">
|
||||
<template template="api.show_replacements.placeholder_list"/>
|
||||
</box>
|
||||
</box>
|
||||
</details>
|
||||
</vbox>
|
||||
<styles>
|
||||
.et2_details_title, .title {
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
font-size: 130%;
|
||||
margin-top: 2ex;
|
||||
|
||||
}
|
||||
.et2_details_title, .group {
|
||||
margin-top: 3ex;
|
||||
font-size: 150%;
|
||||
}
|
||||
|
||||
/** Cosmetics **/
|
||||
#api-show_replacements_title:first-letter, .title {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
</styles>
|
||||
</template>
|
||||
</overlay>
|
@ -1634,7 +1634,7 @@ class calendar_boupdate extends calendar_bo
|
||||
$memberships = $GLOBALS['egw']->accounts->memberships($uid,true);
|
||||
}
|
||||
$memberships[] = $uid;
|
||||
return array_intersect($memberships, array_keys($event['participants'])) && $this->check_perms(Acl::EDIT,0,$uid);
|
||||
return array_intersect($memberships, array_keys($event['participants'] ?? [])) && $this->check_perms(Acl::EDIT,0,$uid);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -660,31 +660,8 @@ class calendar_hooks
|
||||
// Merge print
|
||||
if ($GLOBALS['egw_info']['user']['apps']['filemanager'])
|
||||
{
|
||||
$settings['default_document'] = array(
|
||||
'type' => 'vfs_file',
|
||||
'size' => 60,
|
||||
'label' => 'Default document to insert entries',
|
||||
'name' => 'default_document',
|
||||
'help' => lang('If you specify a document (full vfs path) here, %1 displays an extra document icon for each entry. That icon allows to download the specified document with the data inserted.',lang('calendar')).' '.
|
||||
lang('The document can contain placeholder like {{%1}}, to be replaced with the data.','calendar_title').' '.
|
||||
lang('The following document-types are supported:'). implode(',',Api\Storage\Merge::get_file_extensions()),
|
||||
'run_lang' => false,
|
||||
'xmlrpc' => True,
|
||||
'admin' => False,
|
||||
);
|
||||
$settings['document_dir'] = array(
|
||||
'type' => 'vfs_dirs',
|
||||
'size' => 60,
|
||||
'label' => 'Directory with documents to insert entries',
|
||||
'name' => 'document_dir',
|
||||
'help' => lang('If you specify a directory (full vfs path) here, %1 displays an action for each document. That action allows to download the specified document with the data inserted.',lang('calendar')).' '.
|
||||
lang('The document can contain placeholder like {{%1}}, to be replaced with the data.','calendar_title').' '.
|
||||
lang('The following document-types are supported:'). implode(',',Api\Storage\Merge::get_file_extensions()),
|
||||
'run_lang' => false,
|
||||
'xmlrpc' => True,
|
||||
'admin' => False,
|
||||
'default' => '/templates/calendar',
|
||||
);
|
||||
$merge = new calendar_merge();
|
||||
$settings += $merge->merge_preferences();
|
||||
}
|
||||
|
||||
$settings += array(
|
||||
|
@ -3082,7 +3082,7 @@ class calendar_ical extends calendar_boupdate
|
||||
// check if json_encoded attribute is to big for our table
|
||||
if (($attributes['params'] || count($attributes['values']) > 1) &&
|
||||
strlen($event['##'.$attributes['name']]) >
|
||||
Api\Db::get_column_attribute('cal_extra_value', 'egw_cal_extra', 'calendar', 'precision'))
|
||||
$GLOBALS['egw']->db->get_column_attribute('cal_extra_value', 'egw_cal_extra', 'calendar', 'precision'))
|
||||
{
|
||||
// store content compressed (Outlook/Exchange HTML garbadge is very good compressable)
|
||||
if (function_exists('gzcompress'))
|
||||
@ -3093,7 +3093,7 @@ class calendar_ical extends calendar_boupdate
|
||||
}
|
||||
// if that's not enough --> unset it, as truncating the json gives nothing
|
||||
if (strlen($event['##'.$attributes['name']]) >
|
||||
Api\Db::get_column_attribute('cal_extra_value', 'egw_cal_extra', 'calendar', 'precision'))
|
||||
$GLOBALS['egw']->db->get_column_attribute('cal_extra_value', 'egw_cal_extra', 'calendar', 'precision'))
|
||||
{
|
||||
unset($event['##'.$attributes['name']]);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -878,7 +878,14 @@ class calendar_so
|
||||
$where[] = "$this->user_table.cal_recur_date=0";
|
||||
$cols = str_replace(array('cal_start','cal_end'),array('range_start AS cal_start','(SELECT MIN(cal_end) FROM egw_cal_dates WHERE egw_cal.cal_id=egw_cal_dates.cal_id) AS cal_end'),$cols);
|
||||
// in case cal_start is used in a query, eg. calendar_ical::find_event
|
||||
$where = str_replace(array('cal_start','cal_end'), array('range_start','(SELECT MIN(cal_end) FROM egw_cal_dates WHERE egw_cal.cal_id=egw_cal_dates.cal_id)'), $where);
|
||||
// in contrary to the docu on php.net, 3rd parameter can not be an array: https://3v4l.org/budKH
|
||||
foreach($where as &$val)
|
||||
{
|
||||
if (!is_array($val))
|
||||
{
|
||||
$val = str_replace(array('cal_start','cal_end'), array('range_start','(SELECT MIN(cal_end) FROM egw_cal_dates WHERE egw_cal.cal_id=egw_cal_dates.cal_id)'), $val);
|
||||
}
|
||||
}
|
||||
$params['order'] = str_replace('cal_start', 'range_start', $params['order']);
|
||||
if ($end) $where[] = (int)$end.' > range_start';
|
||||
}
|
||||
|
@ -642,16 +642,20 @@ class calendar_ui
|
||||
*
|
||||
* @param int $event_id
|
||||
* @param Api\DateTime $recurrence_date
|
||||
* @param array|bool|int|null $old_event
|
||||
*
|
||||
* @return boolean True if the event was updated, false if it could not be
|
||||
* updated or was removed.
|
||||
* updated or was removed.
|
||||
*/
|
||||
public function update_client($event_id, Api\DateTime $recurrence_date = null)
|
||||
public function update_client($event_id, Api\DateTime $recurrence_date = null, $old_event = array())
|
||||
{
|
||||
if(!$event_id) return false;
|
||||
if(is_string($event_id) && strpos($event_id,':') !== FALSE)
|
||||
if(!$event_id)
|
||||
{
|
||||
list($event_id, $date) = explode(':',$event_id);
|
||||
return false;
|
||||
}
|
||||
if(is_string($event_id) && strpos($event_id, ':') !== FALSE)
|
||||
{
|
||||
list($event_id, $date) = explode(':', $event_id);
|
||||
$recurrence_date = new Api\DateTime($date);
|
||||
}
|
||||
|
||||
@ -698,6 +702,21 @@ class calendar_ui
|
||||
else if($event['recur_type'] )
|
||||
{
|
||||
$this_month = new Api\DateTime('next month');
|
||||
$data = [];
|
||||
if($old_event && ($old_event['start'] != $event['start'] || $old_event['recur_enddate'] != $event['recur_enddate']))
|
||||
{
|
||||
// Set up to clear old events in case recurrence start/end date changed
|
||||
$old_rrule = calendar_rrule::event2rrule($old_event, true);
|
||||
|
||||
$old_rrule->rewind();
|
||||
do
|
||||
{
|
||||
$occurrence = $old_rrule->current();
|
||||
$data['calendar::' . $old_event['id'] . ':' . $occurrence->format('ts')] = null;
|
||||
$old_rrule->next();
|
||||
}
|
||||
while($old_rrule->valid() && $occurrence <= $this_month);
|
||||
}
|
||||
$rrule = calendar_rrule::event2rrule($event, true);
|
||||
$rrule->rewind();
|
||||
do
|
||||
@ -705,10 +724,17 @@ class calendar_ui
|
||||
$occurrence = $rrule->current();
|
||||
$converted = $this->bo->read($event['id'], $occurrence);
|
||||
$this->to_client($converted);
|
||||
$response->generic('data', array('uid' => 'calendar::'.$converted['row_id'], 'data' => $converted));
|
||||
$data['calendar::' . $converted['row_id']] = $converted;
|
||||
$rrule->next();
|
||||
}
|
||||
while ($rrule->valid() && $occurrence <= $this_month );
|
||||
while($rrule->valid() && $occurrence <= $this_month);
|
||||
|
||||
// Now we have to go through and send each one individually, since client side data can't handle more than one
|
||||
foreach($data as $uid => $cal_data)
|
||||
{
|
||||
$response->apply('egw.dataStoreUID', [$uid, $cal_data]);
|
||||
}
|
||||
$response->apply('app.calendar.update_events', [array_keys($data)]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1007,7 +1007,7 @@ class calendar_uiforms extends calendar_ui
|
||||
$response = Api\Json\Response::get();
|
||||
if($response && $update_type != 'delete' && !$client_updated)
|
||||
{
|
||||
$client_updated = $this->update_client($event['id']);
|
||||
$client_updated = $this->update_client($event['id'], null, is_array($old_event) ? $old_event : []);
|
||||
}
|
||||
|
||||
$msg = $message . ($msg ? ', ' . $msg : '');
|
||||
@ -1267,8 +1267,6 @@ class calendar_uiforms extends calendar_ui
|
||||
Api\DateTime::to($as_of_date,'ts') < time()
|
||||
)
|
||||
{
|
||||
|
||||
unset($orig_event);
|
||||
// copy event by unsetting the id(s)
|
||||
unset($event['id']);
|
||||
unset($event['uid']);
|
||||
@ -1325,7 +1323,8 @@ class calendar_uiforms extends calendar_ui
|
||||
}
|
||||
$last->setTime(0, 0, 0);
|
||||
$old_event['recur_enddate'] = Api\DateTime::to($last, 'ts');
|
||||
if (!$this->bo->update($old_event,true,true,false,true,$dummy=null,$no_notifications))
|
||||
$dummy = null;
|
||||
if (!$this->bo->update($old_event,true,true,false,true,$dummy, $no_notifications))
|
||||
{
|
||||
$msg .= ($msg ? ', ' : '') .lang('Error: the entry has been updated since you opened it for editing!').'<br />'.
|
||||
lang('Copy your changes to the clipboard, %1reload the entry%2 and merge them.','<a href="'.
|
||||
@ -1925,7 +1924,7 @@ class calendar_uiforms extends calendar_ui
|
||||
$sel_options['owner'][$uid] = $this->bo->participant_name($uid);
|
||||
}
|
||||
}
|
||||
$content['no_add_alarm'] = !count($sel_options['owner']); // no rights to set any alarm
|
||||
$content['no_add_alarm'] = empty($sel_options['owner']) || !count((array)$sel_options['owner']); // no rights to set any alarm
|
||||
if (!$event['id'])
|
||||
{
|
||||
$etpl->set_cell_attribute('button[new_alarm]','type','checkbox');
|
||||
@ -2182,7 +2181,7 @@ class calendar_uiforms extends calendar_ui
|
||||
}
|
||||
$user_and_memberships = $GLOBALS['egw']->accounts->memberships($user, true);
|
||||
$user_and_memberships[] = $user;
|
||||
if (!array_intersect(array_keys($event['participants']), $user_and_memberships))
|
||||
if (!array_intersect(array_keys($event['participants'] ?? []), $user_and_memberships))
|
||||
{
|
||||
$event['error'] .= ($event['error'] ? "\n" : '').lang('You are not invited to that event!');
|
||||
if ($event['id'])
|
||||
|
@ -651,22 +651,34 @@ export class CalendarApp extends EgwApp
|
||||
}
|
||||
|
||||
// Do we already have "fresh" data? Most user actions give fresh data in response
|
||||
let existing = egw.dataGetUIDdata('calendar::'+pushData.id);
|
||||
let existing = egw.dataGetUIDdata('calendar::' + pushData.id);
|
||||
if(existing && Math.abs(existing.timestamp - new Date().valueOf()) < 1000)
|
||||
{
|
||||
// Update directly
|
||||
this._update_events(this.state, ['calendar::'+pushData.id]);
|
||||
this._update_events(this.state, ['calendar::' + pushData.id]);
|
||||
return;
|
||||
};
|
||||
}
|
||||
;
|
||||
|
||||
// Ask for the real data, we don't have it
|
||||
egw.request("calendar.calendar_ui.ajax_get", [[pushData.id]]).then((data) =>
|
||||
let process_data = (data) =>
|
||||
{
|
||||
// Store it, which will call all registered listeners
|
||||
egw.dataStoreUID(data.uid, data.data);
|
||||
|
||||
// Any existing events were updated. Run this to catch new events or events moved into view
|
||||
this._update_events(this.state, [data.uid]);
|
||||
}
|
||||
egw.request("calendar.calendar_ui.ajax_get", [[pushData.id]]).then((data) =>
|
||||
{
|
||||
if(typeof data.uid !== "undefined")
|
||||
{
|
||||
return process_data(data)
|
||||
}
|
||||
for(let e of data)
|
||||
{
|
||||
process_data(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -3736,13 +3748,22 @@ export class CalendarApp extends EgwApp
|
||||
else if(typeof framework !== 'undefined')
|
||||
{
|
||||
framework.applications.calendar.sidemenuEntry.hideAjaxLoader();
|
||||
egw.loading_prompt('calendar',false)
|
||||
egw.loading_prompt('calendar', false)
|
||||
|
||||
}
|
||||
}, this,null
|
||||
}, this, null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* We have a list of calendar UIDs of events that need updating.
|
||||
* Public wrapper for _update_events so we can call it from server
|
||||
*/
|
||||
update_events(uids : string[])
|
||||
{
|
||||
return this._update_events(this.state, uids);
|
||||
}
|
||||
|
||||
/**
|
||||
* We have a list of calendar UIDs of events that need updating.
|
||||
*
|
||||
|
@ -46,6 +46,14 @@
|
||||
"type": "pear",
|
||||
"url": "https://pear.horde.org"
|
||||
},
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/egroupware/Crypt"
|
||||
},
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/egroupware/Compress"
|
||||
},
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/IMSGlobal/lti-1-3-php-library"
|
||||
@ -81,6 +89,8 @@
|
||||
"egroupware/adodb-php": "self.version",
|
||||
"egroupware/bookmarks": "self.version",
|
||||
"egroupware/collabora": "self.version",
|
||||
"egroupware/compress": "^2.2.3",
|
||||
"egroupware/crypt": "^2.7.13",
|
||||
"egroupware/guzzlestream": "dev-master",
|
||||
"egroupware/icalendar": "^2.1.9",
|
||||
"egroupware/magicsuggest": "^2.1",
|
||||
@ -99,8 +109,6 @@
|
||||
"npm-asset/as-jqplot": "1.0.*",
|
||||
"npm-asset/gridster": "0.5.*",
|
||||
"oomphinc/composer-installers-extender": "^1.1",
|
||||
"pear-pear.horde.org/horde_compress": "^2.0.8",
|
||||
"pear-pear.horde.org/horde_crypt": "^2.7.9",
|
||||
"pear-pear.horde.org/horde_imap_client": "^2.30.3",
|
||||
"pear-pear.horde.org/horde_mail": "^2.1.2",
|
||||
"pear-pear.horde.org/horde_managesieve": "^1.0.2",
|
||||
|
956
composer.lock
generated
956
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -171,49 +171,19 @@ class filemanager_hooks
|
||||
),
|
||||
);
|
||||
|
||||
$settings[Api\Storage\Merge::PREF_STORE_LOCATION] = array(
|
||||
'type' => 'vfs_dir',
|
||||
'size' => 60,
|
||||
'label' => 'Directory for storing merged documents',
|
||||
'name' => Api\Storage\Merge::PREF_STORE_LOCATION,
|
||||
'help' => lang('When you merge entries into documents, they will be stored here. If no directory is provided, they will be stored in %1', Vfs::get_home_dir())
|
||||
);
|
||||
$settings['default_document'] = array(
|
||||
'type' => 'vfs_file',
|
||||
'size' => 60,
|
||||
'label' => 'Default document to insert entries',
|
||||
'name' => 'default_document',
|
||||
'help' => lang('If you specify a document (full vfs path) here, %1 displays an extra document icon for each entry. That icon allows to download the specified document with the data inserted.', lang('filemanager')) . ' ' .
|
||||
lang('The document can contain placeholder like {{%1}}, to be replaced with the data.', 'name') . ' ' .
|
||||
lang('The following document-types are supported:') . implode(',', Api\Storage\Merge::get_file_extensions()),
|
||||
'run_lang' => false,
|
||||
'xmlrpc' => True,
|
||||
'admin' => False,
|
||||
);
|
||||
$settings['document_dir'] = array(
|
||||
'type' => 'vfs_dirs',
|
||||
'size' => 60,
|
||||
'label' => 'Directory with documents to insert entries',
|
||||
'name' => 'document_dir',
|
||||
'help' => lang('If you specify a directory (full vfs path) here, %1 displays an action for each document. That action allows to download the specified document with the %1 data inserted.', lang('filemanager')).' '.
|
||||
lang('The document can contain placeholder like {{%1}}, to be replaced with the data.','name').' '.
|
||||
lang('The following document-types are supported:'). implode(',',Api\Storage\Merge::get_file_extensions()),
|
||||
'run_lang' => false,
|
||||
'xmlrpc' => True,
|
||||
'admin' => False,
|
||||
'default' => '/templates/filemanager',
|
||||
);
|
||||
$merge = new filemanager_merge();
|
||||
$settings += $merge->merge_preferences();
|
||||
|
||||
$editorLink = self::getEditorLink();
|
||||
$mimes = array('0' => lang('None'));
|
||||
|
||||
foreach ((array)$editorLink['mime'] as $mime => $value)
|
||||
foreach((array)$editorLink['mime'] as $mime => $value)
|
||||
{
|
||||
$mimes[$mime] = lang('%1 file', strtoupper($value['ext'])).' ('.$mime.')';
|
||||
$mimes[$mime] = lang('%1 file', strtoupper($value['ext'])) . ' (' . $mime . ')';
|
||||
|
||||
if (!empty($value['extra_extensions']))
|
||||
if(!empty($value['extra_extensions']))
|
||||
{
|
||||
$mimes[$mime] .= ', '.strtoupper(implode(', ', $value['extra_extensions']));
|
||||
$mimes[$mime] .= ', ' . strtoupper(implode(', ', $value['extra_extensions']));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,15 +26,14 @@ class filemanager_merge extends Api\Storage\Merge
|
||||
* @var array
|
||||
*/
|
||||
var $public_functions = array(
|
||||
'show_replacements' => true,
|
||||
'merge_entries' => true
|
||||
'show_replacements' => true,
|
||||
'merge_entries' => true
|
||||
);
|
||||
|
||||
/**
|
||||
* Fields that are numeric, for special numeric handling
|
||||
*/
|
||||
protected $numeric_fields = array(
|
||||
);
|
||||
protected $numeric_fields = array();
|
||||
|
||||
/**
|
||||
* Fields that are dates or timestamps
|
||||
@ -74,12 +73,12 @@ class filemanager_merge extends Api\Storage\Merge
|
||||
* Get replacements
|
||||
*
|
||||
* @param int $id id of entry
|
||||
* @param string &$content=null content to create some replacements only if they are use
|
||||
* @param string &$content =null content to create some replacements only if they are use
|
||||
* @return array|boolean
|
||||
*/
|
||||
protected function get_replacements($id,&$content=null)
|
||||
protected function get_replacements($id, &$content = null)
|
||||
{
|
||||
if (!($replacements = $this->filemanager_replacements($id, '', $content)))
|
||||
if(!($replacements = $this->filemanager_replacements($id, '', $content)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -90,58 +89,58 @@ class filemanager_merge extends Api\Storage\Merge
|
||||
* Get filemanager replacements
|
||||
*
|
||||
* @param int $id id (vfs path) of entry
|
||||
* @param string $prefix='' prefix like eg. 'erole'
|
||||
* @param string $prefix ='' prefix like eg. 'erole'
|
||||
* @return array|boolean
|
||||
*/
|
||||
public function filemanager_replacements($id,$prefix='', &$content = null)
|
||||
public function filemanager_replacements($id, $prefix = '', &$content = null)
|
||||
{
|
||||
$info = array();
|
||||
$file = Vfs::lstat($id,true);
|
||||
$file = Vfs::lstat($id, true);
|
||||
|
||||
$file['mtime'] = Api\DateTime::to($file['mtime']);
|
||||
$file['ctime'] = Api\DateTime::to($file['ctime']);
|
||||
|
||||
$file['name'] = Vfs::basename($id);
|
||||
$file['dir'] = ($dir = Vfs::dirname($id)) ? Vfs::decodePath($dir) : '';
|
||||
$dirlist = explode('/',$file['dir']);
|
||||
$dirlist = explode('/', $file['dir']);
|
||||
$file['folder'] = array_pop($dirlist);
|
||||
$file['folder_file'] = $file['folder'] . '/'.$file['name'];
|
||||
$file['folder_file'] = $file['folder'] . '/' . $file['name'];
|
||||
$file['path'] = $id;
|
||||
$file['rel_path'] = str_replace($this->dir.'/', '', $id);
|
||||
$file['rel_path'] = str_replace($this->dir . '/', '', $id);
|
||||
$file['hsize'] = Vfs::hsize($file['size']);
|
||||
$file['mime'] = Vfs::mime_content_type($id);
|
||||
$file['gid'] *= -1; // our widgets use negative gid's
|
||||
if (($props = Vfs::propfind($id)))
|
||||
if(($props = Vfs::propfind($id)))
|
||||
{
|
||||
foreach($props as $prop)
|
||||
{
|
||||
$file[$prop['name']] = $prop['val'];
|
||||
}
|
||||
}
|
||||
if (($file['is_link'] = Vfs::is_link($id)))
|
||||
if(($file['is_link'] = Vfs::is_link($id)))
|
||||
{
|
||||
$file['symlink'] = Vfs::readlink($id);
|
||||
}
|
||||
// Custom fields
|
||||
if($content && strpos($content, '#') !== 0)
|
||||
{
|
||||
{
|
||||
// Expand link-to custom fields
|
||||
$this->cf_link_to_expand($file, $content, $info);
|
||||
$this->cf_link_to_expand($file, $content, $info);
|
||||
|
||||
foreach(Api\Storage\Customfields::get('filemanager') as $name => $field)
|
||||
{
|
||||
// Set any missing custom fields, or the marker will stay
|
||||
if(!$file['#'.$name])
|
||||
if(!$file['#' . $name])
|
||||
{
|
||||
$file['#'.$name] = '';
|
||||
$file['#' . $name] = '';
|
||||
continue;
|
||||
}
|
||||
|
||||
// Format date cfs per user Api\Preferences
|
||||
if($field['type'] == 'date' || $field['type'] == 'date-time')
|
||||
{
|
||||
$this->date_fields[] = '#'.$name;
|
||||
$file['#'.$name] = Api\DateTime::to($file['#'.$name], $field['type'] == 'date' ? true : '');
|
||||
$this->date_fields[] = '#' . $name;
|
||||
$file['#' . $name] = Api\DateTime::to($file['#' . $name], $field['type'] == 'date' ? true : '');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -150,17 +149,19 @@ class filemanager_merge extends Api\Storage\Merge
|
||||
if($dirlist[1] == 'apps' && count($dirlist) > 1)
|
||||
{
|
||||
// Try this first - a normal path /apps/appname/id/file
|
||||
list($app, $app_id) = explode('/', substr($file['path'], strpos($file['path'], 'apps/')+5));
|
||||
list($app, $app_id) = explode('/', substr($file['path'], strpos($file['path'], 'apps/') + 5));
|
||||
// Symlink?
|
||||
if(!$app || !(int)$app_id || !array_key_exists($app, $GLOBALS['egw_info']['user']['apps'])) {
|
||||
if(!$app || !(int)$app_id || !array_key_exists($app, $GLOBALS['egw_info']['user']['apps']))
|
||||
{
|
||||
// Try resolving just app + ID - /apps/App Name/Record Title/file
|
||||
$resolved = Vfs::resolve_url_symlinks(implode('/',array_slice(explode('/',$file['dir']),0,4)));
|
||||
list($app, $app_id) = explode('/', substr($resolved, strpos($resolved, 'apps/')+5));
|
||||
$resolved = Vfs::resolve_url_symlinks(implode('/', array_slice(explode('/', $file['dir']), 0, 4)));
|
||||
list($app, $app_id) = explode('/', substr($resolved, strpos($resolved, 'apps/') + 5));
|
||||
|
||||
if(!$app || !(int)$app_id || !array_key_exists($app, $GLOBALS['egw_info']['user']['apps'])) {
|
||||
if(!$app || !(int)$app_id || !array_key_exists($app, $GLOBALS['egw_info']['user']['apps']))
|
||||
{
|
||||
// Get rid of any virtual folders (eg: All$) and symlinks
|
||||
$resolved = Vfs::resolve_url_symlinks($file['path']);
|
||||
list($app, $app_id) = explode('/', substr($resolved, strpos($resolved, 'apps/')+5));
|
||||
list($app, $app_id) = explode('/', substr($resolved, strpos($resolved, 'apps/') + 5));
|
||||
}
|
||||
}
|
||||
if($app && $app_id)
|
||||
@ -170,7 +171,7 @@ class filemanager_merge extends Api\Storage\Merge
|
||||
$app_merge = null;
|
||||
try
|
||||
{
|
||||
$classname = $app .'_merge';
|
||||
$classname = $app . '_merge';
|
||||
if(class_exists($classname))
|
||||
{
|
||||
$app_merge = new $classname();
|
||||
@ -180,9 +181,10 @@ class filemanager_merge extends Api\Storage\Merge
|
||||
}
|
||||
}
|
||||
}
|
||||
// Silently discard & continue
|
||||
catch(Exception $e) {
|
||||
unset($e); // not used
|
||||
// Silently discard & continue
|
||||
catch (Exception $e)
|
||||
{
|
||||
unset($e); // not used
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -211,7 +213,7 @@ class filemanager_merge extends Api\Storage\Merge
|
||||
foreach($file as $key => &$value)
|
||||
{
|
||||
if(!$value) $value = '';
|
||||
$info['$$'.($prefix ? $prefix.'/':'').$key.'$$'] = $value;
|
||||
$info['$$' . ($prefix ? $prefix . '/' : '') . $key . '$$'] = $value;
|
||||
}
|
||||
if($app_placeholders)
|
||||
{
|
||||
@ -239,14 +241,18 @@ class filemanager_merge extends Api\Storage\Merge
|
||||
{
|
||||
return $session;
|
||||
}
|
||||
else if (($session = \EGroupware\Api\Cache::getSession(Api\Sharing::class, "$app::$id")) &&
|
||||
substr($session['share_path'], -strlen($path)) === $path)
|
||||
else
|
||||
{
|
||||
return $session;
|
||||
if(($session = \EGroupware\Api\Cache::getSession(Api\Sharing::class, "$app::$id")) &&
|
||||
substr($session['share_path'], -strlen($path)) === $path)
|
||||
{
|
||||
return $session;
|
||||
}
|
||||
}
|
||||
// Need to create the share here.
|
||||
// No way to know here if it should be writable, or who it's going to
|
||||
$mode = /* ? ? Sharing::WRITABLE :*/ Api\Sharing::READONLY;
|
||||
$mode = /* ? ? Sharing::WRITABLE :*/
|
||||
Api\Sharing::READONLY;
|
||||
$recipients = array();
|
||||
$extra = array();
|
||||
|
||||
@ -254,72 +260,59 @@ class filemanager_merge extends Api\Storage\Merge
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate table with replacements for the Api\Preferences
|
||||
* Hook for extending apps to customise the replacements UI without having to override the whole method
|
||||
*
|
||||
* @param string $template_name
|
||||
* @param $content
|
||||
* @param $sel_options
|
||||
* @param $readonlys
|
||||
*/
|
||||
public function show_replacements()
|
||||
protected function show_replacements_hook(&$template_name, &$content, &$sel_options, &$readonlys)
|
||||
{
|
||||
$GLOBALS['egw_info']['flags']['app_header'] = lang('filemanager').' - '.lang('Replacements for inserting entries into documents');
|
||||
$GLOBALS['egw_info']['flags']['nonavbar'] = false;
|
||||
echo $GLOBALS['egw']->framework->header();
|
||||
$content['extra_template'] = 'filemanager.replacements';
|
||||
}
|
||||
|
||||
echo "<table width='90%' align='center'>\n";
|
||||
echo '<tr><td colspan="4"><h3>'.lang('Filemanager fields:')."</h3></td></tr>";
|
||||
/**
|
||||
* Get a list of placeholders provided.
|
||||
*
|
||||
* Placeholders are grouped logically. Group key should have a user-friendly translation.
|
||||
*/
|
||||
public function get_placeholder_list($prefix = '')
|
||||
{
|
||||
$placeholders = parent::get_placeholder_list($prefix);
|
||||
|
||||
$n = 0;
|
||||
$fields = array(
|
||||
'name' => 'name',
|
||||
'path' => 'Absolute path',
|
||||
'rel_path' => 'Path relative to current directory',
|
||||
'folder' => 'Containing folder',
|
||||
'name' => 'name',
|
||||
'path' => 'Absolute path',
|
||||
'rel_path' => 'Path relative to current directory',
|
||||
'folder' => 'Containing folder',
|
||||
'folder_file' => 'Containing folder and file name',
|
||||
'url' => 'url',
|
||||
'webdav_url' => 'External path using webdav',
|
||||
'link' => 'Clickable link to file',
|
||||
'comment' => 'comment',
|
||||
'mtime' => 'modified',
|
||||
'ctime' => 'created',
|
||||
'mime' => 'Type',
|
||||
'hsize' => 'Size',
|
||||
'size' => 'Size (in bytes)',
|
||||
'url' => 'url',
|
||||
'webdav_url' => 'External path using webdav',
|
||||
'link' => 'Clickable link to file',
|
||||
'comment' => 'comment',
|
||||
'mtime' => 'modified',
|
||||
'ctime' => 'created',
|
||||
'mime' => 'Type',
|
||||
'hsize' => 'Size',
|
||||
'size' => 'Size (in bytes)',
|
||||
);
|
||||
$group = 'placeholders';
|
||||
foreach($fields as $name => $label)
|
||||
{
|
||||
if (!($n&1)) echo '<tr>';
|
||||
echo '<td>{{'.$name.'}}</td><td>'.lang($label).'</td>';
|
||||
if ($n&1) echo "</tr>\n";
|
||||
$n++;
|
||||
$marker = $this->prefix($prefix, $name, '{');
|
||||
if(!array_filter($placeholders, function ($a) use ($marker)
|
||||
{
|
||||
return array_key_exists($marker, $a);
|
||||
}))
|
||||
{
|
||||
$placeholders[$group][] = [
|
||||
'value' => $marker,
|
||||
'label' => $label
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
echo '<tr><td colspan="4"><h3>'.lang('Custom fields').":</h3></td></tr>";
|
||||
foreach(Api\Storage\Customfields::get('filemanager') as $name => $field)
|
||||
{
|
||||
echo '<tr><td>{{#'.$name.'}}</td><td colspan="3">'.$field['label']."</td></tr>\n";
|
||||
}
|
||||
|
||||
echo '<tr><td colspan="4"><h3>'.lang('Application fields').":</h3></td></tr>";
|
||||
echo '<tr><td colspan="4">'.lang('For files linked to an application entry (inside /apps/appname/id/) the placeholders for that application are also available. See the specific application for a list of available placeholders.').'</td></tr>';
|
||||
|
||||
echo '<tr><td colspan="4"><h3>'.lang('General fields:')."</h3></td></tr>";
|
||||
foreach(array(
|
||||
'date' => lang('Date'),
|
||||
'user/n_fn' => lang('Name of current user, all other contact fields are valid too'),
|
||||
'user/account_lid' => lang('Username'),
|
||||
'pagerepeat' => lang('For serial letter use this tag. Put the content, you want to repeat between two Tags.'),
|
||||
'label' => lang('Use this tag for addresslabels. Put the content, you want to repeat, between two tags.'),
|
||||
'labelplacement' => lang('Tag to mark positions for address labels'),
|
||||
'IF fieldname' => lang('Example {{IF n_prefix~Mr~Hello Mr.~Hello Ms.}} - search the field "n_prefix", for "Mr", if found, write Hello Mr., else write Hello Ms.'),
|
||||
'NELF' => lang('Example {{NELF role}} - if field role is not empty, you will get a new line with the value of field role'),
|
||||
'NENVLF' => lang('Example {{NENVLF role}} - if field role is not empty, set a LF without any value of the field'),
|
||||
'LETTERPREFIX' => lang('Example {{LETTERPREFIX}} - Gives a letter prefix without double spaces, if the title is empty for example'),
|
||||
'LETTERPREFIXCUSTOM' => lang('Example {{LETTERPREFIXCUSTOM n_prefix title n_family}} - Example: Mr Dr. James Miller'),
|
||||
) as $name => $label)
|
||||
{
|
||||
echo '<tr><td>{{'.$name.'}}</td><td colspan="3">'.$label."</td></tr>\n";
|
||||
}
|
||||
|
||||
echo "</table>\n";
|
||||
|
||||
echo $GLOBALS['egw']->framework->footer();
|
||||
return $placeholders;
|
||||
}
|
||||
}
|
||||
|
@ -684,7 +684,7 @@ class filemanager_ui
|
||||
*/
|
||||
static public function action($action,$selected,$dir=null,&$errs=null,&$files=null,&$dirs=null)
|
||||
{
|
||||
if (!count($selected))
|
||||
if (!count((array)$selected))
|
||||
{
|
||||
return lang('You need to select some files first!');
|
||||
}
|
||||
|
12
filemanager/templates/default/replacements.xet
Normal file
12
filemanager/templates/default/replacements.xet
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE overlay PUBLIC "-//EGroupware GmbH//eTemplate 2//EN" "http://www.egroupware.org/etemplate2.dtd">
|
||||
<!-- This template adds the extra bits to the replacements list UI -->
|
||||
<overlay>
|
||||
<template id="filemanager.replacements">
|
||||
<vbox>
|
||||
<description class="title" value="Application fields"/>
|
||||
<description
|
||||
value="For files linked to an application entry (inside /apps/appname/id/) the placeholders for that application are also available. See the specific application for a list of available placeholders."/>
|
||||
</vbox>
|
||||
</template>
|
||||
</overlay>
|
@ -126,7 +126,10 @@ class importexport_definitions_bo {
|
||||
$definition = $this->read($key);
|
||||
if($definition['owner'] && $definition['owner'] == $GLOBALS['egw_info']['user']['account_id'] || $GLOBALS['egw_info']['user']['apps']['admin']) {
|
||||
// clear private cache
|
||||
unset($this->definitions[array_search($key,$this->definitions)]);
|
||||
if(is_array($this->definitions))
|
||||
{
|
||||
unset($this->definitions[array_search($key, $this->definitions)]);
|
||||
}
|
||||
} else {
|
||||
unset($keys[$index]);
|
||||
}
|
||||
|
@ -279,7 +279,7 @@ class importexport_export_csv implements importexport_iface_export_record
|
||||
}
|
||||
// Fall through for other settings
|
||||
case 'select':
|
||||
if (count($c_field['values']) == 1 && isset($c_field['values']['@']))
|
||||
if (!empty($c_field['values']) && count($c_field['values']) == 1 && isset($c_field['values']['@']))
|
||||
{
|
||||
$c_field['values'] = Api\Storage\Customfields::get_options_from_file($c_field['values']['@']);
|
||||
}
|
||||
|
@ -183,28 +183,37 @@ class importexport_wizard_basic_export_csv
|
||||
$content['step'] = 'wizard_step40';
|
||||
|
||||
// If editing an existing definition, these will be in plugin_options
|
||||
if(!$content['delimiter'] && $content['plugin_options']['delimiter']) {
|
||||
if(!$content['delimiter'] && $content['plugin_options']['delimiter'])
|
||||
{
|
||||
$content['delimiter'] = $content['plugin_options']['delimiter'];
|
||||
} elseif (!$content['delimiter']) {
|
||||
}
|
||||
elseif(!$content['delimiter'])
|
||||
{
|
||||
$content['delimiter'] = ';';
|
||||
}
|
||||
if(!$content['charset'] && $content['plugin_options']['charset']) {
|
||||
if(!$content['charset'] && $content['plugin_options']['charset'])
|
||||
{
|
||||
$content['charset'] = $content['plugin_options']['charset'] ? $content['plugin_options']['charset'] : 'user';
|
||||
}
|
||||
if(!array_key_exists('begin_with_fieldnames', $content) && array_key_exists('begin_with_fieldnames', $content['plugin_options'])) {
|
||||
if(!array_key_exists('begin_with_fieldnames', $content) &&
|
||||
is_array($content['plugin_options']) &&
|
||||
array_key_exists('begin_with_fieldnames', $content['plugin_options']))
|
||||
{
|
||||
$content['begin_with_fieldnames'] = $content['plugin_options']['begin_with_fieldnames'];
|
||||
}
|
||||
if(!array_key_exists('convert', $content) && array_key_exists('convert', $content['plugin_options'])) {
|
||||
if(!array_key_exists('convert', $content) &&
|
||||
is_array($content['plugin_options']) && array_key_exists('convert', $content['plugin_options']))
|
||||
{
|
||||
$content['convert'] = $content['plugin_options']['convert'];
|
||||
}
|
||||
|
||||
|
||||
$sel_options['begin_with_fieldnames'] = array(
|
||||
0 => lang('No'),
|
||||
1 => lang('Field names'),
|
||||
'label' => lang('Field labels')
|
||||
0 => lang('No'),
|
||||
1 => lang('Field names'),
|
||||
'label' => lang('Field labels')
|
||||
);
|
||||
$sel_options['charset'] = Api\Translation::get_installed_charsets()+
|
||||
$sel_options['charset'] = Api\Translation::get_installed_charsets() +
|
||||
array(
|
||||
'user' => lang('User preference'),
|
||||
);
|
||||
@ -273,12 +282,19 @@ class importexport_wizard_basic_export_csv
|
||||
unset ($preserv['button']);
|
||||
|
||||
$content['set_filter']['fields'] = importexport_helper_functions::get_filter_fields(
|
||||
$content['application'],$content['plugin'],$this
|
||||
$content['application'], $content['plugin'], $this
|
||||
);
|
||||
// Load existing filter from either content or definition
|
||||
if(!array_key_exists('filter', $content) || !is_array($content['filter']))
|
||||
{
|
||||
$content['filter'] = [];
|
||||
}
|
||||
foreach($content['set_filter']['fields'] as $field => $settings)
|
||||
{
|
||||
$content['set_filter'][$field] = $content['filter'][$field];
|
||||
if(array_key_exists($field, $content['filter']))
|
||||
{
|
||||
$content['set_filter'][$field] = $content['filter'][$field];
|
||||
}
|
||||
}
|
||||
|
||||
if(!$content['set_filter']['fields'])
|
||||
|
@ -228,7 +228,7 @@ class infolog_bo
|
||||
{
|
||||
foreach(array_keys($config_data['status']) as $key)
|
||||
{
|
||||
if (!is_array($this->status[$key]))
|
||||
if (!isset($this->status[$key]) || !is_array($this->status[$key]))
|
||||
{
|
||||
$this->status[$key] = array();
|
||||
}
|
||||
@ -262,17 +262,17 @@ class infolog_bo
|
||||
$save_config = true;
|
||||
}
|
||||
}
|
||||
if ($save_config) Api\Config::save_value('customfields',$this->customfields,'infolog');
|
||||
if (!empty($save_config)) Api\Config::save_value('customfields',$this->customfields,'infolog');
|
||||
}
|
||||
if (is_array($config_data['responsible_edit']))
|
||||
if (isset($config_data['responsible_edit']) && is_array($config_data['responsible_edit']))
|
||||
{
|
||||
$this->responsible_edit = array_merge($this->responsible_edit,$config_data['responsible_edit']);
|
||||
}
|
||||
if (is_array($config_data['copy_excludefields']))
|
||||
if (isset($config_data['copy_excludefields']) && is_array($config_data['copy_excludefields']))
|
||||
{
|
||||
$this->copy_excludefields = array_merge($this->copy_excludefields,$config_data['copy_excludefields']);
|
||||
}
|
||||
if (is_array($config_data['sub_excludefields']) && $config_data['sub_excludefields'])
|
||||
if (!empty($config_data['sub_excludefields']) && is_array($config_data['sub_excludefields']))
|
||||
{
|
||||
$this->sub_excludefields = array_merge($this->sub_excludefields,$config_data['sub_excludefields']);
|
||||
}
|
||||
@ -286,7 +286,7 @@ class infolog_bo
|
||||
}
|
||||
$this->history = $config_data['history'];
|
||||
|
||||
$this->limit_modified_n_month = $config_data['limit_modified_n_month'];
|
||||
$this->limit_modified_n_month = $config_data['limit_modified_n_month'] ?? null;
|
||||
}
|
||||
// sort types by there translation
|
||||
foreach($this->enums['type'] as $key => $val)
|
||||
@ -629,12 +629,14 @@ class infolog_bo
|
||||
|
||||
if (!$info_id || ($data = $this->so->read($info_id)) === False)
|
||||
{
|
||||
return null;
|
||||
$null = null;
|
||||
return $null;
|
||||
}
|
||||
|
||||
if (!$ignore_acl && !$this->check_access($data,Acl::READ)) // check behind read, to prevent a double read
|
||||
{
|
||||
return False;
|
||||
$false = False;
|
||||
return $false;
|
||||
}
|
||||
|
||||
if ($data['info_subject'] == $this->subject_from_des($data['info_des']))
|
||||
@ -1092,10 +1094,14 @@ class infolog_bo
|
||||
* Checks for info_contact properly linked, project properly linked and
|
||||
* adds or removes to correct.
|
||||
*
|
||||
* @param Array $values
|
||||
* @param array $values
|
||||
*/
|
||||
protected function write_check_links(&$values)
|
||||
protected function write_check_links(array &$values)
|
||||
{
|
||||
if(!$this->check_access($values, Acl::EDIT))
|
||||
{
|
||||
return;
|
||||
}
|
||||
$old_link_id = (int)$values['info_link_id'];
|
||||
$from = $values['info_from'];
|
||||
|
||||
@ -1104,7 +1110,7 @@ class infolog_bo
|
||||
) || (
|
||||
is_array($values['info_contact']) && $values['info_contact']['id'] == 'none' &&
|
||||
array_key_exists('search', $values['info_contact'])
|
||||
))
|
||||
))
|
||||
{
|
||||
if(is_array($values['info_contact']))
|
||||
{
|
||||
@ -1113,7 +1119,7 @@ class infolog_bo
|
||||
$id = (int)$values['info_contact']['id'];
|
||||
$from = $values['info_contact']['search'];
|
||||
}
|
||||
else if ($values['info_contact'])
|
||||
else if($values['info_contact'])
|
||||
{
|
||||
list($app, $id) = explode(':', $values['info_contact'], 2);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* eGroupWare - Infolog - importexport
|
||||
* EGroupware - InfoLog - importexport
|
||||
*
|
||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||
* @package infolog
|
||||
@ -8,13 +8,12 @@
|
||||
* @link http://www.egroupware.org
|
||||
* @author Nathan Gray
|
||||
* @copyright Nathan Gray
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
/**
|
||||
* class infolog_egw_record
|
||||
*
|
||||
* compability layer for iface_egw_record needet for importexport
|
||||
* compatibility layer for iface_egw_record needed for importexport
|
||||
*/
|
||||
class infolog_egw_record implements importexport_iface_egw_record
|
||||
{
|
||||
@ -53,7 +52,7 @@ class infolog_egw_record implements importexport_iface_egw_record
|
||||
* @param string $_attribute_name
|
||||
*/
|
||||
public function __get($_attribute_name) {
|
||||
return $this->record[$_attribute_name];
|
||||
return $this->record[$_attribute_name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -458,41 +458,8 @@ class infolog_hooks
|
||||
// Merge print
|
||||
if ($GLOBALS['egw_info']['user']['apps']['filemanager'])
|
||||
{
|
||||
$settings['default_document'] = array(
|
||||
'type' => 'vfs_file',
|
||||
'size' => 60,
|
||||
'label' => 'Default document to insert entries',
|
||||
'name' => 'default_document',
|
||||
'help' => lang('If you specify a document (full vfs path) here, %1 displays an extra document icon for each entry. That icon allows to download the specified document with the data inserted.',lang('infolog')).' '.
|
||||
lang('The document can contain placeholder like {{%1}}, to be replaced with the data.','info_subject').' '.
|
||||
lang('The following document-types are supported:').'*.rtf, *.txt',
|
||||
'run_lang' => false,
|
||||
'xmlrpc' => True,
|
||||
'admin' => False,
|
||||
);
|
||||
$settings['document_dir'] = array(
|
||||
'type' => 'vfs_dirs',
|
||||
'size' => 60,
|
||||
'label' => 'Directory with documents to insert entries',
|
||||
'name' => 'document_dir',
|
||||
'help' => lang('If you specify a directory (full vfs path) here, %1 displays an action for each document. That action allows to download the specified document with the data inserted.', lang('infolog')) . ' ' .
|
||||
lang('The document can contain placeholder like {{%1}}, to be replaced with the data.', 'info_subject') . ' ' .
|
||||
lang('The following document-types are supported:') . '*.rtf, *.txt',
|
||||
'run_lang' => false,
|
||||
'xmlrpc' => True,
|
||||
'admin' => False,
|
||||
'default' => '/templates/infolog',
|
||||
);
|
||||
$settings[Api\Storage\Merge::PREF_DOCUMENT_FILENAME] = array(
|
||||
'type' => 'taglist',
|
||||
'label' => 'Document download filename',
|
||||
'name' => 'document_download_name',
|
||||
'values' => Api\Storage\Merge::DOCUMENT_FILENAME_OPTIONS,
|
||||
'help' => 'Choose the default filename for downloaded documents.',
|
||||
'xmlrpc' => True,
|
||||
'admin' => False,
|
||||
'default' => 'document',
|
||||
);
|
||||
$merge = new infolog_merge();
|
||||
$settings += $merge->merge_preferences();
|
||||
}
|
||||
|
||||
if ($GLOBALS['egw_info']['user']['apps']['calendar'])
|
||||
|
@ -138,7 +138,7 @@ class infolog_merge extends Api\Storage\Merge
|
||||
// Set any missing custom fields, or the marker will stay
|
||||
foreach($this->bo->customfields as $name => $field)
|
||||
{
|
||||
if(!$array['#'.$name])
|
||||
if (empty($array['#'.$name]))
|
||||
{
|
||||
$array['#'.$name] = '';
|
||||
}
|
||||
@ -183,9 +183,9 @@ class infolog_merge extends Api\Storage\Merge
|
||||
$info += $this->get_all_links('infolog', $id, $prefix, $content);
|
||||
|
||||
// Add contact fields
|
||||
if($array['info_link'] && $array['info_link']['app'] && $array['info_link']['id'])
|
||||
if($array['info_link'] && is_array($array['info_link']) && $array['info_link']['app'] && $array['info_link']['id'])
|
||||
{
|
||||
$info+=$this->get_app_replacements($array['info_link']['app'], $array['info_link']['id'], $content, 'info_contact');
|
||||
$info += $this->get_app_replacements($array['info_link']['app'], $array['info_link']['id'], $content, 'info_contact');
|
||||
}
|
||||
// Add owner fields
|
||||
$info += $this->contact_replacements(Api\Accounts::id2name($info_owner,'person_id'),'info_owner');
|
||||
@ -198,98 +198,20 @@ class infolog_merge extends Api\Storage\Merge
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate table with replacements for the Api\Preferences
|
||||
*
|
||||
*/
|
||||
public function show_replacements()
|
||||
{
|
||||
$GLOBALS['egw_info']['flags']['app_header'] = lang('infolog').' - '.lang('Replacements for inserting entries into documents');
|
||||
$GLOBALS['egw_info']['flags']['nonavbar'] = false;
|
||||
echo $GLOBALS['egw']->framework->header();
|
||||
|
||||
echo "<table width='90%' align='center'>\n";
|
||||
echo '<tr><td colspan="4"><h3>'.lang('Infolog fields:')."</h3></td></tr>";
|
||||
|
||||
$n = 0;
|
||||
$tracking = new infolog_tracking($this->bo);
|
||||
$fields = array('info_id' => lang('Infolog ID'), 'pm_id' => lang('Project ID'), 'project' => lang('Project name')) + $tracking->field2label + array('info_sum_timesheets' => lang('Used time'));
|
||||
Api\Translation::add_app('projectmanager');
|
||||
foreach($fields as $name => $label)
|
||||
{
|
||||
if (in_array($name,array('custom'))) continue; // dont show them
|
||||
|
||||
if (in_array($name,array('info_subject', 'info_des')) && $n&1) // main values, which should be in the first column
|
||||
{
|
||||
echo "</tr>\n";
|
||||
$n++;
|
||||
}
|
||||
if (!($n&1)) echo '<tr>';
|
||||
echo '<td>{{'.$name.'}}</td><td>'.lang($label).'</td>';
|
||||
if ($n&1) echo "</tr>\n";
|
||||
$n++;
|
||||
}
|
||||
|
||||
echo '<tr><td colspan="4"><h3>'.lang('Custom fields').":</h3></td></tr>";
|
||||
$contact_custom = false;
|
||||
foreach($this->bo->customfields as $name => $field)
|
||||
{
|
||||
echo '<tr><td>{{#'.$name.'}}</td><td colspan="3">'.$field['label'].($field['type'] == 'select-account' ? '*':'')."</td></tr>\n";
|
||||
if($field['type'] == 'select-account') $contact_custom = true;
|
||||
}
|
||||
if($contact_custom)
|
||||
{
|
||||
echo '<tr><td /><td colspan="3">* '.lang('Addressbook placeholders available'). '</td></tr>';
|
||||
}
|
||||
|
||||
echo '<tr><td colspan="4"><h3>'.lang('Parent').":</h3></td></tr>";
|
||||
echo '<tr><td>{{info_id_parent/info_subject}}</td><td colspan="3">'.lang('All other %1 fields are valid',lang('infolog'))."</td></tr>\n";
|
||||
|
||||
echo '<tr><td colspan="4"><h3>'.lang('Contact fields').':</h3></td></tr>';
|
||||
$i = 0;
|
||||
foreach($this->contacts->contact_fields as $name => $label)
|
||||
{
|
||||
if (in_array($name,array('tid','label','geo'))) continue; // dont show them, as they are not used in the UI atm.
|
||||
|
||||
if (in_array($name,array('email','org_name','tel_work','url')) && $n&1) // main values, which should be in the first column
|
||||
{
|
||||
echo "</tr>\n";
|
||||
$i++;
|
||||
}
|
||||
if (!($i&1)) echo '<tr>';
|
||||
echo '<td>{{info_contact/'.$name.'}}</td><td>'.$label.'</td>';
|
||||
if ($i&1) echo "</tr>\n";
|
||||
$i++;
|
||||
}
|
||||
echo '<tr><td colspan="4">'.lang('Owner contact fields also available under info_owner/...').'</td></tr>';
|
||||
|
||||
echo '<tr><td colspan="4"><h3>'.lang('Custom fields').":</h3></td></tr>";
|
||||
foreach($this->contacts->customfields as $name => $field)
|
||||
{
|
||||
echo '<tr><td>{{info_contact/#'.$name.'}}</td><td colspan="3">'.$field['label']."</td></tr>\n";
|
||||
}
|
||||
|
||||
echo '<tr><td colspan="4"><h3>' . lang('General fields:') . "</h3></td></tr>";
|
||||
foreach($this->get_common_replacements() as $name => $label)
|
||||
{
|
||||
echo '<tr><td>{{' . $name . '}}</td><td colspan="3">' . $label . "</td></tr>\n";
|
||||
}
|
||||
|
||||
echo "</table>\n";
|
||||
|
||||
echo $GLOBALS['egw']->framework->footer();
|
||||
}
|
||||
|
||||
public function get_placeholder_list($prefix = '')
|
||||
{
|
||||
$placeholders = parent::get_placeholder_list($prefix);
|
||||
|
||||
$tracking = new infolog_tracking($this->bo);
|
||||
$placeholders = array(
|
||||
'infolog' => [],
|
||||
lang('parent') => [],
|
||||
lang($tracking->field2label['info_from']) => []
|
||||
) + parent::get_placeholder_list($prefix);
|
||||
|
||||
$fields = array('info_id' => lang('Infolog ID'), 'pm_id' => lang('Project ID'),
|
||||
'project' => lang('Project name')) + $tracking->field2label + array('info_sum_timesheets' => lang('Used time'));
|
||||
Api\Translation::add_app('projectmanager');
|
||||
|
||||
$group = 'placeholders';
|
||||
$group = 'infolog';
|
||||
foreach($fields as $name => $label)
|
||||
{
|
||||
if(in_array($name, array('custom')))
|
||||
@ -310,15 +232,27 @@ class infolog_merge extends Api\Storage\Merge
|
||||
}
|
||||
}
|
||||
|
||||
// Add contact placeholders
|
||||
$insert_index = 1;
|
||||
$placeholders = array_slice($placeholders, 0, $insert_index, true) +
|
||||
[lang($tracking->field2label['info_from']) => []] +
|
||||
array_slice($placeholders, $insert_index, count($placeholders) - $insert_index, true);
|
||||
$contact_merge = new Api\Contacts\Merge();
|
||||
$contact = $contact_merge->get_placeholder_list('info_contact');
|
||||
$this->add_linked_placeholders($placeholders, lang($tracking->field2label['info_from']), $contact);
|
||||
// Don't add any linked placeholders if we're not at the top level
|
||||
// This avoids potential recursion
|
||||
if(!$prefix)
|
||||
{
|
||||
// Add contact placeholders
|
||||
$contact_merge = new Api\Contacts\Merge();
|
||||
$contact = $contact_merge->get_placeholder_list($this->prefix($prefix, 'info_contact'));
|
||||
$this->add_linked_placeholders($placeholders, lang($tracking->field2label['info_from']), $contact);
|
||||
|
||||
// Add parent placeholders
|
||||
$this->add_linked_placeholders(
|
||||
$placeholders,
|
||||
lang('parent'),
|
||||
$this->get_placeholder_list(($prefix ? $prefix . '/' : '') . 'info_id_parent')
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($placeholders[lang('parent')]);
|
||||
unset($placeholders[lang($tracking->field2label['info_from'])]);
|
||||
}
|
||||
return $placeholders;
|
||||
}
|
||||
}
|
||||
|
@ -144,7 +144,7 @@ class infolog_tracking extends Api\Storage\Tracking
|
||||
*/
|
||||
function get_subject($data, $old, $deleted = null, $receiver = null)
|
||||
{
|
||||
if ($data['prefix'])
|
||||
if (!empty($data['prefix']))
|
||||
{
|
||||
$prefix = $data['prefix']; // async notification
|
||||
}
|
||||
@ -172,7 +172,7 @@ class infolog_tracking extends Api\Storage\Tracking
|
||||
*/
|
||||
function get_message($data, $old, $receiver = null)
|
||||
{
|
||||
if ($data['message']) return $data['message']; // async notification
|
||||
if (!empty($data['message'])) return $data['message']; // async notification
|
||||
|
||||
if (!$old || $old['info_status'] == 'deleted')
|
||||
{
|
||||
@ -345,16 +345,16 @@ class infolog_tracking extends Api\Storage\Tracking
|
||||
return '';
|
||||
}
|
||||
// Per-type notification
|
||||
$type_config = $info_config[self::CUSTOM_NOTIFICATION][$data['info_type']];
|
||||
$type_config = $info_config[self::CUSTOM_NOTIFICATION][$data['info_type']] ?? null;
|
||||
$global = $info_config[self::CUSTOM_NOTIFICATION]['~global~'];
|
||||
|
||||
// Disabled
|
||||
if(!$type_config['use_custom'] && !$global['use_custom']) return '';
|
||||
if(empty($type_config['use_custom']) && empty($global['use_custom'])) return '';
|
||||
|
||||
// Type or globabl
|
||||
// Type or global
|
||||
$config = trim(strip_tags($type_config['message'])) != '' && $type_config['use_custom'] ? $type_config['message'] : $global['message'];
|
||||
break;
|
||||
}
|
||||
return $config;
|
||||
return $config ?? null;
|
||||
}
|
||||
}
|
||||
|
@ -1874,7 +1874,7 @@ class infolog_ui
|
||||
|
||||
$content['link_to']['to_app'] = 'infolog';
|
||||
$content['link_to']['to_id'] = $info_id;
|
||||
|
||||
/* $info_link_id is never defined
|
||||
if ($info_link_id && strpos($info_link_id,':') !== false) // updating info_link_id if necessary
|
||||
{
|
||||
list($app,$id) = explode(':',$info_link_id);
|
||||
@ -1903,7 +1903,7 @@ class infolog_ui
|
||||
// we need eg. the new modification date, for further updates
|
||||
$content = array_merge($content,$to_write);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
// Need to purge description history after encryption?
|
||||
if($content['clean_history'])
|
||||
@ -2115,7 +2115,7 @@ class infolog_ui
|
||||
// remove types owned by groups the user has no edit grant (current type is made readonly)
|
||||
foreach($this->bo->group_owners as $type => $group)
|
||||
{
|
||||
if (!($this->bo->grants[$group] & Acl::EDIT))
|
||||
if (!(($this->bo->grants[$group]??0) & Acl::EDIT))
|
||||
{
|
||||
if ($type == $content['info_type'])
|
||||
{
|
||||
@ -2172,7 +2172,7 @@ class infolog_ui
|
||||
$readonlys['action'] = true;
|
||||
}
|
||||
// ToDo: use the old status before the delete
|
||||
if ($info_id && $undelete)
|
||||
if ($info_id && !empty($undelete))
|
||||
{
|
||||
$content['info_status'] = $this->bo->status['defaults'][$content['info_type']];
|
||||
$this->tmpl->setElementAttribute('button[save]', 'label', 'Un-Delete');
|
||||
@ -2187,7 +2187,7 @@ class infolog_ui
|
||||
// use a typ-specific template (infolog.edit.xyz), if one exists, otherwise fall back to the generic one
|
||||
if (!$this->tmpl->read('infolog.edit.'.$content['info_type']))
|
||||
{
|
||||
$this->tmpl->read($print ? 'infolog.edit.print':'infolog.edit');
|
||||
$this->tmpl->read(!empty($print) ? 'infolog.edit.print' : 'infolog.edit');
|
||||
}
|
||||
if ($this->bo->has_customfields($content['info_type']))
|
||||
{
|
||||
@ -2252,7 +2252,7 @@ class infolog_ui
|
||||
$tracking = new infolog_tracking($this);
|
||||
foreach($tracking->field2history as $field => $history)
|
||||
{
|
||||
$history_stati[$history] = $tracking->field2label[$field];
|
||||
$history_stati[$history] = $tracking->field2label[$field] ?? null;
|
||||
}
|
||||
// Modified date removed from field2history, we don't need that in the history
|
||||
$history_stati['Mo'] = $tracking->field2label['info_datemodified'];
|
||||
@ -2276,20 +2276,20 @@ class infolog_ui
|
||||
'to_tracker' => array('label' => 'Tracker', 'title' => 'Convert to a ticket'),
|
||||
),
|
||||
);
|
||||
if ($GLOBALS['egw_info']['user']['apps']['calendar'])
|
||||
if (!empty($GLOBALS['egw_info']['user']['apps']['calendar']))
|
||||
{
|
||||
$sel_options['action']['schedule'] = array('label' => 'Schedule', 'title' => 'Schedule appointment');
|
||||
}
|
||||
if ($GLOBALS['egw_info']['user']['apps']['stylite'] && !$GLOBALS['egw_info']['server']['disable_pgp_encryption'])
|
||||
if (!empty($GLOBALS['egw_info']['user']['apps']['stylite']) && empty($GLOBALS['egw_info']['server']['disable_pgp_encryption']))
|
||||
{
|
||||
$content['encryption_ts'] = filemtime(EGW_SERVER_ROOT.'/stylite/js/app.js');
|
||||
}
|
||||
elseif ($GLOBALS['egw_info']['server']['disable_pgp_encryption'])
|
||||
elseif (!empty($GLOBALS['egw_info']['server']['disable_pgp_encryption']))
|
||||
{
|
||||
$readonlys['encrypt'] = true;
|
||||
}
|
||||
$GLOBALS['egw_info']['flags']['app_header'] = lang('InfoLog').' - '.
|
||||
($content['status_only'] ? lang('Edit Status') : lang('Edit'));
|
||||
(!empty($content['status_only']) ? lang('Edit Status') : lang('Edit'));
|
||||
$GLOBALS['egw_info']['flags']['params']['manual'] = array('page' => ($info_id ? 'ManualInfologEdit' : 'ManualInfologAdd'));
|
||||
//error_log(substr($content['info_des'],1793,10));
|
||||
//$content['info_des'] = substr($content['info_des'],0,1793);
|
||||
|
1
json.php
1
json.php
@ -60,6 +60,7 @@ function ajax_exception_handler($e)
|
||||
$response = Json\Response::get();
|
||||
$message .= ($message ? "\n\n" : '').$e->getMessage();
|
||||
|
||||
$message .= "\n\n".$e->getFile().' ('.$e->getLine().')';
|
||||
// only show trace (incl. function arguments) if explicitly enabled, eg. on a development system
|
||||
if ($GLOBALS['egw_info']['server']['exception_show_trace'])
|
||||
{
|
||||
|
@ -216,7 +216,7 @@ class mail_compose
|
||||
|
||||
);
|
||||
$acc_smime = Mail\Smime::get_acc_smime($content['mailaccount']);
|
||||
if ($acc_smime['acc_smime_password'])
|
||||
if ($acc_smime && !empty($acc_smime['acc_smime_password']))
|
||||
{
|
||||
$actions = array_merge($actions, array(
|
||||
'smime_sign' => array (
|
||||
@ -271,9 +271,9 @@ class mail_compose
|
||||
}
|
||||
unset($actions['pgp']);
|
||||
}
|
||||
if ($GLOBALS['egw_info']['server']['disable_pgp_encryption']) unset($actions['pgp']);
|
||||
if (!empty($GLOBALS['egw_info']['server']['disable_pgp_encryption'])) unset($actions['pgp']);
|
||||
// remove vfs actions if the user has no run access to filemanager
|
||||
if (!$GLOBALS['egw_info']['user']['apps']['filemanager'])
|
||||
if (empty($GLOBALS['egw_info']['user']['apps']['filemanager']))
|
||||
{
|
||||
unset($actions['save2vfs']);
|
||||
unset($actions['selectFromVFSForCompose']);
|
||||
@ -1242,16 +1242,16 @@ class mail_compose
|
||||
// address stuff like from, to, cc, replyto
|
||||
$destinationRows = 0;
|
||||
foreach(self::$destinations as $destination) {
|
||||
if (!is_array($content[$destination]))
|
||||
if (!empty($content[$destination]) && !is_array($content[$destination]))
|
||||
{
|
||||
if (!empty($content[$destination])) $content[$destination] = (array)$content[$destination];
|
||||
$content[$destination] = (array)$content[$destination];
|
||||
}
|
||||
$addr_content = $content[strtolower($destination)];
|
||||
$addr_content = $content[strtolower($destination)] ?? [];
|
||||
// we clear the given address array and rebuild it
|
||||
unset($content[strtolower($destination)]);
|
||||
foreach((array)$addr_content as $key => $value) {
|
||||
if ($value=="NIL@NIL") continue;
|
||||
if ($destination=='replyto' && str_replace('"','',$value) ==
|
||||
foreach($addr_content as $value) {
|
||||
if ($value === "NIL@NIL") continue;
|
||||
if ($destination === 'replyto' && str_replace('"','',$value) ===
|
||||
str_replace('"','',$identities[$this->mail_bo->getDefaultIdentity()]))
|
||||
{
|
||||
// preserve/restore the value to content.
|
||||
@ -1261,7 +1261,7 @@ class mail_compose
|
||||
//error_log(__METHOD__.__LINE__.array2string(array('key'=>$key,'value'=>$value)));
|
||||
$value = str_replace("\"\"",'"', htmlspecialchars_decode($value, ENT_COMPAT));
|
||||
foreach(Mail::parseAddressList($value) as $addressObject) {
|
||||
if ($addressObject->host == '.SYNTAX-ERROR.') continue;
|
||||
if ($addressObject->host === '.SYNTAX-ERROR.') continue;
|
||||
$address = imap_rfc822_write_address($addressObject->mailbox,$addressObject->host,$addressObject->personal);
|
||||
//$address = Mail::htmlentities($address, $this->displayCharset);
|
||||
$content[strtolower($destination)][]=$address;
|
||||
@ -1289,7 +1289,7 @@ class mail_compose
|
||||
$content['mail_'.($content['mimeType'] == 'html'?'html':'plain').'text'] =$content['body'];
|
||||
$content['showtempname']=0;
|
||||
//if (is_array($content['attachments']))error_log(__METHOD__.__LINE__.'before merging content with uploadforCompose:'.array2string($content['attachments']));
|
||||
$content['attachments']=(is_array($content['attachments'])&&is_array($content['uploadForCompose'])?array_merge($content['attachments'],(!empty($content['uploadForCompose'])?$content['uploadForCompose']:array())):(is_array($content['uploadForCompose'])?$content['uploadForCompose']:(is_array($content['attachments'])?$content['attachments']:null)));
|
||||
$content['attachments'] = array_merge($content['attachments'] ?? [], $content['uploadForCompose'] ?? []);
|
||||
//if (is_array($content['attachments'])) foreach($content['attachments'] as $k => &$file) $file['delete['.$file['tmp_name'].']']=0;
|
||||
$content['no_griddata'] = empty($content['attachments']);
|
||||
$preserv['attachments'] = $content['attachments'];
|
||||
@ -1297,21 +1297,21 @@ class mail_compose
|
||||
|
||||
//if (is_array($content['attachments']))error_log(__METHOD__.__LINE__.' Attachments:'.array2string($content['attachments']));
|
||||
// if no filemanager -> no vfsFileSelector
|
||||
if (!$GLOBALS['egw_info']['user']['apps']['filemanager'])
|
||||
if (empty($GLOBALS['egw_info']['user']['apps']['filemanager']))
|
||||
{
|
||||
$content['vfsNotAvailable'] = "mail_DisplayNone";
|
||||
}
|
||||
// if no infolog -> no save as infolog
|
||||
if (!$GLOBALS['egw_info']['user']['apps']['infolog'])
|
||||
if (empty($GLOBALS['egw_info']['user']['apps']['infolog']))
|
||||
{
|
||||
$content['noInfologAvailable'] = "mail_DisplayNone";
|
||||
}
|
||||
// if no tracker -> no save as tracker
|
||||
if (!$GLOBALS['egw_info']['user']['apps']['tracker'])
|
||||
if (empty($GLOBALS['egw_info']['user']['apps']['tracker']))
|
||||
{
|
||||
$content['noTrackerAvailable'] = "mail_DisplayNone";
|
||||
}
|
||||
if (!$GLOBALS['egw_info']['user']['apps']['infolog'] && !$GLOBALS['egw_info']['user']['apps']['tracker'])
|
||||
if (empty($GLOBALS['egw_info']['user']['apps']['infolog']) && empty($GLOBALS['egw_info']['user']['apps']['tracker']))
|
||||
{
|
||||
$content['noSaveAsAvailable'] = "mail_DisplayNone";
|
||||
}
|
||||
@ -1324,12 +1324,12 @@ class mail_compose
|
||||
$sel_options['mimeType'] = self::$mimeTypes;
|
||||
$sel_options['priority'] = self::$priorities;
|
||||
$sel_options['filemode'] = Vfs\Sharing::$modes;
|
||||
if (!isset($content['priority']) || empty($content['priority'])) $content['priority']=3;
|
||||
if (empty($content['priority'])) $content['priority']=3;
|
||||
//$GLOBALS['egw_info']['flags']['currentapp'] = 'mail';//should not be needed
|
||||
$etpl = new Etemplate('mail.compose');
|
||||
|
||||
$etpl->setElementAttribute('composeToolbar', 'actions', self::getToolbarActions($content));
|
||||
if ($content['mimeType']=='html')
|
||||
if ($content['mimeType'] == 'html')
|
||||
{
|
||||
//mode="$cont[rtfEditorFeatures]" validation_rules="$cont[validation_rules]" base_href="$cont[upload_dir]"
|
||||
$_htmlConfig = Mail::$htmLawed_config;
|
||||
@ -1340,7 +1340,7 @@ class mail_compose
|
||||
Mail::$htmLawed_config = $_htmlConfig;
|
||||
}
|
||||
|
||||
if (isset($content['composeID'])&&!empty($content['composeID']))
|
||||
if (!empty($content['composeID']))
|
||||
{
|
||||
$composeCache = $content;
|
||||
unset($composeCache['body']);
|
||||
@ -1348,21 +1348,21 @@ class mail_compose
|
||||
unset($composeCache['mail_plaintext']);
|
||||
Api\Cache::setCache(Api\Cache::SESSION,'mail','composeCache'.trim($GLOBALS['egw_info']['user']['account_id']).'_'.$this->composeID,$composeCache,$expiration=60*60*2);
|
||||
}
|
||||
if (!isset($_content['serverID'])||empty($_content['serverID']))
|
||||
if (empty($_content['serverID']))
|
||||
{
|
||||
$content['serverID'] = $this->mail_bo->profileID;
|
||||
}
|
||||
$preserv['serverID'] = $content['serverID'];
|
||||
$preserv['lastDrafted'] = $content['lastDrafted'];
|
||||
$preserv['processedmail_id'] = $content['processedmail_id'];
|
||||
$preserv['references'] = $content['references'];
|
||||
$preserv['in-reply-to'] = $content['in-reply-to'];
|
||||
$preserv['lastDrafted'] = $content['lastDrafted'] ?? null;
|
||||
$preserv['processedmail_id'] = $content['processedmail_id'] ?? null;
|
||||
$preserv['references'] = $content['references'] ?? null;
|
||||
$preserv['in-reply-to'] = $content['in-reply-to'] ?? null;
|
||||
// thread-topic is a proprietary microsoft header and deprecated with the current version
|
||||
// horde does not support the encoding of thread-topic, and probably will not no so in the future
|
||||
//$preserv['thread-topic'] = $content['thread-topic'];
|
||||
$preserv['thread-index'] = $content['thread-index'];
|
||||
$preserv['list-id'] = $content['list-id'];
|
||||
$preserv['mode'] = $content['mode'];
|
||||
$preserv['thread-index'] = $content['thread-index'] ?? null;
|
||||
$preserv['list-id'] = $content['list-id'] ?? null;
|
||||
$preserv['mode'] = $content['mode'] ?? null;
|
||||
// convert it back to checkbox expectations
|
||||
if($content['mimeType'] == 'html') {
|
||||
$content['mimeType']=1;
|
||||
@ -1391,11 +1391,11 @@ class mail_compose
|
||||
// Resolve distribution list before send content to client
|
||||
foreach(array('to', 'cc', 'bcc', 'replyto') as $f)
|
||||
{
|
||||
if (is_array($content[$f])) $content[$f]= self::resolveEmailAddressList ($content[$f]);
|
||||
if (isset($content[$f]) && is_array($content[$f])) $content[$f]= self::resolveEmailAddressList ($content[$f]);
|
||||
}
|
||||
|
||||
// set filemode icons for all attachments
|
||||
if($content['attachments'] && is_array($content['attachments']))
|
||||
if(!empty($content['attachments']))
|
||||
{
|
||||
foreach($content['attachments'] as &$attach)
|
||||
{
|
||||
@ -1407,9 +1407,9 @@ class mail_compose
|
||||
}
|
||||
}
|
||||
|
||||
$content['to'] = self::resolveEmailAddressList($content['to']);
|
||||
if (isset($content['to'])) $content['to'] = self::resolveEmailAddressList($content['to']);
|
||||
$content['html_toolbar'] = empty(Mail::$mailConfig['html_toolbar']) ?
|
||||
join(',', Etemplate\Widget\HtmlArea::$toolbar_default_list) : join(',', Mail::$mailConfig['html_toolbar']);
|
||||
implode(',', Etemplate\Widget\HtmlArea::$toolbar_default_list) : implode(',', Mail::$mailConfig['html_toolbar']);
|
||||
//error_log(__METHOD__.__LINE__.array2string($content));
|
||||
$etpl->exec('mail.mail_compose.compose',$content,$sel_options,array(),$preserv,2);
|
||||
}
|
||||
@ -2022,7 +2022,7 @@ class mail_compose
|
||||
'size' => $_size,
|
||||
'folder' => $_folder,
|
||||
'winmailFlag' => $_is_winmail,
|
||||
'tmp_name' => mail_ui::generateRowID($this->mail_bo->profileID, $_folder, $_uid).'_'.(!empty($_partID)?$_partID:count($this->sessionData['attachments'])+1),
|
||||
'tmp_name' => mail_ui::generateRowID($this->mail_bo->profileID, $_folder, $_uid).'_'.(!empty($_partID)?$_partID:count($this->sessionData['attachments'] ?? [])+1),
|
||||
);
|
||||
}
|
||||
|
||||
@ -2485,7 +2485,7 @@ class mail_compose
|
||||
if(!empty($_formData['list-id'])) {
|
||||
$_mailObject->addHeader('List-Id', $_formData['list-id']);
|
||||
}
|
||||
if($_formData['disposition']=='on') {
|
||||
if(isset($_formData['disposition']) && $_formData['disposition'] === 'on') {
|
||||
$_mailObject->addHeader('Disposition-Notification-To', $_identity['ident_email']);
|
||||
}
|
||||
|
||||
@ -2522,6 +2522,8 @@ class mail_compose
|
||||
if ($_formData['attachments'] && $_formData['filemode'] != Vfs\Sharing::ATTACH && !$_autosaving)
|
||||
{
|
||||
$attachment_links = $this->_getAttachmentLinks($_formData['attachments'], $_formData['filemode'],
|
||||
// @TODO: $content['mimeType'] could type string/boolean. At the moment we can't strictly check them :(.
|
||||
// @TODO: This needs to be fixed in compose function to get the right type from the content.
|
||||
$_formData['mimeType'] == 'html',
|
||||
array_unique(array_merge((array)$_formData['to'], (array)$_formData['cc'], (array)$_formData['bcc'])),
|
||||
$_formData['expiration'], $_formData['password']);
|
||||
@ -2530,7 +2532,7 @@ class mail_compose
|
||||
{
|
||||
case 'html':
|
||||
$body = $_formData['body'];
|
||||
if ($attachment_links)
|
||||
if (!empty($attachment_links))
|
||||
{
|
||||
if (strpos($body, '<!-- HTMLSIGBEGIN -->') !== false)
|
||||
{
|
||||
@ -2567,7 +2569,7 @@ class mail_compose
|
||||
default:
|
||||
$body = $this->convertHTMLToText($_formData['body'],false, false, true, true);
|
||||
|
||||
if ($attachment_links) $body .= $attachment_links;
|
||||
if (!empty($attachment_links)) $body .= $attachment_links;
|
||||
|
||||
#$_mailObject->Body = $_formData['body'];
|
||||
if(!empty($signature)) {
|
||||
@ -2653,7 +2655,7 @@ class mail_compose
|
||||
}
|
||||
if ($connection_opened) $mail_bo->closeConnection();
|
||||
}
|
||||
return is_array($inline_images)?$inline_images:array();
|
||||
return $inline_images ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2761,7 +2763,7 @@ class mail_compose
|
||||
$dmailbox = $dhA['folder'];
|
||||
// beware: do not delete the original mail as found in processedmail_id
|
||||
$pMuid='';
|
||||
if ($content['processedmail_id'])
|
||||
if (!empty($content['processedmail_id']))
|
||||
{
|
||||
$pMhA = mail_ui::splitRowID($content['processedmail_id']);
|
||||
$pMuid = $pMhA['msgUID'];
|
||||
@ -3021,7 +3023,7 @@ class mail_compose
|
||||
// create the messages and store inline images
|
||||
$inline_images = $this->createMessage($mail, $_formData, $identity);
|
||||
// remember the identity
|
||||
if ($_formData['to_infolog'] == 'on' || $_formData['to_tracker'] == 'on') $fromAddress = $mail->From;//$mail->FromName.($mail->FromName?' <':'').$mail->From.($mail->FromName?'>':'');
|
||||
if (!empty($mail->From) && ($_formData['to_infolog'] == 'on' || $_formData['to_tracker'] == 'on')) $fromAddress = $mail->From;//$mail->FromName.($mail->FromName?' <':'').$mail->From.($mail->FromName?'>':'');
|
||||
#print "<pre>". $mail->getMessageHeader() ."</pre><hr><br>";
|
||||
#print "<pre>". $mail->getMessageBody() ."</pre><hr><br>";
|
||||
#exit;
|
||||
@ -3317,14 +3319,14 @@ class mail_compose
|
||||
if (isset($lastDrafted['uid']) && !empty($lastDrafted['uid'])) $lastDrafted['uid']=trim($lastDrafted['uid']);
|
||||
// manually drafted, do not delete
|
||||
// will be handled later on IF mode was $_formData['mode']=='composefromdraft'
|
||||
if (isset($lastDrafted['uid']) && (empty($lastDrafted['uid']) || $lastDrafted['uid'] == $this->sessionData['uid'])) $lastDrafted=false;
|
||||
if (isset($lastDrafted['uid']) && (empty($lastDrafted['uid']) || $lastDrafted['uid'] == ($this->sessionData['uid']??null))) $lastDrafted=false;
|
||||
//error_log(__METHOD__.__LINE__.array2string($lastDrafted));
|
||||
}
|
||||
if ($lastDrafted && is_array($lastDrafted) && $mail_bo->isDraftFolder($lastDrafted['folder']))
|
||||
{
|
||||
try
|
||||
{
|
||||
if ($this->sessionData['lastDrafted'] != $this->sessionData['uid'] || !($_formData['mode']=='composefromdraft' &&
|
||||
if ($this->sessionData['lastDrafted'] != ($this->sessionData['uid']??null) || !($_formData['mode']=='composefromdraft' &&
|
||||
($_formData['to_infolog'] == 'on' || $_formData['to_tracker'] == 'on' || $_formData['to_calendar'] == 'on' )&&$this->sessionData['attachments']))
|
||||
{
|
||||
//error_log(__METHOD__.__LINE__."#".$lastDrafted['uid'].'#'.$lastDrafted['folder'].array2string($_formData));
|
||||
@ -3399,7 +3401,7 @@ class mail_compose
|
||||
}
|
||||
if (is_array($this->sessionData['cc'])) $mailaddresses['cc'] = $this->sessionData['cc'];
|
||||
if (is_array($this->sessionData['bcc'])) $mailaddresses['bcc'] = $this->sessionData['bcc'];
|
||||
if (!empty($mailaddresses)) $mailaddresses['from'] = Mail\Html::decodeMailHeader($fromAddress);
|
||||
if (!empty($mailaddresses) && !empty($fromAddress)) $mailaddresses['from'] = Mail\Html::decodeMailHeader($fromAddress);
|
||||
|
||||
if ($_formData['to_infolog'] == 'on' || $_formData['to_tracker'] == 'on' || $_formData['to_calendar'] == 'on' )
|
||||
{
|
||||
@ -3407,7 +3409,7 @@ class mail_compose
|
||||
|
||||
foreach(array('to_infolog','to_tracker','to_calendar') as $app_key)
|
||||
{
|
||||
$entryid = $_formData['to_integrate_ids'][0][$app_key];
|
||||
$entryid = $_formData['to_integrate_ids'][0][$app_key] ?? null;
|
||||
if ($_formData[$app_key] == 'on')
|
||||
{
|
||||
$app_name = substr($app_key,3);
|
||||
|
@ -252,6 +252,10 @@ class mail_sieve
|
||||
break;
|
||||
case 'reject':
|
||||
$content['action_reject_text'] = $rules['action_arg'];
|
||||
break;
|
||||
case 'flags':
|
||||
$content['action_flags_list'] = explode(' ', $rules['action_arg']);
|
||||
break;
|
||||
}
|
||||
$content['anyof'] = $rules['anyof'] != 0?1:0;
|
||||
}
|
||||
@ -302,10 +306,15 @@ class mail_sieve
|
||||
break;
|
||||
case 'reject':
|
||||
$newRule['action_arg'] = $content['action_reject_text'];
|
||||
break;
|
||||
case 'flags':
|
||||
$newRule['action_arg'] = implode(' ', $content['action_flags_list']);
|
||||
break;
|
||||
}
|
||||
unset($newRule['action_folder_text']);
|
||||
unset($newRule['action_address_text']);
|
||||
unset($newRule['action_reject_text']);
|
||||
unset($newRule['action_flags_list']);
|
||||
|
||||
$newRule['flg'] = 0 ;
|
||||
if( $newRule['continue'] ) { $newRule['flg'] += 1; }
|
||||
@ -550,7 +559,7 @@ class mail_sieve
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($icServer->acc_imap_administration)
|
||||
if ($icServer->acc_imap_administration || (!empty($icServer->getExtensions()) && in_array('DATE', $icServer->getExtensions())))
|
||||
{
|
||||
$ByDate = array('by_date' => lang('By date'));
|
||||
}
|
||||
@ -949,11 +958,11 @@ class mail_sieve
|
||||
break;
|
||||
case 'enable':
|
||||
$msg = lang('rule with priority ') . $checked . lang(' enabled!');
|
||||
$this->rules[$checked][status] = 'ENABLED';
|
||||
$this->rules[$checked]['status'] = 'ENABLED';
|
||||
break;
|
||||
case 'disable':
|
||||
$msg = lang('rule with priority ') . $checked . lang(' disabled!');
|
||||
$this->rules[$checked][status] = 'DISABLED';
|
||||
$this->rules[$checked]['status'] = 'DISABLED';
|
||||
break;
|
||||
case 'move':
|
||||
break;
|
||||
|
@ -425,7 +425,7 @@ class mail_ui
|
||||
protected static function image_proxy()
|
||||
{
|
||||
$configs = Api\Config::read('mail');
|
||||
$image_proxy = $configs[self::IMAGE_PROXY_CONFIG] ?: self::DEFAULT_IMAGE_PROXY;
|
||||
$image_proxy = $configs[self::IMAGE_PROXY_CONFIG] ?? self::DEFAULT_IMAGE_PROXY;
|
||||
if (strpos(self::EGROUPWARE_IMAGE_PROXY, parse_url($image_proxy, PHP_URL_HOST)))
|
||||
{
|
||||
$image_proxy = self::EGROUPWARE_IMAGE_PROXY;
|
||||
@ -565,7 +565,7 @@ class mail_ui
|
||||
$etpl->setElementAttribute(self::$nm_index.'[foldertree]','actions', $this->get_tree_actions());
|
||||
|
||||
// sending preview toolbar actions
|
||||
if ($content['mailSplitter']) $etpl->setElementAttribute('mailPreview[toolbar]', 'actions', $this->get_toolbar_actions());
|
||||
if (!empty($content['mailSplitter'])) $etpl->setElementAttribute('mailPreview[toolbar]', 'actions', $this->get_toolbar_actions());
|
||||
|
||||
// We need to send toolbar actions to client-side because view template needs them
|
||||
if (Api\Header\UserAgent::mobile()) $sel_options['toolbar'] = $this->get_toolbar_actions();
|
||||
@ -1827,7 +1827,7 @@ $filter['before']= date("d-M-Y", $cutoffdate2);
|
||||
// we have an own created rowID; prepend app=mail
|
||||
array_unshift($res,'mail');
|
||||
}
|
||||
return array('app'=>$res[0], 'accountID'=>$res[1], 'profileID'=>$res[2], 'folder'=>base64_decode($res[3]), 'msgUID'=>$res[4]);
|
||||
return array('app'=>$res[0], 'accountID'=>$res[1]??null, 'profileID'=>$res[2]??null, 'folder'=>base64_decode($res[3]??null), 'msgUID'=>$res[4]??null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -98,6 +98,16 @@
|
||||
<radio label="Discard message" id="action" options="discard"/>
|
||||
<description/>
|
||||
</row>
|
||||
<row>
|
||||
<radio label="set flags" id="action" options="flags"/>
|
||||
<taglist id="action_flags_list">
|
||||
<option value="\\Flagged">Flagged</option>
|
||||
<option value="\\Deleted">Deleted</option>
|
||||
<option value="\\Seen">Read</option>
|
||||
<option value="\\Answered">Answered</option>
|
||||
<option value="\\Draft">Draft</option>
|
||||
</taglist>
|
||||
</row>
|
||||
<row>
|
||||
<description value="(*) Please consider, forward to multiple addresses will not work if number of addresses exceeds the Limit. For most mail Servers the limit is 4 by default, please contact your mail server administrator for further info."/>
|
||||
</row>
|
||||
|
@ -160,7 +160,7 @@ class notifications_popup implements notifications_iface {
|
||||
foreach ($rs as $notification) {
|
||||
$actions = null;
|
||||
$data = json_decode($notification['notify_data'], true);
|
||||
if ($data['appname'] && $data['data'])
|
||||
if (!empty($data['appname']) && !empty($data['data']))
|
||||
{
|
||||
$_actions = Api\Hooks::process (array(
|
||||
'location' => 'notifications_actions',
|
||||
@ -175,7 +175,7 @@ class notifications_popup implements notifications_iface {
|
||||
'created' => Api\DateTime::server2user($notification['notify_created']),
|
||||
'current' => new Api\DateTime('now'),
|
||||
'actions' => is_array($actions)?$actions:NULL,
|
||||
'extra_data' => ($data['data'] ? $data['data'] : array())
|
||||
'extra_data' => $data['data'] ?? [],
|
||||
);
|
||||
|
||||
}
|
||||
|
@ -85,13 +85,13 @@ class pixelegg_framework extends Api\Framework\Ajax
|
||||
{
|
||||
$ret = parent::_get_css();
|
||||
// color to use
|
||||
$color = str_replace('custom',$GLOBALS['egw_info']['user']['preferences']['common']['template_custom_color'],
|
||||
$GLOBALS['egw_info']['user']['preferences']['common']['template_color']);
|
||||
$color = str_replace('custom', $GLOBALS['egw_info']['user']['preferences']['common']['template_custom_color'] ?? null,
|
||||
$GLOBALS['egw_info']['user']['preferences']['common']['template_color'] ?? null);
|
||||
|
||||
// Create a dark variant of the color
|
||||
$color_darker = empty($color) ? '' :$this->_color_shader($color, -30);
|
||||
|
||||
if (preg_match('/^(#[0-9A-F]+|[A-Z]+)$/i', $GLOBALS['egw_info']['user']['preferences']['common']['sidebox_custom_color']))
|
||||
if (!empty($GLOBALS['egw_info']['user']['preferences']['common']['sidebox_custom_color']) && preg_match('/^(#[0-9A-F]+|[A-Z]+)$/i', $GLOBALS['egw_info']['user']['preferences']['common']['sidebox_custom_color']))
|
||||
{
|
||||
$sidebox_color_hover = $GLOBALS['egw_info']['user']['preferences']['common']['sidebox_custom_color'];
|
||||
$sidebox_color = $this->_color_shader($sidebox_color_hover, -30);
|
||||
@ -101,7 +101,7 @@ class pixelegg_framework extends Api\Framework\Ajax
|
||||
$sidebox_color_hover = $color;
|
||||
$sidebox_color = $color_darker;
|
||||
}
|
||||
if (preg_match('/^(#[0-9A-F]+|[A-Z]+)$/i', $GLOBALS['egw_info']['user']['preferences']['common']['loginbox_custom_color']))
|
||||
if (!empty($GLOBALS['egw_info']['user']['preferences']['common']['loginbox_custom_color']) && preg_match('/^(#[0-9A-F]+|[A-Z]+)$/i', $GLOBALS['egw_info']['user']['preferences']['common']['loginbox_custom_color']))
|
||||
{
|
||||
$loginbox_color = $GLOBALS['egw_info']['user']['preferences']['common']['loginbox_custom_color'];
|
||||
}
|
||||
@ -109,8 +109,8 @@ class pixelegg_framework extends Api\Framework\Ajax
|
||||
{
|
||||
$loginbox_color = $color_darker;
|
||||
}
|
||||
//alway set header logo used in sharing regardless of custom color being set
|
||||
$header = $GLOBALS['egw_info']['server']['login_logo_header'] ? Api\Framework::get_login_logo_or_bg_url('login_logo_header', 'logo')
|
||||
//always set header logo used in sharing regardless of custom color being set
|
||||
$header = !empty($GLOBALS['egw_info']['server']['login_logo_header']) ? Api\Framework::get_login_logo_or_bg_url('login_logo_header', 'logo')
|
||||
: Api\Framework::get_login_logo_or_bg_url('login_logo_file', 'logo');
|
||||
$ret['app_css'] .= "
|
||||
/*
|
||||
|
@ -451,7 +451,7 @@ class timesheet_bo extends Api\Storage
|
||||
{
|
||||
$extra_cols[] = $total_sql.' AS ts_total';
|
||||
}
|
||||
if (!isset($filter['ts_owner']) || !count($filter['ts_owner']))
|
||||
if (!isset($filter['ts_owner']) || !count((array)$filter['ts_owner']))
|
||||
{
|
||||
$filter['ts_owner'] = array_keys($this->grants);
|
||||
}
|
||||
|
@ -178,41 +178,8 @@ class timesheet_hooks
|
||||
// Merge print
|
||||
if ($GLOBALS['egw_info']['user']['apps']['filemanager'])
|
||||
{
|
||||
$settings['default_document'] = array(
|
||||
'type' => 'vfs_file',
|
||||
'size' => 60,
|
||||
'label' => 'Default document to insert entries',
|
||||
'name' => 'default_document',
|
||||
'help' => lang('If you specify a document (full vfs path) here, %1 displays an extra document icon for each entry. That icon allows to download the specified document with the data inserted.',lang('timesheet')).' '.
|
||||
lang('The document can contain placeholder like {{%1}}, to be replaced with the data.', 'ts_title').' '.
|
||||
lang('The following document-types are supported:'). implode(',',Api\Storage\Merge::get_file_extensions()),
|
||||
'run_lang' => false,
|
||||
'xmlrpc' => True,
|
||||
'admin' => False,
|
||||
);
|
||||
$settings['document_dir'] = array(
|
||||
'type' => 'vfs_dirs',
|
||||
'size' => 60,
|
||||
'label' => 'Directory with documents to insert entries',
|
||||
'name' => 'document_dir',
|
||||
'help' => lang('If you specify a directory (full vfs path) here, %1 displays an action for each document. That action allows to download the specified document with the %1 data inserted.', lang('timesheet')) . ' ' .
|
||||
lang('The document can contain placeholder like {{%1}}, to be replaced with the data.', 'ts_title') . ' ' .
|
||||
lang('The following document-types are supported:') . implode(',', Api\Storage\Merge::get_file_extensions()),
|
||||
'run_lang' => false,
|
||||
'xmlrpc' => True,
|
||||
'admin' => False,
|
||||
'default' => '/templates/timesheet',
|
||||
);
|
||||
$settings[Api\Storage\Merge::PREF_DOCUMENT_FILENAME] = array(
|
||||
'type' => 'taglist',
|
||||
'label' => 'Document download filename',
|
||||
'name' => 'document_download_name',
|
||||
'values' => Api\Storage\Merge::DOCUMENT_FILENAME_OPTIONS,
|
||||
'help' => 'Choose the default filename for downloaded documents.',
|
||||
'xmlrpc' => True,
|
||||
'admin' => False,
|
||||
'default' => 'document',
|
||||
);
|
||||
$merge = new timesheet_merge();
|
||||
$settings += $merge->merge_preferences();
|
||||
}
|
||||
|
||||
return $settings;
|
||||
|
@ -156,68 +156,6 @@ class timesheet_merge extends Api\Storage\Merge
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate table with replacements for the Api\Preferences
|
||||
*
|
||||
*/
|
||||
public function show_replacements()
|
||||
{
|
||||
$GLOBALS['egw_info']['flags']['app_header'] = lang('timesheet').' - '.lang('Replacements for inserting entries into documents');
|
||||
$GLOBALS['egw_info']['flags']['nonavbar'] = false;
|
||||
echo $GLOBALS['egw']->framework->header();
|
||||
|
||||
echo "<table width='90%' align='center'>\n";
|
||||
echo '<tr><td colspan="4"><h3>'.lang('Timesheet fields:')."</h3></td></tr>";
|
||||
|
||||
$n = 0;
|
||||
$fields = array('ts_id' => lang('Timesheet ID')) + $this->bo->field2label + array(
|
||||
'ts_total' => lang('total'),
|
||||
'ts_created' => lang('Created'),
|
||||
'ts_modified' => lang('Modified'),
|
||||
);
|
||||
foreach($fields as $name => $label)
|
||||
{
|
||||
if (in_array($name,array('pl_id','customfields'))) continue; // dont show them
|
||||
|
||||
if (in_array($name,array('ts_title', 'ts_description')) && $n&1) // main values, which should be in the first column
|
||||
{
|
||||
echo "</tr>\n";
|
||||
$n++;
|
||||
}
|
||||
if (!($n&1)) echo '<tr>';
|
||||
echo '<td>{{'.$name.'}}</td><td>'.lang($label).'</td>';
|
||||
if ($n&1) echo "</tr>\n";
|
||||
$n++;
|
||||
}
|
||||
|
||||
echo '<tr><td colspan="4"><h3>'.lang('Custom fields').":</h3></td></tr>";
|
||||
foreach($this->bo->customfields as $name => $field)
|
||||
{
|
||||
echo '<tr><td>{{#'.$name.'}}</td><td colspan="3">'.$field['label']."</td></tr>\n";
|
||||
}
|
||||
|
||||
echo '<tr><td colspan="4"><h3>'.lang('Project fields').':</h3></td></tr>';
|
||||
$pm_merge = new projectmanager_merge();
|
||||
$i = 0;
|
||||
foreach($pm_merge->projectmanager_fields as $name => $label)
|
||||
{
|
||||
if (!($i&1)) echo '<tr>';
|
||||
echo '<td>{{ts_project/'.$name.'}}</td><td>'.$label.'</td>';
|
||||
if ($i&1) echo "</tr>\n";
|
||||
$i++;
|
||||
}
|
||||
|
||||
echo '<tr><td colspan="4"><h3>' . lang('General fields:') . "</h3></td></tr>";
|
||||
foreach($this->get_common_replacements() as $name => $label)
|
||||
{
|
||||
echo '<tr><td>{{' . $name . '}}</td><td colspan="3">' . $label . "</td></tr>\n";
|
||||
}
|
||||
|
||||
echo "</table>\n";
|
||||
|
||||
echo $GLOBALS['egw']->framework->footer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of placeholders provided.
|
||||
*
|
||||
@ -256,10 +194,14 @@ class timesheet_merge extends Api\Storage\Merge
|
||||
}
|
||||
}
|
||||
|
||||
// Add project placeholders
|
||||
$pm_merge = new projectmanager_merge();
|
||||
$this->add_linked_placeholders($placeholders, lang('Project'), $pm_merge->get_placeholder_list('ts_project'));
|
||||
|
||||
// Don't add any linked placeholders if we're not at the top level
|
||||
// This avoids potential recursion
|
||||
if(!$prefix)
|
||||
{
|
||||
// Add project placeholders
|
||||
$pm_merge = new projectmanager_merge();
|
||||
$this->add_linked_placeholders($placeholders, lang('Project'), $pm_merge->get_placeholder_list('ts_project'));
|
||||
}
|
||||
return $placeholders;
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user