mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-28 02:43:22 +01:00
* Collabora: Merge placeholder dialogs
Added merge placeholder & address dialogs to Collabora. Also some new merge preferences for target filename and location, and placeholder list UI
This commit is contained in:
parent
09f93f2b9d
commit
82103dd514
@ -291,31 +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',
|
||||
);
|
||||
$merge = new Api\Contacts\Merge();
|
||||
$settings += $merge->merge_preferences();
|
||||
}
|
||||
|
||||
if ($GLOBALS['egw_info']['user']['apps']['felamimail'] || $GLOBALS['egw_info']['user']['apps']['mail'])
|
||||
|
562
api/js/etemplate/et2_widget_placeholder.ts
Normal file
562
api/js/etemplate/et2_widget_placeholder.ts
Normal file
@ -0,0 +1,562 @@
|
||||
/**
|
||||
* EGroupware eTemplate2 - JS Placeholder widgets
|
||||
*
|
||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||
* @package etemplate
|
||||
* @subpackage api
|
||||
* @link https://www.egroupware.org
|
||||
* @author Nathan Gray
|
||||
* @copyright Nathan Gray 2021
|
||||
*/
|
||||
|
||||
/*egw:uses
|
||||
et2_core_inputWidget;
|
||||
et2_core_valueWidget;
|
||||
et2_widget_description;
|
||||
*/
|
||||
|
||||
import {et2_createWidget, et2_register_widget, WidgetConfig} from "./et2_core_widget";
|
||||
import {ClassWithAttributes} from "./et2_core_inheritance";
|
||||
import {et2_dialog} from "./et2_widget_dialog";
|
||||
import {et2_inputWidget} from "./et2_core_inputWidget";
|
||||
import type {egw} from "../jsapi/egw_global";
|
||||
import {et2_selectbox} from "./et2_widget_selectbox";
|
||||
import {et2_description} from "./et2_widget_description";
|
||||
import {et2_link_entry} from "./et2_widget_link";
|
||||
import type {et2_button} from "./et2_widget_button";
|
||||
|
||||
|
||||
/**
|
||||
* Display a dialog to choose a placeholder
|
||||
*/
|
||||
export class et2_placeholder_select extends et2_inputWidget
|
||||
{
|
||||
static readonly _attributes : any = {
|
||||
insert_callback: {
|
||||
"name": "Insert callback",
|
||||
"description": "Method called with the selected placeholder text",
|
||||
"type": "js"
|
||||
},
|
||||
dialog_title: {
|
||||
"name": "Dialog title",
|
||||
"type": "string",
|
||||
"default": "Insert Placeholder"
|
||||
}
|
||||
};
|
||||
|
||||
static placeholders : Object | null = null;
|
||||
|
||||
button : JQuery;
|
||||
submit_callback : any;
|
||||
dialog : et2_dialog;
|
||||
protected value : any;
|
||||
|
||||
protected LIST_URL = 'EGroupware\\Api\\Etemplate\\Widget\\Placeholder::ajax_get_placeholders';
|
||||
protected TEMPLATE = '/api/templates/default/insert_merge_placeholder.xet?1';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param _parent
|
||||
* @param _attrs
|
||||
* @memberOf et2_vfsSelect
|
||||
*/
|
||||
constructor(_parent, _attrs? : WidgetConfig, _child? : object)
|
||||
{
|
||||
// Call the inherited constructor
|
||||
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_placeholder_select._attributes, _child || {}));
|
||||
|
||||
// Allow no child widgets
|
||||
this.supportedWidgetClasses = [];
|
||||
}
|
||||
|
||||
_content(_content, _callback)
|
||||
{
|
||||
let self = this;
|
||||
if(this.dialog && this.dialog.div)
|
||||
{
|
||||
this.dialog.div.dialog('close');
|
||||
}
|
||||
|
||||
var callback = _callback || this._buildDialog;
|
||||
if(et2_placeholder_select.placeholders === null)
|
||||
{
|
||||
this.egw().loading_prompt('placeholder_select', true, '', 'body');
|
||||
this.egw().json(
|
||||
this.LIST_URL,
|
||||
[],
|
||||
function(_content)
|
||||
{
|
||||
if(typeof _content === 'object' && _content.message)
|
||||
{
|
||||
// Something went wrong
|
||||
this.egw().message(_content.message, 'error');
|
||||
return;
|
||||
}
|
||||
this.egw().loading_prompt('placeholder_select', false);
|
||||
et2_placeholder_select.placeholders = _content;
|
||||
callback.apply(self, arguments);
|
||||
}.bind(this)
|
||||
).sendRequest(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
this._buildDialog(et2_placeholder_select.placeholders);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds placeholder selection dialog
|
||||
*
|
||||
* @param {object} _data content
|
||||
*/
|
||||
protected _buildDialog(_data)
|
||||
{
|
||||
|
||||
let self = this;
|
||||
let buttons = [
|
||||
{
|
||||
text: this.egw().lang("Insert"),
|
||||
id: "submit",
|
||||
image: "export"
|
||||
}
|
||||
];
|
||||
let extra_buttons_action = {};
|
||||
|
||||
if(this.options.extra_buttons && this.options.method)
|
||||
{
|
||||
for(let i = 0; i < this.options.extra_buttons.length; i++)
|
||||
{
|
||||
delete (this.options.extra_buttons[i]['click']);
|
||||
buttons.push(this.options.extra_buttons[i]);
|
||||
extra_buttons_action[this.options.extra_buttons[i]['id']] = this.options.extra_buttons[i]['id'];
|
||||
}
|
||||
|
||||
}
|
||||
buttons.push({text: this.egw().lang("Cancel"), id: "cancel", image: "cancel"});
|
||||
|
||||
let data = {
|
||||
content: {app: '', group: '', entry: {}},
|
||||
sel_options: {app: [], group: []},
|
||||
modifications: {
|
||||
outer_box: {
|
||||
entry: {
|
||||
application_list: []
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Object.keys(_data).map((key) =>
|
||||
{
|
||||
data.sel_options.app.push(
|
||||
{
|
||||
value: key,
|
||||
label: this.egw().lang(key)
|
||||
});
|
||||
});
|
||||
data.sel_options.group = this._get_group_options(Object.keys(_data)[0]);
|
||||
data.content.app = data.sel_options.app[0].value;
|
||||
data.content.group = data.sel_options.group[0]?.value;
|
||||
data.content.entry = {app: data.content.app};
|
||||
data.modifications.outer_box.entry.application_list = Object.keys(_data);
|
||||
// Remove non-app placeholders (user & general)
|
||||
let non_apps = ['user', 'general'];
|
||||
for(let i = 0; i < non_apps.length; i++)
|
||||
{
|
||||
let index = data.modifications.outer_box.entry.application_list.indexOf(non_apps[i]);
|
||||
data.modifications.outer_box.entry.application_list.splice(index, 1);
|
||||
}
|
||||
|
||||
// callback for dialog
|
||||
this.submit_callback = function(submit_button_id, submit_value)
|
||||
{
|
||||
if((submit_button_id == 'submit' || (extra_buttons_action && extra_buttons_action[submit_button_id])) && submit_value)
|
||||
{
|
||||
this._do_insert_callback(submit_value);
|
||||
return true;
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
this.dialog = <et2_dialog>et2_createWidget("dialog",
|
||||
{
|
||||
callback: this.submit_callback,
|
||||
title: this.egw().lang(this.options.dialog_title) || this.egw().lang("Insert Placeholder"),
|
||||
buttons: buttons,
|
||||
minWidth: 500,
|
||||
minHeight: 400,
|
||||
width: 400,
|
||||
value: data,
|
||||
template: this.egw().webserverUrl + this.TEMPLATE,
|
||||
resizable: true
|
||||
}, et2_dialog._create_parent('api'));
|
||||
this.dialog.template.uniqueId = 'api.insert_merge_placeholder';
|
||||
|
||||
|
||||
this.dialog.div.on('load', this._on_template_load.bind(this));
|
||||
}
|
||||
|
||||
doLoadingFinished()
|
||||
{
|
||||
this._content.call(this, null);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Post-load of the dialog
|
||||
* Bind internal events, set some things that are difficult to do in the template
|
||||
*/
|
||||
_on_template_load()
|
||||
{
|
||||
let app = <et2_selectbox>this.dialog.template.widgetContainer.getDOMWidgetById("app");
|
||||
let group = <et2_selectbox>this.dialog.template.widgetContainer.getDOMWidgetById("group");
|
||||
let placeholder_list = <et2_selectbox>this.dialog.template.widgetContainer.getDOMWidgetById("placeholder_list");
|
||||
let preview = <et2_description>this.dialog.template.widgetContainer.getDOMWidgetById("preview_placeholder");
|
||||
let entry = <et2_link_entry>this.dialog.template.widgetContainer.getDOMWidgetById("entry");
|
||||
|
||||
|
||||
placeholder_list.set_select_options(this._get_placeholders(app.get_value(), group.get_value()));
|
||||
|
||||
// Further setup / styling that can't be done in etemplate
|
||||
this.dialog.template.DOMContainer.style.display = "flex";
|
||||
this.dialog.template.DOMContainer.firstChild.style.display = "flex";
|
||||
group.getDOMNode().size = 5;
|
||||
placeholder_list.getDOMNode().size = 5;
|
||||
|
||||
// Bind some handlers
|
||||
app.onchange = (node, widget) =>
|
||||
{
|
||||
preview.set_value("");
|
||||
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: ''});
|
||||
}
|
||||
else if(widget.get_value() == 'general')
|
||||
{
|
||||
// Don't change entry app, leave it
|
||||
entry.set_disabled(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.set_disabled(false);
|
||||
entry.app_select.val(widget.get_value());
|
||||
entry.set_value({app: widget.get_value(), id: '', query: ''});
|
||||
}
|
||||
let groups = this._get_group_options(widget.get_value());
|
||||
group.set_select_options(groups);
|
||||
group.set_value(groups[0].value);
|
||||
group.onchange();
|
||||
}
|
||||
group.onchange = (select_node, select_widget) =>
|
||||
{
|
||||
let options = this._get_placeholders(app.get_value(), group.get_value())
|
||||
placeholder_list.set_select_options(options);
|
||||
preview.set_value("");
|
||||
placeholder_list.set_value(options[0].value);
|
||||
}
|
||||
placeholder_list.onchange = this._on_placeholder_select.bind(this);
|
||||
entry.onchange = this._on_placeholder_select.bind(this);
|
||||
(<et2_button>this.dialog.template.widgetContainer.getDOMWidgetById("insert_placeholder")).onclick = () =>
|
||||
{
|
||||
this.options.insert_callback(this.dialog.template.widgetContainer.getDOMWidgetById("preview_placeholder").getDOMNode().textContent);
|
||||
};
|
||||
(<et2_button>this.dialog.template.widgetContainer.getDOMWidgetById("insert_content")).onclick = () =>
|
||||
{
|
||||
this.options.insert_callback(this.dialog.template.widgetContainer.getDOMWidgetById("preview_content").getDOMNode().textContent);
|
||||
};
|
||||
|
||||
app.set_value(app.get_value());
|
||||
}
|
||||
|
||||
/**
|
||||
* User has selected a placeholder
|
||||
* Update the UI, and if they have an entry selected do the replacement and show that.
|
||||
*/
|
||||
_on_placeholder_select()
|
||||
{
|
||||
let app = <et2_link_entry>this.dialog.template.widgetContainer.getDOMWidgetById("app");
|
||||
let entry = <et2_link_entry>this.dialog.template.widgetContainer.getDOMWidgetById("entry");
|
||||
let placeholder_list = <et2_selectbox>this.dialog.template.widgetContainer.getDOMWidgetById("placeholder_list");
|
||||
let preview = <et2_description>this.dialog.template.widgetContainer.getDOMWidgetById("preview_placeholder");
|
||||
let preview_content = <et2_description>this.dialog.template.widgetContainer.getDOMWidgetById("preview_content");
|
||||
|
||||
// Show the selected placeholder
|
||||
this.set_value(placeholder_list.get_value());
|
||||
preview.set_value(placeholder_list.get_value());
|
||||
preview.getDOMNode().parentNode.style.visibility = placeholder_list.get_value().trim() ? null : 'hidden';
|
||||
|
||||
if(placeholder_list.get_value() && entry.get_value())
|
||||
{
|
||||
// Show the selected placeholder replaced with value from the selected entry
|
||||
this.egw().json(
|
||||
'EGroupware\\Api\\Etemplate\\Widget\\Placeholder::ajax_fill_placeholders',
|
||||
[placeholder_list.get_value(), entry.get_value()],
|
||||
function(_content)
|
||||
{
|
||||
if(!_content)
|
||||
{
|
||||
_content = '';
|
||||
}
|
||||
preview_content.set_value(_content);
|
||||
preview_content.getDOMNode().parentNode.style.visibility = _content.trim() ? null : 'hidden';
|
||||
}.bind(this)
|
||||
).sendRequest(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No value, hide the row
|
||||
preview_content.getDOMNode().parentNode.style.visibility = 'hidden';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of placeholder groups under the selected application
|
||||
* @param appname
|
||||
* @returns {value:string, label:string}[]
|
||||
*/
|
||||
_get_group_options(appname : string)
|
||||
{
|
||||
let options = [];
|
||||
Object.keys(et2_placeholder_select.placeholders[appname]).map((key) =>
|
||||
{
|
||||
// @ts-ignore
|
||||
if(Object.keys(et2_placeholder_select.placeholders[appname][key]).filter((key) => isNaN(key)).length > 0)
|
||||
{
|
||||
// Handle groups of groups
|
||||
if(typeof et2_placeholder_select.placeholders[appname][key].label !== "undefined")
|
||||
{
|
||||
options[key] = et2_placeholder_select.placeholders[appname][key];
|
||||
}
|
||||
else
|
||||
{
|
||||
options[this.egw().lang(key)] = [];
|
||||
for(let sub of Object.keys(et2_placeholder_select.placeholders[appname][key]))
|
||||
{
|
||||
if(!et2_placeholder_select.placeholders[appname][key][sub])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
options[this.egw().lang(key)].push({
|
||||
value: key + '-' + sub,
|
||||
label: this.egw().lang(sub)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
options.push({
|
||||
value: key,
|
||||
label: this.egw().lang(key)
|
||||
});
|
||||
}
|
||||
});
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of placeholders under the given application + group
|
||||
*
|
||||
* @param appname
|
||||
* @param group
|
||||
* @returns {value:string, label:string}[]
|
||||
*/
|
||||
_get_placeholders(appname : string, group : string)
|
||||
{
|
||||
let _group = group.split('-', 2);
|
||||
let ph = et2_placeholder_select.placeholders[appname];
|
||||
for(let i = 0; typeof ph !== "undefined" && i < _group.length; i++)
|
||||
{
|
||||
ph = ph[_group[i]];
|
||||
}
|
||||
return ph || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the correct insert text call the insert callback with it
|
||||
*
|
||||
* @param dialog_values
|
||||
*/
|
||||
_do_insert_callback(dialog_values : Object)
|
||||
{
|
||||
this.options.insert_callback(this.get_value());
|
||||
}
|
||||
|
||||
set_value(value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
getValue()
|
||||
{
|
||||
return this.value;
|
||||
}
|
||||
};
|
||||
et2_register_widget(et2_placeholder_select, ["placeholder-select"]);
|
||||
|
||||
/**
|
||||
* Display a dialog to choose from a set list of placeholder snippets
|
||||
*/
|
||||
export class et2_placeholder_snippet_select extends et2_placeholder_select
|
||||
{
|
||||
static readonly _attributes : any = {
|
||||
dialog_title: {
|
||||
"default": "Insert address"
|
||||
}
|
||||
};
|
||||
static placeholders = {
|
||||
"addressbook": {
|
||||
"addresses": {
|
||||
"{{org_name}}\n{{n_fn}}\n{{adr_one_street}}{{NELF adr_one_street2}}\n{{adr_one_formatted}}": "Business address",
|
||||
"{{n_fn}}\n{{adr_two_street}}{{NELF adr_two_street2}}\n{{adr_two_formatted}}": "Home address",
|
||||
"{{n_fn}}\n{{email}}\n{{tel_work}}": "Name, email, phone"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
button : JQuery;
|
||||
submit_callback : any;
|
||||
dialog : et2_dialog;
|
||||
protected value : any;
|
||||
|
||||
protected LIST_URL = 'EGroupware\\Api\\Etemplate\\Widget\\Placeholder::ajax_get_placeholders';
|
||||
protected TEMPLATE = '/api/templates/default/placeholder_snippet.xet?1';
|
||||
|
||||
/**
|
||||
* Post-load of the dialog
|
||||
* Bind internal events, set some things that are difficult to do in the template
|
||||
*/
|
||||
_on_template_load()
|
||||
{
|
||||
let app = <et2_selectbox>this.dialog.template.widgetContainer.getDOMWidgetById("app");
|
||||
let placeholder_list = <et2_selectbox>this.dialog.template.widgetContainer.getDOMWidgetById("placeholder_list");
|
||||
let preview = <et2_description>this.dialog.template.widgetContainer.getDOMWidgetById("preview_content");
|
||||
let entry = <et2_link_entry>this.dialog.template.widgetContainer.getDOMWidgetById("entry");
|
||||
|
||||
|
||||
placeholder_list.set_select_options(this._get_placeholders("addressbook", "addresses"));
|
||||
|
||||
// Further setup / styling that can't be done in etemplate
|
||||
app.getInputNode().setAttribute("readonly", true);
|
||||
this.dialog.template.DOMContainer.style.display = "flex";
|
||||
this.dialog.template.DOMContainer.firstChild.style.display = "flex";
|
||||
placeholder_list.getDOMNode().size = 5;
|
||||
|
||||
// Bind some handlers
|
||||
app.onchange = (node, widget) =>
|
||||
{
|
||||
entry.set_value({app: widget.get_value()});
|
||||
placeholder_list.set_select_options(this._get_placeholders(app.get_value(), "addresses"));
|
||||
}
|
||||
placeholder_list.onchange = this._on_placeholder_select.bind(this);
|
||||
entry.onchange = this._on_placeholder_select.bind(this);
|
||||
|
||||
app.set_value(app.get_value());
|
||||
this._on_placeholder_select();
|
||||
}
|
||||
|
||||
/**
|
||||
* User has selected a placeholder
|
||||
* Update the UI, and if they have an entry selected do the replacement and show that.
|
||||
*/
|
||||
_on_placeholder_select()
|
||||
{
|
||||
let app = <et2_link_entry>this.dialog.template.widgetContainer.getDOMWidgetById("app");
|
||||
let entry = <et2_link_entry>this.dialog.template.widgetContainer.getDOMWidgetById("entry");
|
||||
let placeholder_list = <et2_selectbox>this.dialog.template.widgetContainer.getDOMWidgetById("placeholder_list");
|
||||
let preview = <et2_description>this.dialog.template.widgetContainer.getDOMWidgetById("preview_placeholder");
|
||||
let preview_content = <et2_description>this.dialog.template.widgetContainer.getDOMWidgetById("preview_content");
|
||||
|
||||
if(placeholder_list.get_value() && entry.get_value())
|
||||
{
|
||||
// Show the selected placeholder replaced with value from the selected entry
|
||||
this.egw().json(
|
||||
'EGroupware\\Api\\Etemplate\\Widget\\Placeholder::ajax_fill_placeholders',
|
||||
[placeholder_list.get_value(), {app: "addressbook", id: entry.get_value()}],
|
||||
function(_content)
|
||||
{
|
||||
if(!_content)
|
||||
{
|
||||
_content = '';
|
||||
}
|
||||
this.set_value(_content);
|
||||
preview_content.set_value(_content);
|
||||
preview_content.getDOMNode().parentNode.style.visibility = _content.trim() ? null : 'hidden';
|
||||
}.bind(this)
|
||||
).sendRequest(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No value, hide the row
|
||||
preview_content.getDOMNode().parentNode.style.visibility = 'hidden';
|
||||
}
|
||||
if(!entry.get_value())
|
||||
{
|
||||
entry.search.get(0).focus();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of placeholder groups under the selected application
|
||||
* @param appname
|
||||
* @returns {value:string, label:string}[]
|
||||
*/
|
||||
_get_group_options(appname : string)
|
||||
{
|
||||
let options = [];
|
||||
Object.keys(et2_placeholder_select.placeholders[appname]).map((key) =>
|
||||
{
|
||||
options.push(
|
||||
{
|
||||
value: key,
|
||||
label: this.egw().lang(key)
|
||||
});
|
||||
});
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of placeholders under the given application + group
|
||||
*
|
||||
* @param appname
|
||||
* @param group
|
||||
* @returns {value:string, label:string}[]
|
||||
*/
|
||||
_get_placeholders(appname : string, group : string)
|
||||
{
|
||||
let options = [];
|
||||
Object.keys(et2_placeholder_snippet_select.placeholders[appname][group]).map((key) =>
|
||||
{
|
||||
options.push(
|
||||
{
|
||||
value: key,
|
||||
label: this.egw().lang(et2_placeholder_snippet_select.placeholders[appname][group][key])
|
||||
});
|
||||
});
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the correct insert text call the insert callback with it
|
||||
*
|
||||
* @param dialog_values
|
||||
*/
|
||||
_do_insert_callback(dialog_values : Object)
|
||||
{
|
||||
this.options.insert_callback(this.get_value());
|
||||
}
|
||||
|
||||
set_value(value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
getValue()
|
||||
{
|
||||
return this.value;
|
||||
}
|
||||
};
|
||||
et2_register_widget(et2_placeholder_snippet_select, ["placeholder-snippet"]);
|
@ -738,7 +738,7 @@ var et2_selectbox = /** @class */ (function (_super) {
|
||||
if (sub == 'value')
|
||||
continue;
|
||||
if (typeof _options[key][sub] === 'object' && _options[key][sub] !== null) {
|
||||
this._appendOptionElement(sub, _options[key][sub]["label"] ? _options[key][sub]["label"] : "", _options[key][sub]["title"] ? _options[key][sub]["title"] : "", group);
|
||||
this._appendOptionElement(typeof _options[key][sub]["value"] !== "undefined" ? _options[key][sub]["value"] : sub, _options[key][sub]["label"] ? _options[key][sub]["label"] : "", _options[key][sub]["title"] ? _options[key][sub]["title"] : "", group);
|
||||
}
|
||||
else {
|
||||
this._appendOptionElement(sub, _options[key][sub], undefined, group);
|
||||
|
@ -982,7 +982,8 @@ export class et2_selectbox extends et2_inputWidget
|
||||
if(sub == 'value') continue;
|
||||
if (typeof _options[key][sub] === 'object' && _options[key][sub] !== null)
|
||||
{
|
||||
this._appendOptionElement(sub,
|
||||
this._appendOptionElement(
|
||||
typeof _options[key][sub]["value"] !== "undefined" ? _options[key][sub]["value"] : sub,
|
||||
_options[key][sub]["label"] ? _options[key][sub]["label"] : "",
|
||||
_options[key][sub]["title"] ? _options[key][sub]["title"] : "",
|
||||
group
|
||||
|
@ -50,6 +50,7 @@
|
||||
et2_widget_file;
|
||||
et2_widget_link;
|
||||
et2_widget_progress;
|
||||
et2_widget_placeholder;
|
||||
et2_widget_portlet;
|
||||
et2_widget_selectAccount;
|
||||
et2_widget_ajaxSelect;
|
||||
|
@ -49,6 +49,7 @@
|
||||
et2_widget_file;
|
||||
et2_widget_link;
|
||||
et2_widget_progress;
|
||||
et2_widget_placeholder;
|
||||
et2_widget_portlet;
|
||||
et2_widget_selectAccount;
|
||||
et2_widget_ajaxSelect;
|
||||
|
@ -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.
|
||||
@ -724,6 +728,7 @@ insert new column behind this one common de Neue Spalte hinter dieser einfügen
|
||||
insert new column in front of all common de Neue Spalte vor dieser einfügen
|
||||
insert new row after this one common de Neue Zeile nach dieser einfügen
|
||||
insert new row in front of first line common de Neue Zeile vor dieser einfügen
|
||||
insert placeholder common de Platzhalter einfügen
|
||||
insert row after common de Zeile danach einfügen
|
||||
insert row before common de Zeile davor einfügen
|
||||
insert timestamp into description field common de Zeitstempel in das Beschreibungs-Feld einfügen
|
||||
@ -1072,6 +1077,7 @@ preference common de Einstellung
|
||||
preferences common de Einstellungen
|
||||
preferences for the %1 template set preferences de Einstellungen für das %1 Template
|
||||
prev common de Vorheriger
|
||||
preview with entry common de Vorschau aus Eintrag
|
||||
previous common de Vorherige
|
||||
previous page common de Vorherige Seite
|
||||
primary group common de Hauptgruppe
|
||||
@ -1498,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.
|
||||
@ -724,6 +728,7 @@ insert new column behind this one common en Insert new column after
|
||||
insert new column in front of all common en Insert new column before all
|
||||
insert new row after this one common en Insert new row after
|
||||
insert new row in front of first line common en Insert new row before first line
|
||||
insert placeholder common en Insert placeholder
|
||||
insert row after common en Insert row after
|
||||
insert row before common en Insert row before
|
||||
insert timestamp into description field common en Insert timestamp into description field
|
||||
@ -855,6 +860,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.
|
||||
@ -1072,6 +1078,7 @@ preference common en Preference
|
||||
preferences common en Preferences
|
||||
preferences for the %1 template set preferences en Preferences for the %1 template set
|
||||
prev common en Prev
|
||||
preview with entry common en Preview with entry
|
||||
previous common en Previous
|
||||
previous page common en Previous page
|
||||
primary group common en Primary group
|
||||
@ -1498,6 +1505,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
|
||||
|
@ -156,109 +156,139 @@ class Merge extends Api\Storage\Merge
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate table with replacements for the preferences
|
||||
* Get a list of placeholders provided.
|
||||
*
|
||||
* Placeholders are grouped logically. Group key should have a user-friendly translation.
|
||||
*/
|
||||
public function show_replacements()
|
||||
public function get_placeholder_list($prefix = '')
|
||||
{
|
||||
$GLOBALS['egw_info']['flags']['app_header'] = lang('Addressbook').' - '.lang('Replacements for inserting contacts into documents');
|
||||
$GLOBALS['egw_info']['flags']['nonavbar'] = (bool)$_GET['nonavbar'];
|
||||
// Specific order for these ones
|
||||
$placeholders = [
|
||||
'contact' => [],
|
||||
'details' => [
|
||||
[
|
||||
'value' => $this->prefix($prefix, 'categories', '{'),
|
||||
'label' => lang('Category path')
|
||||
],
|
||||
['value' => $this->prefix($prefix, 'note', '{'),
|
||||
'label' => $this->contacts->contact_fields['note']],
|
||||
['value' => $this->prefix($prefix, 'id', '{'),
|
||||
'label' => $this->contacts->contact_fields['id']],
|
||||
['value' => $this->prefix($prefix, 'owner', '{'),
|
||||
'label' => $this->contacts->contact_fields['owner']],
|
||||
['value' => $this->prefix($prefix, 'private', '{'),
|
||||
'label' => $this->contacts->contact_fields['private']],
|
||||
['value' => $this->prefix($prefix, 'cat_id', '{'),
|
||||
'label' => $this->contacts->contact_fields['cat_id']],
|
||||
],
|
||||
|
||||
ob_start();
|
||||
echo "<table width='90%' align='center'>\n";
|
||||
echo '<tr><td colspan="4"><h3>'.lang('Contact fields:')."</h3></td></tr>";
|
||||
];
|
||||
|
||||
$n = 0;
|
||||
// Iterate through the list & switch groups as we go
|
||||
// Hopefully a little better than assigning each field to a group
|
||||
$group = 'contact';
|
||||
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
|
||||
if(in_array($name, array('tid', 'label', 'geo')))
|
||||
{
|
||||
echo "</tr>\n";
|
||||
$n++;
|
||||
}
|
||||
if (!($n&1)) echo '<tr>';
|
||||
echo '<td>{{'.$name.'}}</td><td>'.$label.'</td>';
|
||||
if($name == 'cat_id')
|
||||
continue;
|
||||
} // dont show them, as they are not used in the UI atm.
|
||||
|
||||
switch($name)
|
||||
{
|
||||
if ($n&1) echo "</tr>\n";
|
||||
echo '<td>{{categories}}</td><td>'.lang('Category path').'</td>';
|
||||
$n++;
|
||||
case 'adr_one_street':
|
||||
$group = 'business';
|
||||
break;
|
||||
case 'adr_two_street':
|
||||
$group = 'private';
|
||||
break;
|
||||
case 'tel_work':
|
||||
$group = 'phone';
|
||||
break;
|
||||
case 'email':
|
||||
case 'email_home':
|
||||
$group = 'email';
|
||||
break;
|
||||
case 'freebusy_uri':
|
||||
$group = 'details';
|
||||
}
|
||||
$marker = $this->prefix($prefix, $name, '{');
|
||||
if(!array_filter($placeholders, function ($a) use ($marker)
|
||||
{
|
||||
count(array_filter($a, function ($b) use ($marker)
|
||||
{
|
||||
return $b['value'] == $marker;
|
||||
})
|
||||
) > 0;
|
||||
}))
|
||||
{
|
||||
$placeholders[$group][] = [
|
||||
'value' => $marker,
|
||||
'label' => $label
|
||||
];
|
||||
}
|
||||
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)
|
||||
// Correctly formatted address by country / preference
|
||||
$placeholders['business'][] = [
|
||||
'value' => $this->prefix($prefix, 'adr_one_formatted', '{'),
|
||||
'label' => "Formatted business address"
|
||||
];
|
||||
$placeholders['private'][] = [
|
||||
'value' => $this->prefix($prefix, 'adr_two_formatted', '{'),
|
||||
'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)
|
||||
{
|
||||
echo '<tr><td>{{#'.$name.'}}</td><td colspan="3">'.$field['label']."</td></tr>\n";
|
||||
$this->add_calendar_placeholders($placeholders, $prefix);
|
||||
}
|
||||
|
||||
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";
|
||||
return $placeholders;
|
||||
}
|
||||
|
||||
protected function add_calendar_placeholders(&$placeholders, $prefix)
|
||||
{
|
||||
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());
|
||||
// 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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
237
api/src/Etemplate/Widget/Placeholder.php
Normal file
237
api/src/Etemplate/Widget/Placeholder.php
Normal file
@ -0,0 +1,237 @@
|
||||
<?php
|
||||
/**
|
||||
* EGroupware - eTemplate serverside of linking widgets
|
||||
*
|
||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||
* @package api
|
||||
* @subpackage etemplate
|
||||
* @link http://www.egroupware.org
|
||||
* @author Nathan Gray
|
||||
* @copyright 2011 Nathan Gray
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
namespace EGroupware\Api\Etemplate\Widget;
|
||||
|
||||
use EGroupware\Api\Etemplate;
|
||||
use EGroupware\Api;
|
||||
|
||||
/**
|
||||
* eTemplate Placeholder
|
||||
* Deals with listing & inserting placeholders, usually into Collabora
|
||||
*/
|
||||
class Placeholder extends Etemplate\Widget
|
||||
{
|
||||
|
||||
public $public_functions = array(
|
||||
'ajax_get_placeholders' => true,
|
||||
'ajax_fill_placeholder' => true
|
||||
);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string|\XMLReader $xml string with xml or XMLReader positioned on the element to construct
|
||||
* @throws Api\Exception\WrongParameter
|
||||
*/
|
||||
public function __construct($xml = '')
|
||||
{
|
||||
if($xml)
|
||||
{
|
||||
parent::__construct($xml);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up what we know on the server side.
|
||||
*
|
||||
* Set the options for the application select.
|
||||
*
|
||||
* @param string $cname
|
||||
* @param array $expand values for keys 'c', 'row', 'c_', 'row_', 'cont'
|
||||
*/
|
||||
public function beforeSendToClient($cname, array $expand = null)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the placeholders that match the given parameters.
|
||||
* Default options will get all placeholders in a single request.
|
||||
*/
|
||||
public static function ajax_get_placeholders($apps = null, $group = null)
|
||||
{
|
||||
$placeholders = [];
|
||||
|
||||
if(is_null($apps))
|
||||
{
|
||||
$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')),
|
||||
// Filemanager doesn't support links, but add it anyway
|
||||
['filemanager']
|
||||
);
|
||||
}
|
||||
|
||||
foreach($apps as $appname)
|
||||
{
|
||||
$merge = Api\Storage\Merge::get_app_class($appname);
|
||||
switch($appname)
|
||||
{
|
||||
case 'user':
|
||||
$list = $merge->get_user_placeholder_list();
|
||||
break;
|
||||
case 'general':
|
||||
$list = $merge->get_common_placeholder_list();
|
||||
break;
|
||||
default:
|
||||
if(get_class($merge) === 'EGroupware\Api\Contacts\Merge' && $appname !== 'addressbook' || $placeholders[$appname])
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
if(!is_null($group) && is_array($list))
|
||||
{
|
||||
$list = array_intersect_key($list, $group);
|
||||
}
|
||||
// Remove if empty
|
||||
foreach($list as $p_group => $p_list)
|
||||
{
|
||||
if(count($p_list) == 0)
|
||||
{
|
||||
unset($list[$p_group]);
|
||||
}
|
||||
}
|
||||
|
||||
if($list)
|
||||
{
|
||||
$placeholders[$appname] = $list;
|
||||
}
|
||||
}
|
||||
|
||||
$response = Api\Json\Response::get();
|
||||
$response->data($placeholders);
|
||||
}
|
||||
|
||||
public function ajax_fill_placeholders($content, $entry)
|
||||
{
|
||||
$merge = Api\Storage\Merge::get_app_class($entry['app']);
|
||||
$err = "";
|
||||
|
||||
switch($entry['app'])
|
||||
{
|
||||
case 'user':
|
||||
$entry = ['id' => $GLOBALS['egw_info']['user']['person_id']];
|
||||
// fall through
|
||||
default:
|
||||
$merged = $merge->merge_string($content, [$entry['id']], $err, 'text/plain');
|
||||
}
|
||||
$response = Api\Json\Response::get();
|
||||
$response->data($merged);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate input
|
||||
*
|
||||
* Following attributes get checked:
|
||||
* - needed: value must NOT be empty
|
||||
* - min, max: int and float widget only
|
||||
* - maxlength: maximum length of string (longer strings get truncated to allowed size)
|
||||
* - preg: perl regular expression incl. delimiters (set by default for int, float and colorpicker)
|
||||
* - int and float get casted to their type
|
||||
*
|
||||
* @param string $cname current namespace
|
||||
* @param array $expand values for keys 'c', 'row', 'c_', 'row_', 'cont'
|
||||
* @param array $content
|
||||
* @param array &$validated =array() validated content
|
||||
*/
|
||||
public function validate($cname, array $expand, array $content, &$validated = array())
|
||||
{
|
||||
$form_name = self::form_name($cname, $this->id, $expand);
|
||||
|
||||
if(!$this->is_readonly($cname, $form_name))
|
||||
{
|
||||
$value = $value_in =& self::get_array($content, $form_name);
|
||||
|
||||
// keep values added into request by other ajax-functions, eg. files draged into htmlarea (Vfs)
|
||||
if((!$value || is_array($value) && !$value['to_id']) && is_array($expand['cont'][$this->id]) && !empty($expand['cont'][$this->id]['to_id']))
|
||||
{
|
||||
if(!is_array($value))
|
||||
{
|
||||
$value = array(
|
||||
'to_app' => $expand['cont'][$this->id]['to_app'],
|
||||
);
|
||||
}
|
||||
$value['to_id'] = $expand['cont'][$this->id]['to_id'];
|
||||
}
|
||||
|
||||
// Link widgets can share IDs, make sure to preserve values from others
|
||||
$already = self::get_array($validated, $form_name);
|
||||
if($already != null)
|
||||
{
|
||||
$value = array_merge($value, $already);
|
||||
}
|
||||
// Automatically do link if user selected entry but didn't click 'Link' button
|
||||
$link = self::get_array($content, self::form_name($cname, $this->id . '_link_entry'));
|
||||
if($this->type == 'link-to' && is_array($link) && $link['app'] && $link['id'])
|
||||
{
|
||||
// Do we have enough information to link automatically?
|
||||
if(is_array($value) && $value['to_id'])
|
||||
{
|
||||
Api\Link::link($value['to_app'], $value['to_id'], $link['app'], $link['id']);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not enough information, leave it to the application
|
||||
if(!is_array($value['to_id']))
|
||||
{
|
||||
$value['to_id'] = array();
|
||||
}
|
||||
$value['to_id'][] = $link;
|
||||
}
|
||||
}
|
||||
|
||||
// Look for files - normally handled by ajax
|
||||
$files = self::get_array($content, self::form_name($cname, $this->id . '_file'));
|
||||
if(is_array($files) && !(is_array($value) && $value['to_id']))
|
||||
{
|
||||
$value = array();
|
||||
if(is_dir($GLOBALS['egw_info']['server']['temp_dir']) && is_writable($GLOBALS['egw_info']['server']['temp_dir']))
|
||||
{
|
||||
$path = $GLOBALS['egw_info']['server']['temp_dir'] . '/';
|
||||
}
|
||||
else
|
||||
{
|
||||
$path = '';
|
||||
}
|
||||
foreach($files as $name => $attrs)
|
||||
{
|
||||
if(!is_array($value['to_id']))
|
||||
{
|
||||
$value['to_id'] = array();
|
||||
}
|
||||
$value['to_id'][] = array(
|
||||
'app' => Api\Link::VFS_APPNAME,
|
||||
'id' => array(
|
||||
'name' => $attrs['name'],
|
||||
'type' => $attrs['type'],
|
||||
'tmp_name' => $path . $name
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
$valid =& self::get_array($validated, $form_name, true);
|
||||
if(true)
|
||||
{
|
||||
$valid = $value;
|
||||
}
|
||||
//error_log($this);
|
||||
//error_log(" " . array2string($valid));
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
86
api/templates/default/insert_merge_placeholder.xet
Normal file
86
api/templates/default/insert_merge_placeholder.xet
Normal file
@ -0,0 +1,86 @@
|
||||
<?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="etemplate.insert_merge_placeholder" template="" lang="" group="0" version="21.1.001">
|
||||
<vbox id="outer_box">
|
||||
<hbox id="selects">
|
||||
<vbox>
|
||||
<select id="app"/>
|
||||
<select id="group"/>
|
||||
|
||||
</vbox>
|
||||
<select id="placeholder_list"/>
|
||||
</hbox>
|
||||
<hbox class="preview">
|
||||
<description id="preview_placeholder"/>
|
||||
<button id="insert_placeholder" label="Insert" statustext="Insert placeholder" image="export"></button>
|
||||
</hbox>
|
||||
<hrule/>
|
||||
<link-entry id="entry" label="Preview with entry"/>
|
||||
<hbox class="preview">
|
||||
<description id="preview_content"/>
|
||||
<button id="insert_content" label="Insert" statustext="Insert merged content" image="export"></button>
|
||||
</hbox>
|
||||
</vbox>
|
||||
<styles>
|
||||
/** Structural stuff **/
|
||||
#api\.insert_merge_placeholder_outer_box > #api\.insert_merge_placeholder_selects {
|
||||
flex: 1 1 80%;
|
||||
}
|
||||
#api\.insert_merge_placeholder_outer_box > label.et2_label {
|
||||
flex: 0 1 auto;
|
||||
}
|
||||
#api\.insert_merge_placeholder_outer_box .preview {
|
||||
flex: 1 1 2em;
|
||||
font-size: larger;
|
||||
}
|
||||
select#api\.insert_merge_placeholder_app {
|
||||
flex-grow: 0;
|
||||
}
|
||||
.ui-dialog-content, div.et2_box_widget, div.et2_box_widget > div.et2_box_widget {
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
div.et2_hbox {
|
||||
flex-direction: row;
|
||||
flex-grow: 1;
|
||||
}
|
||||
div.et2_vbox {
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
}
|
||||
div.et2_box_widget > * {
|
||||
flex: 1 1 auto;
|
||||
width: 100%;
|
||||
}
|
||||
div.et2_link_entry {
|
||||
flex-grow: 0;
|
||||
}
|
||||
div.et2_link_entry input.ui-autocomplete-input {
|
||||
width: 75%
|
||||
}
|
||||
div.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset button, button#cancel, .et2_button {
|
||||
border: none;
|
||||
border-radius: 0px;
|
||||
background-color: transparent;
|
||||
}
|
||||
div.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset button:hover, button#cancel:hover {
|
||||
box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
}
|
||||
.preview .et2_button {
|
||||
flex: 0 1 24px;
|
||||
height: 24px;
|
||||
border: none;
|
||||
border-radius: 0px;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/** Cosmetics **/
|
||||
#api\.insert_merge_placeholder_outer_box option:first-letter {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
</styles>
|
||||
</template>
|
||||
</overlay>
|
72
api/templates/default/placeholder_snippet.xet
Normal file
72
api/templates/default/placeholder_snippet.xet
Normal file
@ -0,0 +1,72 @@
|
||||
<?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="etemplate.placeholder_snippet" template="" lang="" group="0" version="21.1.001">
|
||||
<vbox id="outer_box">
|
||||
<vbox id="selects">
|
||||
<select id="app"/>
|
||||
<select id="placeholder_list"/>
|
||||
</vbox>
|
||||
<hrule/>
|
||||
<link-entry id="entry" label="Select entry" only_app="addressbook"/>
|
||||
<hbox class="preview">
|
||||
<description id="preview_content"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
<styles>
|
||||
|
||||
#api\.insert_merge_placeholder_outer_box > #api\.insert_merge_placeholder_selects {
|
||||
flex: 1 1 50%;
|
||||
}
|
||||
#api\.insert_merge_placeholder_outer_box > label.et2_label {
|
||||
flex: 0 1 auto;
|
||||
}
|
||||
#api\.insert_merge_placeholder_outer_box .preview {
|
||||
flex: 1 1 50%;
|
||||
font-size: larger;
|
||||
}
|
||||
select#api\.insert_merge_placeholder_app {
|
||||
flex-grow: 0;
|
||||
}
|
||||
.ui-dialog-content, div.et2_box_widget, div.et2_box_widget > div.et2_box_widget {
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
div.et2_hbox {
|
||||
flex-direction: row;
|
||||
flex-grow: 1;
|
||||
}
|
||||
div.et2_vbox {
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
}
|
||||
div.et2_box_widget > * {
|
||||
flex: 1 1 auto;
|
||||
width: 100%;
|
||||
}
|
||||
div.et2_link_entry {
|
||||
flex-grow: 0;
|
||||
}
|
||||
div.et2_link_entry input.ui-autocomplete-input {
|
||||
width: 70%
|
||||
}
|
||||
div.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset button, button#cancel, .et2_button {
|
||||
border: none;
|
||||
border-radius: 0px;
|
||||
background-color: transparent;
|
||||
}
|
||||
div.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset button:hover, button#cancel:hover {
|
||||
box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
}
|
||||
.preview .et2_button {
|
||||
flex: 0 1 24px;
|
||||
height: 24px;
|
||||
border: none;
|
||||
border-radius: 0px;
|
||||
background-color: transparent;
|
||||
}
|
||||
</styles>
|
||||
</template>
|
||||
</overlay>
|
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>
|
@ -22,11 +22,13 @@ require_once realpath(__DIR__.'/../WidgetBaseTest.php');
|
||||
|
||||
use EGroupware\Api\Etemplate;
|
||||
|
||||
class EntryTest extends \EGroupware\Api\Etemplate\WidgetBaseTest {
|
||||
class ContactEntryTest extends \EGroupware\Api\Etemplate\WidgetBaseTest
|
||||
{
|
||||
|
||||
const TEST_TEMPLATE = 'api.entry_test_contact';
|
||||
|
||||
public static function setUpBeforeClass() : void {
|
||||
public static function setUpBeforeClass() : void
|
||||
{
|
||||
parent::setUpBeforeClass();
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
namespace EGroupware\Api\Storage;
|
||||
|
||||
require_once __DIR__ . '/../LoggedInTest.php';
|
||||
use EGroupware\Api\LoggedInTest as LoggedInTest;
|
||||
|
||||
class CustomfieldsTest extends LoggedInTest
|
||||
@ -20,19 +21,19 @@ class CustomfieldsTest extends LoggedInTest
|
||||
protected $customfields = null;
|
||||
|
||||
protected $simple_field = array(
|
||||
'app' => self::APP,
|
||||
'name' => 'test_field',
|
||||
'label' => 'Custom field',
|
||||
'type' => 'text',
|
||||
'type2' => array(),
|
||||
'help' => 'Custom field created for automated testing by CustomfieldsTest',
|
||||
'values' => null,
|
||||
'len' => null,
|
||||
'rows' => null,
|
||||
'order' => null,
|
||||
'needed' => null,
|
||||
'private' => array()
|
||||
);
|
||||
'app' => self::APP,
|
||||
'name' => 'test_field',
|
||||
'label' => 'Custom field',
|
||||
'type' => 'text',
|
||||
'type2' => array(),
|
||||
'help' => 'Custom field created for automated testing by CustomfieldsTest',
|
||||
'values' => null,
|
||||
'len' => null,
|
||||
'rows' => null,
|
||||
'order' => null,
|
||||
'needed' => null,
|
||||
'private' => array()
|
||||
);
|
||||
|
||||
public function tearDown(): void
|
||||
{
|
||||
@ -209,35 +210,35 @@ class CustomfieldsTest extends LoggedInTest
|
||||
// Expected options, file
|
||||
return array(
|
||||
array(array(
|
||||
'' => 'Select',
|
||||
'Α'=> 'α Alpha',
|
||||
'Β'=> 'β Beta',
|
||||
'Γ'=> 'γ Gamma',
|
||||
'Δ'=> 'δ Delta',
|
||||
'Ε'=> 'ε Epsilon',
|
||||
'Ζ'=> 'ζ Zeta',
|
||||
'Η'=> 'η Eta',
|
||||
'Θ'=> 'θ Theta',
|
||||
'Ι'=> 'ι Iota',
|
||||
'Κ'=> 'κ Kappa',
|
||||
'Λ'=> 'λ Lambda',
|
||||
'Μ'=> 'μ Mu',
|
||||
'Ν'=> 'ν Nu',
|
||||
'Ξ'=> 'ξ Xi',
|
||||
'Ο'=> 'ο Omicron',
|
||||
'Π'=> 'π Pi',
|
||||
'Ρ'=> 'ρ Rho',
|
||||
'Σ'=> 'σ Sigma',
|
||||
'Τ'=> 'τ Tau',
|
||||
'Υ'=> 'υ Upsilon',
|
||||
'Φ'=> 'φ Phi',
|
||||
'Χ'=> 'χ Chi',
|
||||
'Ψ'=> 'ψ Psi',
|
||||
'Ω'=> 'ω Omega'
|
||||
), 'greek_options.php'),
|
||||
'' => 'Select',
|
||||
'Α' => 'α Alpha',
|
||||
'Β' => 'β Beta',
|
||||
'Γ' => 'γ Gamma',
|
||||
'Δ' => 'δ Delta',
|
||||
'Ε' => 'ε Epsilon',
|
||||
'Ζ' => 'ζ Zeta',
|
||||
'Η' => 'η Eta',
|
||||
'Θ' => 'θ Theta',
|
||||
'Ι' => 'ι Iota',
|
||||
'Κ' => 'κ Kappa',
|
||||
'Λ' => 'λ Lambda',
|
||||
'Μ' => 'μ Mu',
|
||||
'Ν' => 'ν Nu',
|
||||
'Ξ' => 'ξ Xi',
|
||||
'Ο' => 'ο Omicron',
|
||||
'Π' => 'π Pi',
|
||||
'Ρ' => 'ρ Rho',
|
||||
'Σ' => 'σ Sigma',
|
||||
'Τ' => 'τ Tau',
|
||||
'Υ' => 'υ Upsilon',
|
||||
'Φ' => 'φ Phi',
|
||||
'Χ' => 'χ Chi',
|
||||
'Ψ' => 'ψ Psi',
|
||||
'Ω' => 'ω Omega'
|
||||
), 'greek_options.php'),
|
||||
array(array(
|
||||
'View Subs' => "egw_open('','infolog','list',{action:'sp',action_id:widget.getRoot().getArrayMgr('content').getEntry('info_id')},'infolog','infolog');"
|
||||
), 'infolog_subs_option.php')
|
||||
'View Subs' => "egw_open('','infolog','list',{action:'sp',action_id:widget.getRoot().getArrayMgr('content').getEntry('info_id')},'infolog','infolog');"
|
||||
), 'infolog_subs_option.php')
|
||||
);
|
||||
}
|
||||
|
||||
@ -302,8 +303,8 @@ class CustomfieldsTest extends LoggedInTest
|
||||
protected function get_another_user()
|
||||
{
|
||||
$accounts = $GLOBALS['egw']->accounts->search(array(
|
||||
'type' => 'accounts'
|
||||
));
|
||||
'type' => 'accounts'
|
||||
));
|
||||
unset($accounts[$GLOBALS['egw_info']['user']['account_id']]);
|
||||
if(count($accounts) == 0)
|
||||
{
|
||||
|
@ -14,8 +14,10 @@
|
||||
namespace EGroupware\Api\Storage;
|
||||
|
||||
|
||||
require_once __DIR__ . '/../../src/Storage/Tracking.php';
|
||||
|
||||
class TestTracking extends Tracking {
|
||||
class TestTracking extends Tracking
|
||||
{
|
||||
|
||||
var $app = 'test';
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
namespace EGroupware\Api\Storage;
|
||||
|
||||
require_once __DIR__ . '/../LoggedInTest.php';
|
||||
require_once __DIR__ . '/TestTracking.php';
|
||||
|
||||
use EGroupware\Api;
|
||||
@ -22,18 +23,18 @@ class TrackingTest extends LoggedInTest
|
||||
const APP = 'test';
|
||||
|
||||
protected $simple_field = array(
|
||||
'app' => self::APP,
|
||||
'name' => 'test_field',
|
||||
'label' => 'Custom field',
|
||||
'type' => 'text',
|
||||
'type2' => array(),
|
||||
'help' => 'Custom field created for automated testing by CustomfieldsTest',
|
||||
'values' => null,
|
||||
'len' => null,
|
||||
'rows' => null,
|
||||
'order' => null,
|
||||
'needed' => null,
|
||||
'private' => array()
|
||||
'app' => self::APP,
|
||||
'name' => 'test_field',
|
||||
'label' => 'Custom field',
|
||||
'type' => 'text',
|
||||
'type2' => array(),
|
||||
'help' => 'Custom field created for automated testing by CustomfieldsTest',
|
||||
'values' => null,
|
||||
'len' => null,
|
||||
'rows' => null,
|
||||
'order' => null,
|
||||
'needed' => null,
|
||||
'private' => array()
|
||||
);
|
||||
|
||||
/**
|
||||
@ -57,8 +58,8 @@ class TrackingTest extends LoggedInTest
|
||||
|
||||
// Get another user
|
||||
$accounts = $GLOBALS['egw']->accounts->search(array(
|
||||
'type' => 'accounts'
|
||||
));
|
||||
'type' => 'accounts'
|
||||
));
|
||||
unset($accounts[$GLOBALS['egw_info']['user']['account_id']]);
|
||||
if(count($accounts) == 0)
|
||||
{
|
||||
|
@ -296,15 +296,18 @@ class SharingBase extends LoggedInTest
|
||||
*/
|
||||
protected function mountVersioned($path)
|
||||
{
|
||||
if (!class_exists('EGroupware\Stylite\Vfs\Versioning\StreamWrapper'))
|
||||
if(!class_exists('EGroupware\Stylite\Vfs\Versioning\StreamWrapper'))
|
||||
{
|
||||
$this->markTestSkipped("No versioning available");
|
||||
}
|
||||
if(substr($path, -1) == '/') $path = substr($path, 0, -1);
|
||||
if(substr($path, -1) == '/')
|
||||
{
|
||||
$path = substr($path, 0, -1);
|
||||
}
|
||||
$backup = Vfs::$is_root;
|
||||
Vfs::$is_root = true;
|
||||
$url = Versioning\StreamWrapper::PREFIX.$path;
|
||||
$this->assertTrue(Vfs::mount($url,$path), "Unable to mount $path as versioned");
|
||||
$url = Versioning\StreamWrapper::PREFIX . $path;
|
||||
$this->assertTrue(Vfs::mount($url, $path, false), "Unable to mount $path as versioned");
|
||||
Vfs::$is_root = $backup;
|
||||
|
||||
$this->mounts[] = $path;
|
||||
@ -362,8 +365,8 @@ class SharingBase extends LoggedInTest
|
||||
Vfs::chmod($path, 0750);
|
||||
Vfs::chown($path, $GLOBALS['egw_info']['user']['account_id']);
|
||||
|
||||
$url = \EGroupware\Stylite\Vfs\Merge\StreamWrapper::SCHEME.'://default'.$path.'?merge=' . realpath(__DIR__ . '/../fixtures/Vfs/filesystem_mount');
|
||||
$this->assertTrue(Vfs::mount($url,$path), "Unable to mount $url to $path");
|
||||
$url = \EGroupware\Stylite\Vfs\Merge\StreamWrapper::SCHEME . '://default' . $path . '?merge=' . realpath(__DIR__ . '/../fixtures/Vfs/filesystem_mount');
|
||||
$this->assertTrue(Vfs::mount($url, $path, false), "Unable to mount $url to $path");
|
||||
Vfs::$is_root = $backup;
|
||||
|
||||
$this->mounts[] = $path;
|
||||
|
@ -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(
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -161,52 +161,29 @@ class filemanager_hooks
|
||||
'forced' => 'yes',
|
||||
),
|
||||
'showusers' => array(
|
||||
'type' => 'select',
|
||||
'name' => 'showusers',
|
||||
'values' => $yes_no,
|
||||
'label' => lang('Show link "%1" in side box menu?',lang('Users and groups')),
|
||||
'xmlrpc' => True,
|
||||
'admin' => False,
|
||||
'forced' => 'yes',
|
||||
'type' => 'select',
|
||||
'name' => 'showusers',
|
||||
'values' => $yes_no,
|
||||
'label' => lang('Show link "%1" in side box menu?', lang('Users and groups')),
|
||||
'xmlrpc' => True,
|
||||
'admin' => False,
|
||||
'forced' => 'yes',
|
||||
),
|
||||
);
|
||||
|
||||
$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;
|
||||
}
|
||||
}
|
||||
|
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>
|
@ -458,31 +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',
|
||||
);
|
||||
$merge = new infolog_merge();
|
||||
$settings += $merge->merge_preferences();
|
||||
}
|
||||
|
||||
if ($GLOBALS['egw_info']['user']['apps']['calendar'])
|
||||
|
@ -64,23 +64,42 @@ class infolog_merge extends Api\Storage\Merge
|
||||
* @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->infolog_replacements($id, '', $content)))
|
||||
if(!($replacements = $this->infolog_replacements($id, '', $content)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return $replacements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override contact filename placeholder to use info_contact
|
||||
*
|
||||
* @param $document
|
||||
* @param $ids
|
||||
* @return array|void
|
||||
*/
|
||||
public function get_filename_placeholders($document, $ids)
|
||||
{
|
||||
$placeholders = parent::get_filename_placeholders($document, $ids);
|
||||
if(count($ids) == 1 && ($info = $this->bo->read($ids[0])))
|
||||
{
|
||||
$placeholders['$$contact_title$$'] = $info['info_contact']['title'] ??
|
||||
(is_array($info['info_contact']) && Link::title($info['info_contact']['app'], $info['info_contact']['id'])) ??
|
||||
'';
|
||||
}
|
||||
return $placeholders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get infolog replacements
|
||||
*
|
||||
* @param int $id id of entry
|
||||
* @param string $prefix='' prefix like eg. 'erole'
|
||||
* @param string $prefix ='' prefix like eg. 'erole'
|
||||
* @return array|boolean
|
||||
*/
|
||||
public function infolog_replacements($id,$prefix='', &$content = '')
|
||||
public function infolog_replacements($id, $prefix = '', &$content = '')
|
||||
{
|
||||
$record = new infolog_egw_record($id);
|
||||
$info = array();
|
||||
@ -179,84 +198,61 @@ class infolog_merge extends Api\Storage\Merge
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate table with replacements for the Api\Preferences
|
||||
*
|
||||
*/
|
||||
public function show_replacements()
|
||||
public function get_placeholder_list($prefix = '')
|
||||
{
|
||||
$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'));
|
||||
$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 = 'infolog';
|
||||
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
|
||||
if(in_array($name, array('custom')))
|
||||
{
|
||||
echo "</tr>\n";
|
||||
$n++;
|
||||
// dont show them
|
||||
continue;
|
||||
}
|
||||
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
|
||||
$marker = $this->prefix($prefix, $name, '{');
|
||||
if(!array_filter($placeholders, function ($a) use ($marker)
|
||||
{
|
||||
echo "</tr>\n";
|
||||
$i++;
|
||||
return array_key_exists($marker, $a);
|
||||
}))
|
||||
{
|
||||
$placeholders[$group][] = [
|
||||
'value' => $marker,
|
||||
'label' => $label
|
||||
];
|
||||
}
|
||||
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"><h3>'.lang('Custom fields').":</h3></td></tr>";
|
||||
foreach($this->contacts->customfields as $name => $field)
|
||||
// Don't add any linked placeholders if we're not at the top level
|
||||
// This avoids potential recursion
|
||||
if(!$prefix)
|
||||
{
|
||||
echo '<tr><td>{{info_contact/#'.$name.'}}</td><td colspan="3">'.$field['label']."</td></tr>\n";
|
||||
}
|
||||
// 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);
|
||||
|
||||
echo '<tr><td colspan="4"><h3>'.lang('General fields:')."</h3></td></tr>";
|
||||
foreach($this->get_common_replacements() as $name => $label)
|
||||
// Add parent placeholders
|
||||
$this->add_linked_placeholders(
|
||||
$placeholders,
|
||||
lang('parent'),
|
||||
$this->get_placeholder_list(($prefix ? $prefix . '/' : '') . 'info_id_parent')
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
echo '<tr><td>{{'.$name.'}}</td><td colspan="3">'.$label."</td></tr>\n";
|
||||
unset($placeholders[lang('parent')]);
|
||||
unset($placeholders[lang($tracking->field2label['info_from'])]);
|
||||
}
|
||||
|
||||
echo "</table>\n";
|
||||
|
||||
echo $GLOBALS['egw']->framework->footer();
|
||||
return $placeholders;
|
||||
}
|
||||
}
|
||||
|
@ -178,31 +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',
|
||||
);
|
||||
$merge = new timesheet_merge();
|
||||
$settings += $merge->merge_preferences();
|
||||
}
|
||||
|
||||
return $settings;
|
||||
|
@ -157,64 +157,51 @@ class timesheet_merge extends Api\Storage\Merge
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate table with replacements for the Api\Preferences
|
||||
* Get a list of placeholders provided.
|
||||
*
|
||||
* Placeholders are grouped logically. Group key should have a user-friendly translation.
|
||||
*/
|
||||
public function show_replacements()
|
||||
public function get_placeholder_list($prefix = '')
|
||||
{
|
||||
$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();
|
||||
$placeholders = array(
|
||||
'timesheet' => [],
|
||||
lang('Project') => []
|
||||
) + parent::get_placeholder_list($prefix);
|
||||
|
||||
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'),
|
||||
);
|
||||
'ts_total' => lang('total'),
|
||||
'ts_created' => lang('Created'),
|
||||
'ts_modified' => lang('Modified'),
|
||||
);
|
||||
$group = 'timesheet';
|
||||
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
|
||||
if(in_array($name, array('custom')))
|
||||
{
|
||||
echo "</tr>\n";
|
||||
$n++;
|
||||
// dont show them
|
||||
continue;
|
||||
}
|
||||
$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
|
||||
];
|
||||
}
|
||||
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)
|
||||
// Don't add any linked placeholders if we're not at the top level
|
||||
// This avoids potential recursion
|
||||
if(!$prefix)
|
||||
{
|
||||
echo '<tr><td>{{#'.$name.'}}</td><td colspan="3">'.$field['label']."</td></tr>\n";
|
||||
// Add project placeholders
|
||||
$pm_merge = new projectmanager_merge();
|
||||
$this->add_linked_placeholders($placeholders, lang('Project'), $pm_merge->get_placeholder_list('ts_project'));
|
||||
}
|
||||
|
||||
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();
|
||||
return $placeholders;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user