|
|
|
@@ -112,9 +112,16 @@ class OrganonSortHelper extends Base
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Number-aware title sort (strnatcasecmp, so "Task 2" precedes "Task 10"), then write positions.
|
|
|
|
* Title sort, then write positions. Mirrors the native reorder scope (all tasks in the
|
|
|
|
* Mirrors the native reorder scope (all tasks in the column+swimlane, no status filter). Core's
|
|
|
|
* column+swimlane, no status filter). Core's TaskReorderModel::reorderTasks is protected, so the
|
|
|
|
* TaskReorderModel::reorderTasks is protected, so the 3-line position write is replicated here.
|
|
|
|
* 3-line position write is replicated here.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* Each title is reduced to a comparison slug -- lowercased, accent-stripped, spaces -> dashes --
|
|
|
|
|
|
|
|
* then compared with strnatcasecmp. The dash is deliberate: strnatcasecmp SKIPS whitespace, so a
|
|
|
|
|
|
|
|
* plain space would make "Luiz Eduardo" sort after "Luiza..." (it would compare "LuizEduardo" vs
|
|
|
|
|
|
|
|
* "Luiza..."); a dash is not whitespace, so the separator is honoured, while strnatcasecmp still
|
|
|
|
|
|
|
|
* groups digit runs ("Task 2" < "Task 10"). The result is case-insensitive, accent-insensitive,
|
|
|
|
|
|
|
|
* whitespace-correct and number-aware.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
private function reorderByTitle($project_id, $swimlane_id, $column_id, $direction)
|
|
|
|
private function reorderByTitle($project_id, $swimlane_id, $column_id, $direction)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
@@ -125,8 +132,13 @@ class OrganonSortHelper extends Base
|
|
|
|
->eq('column_id', $column_id)
|
|
|
|
->eq('column_id', $column_id)
|
|
|
|
->findAll();
|
|
|
|
->findAll();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
foreach ($rows as &$row) {
|
|
|
|
|
|
|
|
$row['sortkey'] = str_replace(' ', '-', $this->stripAccents(mb_strtolower((string) $row['title'], 'UTF-8')));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
unset($row);
|
|
|
|
|
|
|
|
|
|
|
|
usort($rows, function ($a, $b) use ($direction) {
|
|
|
|
usort($rows, function ($a, $b) use ($direction) {
|
|
|
|
$cmp = strnatcasecmp((string) $a['title'], (string) $b['title']);
|
|
|
|
$cmp = strnatcasecmp($a['sortkey'], $b['sortkey']);
|
|
|
|
|
|
|
|
|
|
|
|
return $direction === 'desc' ? -$cmp : $cmp;
|
|
|
|
return $direction === 'desc' ? -$cmp : $cmp;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
@@ -137,4 +149,28 @@ class OrganonSortHelper extends Base
|
|
|
|
$position++;
|
|
|
|
$position++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Flatten a string for sorting: strip accents to their base letter so accented names sort by that
|
|
|
|
|
|
|
|
* base (Avila near A, not after Z). Uses ext-intl Normalizer when available (deterministic and
|
|
|
|
|
|
|
|
* locale-independent), falling back to iconv //TRANSLIT (locale-dependent), then the raw string
|
|
|
|
|
|
|
|
* (still sorts, just accent-naive). Case is already lowered by the caller.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* @param string $s
|
|
|
|
|
|
|
|
* @return string
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
private function stripAccents($s)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (class_exists('Normalizer')) {
|
|
|
|
|
|
|
|
$decomposed = \Normalizer::normalize($s, \Normalizer::FORM_D);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ($decomposed !== false) {
|
|
|
|
|
|
|
|
return preg_replace('/\p{Mn}/u', '', $decomposed);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$translit = @iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return $translit !== false ? $translit : $s;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|