I think I may have something. Although it uses a workaround. Here goes!
Snippet Call: [[GlossaryList? &parents=145
&tvList=tags
]]
Snippet: (named it GlossaryList)
<?php
$output = '';
for($alpha = 65; $alpha <= 90; $alpha++) {
// run snippet
$params['debug'] = 0;
$params['parents'] = $parents;
$params['field'] = 'c.pagetitle';
$params['char'] = chr($alpha);
$params['register'] = 1;
$params['tvList'] = $tvList;
$params['filter_delimiter']=', ';
$params['filters']='AND(tv:'.$params['tvList'].':like-r:'.chr($alpha).')';
$params['paginate'] = 'pages';
$params['tpl'] = "@CODE:<li><img src='[+tv.image+]' alt='[+e.pagetitle+]'><a href='[+url+]'>[+title+] ([+id+]) </a></li>";
$output .= "<h2>".chr($alpha)."</h2>";
$output .= $modx->runSnippet("DLGlossaryTags", $params);
$output .= '<div id="dl_pages">'.$id.'[+'.$id.'.pages+]</div>';
}
return $output;
Now here's the thing. In order to make it work correctly you have to create the snippet DLGlossaryTags. Which is a duplicate snippet DLGlossary with one change. Create the snippet and paste the code below.
Snippet DLGlossaryTags:
<?php
if (! defined('MODX_BASE_PATH')) {
die('HACK???');
}
require_once(MODX_BASE_PATH . "assets/snippets/DocLister/lib/sqlHelper.class.php");
switch (true) {
case ( ! empty($fromget)):
/** Брать ли данные из GET */
$data = $_GET;
$from = $fromget;
break;
case ( ! empty($frompost)):
/** Брать ли данные из POST */
$data = $_POST;
$from = $frompost;
break;
default:
$from = $data = null;
}
if (! empty($from)) {
$char = (isset($data[$from]) && is_scalar($data[$from])) ? $data[$from] : null;
}
$char = ( ! empty($char) || (isset($char) && $char == 0)) ? $char : '';
/** С какого символа должен начинаться текст */
$field = ! empty($field) ? $field : 'c.pagetitle';
/** Поле по которому фильтровать */
$setActive = ! empty($setActive) ? true : false;
/** Активировать наборы символов */
$regexpSep = ! empty($regexpSep) ? $regexpSep : '||';
/** Разделитель в наборах регулярок */
$regexp = ! empty($regexp) ? $regexp : 'az||0-9||a-z';
/** Наборы поддерживаемых регулярок */
$regexp = explode($regexpSep, $regexp);
$loadfilter = ! empty($loadfilter) ? $loadfilter : '';
/** Какой фильтр загружать */
$register = empty($register) ? true : false; //Чувствительность к регистру.
if (preg_match("/\s+/", $field)) {
/** SQL-injection protection :-) */
$char = '';
}
$out = $where = '';
$action = "like-r";
if ($char !== null) {
if ($register) {
$char = mb_strtolower($char, 'UTF-8');
}
if (mb_strlen($char, 'UTF-8') == 1) {
$char = preg_match('/^[aza-z0-9]/iu', $char) ? $char : null;
} else {
if ($setActive && in_array($char, $regexp)) {
$action = "regexp";
$char = "^[{$char}]";
} else {
$char = null;
}
}
}
if ($char === null) {
$modx->sendErrorPage();
}
$p = &$modx->event->params;
if (! is_array($p)) {
$p = array();
}
if (! empty($loadfilter)) {
$field = explode(".", $field);
$field = end($field);
if (! empty($p['filters'])) {
$p['filters'] = rtrim(trim($p['filters']), ";") . ";";
}
$p['filters'] = "AND({$loadfilter}:{$field}:{$action}:{$char})";
} else {
$field = sqlHelper::tildeField($field);
if ($action === 'regexp') {
$where = $field . " REGEXP '" . $modx->db->escape($char) . "'";
} else {
$where = sqlHelper::LikeEscape($modx, $field, $char, '=', '[+value+]%');
}
if (empty($p['addWhereList'])) {
$p['addWhereList'] = $where;
} else {
$p['addWhereList'] = sqlHelper::trimLogicalOp($p['addWhereList']) . " AND " . $where;
}
}
// We have to unset this parameter so the SQL statement looks in the TVs only.
unset($p['addWhereList']);
return $modx->runSnippet("DocLister", $p);
Caveat
This looks at the TV value as a string it does NOT look at it as a comma separated list.
Best I've got @risingisland