/** * Replace "^" prefixed tags (eg. ^forename) in a template with values from an array * * @param string $ps_template String with embedded tags. Tags are just alphanumeric strings prefixed with a caret ("^") * @param string $pm_tablename_or_num Table name or number of table from which values are being formatted * @param string $pa_row_ids An array of primary key values in the specified table to be pulled into the template * @param array $pa_options Supported options are: * returnAsArray = if true an array of processed template values is returned, otherwise the template values are returned as a string joined together with a delimiter. Default is false. * delimiter = value to string together template values with when returnAsArray is false. Default is ';' (semicolon) * placeholderPrefix = attribute container to implicitly place primary record fields into. Ex. if the table is "ca_entities" and the placeholder is "address" then tags like ^city will resolve to ca_entities.address.city * requireLinkTags = if set then links are only added when explicitly defined with <l> tags. [Default is true] * primaryIDs = row_ids for primary rows in related table, keyed by table name; when resolving ambiguous relationships the row_ids will be excluded from consideration. This option is rarely used and exists primarily to take care of a single * edge case: you are processing a template relative to a self-relationship such as ca_entities_x_entities that includes references to the subject table (ca_entities, in the case of ca_entities_x_entities). There are * two possible paths to take in this situations; primaryIDs lets you specify which ones you *don't* want to take by row_id. For interstitial editors, the ids will be set to a single id: that of the subject (Eg. ca_entities) row * from which the interstitial was launched. * sort = optional list of tag values to sort repeating values within a row template on. The tag must appear in the template. You can specify more than one tag by separating the tags with semicolons. * sortDirection = The direction of the sort of repeating values within a row template. May be either ASC (ascending) or DESC (descending). [Default is ASC] * linkTarget = Optional target to use when generating <l> tag-based links. By default links point to standard detail pages, but plugins may define linkTargets that point elsewhere. * skipIfExpression = skip the elements in $pa_row_ids for which the given expression does not evaluate true * includeBlankValuesInArray = include blank template values in primary template and all <unit>s in returned array when returnAsArray is set. If you need the returned array of values to line up with the row_ids in $pa_row_ids this should be set. [Default is false] * includeBlankValuesInTopLevelForPrefetch = include blank template values in *primary template* (not <unit>s) in returned array when returnAsArray is set. Used by template prefetcher to ensure returned values align with id indices. [Default is false] * forceValues = Optional array of values indexed by placeholder without caret (eg. ca_objects.idno) and row_id. When present these values will be used in place of the placeholders, rather than whatever value normal processing would result in. [Default is null] * aggregateUnique = Remove duplicate values. If set then array of evaluated templates may not correspond one-to-one with the original list of row_ids set in $pa_row_ids. [Default is false] * * @return mixed Output of processed templates * * TODO: sort and sortDirection are not currently supported! They are ignored for the time being */ public static function process($ps_template, $pm_tablename_or_num, array $pa_row_ids, array $pa_options = null) { // Set up options foreach (array('request', 'template', 'restrict_to_relationship_types', 'restrictToRelationshipTypes', 'excludeRelationshipTypes', 'useLocaleCodes') as $vs_k) { unset($pa_options[$vs_k]); } if (!isset($pa_options['convertCodesToDisplayText'])) { $pa_options['convertCodesToDisplayText'] = true; } $pb_return_as_array = (bool) caGetOption('returnAsArray', $pa_options, false); unset($pa_options['returnAsArray']); if (($pa_sort = caGetOption('sort', $pa_options, null)) && !is_array($pa_sort)) { $pa_sort = explode(";", $pa_sort); } $ps_sort_direction = caGetOption('sortDirection', $pa_options, null, array('forceUppercase' => true)); if (!in_array($ps_sort_direction, array('ASC', 'DESC'))) { $ps_sort_direction = 'ASC'; } $ps_delimiter = caGetOption('delimiter', $pa_options, '; '); $pb_include_blanks = caGetOption('includeBlankValuesInArray', $pa_options, false); $pb_include_blanks_for_prefetch = caGetOption('includeBlankValuesInTopLevelForPrefetch', $pa_options, false); // Bail if no rows or template are set if (!is_array($pa_row_ids) || !sizeof($pa_row_ids) || !$ps_template) { return $pb_return_as_array ? array() : ""; } // Parse template if (!is_array($va_template = DisplayTemplateParser::parse($ps_template, $pa_options))) { return null; } $o_dm = Datamodel::load(); $ps_tablename = is_numeric($pm_tablename_or_num) ? $o_dm->getTableName($pm_tablename_or_num) : $pm_tablename_or_num; $t_instance = $o_dm->getInstanceByTableName($ps_tablename, true); $vs_pk = $t_instance->primaryKey(); // Prefetch related items for <units> if (!$pa_options['isUnit'] && !caGetOption('dontPrefetchRelated', $pa_options, false)) { DisplayTemplateParser::prefetchAllRelatedIDs($va_template['tree']->children, $ps_tablename, $pa_row_ids, $pa_options); } $qr_res = caMakeSearchResult($ps_tablename, $pa_row_ids); if (!$qr_res) { return $pb_return_as_array ? array() : ""; } $pa_check_access = $t_instance->hasField('access') ? caGetOption('checkAccess', $pa_options, null) : null; if (!is_array($pa_check_access) || !sizeof($pa_check_access)) { $pa_check_access = null; } $ps_skip_if_expression = caGetOption('skipIfExpression', $pa_options, false); $va_skip_if_expression_tags = caGetTemplateTags($ps_skip_if_expression); $va_proc_templates = []; while ($qr_res->nextHit()) { // check access if ($pa_check_access && !in_array($qr_res->get("{$ps_tablename}.access"), $pa_check_access)) { continue; } // check if we skip this row because of skipIfExpression if (strlen($ps_skip_if_expression) > 0) { $va_expression_vars = []; foreach ($va_skip_if_expression_tags as $vs_expression_tag) { if (!isset($va_expression_vars[$vs_expression_tag])) { $va_expression_vars[$vs_expression_tag] = $qr_res->get($vs_expression_tag, ['assumeDisplayField' => true, 'returnIdno' => true, 'delimiter' => $ps_delimiter]); } } if (ExpressionParser::evaluate($ps_skip_if_expression, $va_expression_vars)) { continue; } } if ($pa_options['relativeToContainer']) { $va_vals = DisplayTemplateParser::_getValues($qr_res, $va_template['tags'], $pa_options); if (isset($pa_options['sort']) && is_array($pa_options['sort'])) { $va_vals = caSortArrayByKeyInValue($va_vals, array('__sort__'), $pa_options['sortDirection'], array('dontRemoveKeyPrefixes' => true)); } foreach ($va_vals as $vn_index => $va_val_list) { $va_proc_templates[] = is_array($va_val_list) ? DisplayTemplateParser::_processChildren($qr_res, $va_template['tree']->children, $va_val_list, array_merge($pa_options, ['index' => $vn_index, 'returnAsArray' => $pa_options['aggregateUnique']])) : ''; } } else { $va_proc_templates[] = DisplayTemplateParser::_processChildren($qr_res, $va_template['tree']->children, DisplayTemplateParser::_getValues($qr_res, $va_template['tags'], $pa_options), array_merge($pa_options, ['returnAsArray' => $pa_options['aggregateUnique']])); } } if ($pa_options['aggregateUnique']) { $va_acc = []; foreach ($va_proc_templates as $va_val_list) { if (is_array($va_val_list)) { $va_acc = array_merge($va_acc, $va_val_list); } else { $va_acc[] = $va_val_list; } } $va_proc_templates = array_unique($va_acc); } if (!$pb_include_blanks && !$pb_include_blanks_for_prefetch) { $va_proc_templates = array_filter($va_proc_templates, 'strlen'); } // Transform links $va_proc_templates = caCreateLinksFromText($va_proc_templates, $ps_tablename, $pa_row_ids, null, caGetOption('linkTarget', $pa_options, null), array_merge(['addRelParameter' => true, 'requireLinkTags' => true], $pa_options)); if (!$pb_include_blanks && !$pb_include_blanks_for_prefetch) { $va_proc_templates = array_filter($va_proc_templates, 'strlen'); } if (!$pb_return_as_array) { return join($ps_delimiter, $va_proc_templates); } return $va_proc_templates; }