Skip to content
123 changes: 58 additions & 65 deletions manager/includes/document.parser.class.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -4715,6 +4715,7 @@ public function mb_strftime($format = '%Y/%m/%d', $timestamp = '')

// Modified by Raymond for TV - Orig Modified by Apodigm - DocVars
# returns a single TV record. $idnames - can be an id or name that belongs the template that the current document is using
# $fields is kept for backward compatibility but no longer used (always selects all columns)
public function getTemplateVar($idname = '', $fields = '*', $docid = '', $published = 1)
{
if ($idname == '') {
Expand All @@ -4726,97 +4727,89 @@ public function getTemplateVar($idname = '', $fields = '*', $docid = '', $publis
}

# returns an array of TV records. $idnames - can be an id or name that belongs the template that the current document is using
# $fields is kept for backward compatibility but no longer used (always selects all columns)
public function getTemplateVars($idnames = '*', $fields = '*', $docid = '', $published = 1, $sort = 'rank', $dir = 'ASC')
{
// get document record
if ($docid === '' && $this->documentIdentifier) {
$docid = $this->documentIdentifier;
}

if ($this->array_get($this->previewObject, 'template')) {
$resource = $this->getDocument($docid, '*', null); //Ignore published when the preview.
$resource['template'] = $this->previewObject['template'];
} elseif ($docid == $this->documentIdentifier) {
$resource = $this->documentObject;
} else {
$resource = $this->getDocument($docid, '*', $published);
}

$resource = $this->getResourceForTV($docid, $published);
if (!$resource) {
return false;
}
if (!$resource['template']) {
$result = [];
foreach ($resource as $key => $value) {
if ($idnames === '*' || (is_string($idnames) && in_array($key, explode(',', $idnames)))) {
$result[] = ['name' => $key, 'value' => $value];
}
}
return $result;
}

if ($fields === '*' || $fields === '') {
$fields = "tv.*, IF(tvc.value!='',tvc.value,tv.default_text) as value";
} else {
$fields = sprintf(
"%s, IF(tvc.value!='',tvc.value,tv.default_text) as value",
array_map(
function ($v) {
return 'tv.' . $v;
},
explode(',', $fields)
)
);
}
// built-in resource fields
$result = $this->collectResourceFields($resource, $idnames);

Comment on lines +4744 to 4746
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve TV-first ordering in getTemplateVars results

$result is now prefilled with built-in resource fields before the TV query runs, which reverses the previous ordering (TV rows first, then built-ins). This changes observable behavior for callers that iterate getTemplateVars('*', ...) and rely on $sort to control the leading records, because entries like alias/id now appear before any sorted TV rows and can alter snippet/output logic that processes the list in order.

Useful? React with 👍 / 👎.

if (is_array($idnames) && !empty($idnames)) {
$idnames = sprintf(
"'%s'",
implode("','", db()->escape($idnames))
// user-defined template variables
if ($resource['template']) {
$where = $this->buildTVWhereClause($idnames);
$orderby = $sort
? sprintf('%s %s', $this->join(',', explode(',', $sort), 'tv.'), $dir)
: '';

$rs = db()->select(
"tv.*, IF(tvc.value!='',tvc.value,tv.default_text) as value",
[
'[+prefix+]site_tmplvars tv',
'INNER JOIN [+prefix+]site_tmplvar_templates tvtpl ON tvtpl.tmplvarid = tv.id',
sprintf(
"LEFT JOIN [+prefix+]site_tmplvar_contentvalues tvc ON tvc.tmplvarid=tv.id AND tvc.contentid='%s'",
$docid
)
],
sprintf('%s AND tvtpl.templateid=%s', $where, $resource['template']),
$orderby
);
while ($row = db()->getRow($rs)) {
$result[] = $row;
}
}

if ($idnames === '*') {
$where = 'tv.id<>0';
} elseif (preg_match('@^[1-9][0-9]*$@', $idnames)) {
$where = sprintf('tv.id=%s', $idnames);
} elseif (strpos($idnames, "'") === 0) {
$where = sprintf("tv.name IN (%s)", $idnames);
return $result;
}

private function getResourceForTV($docid, $published)
{
if ($this->array_get($this->previewObject, 'template')) {
$resource = $this->getDocument($docid, '*', null);
$resource['template'] = $this->previewObject['template'];
} elseif ($docid == $this->documentIdentifier) {
$resource = $this->documentObject;
} else {
$where = sprintf("tv.name='%s'", db()->escape($idnames));
$resource = $this->getDocument($docid, '*', $published);
}
return $resource ?: false;
}

private function collectResourceFields($resource, $idnames)
{
$result = [];
$rs = db()->select(
$fields,
[
'[+prefix+]site_tmplvars tv',
'INNER JOIN [+prefix+]site_tmplvar_templates tvtpl ON tvtpl.tmplvarid = tv.id',
sprintf(
"LEFT JOIN [+prefix+]site_tmplvar_contentvalues tvc ON tvc.tmplvarid=tv.id AND tvc.contentid='%s'",
$docid
)
],
sprintf('%s AND tvtpl.templateid=%s', $where, $resource['template']),
$sort ?
sprintf('%s %s', $this->join(',', explode(',', $sort), 'tv.'), $dir)
:
''
);
while ($row = db()->getRow($rs)) {
$result[] = $row;
}

// get default/built-in template variables
ksort($resource);
foreach ($resource as $key => $value) {
if ($idnames === '*' || in_array($key, explode(',', $idnames))) {
if ($idnames === '*' || (is_string($idnames) && in_array($key, explode(',', $idnames)))) {
$result[] = ['name' => $key, 'value' => $value];
}
}
return $result;
}

private function buildTVWhereClause($idnames)
{
if (is_array($idnames) && !empty($idnames)) {
$escaped = implode("','", db()->escape($idnames));
return sprintf("tv.name IN ('%s')", $escaped);
}
if ($idnames === '*') {
return 'tv.id<>0';
}
if (preg_match('@^[1-9][0-9]*$@', $idnames)) {
return sprintf('tv.id=%s', $idnames);
}
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

buildTVWhereClause()が、従来getTemplateVars()で受け付けていた「先頭が'で始まるIN句用の文字列」(例: "'tv1','tv2'")を扱えなくなっている。旧実装ではこの形式を検出して tv.name IN (...) を構築していたが、現状だとtv.name=''tv1','tv2''のような等価比較になり一致しない。後方互換性のため、先頭が'の場合はIN句として扱う分岐をbuildTVWhereClause()に追加するか、サポート終了を明示して呼び出し側を配列入力へ統一する必要がある。

Suggested change
}
}
if (is_string($idnames) && strlen($idnames) > 0 && $idnames[0] === "'") {
return sprintf('tv.name IN (%s)', $idnames);
}

Copilot uses AI. Check for mistakes.
return sprintf("tv.name='%s'", db()->escape($idnames));
}

# returns an associative array containing TV rendered output values. $idnames - can be an id or name that belongs the template that the current document is using
function getTemplateVarOutput($idnames = '*', $docid = '', $published = 1, $sep = '')
{
Expand Down