fix many errors around et2-select emulating old taglist:

- preprocessor: translate attributes autocomplete_url -> searchUrl, autocomplete_params -> searchOptions, and allow options
- sending search query as URL/GET parameter with default of app: <appname>
- handle searchUrl like allowFreeEntries by adding selected result to select_options via createFreeEntries, as they otherwise get removed by fix_bad_value not finding the value in select_options
- change taglist validation (again) to not validate search values
- still requires changes in application code, as taglist always behaved like multiple=true (returning and expecting an array of values) and did automatically search from client-side for it's initial value(s)
--> maybe more changes are in order to NOT require changing application code
--> fixes editing Sieve rules
This commit is contained in:
ralf 2022-08-08 17:27:21 +02:00
parent b0d1d82736
commit e7eb9f42e3
6 changed files with 49 additions and 27 deletions

View File

@ -199,7 +199,7 @@ function send_template()
}, $str);
// handling of select and taglist widget, incl. removing of type attribute
$str = preg_replace_callback('#<(select|taglist|listbox)(-[^ ]+)? ([^>]+?)(/|>(.*?)</select)>#s', static function (array $matches)
$str = preg_replace_callback('#<(select|taglist|listbox)(-[^ ]+)? ([^>]+?)(/|>(.*?)</(select|taglist|listbox))>#s', static function (array $matches)
{
$attrs = parseAttrs($matches[3]);
@ -209,16 +209,31 @@ function send_template()
$attrs['multiple'] = 'true';
unset($attrs['tags']);
}
// taglist had allowFreeEntries and enableEditMode with a default of true, while et2-select has it with a default of false
if($matches['1'] === 'taglist' && !$matches[2])
// converting taglist to et2-select
if($matches['1'] === 'taglist')
{
if(!isset($attrs['allowFreeEntries']))
// taglist had allowFreeEntries and enableEditMode with a default of true, while et2-select has it with a default of false
if(!$matches[2] && !isset($attrs['allowFreeEntries']) && (empty($matches[5]) || !preg_match('#</?option(\s[^>]+|/)>#', $matches[5])))
{
$attrs['allowFreeEntries'] = 'true';
if(!isset($attrs['editModeEnabled']))
{
$attrs['editModeEnabled'] = 'true';
}
}
if(!isset($attrs['editModeEnabled']))
if (isset($attrs['autocomplete_url']))
{
$attrs['editModeEnabled'] = 'true';
$attrs['searchUrl'] = $attrs['autocomplete_url'];
if (isset($attrs['autocomplete_params']))
{
$attrs['searchOptions'] = $attrs['autocomplete_params'];
}
unset($attrs['autocomplete_url'], $attrs['autocomplete_params']);
}
if (isset($attrs['maxSelection']) && $attrs['maxSelection'] === '1')
{
unset($attrs['multiple'], $attrs['maxSelection']);
}
}
// no multiple="toggle" or expand_multiple_rows="N" currently, thought Shoelace's select multiple="true" is relative close

View File

@ -235,7 +235,13 @@ export function cleanSelectOptions(options : SelectOption[] | string[] | object)
// make sure value is a string, and label not an object with sub-options
options.forEach(option =>
{
if (typeof option.value !== 'string')
// old taglist used id, instead of value
if (typeof option.value === 'undefined' && typeof option.id !== 'undefined')
{
option.value = option.id;
delete option.id;
}
if (typeof option.value === 'number')
{
option.value = option.value.toString();
}

View File

@ -239,7 +239,7 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
this.search = false;
this.searchUrl = "";
this.searchOptions = {};
this.searchOptions = {app: "addressbook"};
this.allowFreeEntries = false;
this.editModeEnabled = false;
@ -402,13 +402,13 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
{
super.value = new_value;
if(!new_value || !this.allowFreeEntries)
if(!new_value || !this.allowFreeEntries && !this.searchUrl)
{
return;
}
// Overridden to add options if allowFreeEntries=true
if(typeof this.value == "string" && !this._menuItems.find(o => o.value == this.value))
if(typeof this.value == "string" && !this._menuItems.find(o => o.value == this.value && !o.classList.contains('remote')))
{
this.createFreeEntry(this.value);
}
@ -416,7 +416,7 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
{
this.value.forEach((e) =>
{
if(!this._menuItems.find(o => o.value == e))
if(!this._menuItems.find(o => o.value == e && !o.classList.contains('remote')))
{
this.createFreeEntry(e);
}
@ -696,7 +696,9 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
/**
* Actually query the server.
*
* This can be overridden to change request parameters
* This can be overridden to change request parameters or eg. send them as POST parameters.
*
* Default implementation here sends options as (additional) GET paramters plus search string as $_GET['query']!
*
* @param {string} search
* @param {object} options
@ -705,7 +707,7 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
*/
protected remoteQuery(search : string, options : object)
{
return this.egw().request(this.searchUrl, [search, options]).then((result) =>
return this.egw().request(this.egw().link(this.egw().ajaxUrl(this.searchUrl), {query: search, ...options})).then((result) =>
{
this.processRemoteResults(result);
});

View File

@ -167,7 +167,7 @@ class Taglist extends Etemplate\Widget
}
continue;
}
if(count($allowed) && !$this->attrs['allowFreeEntries'] && !array_key_exists($val,$allowed))
if(count($allowed) && !$this->attrs['allowFreeEntries'] && empty($this->attrs['autocomplete_url']) && !array_key_exists($val,$allowed))
{
self::set_validation_error($form_name,lang("'%1' is NOT allowed ('%2')!",$val,implode("','",array_keys($allowed))),'');
unset($value[$key]);

View File

@ -230,7 +230,7 @@ class mail_sieve
{
//Instantiate an eTemplate object, representing sieve.edit template
$etmpl = new Etemplate('mail.sieve.edit');
$etmpl->setElementAttribute('action_folder_text','autocomplete_params', array('noPrefixId'=> true));
$etmpl->setElementAttribute('action_folder_text','searchOptions', array('noPrefixId'=> true));
if (!is_array($content))
{
if ( $this->getRules($_GET['ruleID']) && isset($_GET['ruleID']))
@ -242,7 +242,7 @@ class mail_sieve
switch ($rules['action'])
{
case 'folder':
$content['action_folder_text'][] = $rules['action_arg'];
$content['action_folder_text'] = $rules['action_arg'];
break;
case 'address':
@ -297,7 +297,7 @@ class mail_sieve
switch ($content['action'])
{
case 'folder':
$newRule['action_arg'] = implode($content['action_folder_text']);
$newRule['action_arg'] = $content['action_folder_text'];
break;
case 'address':
$content['action_address_text'] = self::strip_rfc882_addresses($content['action_address_text']);
@ -398,8 +398,10 @@ class mail_sieve
$content['no_forward'] = $this->account->acc_smtp_type !== Api\Mail\Smtp::class && !$this->account->acc_user_forward;
//Set the preselect_options for mail/folders as we are not allow free entry for folder taglist
$sel_options['action_folder_text'] = $this->ajax_getFolders(0,true,null,true);
if (!empty($content['action_folder_text']))
{
$sel_options['action_folder_text'] = [$content['action_folder_text'] => $content['action_folder_text']];
}
return $etmpl->exec('mail.mail_sieve.edit',$content,$sel_options,$readonlys,array(),2);
}
@ -1299,5 +1301,4 @@ class mail_sieve
if ($_REQUEST['noPrefixId']) $_noPrefixId = $_REQUEST['noPrefixId'];
$mailCompose->ajax_searchFolder($_searchStringLength, $_returnList, $_mailaccountToSearch, $_noPrefixId);
}
}
}

View File

@ -149,16 +149,14 @@
<checkbox label="Use regular expressions (see wikipedia for information on POSIX regular expressions)" id="regexp"/>
</row>
<row class="dialogFooterToolbar">
<hbox>
<hbox width="100%">
<button statustext="Saves this rule" label="Save" id="button[save]"/>
<button statustext="Applies the changes made" label="Apply" id="button[apply]"/>
<button label="Cancel" id="button[cancel]"/>
<hbox align="right">
<button label="Delete" id="button[delete]" />
</hbox>
<button label="Cancel" id="button[cancel]" onclick="window.close()"/>
<button label="Delete" id="button[delete]" align="right"/>
</hbox>
</row>
</rows>
</grid>
</template>
</overlay>
</overlay>