From 9e2bb05deda2ac9d64dc25d3a89bbdb700d0b95b Mon Sep 17 00:00:00 2001 From: nathan Date: Tue, 12 Oct 2021 14:18:18 -0600 Subject: [PATCH] Placeholders list common UI Should reduce maintenance by using a common UI to show placeholders based on the placeholder list --- api/src/Contacts/Merge.php | 161 ++++++------------ api/src/Storage/Merge.php | 105 ++++++++++-- api/templates/default/show_replacements.xet | 65 +++++++ .../inc/class.filemanager_merge.inc.php | 71 +------- .../templates/default/replacements.xet | 12 ++ infolog/inc/class.infolog_merge.inc.php | 115 +++---------- timesheet/inc/class.timesheet_merge.inc.php | 74 +------- 7 files changed, 253 insertions(+), 350 deletions(-) create mode 100644 api/templates/default/show_replacements.xet create mode 100644 filemanager/templates/default/replacements.xet diff --git a/api/src/Contacts/Merge.php b/api/src/Contacts/Merge.php index 4dc6d5a47c..169e233abc 100644 --- a/api/src/Contacts/Merge.php +++ b/api/src/Contacts/Merge.php @@ -155,119 +155,6 @@ class Merge extends Api\Storage\Merge return $replacements; } - /** - * Generate table with replacements for the preferences - * - */ - public function show_replacements() - { - $GLOBALS['egw_info']['flags']['app_header'] = lang('Addressbook').' - '.lang('Replacements for inserting contacts into documents'); - $GLOBALS['egw_info']['flags']['nonavbar'] = (bool)$_GET['nonavbar']; - - ob_start(); - echo "\n"; - echo '"; - - $n = 0; - foreach($this->contacts->contact_fields as $name => $label) - { - if (in_array($name,array('tid','label','geo'))) continue; // dont show them, as they are not used in the UI atm. - - if (in_array($name,array('email','org_name','tel_work','url')) && $n&1) // main values, which should be in the first column - { - echo "\n"; - $n++; - } - if (!($n&1)) echo ''; - echo ''; - if($name == 'cat_id') - { - if ($n&1) echo "\n"; - echo ''; - $n++; - } - if ($n&1) echo "\n"; - $n++; - } - - echo '"; - foreach($this->contacts->customfields as $name => $field) - { - echo '\n"; - } - - echo '"; - 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 '\n"; - } - - echo '"; - echo '\n"; - - Api\Translation::add_app('calendar'); - echo '"; - 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 "\n"; - $n++; - } - if(!($n & 1)) - { - echo ''; - } - echo ''; - if($n & 1) - { - echo "\n"; - } - $n++; - } - echo "

'.lang('Contact fields:')."

{{'.$name.'}}'.$label.'
{{categories}}'.lang('Category path').'

'.lang('Custom fields').":

{{#'.$name.'}}'.$field['label']."

'.lang('General fields:')."

{{'.$name.'}}'.$label."

'.lang('EPL Only').":

{{share}}'.lang('Public sharing URL')."

'.lang('Calendar fields:')." # = 1, 2, ..., 20, -1

{{calendar/#/' . $name . '}}' . $label . '
\n"; - - $GLOBALS['egw']->framework->render(ob_get_clean()); - } - /** * Get a list of placeholders provided. * @@ -352,10 +239,58 @@ class Merge extends Api\Storage\Merge 'label' => "Formatted private address" ]; + $placeholders['EPL only'][] = [ + 'value' => $this->prefix($prefix, 'share', '{'), + 'label' => 'Public sharing URL' + ]; + $this->add_customfield_placeholders($placeholders, $prefix); + + // Don't add any linked placeholders if we're not at the top level + // This avoids potential recursion + if(!$prefix) + { + $this->add_calendar_placeholders($placeholders, $prefix); + } + return $placeholders; } + protected function add_calendar_placeholders(&$placeholders, $prefix) + { + Api\Translation::add_app('calendar'); + + // NB: The -1 is actually ‑1, a non-breaking hyphen to avoid UI issues where we split on - + $group = lang('Calendar fields:') . " # = 1, 2, ..., 20, ‑1"; + foreach(array( + 'title' => lang('Title'), + 'description' => lang('Description'), + 'participants' => lang('Participants'), + 'location' => lang('Location'), + 'start' => lang('Start') . ': ' . lang('Date') . '+' . lang('Time'), + 'startday' => lang('Start') . ': ' . lang('Weekday'), + 'startdate' => lang('Start') . ': ' . lang('Date'), + 'starttime' => lang('Start') . ': ' . lang('Time'), + 'end' => lang('End') . ': ' . lang('Date') . '+' . lang('Time'), + 'endday' => lang('End') . ': ' . lang('Weekday'), + 'enddate' => lang('End') . ': ' . lang('Date'), + 'endtime' => lang('End') . ': ' . lang('Time'), + 'duration' => lang('Duration'), + 'category' => lang('Category'), + 'priority' => lang('Priority'), + 'updated' => lang('Updated'), + 'recur_type' => lang('Repetition'), + 'access' => lang('Access') . ': ' . lang('public') . ', ' . lang('private'), + 'owner' => lang('Owner'), + ) as $name => $label) + { + $placeholders[$group][] = array( + 'value' => $this->prefix(($prefix ? $prefix . '/' : '') . 'calendar/#', $name, '{'), + 'label' => $label + ); + } + } + /** * Get insert-in-document action with optional default document on top * diff --git a/api/src/Storage/Merge.php b/api/src/Storage/Merge.php index 3dd5924ac4..9e4e5e00a2 100644 --- a/api/src/Storage/Merge.php +++ b/api/src/Storage/Merge.php @@ -13,6 +13,7 @@ namespace EGroupware\Api\Storage; +use ceLTIc\LTI\Service\Result; use DOMDocument; use EGroupware\Api; use EGroupware\Api\Vfs; @@ -433,11 +434,11 @@ abstract class Merge * * Uses egw_link system to get link titles * - * @param app Name of current app - * @param id ID of current entry - * @param only_app Restrict links to only given application - * @param exclude Exclude links to these applications - * @param style String One of: + * @param string app Name of current app + * @param string id ID of current entry + * @param string only_app Restrict links to only given application + * @param string[] exclude Exclude links to these applications + * @param string style One of: * 'title' - plain text, just the title of the link * 'link' - URL to the entry * 'href' - HREF tag wrapped around the title @@ -1785,7 +1786,7 @@ abstract class Merge /** * Get the replacements for any entry specified by app & id * - * @param stribg $app + * @param string $app * @param string $id * @param string $content * @return array @@ -1846,10 +1847,10 @@ abstract class Merge /** * Process special flags, such as IF or NELF * - * @param content Text to be examined and changed - * @param replacements array of markers => replacement + * @param string content Text to be examined and changed + * @param array replacements array of markers => replacement * - * @return changed content + * @return string changed content */ private function process_commands($content, $replacements) { @@ -2031,7 +2032,7 @@ abstract class Merge * @param array $ids array with contact id(s) * @param string $name ='' name to use for downloaded document * @param string $dirs comma or whitespace separated directories, used if $document is a relative path - * @param Array $header File name, mime & filesize if you want to send a header + * @param array $header File name, mime & filesize if you want to send a header * * @return string with error-message on error * @throws Api\Exception @@ -2546,8 +2547,8 @@ abstract class Merge * back to the client like the other documents. Merging for a single selected * contact opens a compose window, multiple contacts just sends. * - * @param Array &$action Action to be modified for mail - * @param Array $file Array of information about the document from Api\Vfs::find + * @param array &$action Action to be modified for mail + * @param array $file Array of information about the document from Api\Vfs::find * @return void */ private static function document_mail_action(array &$action, $file) @@ -2580,8 +2581,8 @@ abstract class Merge * Set up a document action so the generated file is saved and opened in * the collabora editor (if collabora is available) * - * @param Array &$action Action to be modified for editor - * @param Array $file Array of information about the document from Api\Vfs::find + * @param array &$action Action to be modified for editor + * @param array $file Array of information about the document from Api\Vfs::find * @return void */ private static function document_editable_action(array &$action, $file) @@ -3198,4 +3199,80 @@ abstract class Merge return $settings; } + + /** + * Show replacement placeholders for the app + * + * Generates a page that shows all the available placeholders for this appliction. By default, + * we have all placeholders generated in get_placeholder_list() (including any custom fields) + * as well as the common and current user placeholders. + * + * By overridding show_replacements_hook(), extending classes can override without having to + * re-implement everything. + */ + public function show_replacements() + { + + $template_name = 'api.show_replacements'; + $content = $sel_options = $readonlys = $preserve = array(); + + $content['appname'] = $this->get_app(); + $content['placeholders'] = $this->remap_replacement_list($this->get_placeholder_list()); + $content['extra'] = array(); + $content['common'] = $this->remap_replacement_list($this->get_common_placeholder_list()); + $content['user'] = $this->remap_replacement_list($this->get_user_placeholder_list()); + + $this->show_replacements_hook($template_name, $content, $sel_options, $readonlys); + $etemplate = new Api\Etemplate($template_name); + + $etemplate->exec('filemanager.filemanager_ui.file', $content, $sel_options, $readonlys, $preserve, 2); + } + + /** + * Helper function for show_replacements() to change the output of get_placeholder_list() into somethig + * more suited for etemplate repeating rows. + * + * @param $list + * @return array + */ + protected function remap_replacement_list($list, $title_prefix = '') + { + $new_list = []; + foreach($list as $group_title => $group_placeholders) + { + if(is_array($group_placeholders) && !array_key_exists('0', $group_placeholders)) + { + // Limit how far we go through linked entries + if($title_prefix) + { + continue; + } + $new_list = array_merge($new_list, $this->remap_replacement_list($group_placeholders, $group_title)); + } + else + { + $new_list[] = [ + 'title' => ($title_prefix ? $title_prefix . ': ' : '') . $group_title, + 'placeholders' => $group_placeholders + ]; + } + } + return $new_list; + } + + /** + * Hook for extending apps to customise the replacements UI without having to override the whole method. + * + * This can include detailed descriptions or instructions, documentation of tables and custom stuff + * Set $content['extra_template'] to a template ID with extra descriptions or instructions and it will be + * added into the main template. + * + * @param string $template_name + * @param $content + * @param $sel_options + * @param $readonlys + */ + protected function show_replacements_hook(&$template_name, &$content, &$sel_options, &$readonlys) + { + } } diff --git a/api/templates/default/show_replacements.xet b/api/templates/default/show_replacements.xet new file mode 100644 index 0000000000..576563f3d8 --- /dev/null +++ b/api/templates/default/show_replacements.xet @@ -0,0 +1,65 @@ + + + + + +