diff --git a/api/js/etemplate/Et2Select/Select/Et2SelectApp.ts b/api/js/etemplate/Et2Select/Select/Et2SelectApp.ts index c748c6edad..87e199f6ff 100644 --- a/api/js/etemplate/Et2Select/Select/Et2SelectApp.ts +++ b/api/js/etemplate/Et2Select/Select/Et2SelectApp.ts @@ -1,13 +1,20 @@ import {Et2Select} from "../Et2Select"; import {Et2StaticSelectMixin, StaticOptions as so} from "../StaticOptions"; import {cleanSelectOptions} from "../FindSelectOptions"; +import {property} from "lit/decorators/property.js"; export class Et2SelectApp extends Et2StaticSelectMixin(Et2Select) { + /** + * Which apps to show: 'user'=apps of current user, 'enabled', 'installed' (default), 'all' = not installed ones too, 'all+setup' + */ + @property({type: String}) + apps : 'user' | 'enabled' | 'installed' | 'all' | 'all+setup' = 'installed'; + public connectedCallback() { super.connectedCallback() - this.fetchComplete = so.app(this, {}).then((options) => + this.fetchComplete = so.app(this, {apps: this.apps}).then((options) => { this.set_static_options(cleanSelectOptions(options)); }) diff --git a/api/js/etemplate/Et2Select/Select/Et2SelectReadonly.ts b/api/js/etemplate/Et2Select/Select/Et2SelectReadonly.ts index d46f7a6ec1..16fd244bbf 100644 --- a/api/js/etemplate/Et2Select/Select/Et2SelectReadonly.ts +++ b/api/js/etemplate/Et2Select/Select/Et2SelectReadonly.ts @@ -15,6 +15,7 @@ import {Et2Widget} from "../../Et2Widget/Et2Widget"; import {Et2StaticSelectMixin, StaticOptions, StaticOptions as so} from "../StaticOptions"; import {cleanSelectOptions, find_select_options, SelectOption} from "../FindSelectOptions"; import {SelectAccountMixin} from "../SelectAccountMixin"; +import {property} from "lit/decorators/property.js"; /** * This is a stripped-down read-only widget used in nextmatch @@ -22,6 +23,17 @@ import {SelectAccountMixin} from "../SelectAccountMixin"; */ export class Et2SelectReadonly extends Et2Widget(LitElement) implements et2_IDetachedDOM { + @property({type: String}) + set emptyLabel(_label: string) + { + this.__emptyLabel = _label; + this.select_options = this.__select_options; + } + get emptyLabel() + { + return this.__emptyLabel; + } + static get styles() { return [ @@ -192,6 +204,11 @@ li { return; } this.__select_options = new_options; + + if (this.emptyLabel) + { + this.__select_options.unshift({value: '', label: this.emptyLabel}); + } } /** @@ -241,7 +258,7 @@ li { getDetachedAttributes(attrs) { - attrs.push("id", "value", "class", "statustext"); + attrs.push("id", "value", "class", "statustext", "emptyLabel"); } getDetachedNodes() : HTMLElement[] @@ -277,6 +294,12 @@ customElements.define("et2-select-account_ro", Et2SelectAccountReadonly); export class Et2SelectAppReadonly extends Et2StaticSelectMixin(Et2SelectReadonly) { + /** + * Which apps to show: 'user'=apps of current user, 'enabled', 'installed' (default), 'all' = not installed ones too, 'all+setup' + */ + @property({type: String}) + apps : 'user' | 'enabled' | 'installed' | 'all' | 'all+setup' = 'installed'; + protected find_select_options(_attrs) { this.fetchComplete = so.app(this, _attrs).then((options) => diff --git a/api/js/etemplate/Et2Select/StaticOptions.ts b/api/js/etemplate/Et2Select/StaticOptions.ts index e956dde47c..ab5a40cc39 100644 --- a/api/js/etemplate/Et2Select/StaticOptions.ts +++ b/api/js/etemplate/Et2Select/StaticOptions.ts @@ -375,7 +375,8 @@ export const StaticOptions = new class StaticOptionsType app(widget : Et2SelectWidgets | Et2Select, attrs) : Promise { - var options = ',' + (attrs.other || []).join(','); + const options = widget.apps ? ',,'+widget.apps : widget.options || ''; + return this.cached_server_side(widget, 'select-app', options, true); } diff --git a/api/js/etemplate/et2_extension_nextmatch.ts b/api/js/etemplate/et2_extension_nextmatch.ts index 0de1073379..29933f6faa 100644 --- a/api/js/etemplate/et2_extension_nextmatch.ts +++ b/api/js/etemplate/et2_extension_nextmatch.ts @@ -3746,13 +3746,6 @@ export class et2_nextmatch_header_bar extends et2_DOMWidget implements et2_INext this.egw().debug('warn', 'Nextmatch filter options in a weird place - "%s". Should be in sel_options[%s].', row_id, name); } } - // Legacy: Add in 'All' option for cat_id, if not provided. - if(name == 'cat_id' && (options == null || options != null && (typeof options[''] == 'undefined' && typeof options[0] != 'undefined' && options[0].value != '')) - // Not mail, since it needs to be different - && !['mail'].includes(this.getInstanceManager().app)) - { - widget_options.emptyLabel = this.egw().lang('All categories'); - } // Create widget const select = loadWebComponent(type, widget_options, this); @@ -3807,6 +3800,12 @@ export class et2_nextmatch_header_bar extends et2_DOMWidget implements et2_INext select.updateComplete.then(async() => { await select.updateComplete; + // Legacy: Add in 'All' option for cat_id, if not provided. + if (name == 'cat_id' && !['mail'].includes(this.getInstanceManager().app) && + !select.select_options.filter(options => option.value === '').length) + { + select.emptyLabel = this.egw().lang('All categories'); + } select.requestUpdate("value"); }) return select; diff --git a/api/src/Etemplate/Widget/Select.php b/api/src/Etemplate/Widget/Select.php index 8b115d3357..eaab7e2bbb 100644 --- a/api/src/Etemplate/Widget/Select.php +++ b/api/src/Etemplate/Widget/Select.php @@ -984,8 +984,8 @@ class Select extends Etemplate\Widget * Get available apps as options * * @param string $type2 ='installed[:home;groupdav; ...]' 'user'=apps of current user, - * 'enabled', 'installed' (default), 'all' = not installed ones too. In order to - * exclude apps explicitly we can list them (app name separator is ';') in front of the type. + * 'enabled', 'installed' (default), 'all' = not installed ones too, 'all+setup'. + * In order to exclude apps explicitly we can list them (app name separator is ';') in front of the type. * * @return array app => label pairs sorted by label */ @@ -1007,15 +1007,15 @@ class Select extends Etemplate\Widget $apps[$app] = lang($app); } } - if ($type2 == 'all') + if ($type2 == 'all' || $type2 === 'all+setup') { $dir = opendir(EGW_SERVER_ROOT); - while ($file = readdir($dir)) + while ($app = readdir($dir)) { - if (@is_dir(EGW_SERVER_ROOT."/$file/setup") && $file[0] != '.' && - !isset($apps[$app = basename($file)])) + if (is_dir(EGW_SERVER_ROOT."/$app/setup") && $app[0] != '.' && !isset($apps[$app]) || + $app === 'setup' && $type2 === 'all+setup') { - $apps[$app] = $app . ' (*)'; + $apps[$app] = lang($app); } } closedir($dir);