From 0a5e2204a50037db9ecbb021bb527663f142df84 Mon Sep 17 00:00:00 2001 From: Travis Risner Date: Thu, 11 Sep 2025 11:03:43 -0500 Subject: [PATCH 01/11] resolve alias conflicts when duplicating filters --- .../com_finder/src/Model/FilterModel.php | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/administrator/components/com_finder/src/Model/FilterModel.php b/administrator/components/com_finder/src/Model/FilterModel.php index f785e07312386..172ca5e622eb8 100644 --- a/administrator/components/com_finder/src/Model/FilterModel.php +++ b/administrator/components/com_finder/src/Model/FilterModel.php @@ -11,9 +11,11 @@ namespace Joomla\Component\Finder\Administrator\Model; use Joomla\CMS\Factory; +use Joomla\CMS\Filter\OutputFilter; use Joomla\CMS\Form\Form; use Joomla\CMS\MVC\Model\AdminModel; use Joomla\Component\Finder\Administrator\Table\FilterTable; +use Joomla\String\StringHelper; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; @@ -151,4 +153,76 @@ public function getTotal() return $db->setQuery($query)->loadResult(); } + + /** + * Method to save the form data. + * + * Overrides the parent to correctly handle the 'save2copy' task for Finder filters. + * + * @param array $data The form data. + * + * @return boolean True on success, false on failure. + * + * @since 5.3 + */ + public function save($data) + { + $task = Factory::getApplication()->getInput()->get('task', '', 'cmd'); + + if ($task === 'save2copy') { + $data['filter_id'] = 0; + + $title = trim((string) ($data['title'] ?? '')); + $alias = trim((string) ($data['alias'] ?? '')); + + if ($alias === '') { + $alias = OutputFilter::stringURLSafe($title); + } + + $data['alias'] = $this->getUniqueAlias($alias); + } + + return parent::save($data); + } + + /** + * Ensure a unique alias in the table by incrementing with dash style. + * + * + * @param string $base The starting alias (already URL-safe). + * + * @return string A unique alias. + * + * @since 5.3 + */ + protected function getUniqueAlias(string $base): string + { + $alias = $base !== '' ? $base : OutputFilter::stringURLSafe(uniqid('filter-', true)); + + while ($this->aliasExists($alias)) { + $alias = StringHelper::increment($alias, 'dash'); + } + + return $alias; + } + + /** + * Check whether an alias exists in the table. + * + * @param string $alias The alias to test. + * + * @return boolean True if it exists, false otherwise. + * + * @since 5.3 + */ + protected function aliasExists(string $alias): bool + { + $db = $this->getDatabase(); + $query = $db->getQuery(true) + ->select('COUNT(*)') + ->from($db->quoteName('#__finder_filters')) + ->where($db->quoteName('alias') . ' = ' . $db->quote($alias)); + + return (int) $db->setQuery($query)->loadResult() > 0; + } } From 2171c28eef0a158e667240c25ec64bd47a1b6f31 Mon Sep 17 00:00:00 2001 From: Travis Risner Date: Thu, 11 Sep 2025 11:23:53 -0500 Subject: [PATCH 02/11] resolve codestyle errors --- administrator/components/com_finder/src/Model/FilterModel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/administrator/components/com_finder/src/Model/FilterModel.php b/administrator/components/com_finder/src/Model/FilterModel.php index 172ca5e622eb8..fad998600b32c 100644 --- a/administrator/components/com_finder/src/Model/FilterModel.php +++ b/administrator/components/com_finder/src/Model/FilterModel.php @@ -224,5 +224,5 @@ protected function aliasExists(string $alias): bool ->where($db->quoteName('alias') . ' = ' . $db->quote($alias)); return (int) $db->setQuery($query)->loadResult() > 0; - } + } } From 60deaec547f2dc548ae3d9837586f730f3d21c50 Mon Sep 17 00:00:00 2001 From: Travis Risner Date: Thu, 18 Sep 2025 07:56:32 -0500 Subject: [PATCH 03/11] convert query to prepared statement --- administrator/components/com_finder/src/Model/FilterModel.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/administrator/components/com_finder/src/Model/FilterModel.php b/administrator/components/com_finder/src/Model/FilterModel.php index fad998600b32c..f4068ec81125f 100644 --- a/administrator/components/com_finder/src/Model/FilterModel.php +++ b/administrator/components/com_finder/src/Model/FilterModel.php @@ -15,6 +15,7 @@ use Joomla\CMS\Form\Form; use Joomla\CMS\MVC\Model\AdminModel; use Joomla\Component\Finder\Administrator\Table\FilterTable; +use Joomla\Database\ParameterType; use Joomla\String\StringHelper; // phpcs:disable PSR1.Files.SideEffects @@ -221,7 +222,8 @@ protected function aliasExists(string $alias): bool $query = $db->getQuery(true) ->select('COUNT(*)') ->from($db->quoteName('#__finder_filters')) - ->where($db->quoteName('alias') . ' = ' . $db->quote($alias)); + ->where($db->quoteName('alias') . ' = :alias') + ->bind(':alias', $alias, ParameterType::STRING); return (int) $db->setQuery($query)->loadResult() > 0; } From ed98e28cc1c8c9953292e37ebef205714555523e Mon Sep 17 00:00:00 2001 From: Travis Risner Date: Thu, 18 Sep 2025 08:19:54 -0500 Subject: [PATCH 04/11] remove unnecessary items --- administrator/components/com_finder/src/Model/FilterModel.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/administrator/components/com_finder/src/Model/FilterModel.php b/administrator/components/com_finder/src/Model/FilterModel.php index f4068ec81125f..52ab6de381490 100644 --- a/administrator/components/com_finder/src/Model/FilterModel.php +++ b/administrator/components/com_finder/src/Model/FilterModel.php @@ -15,7 +15,6 @@ use Joomla\CMS\Form\Form; use Joomla\CMS\MVC\Model\AdminModel; use Joomla\Component\Finder\Administrator\Table\FilterTable; -use Joomla\Database\ParameterType; use Joomla\String\StringHelper; // phpcs:disable PSR1.Files.SideEffects @@ -223,7 +222,7 @@ protected function aliasExists(string $alias): bool ->select('COUNT(*)') ->from($db->quoteName('#__finder_filters')) ->where($db->quoteName('alias') . ' = :alias') - ->bind(':alias', $alias, ParameterType::STRING); + ->bind(':alias', $alias); return (int) $db->setQuery($query)->loadResult() > 0; } From 2a19b3e410346ed5139d01d0ea1fcabf9b80acd6 Mon Sep 17 00:00:00 2001 From: Travis Risner Date: Fri, 10 Oct 2025 14:58:57 -0500 Subject: [PATCH 05/11] increment filter title as well --- .../com_finder/src/Model/FilterModel.php | 70 ++++++++++++++++++- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/administrator/components/com_finder/src/Model/FilterModel.php b/administrator/components/com_finder/src/Model/FilterModel.php index 52ab6de381490..fbf17d6cb118d 100644 --- a/administrator/components/com_finder/src/Model/FilterModel.php +++ b/administrator/components/com_finder/src/Model/FilterModel.php @@ -167,7 +167,8 @@ public function getTotal() */ public function save($data) { - $task = Factory::getApplication()->getInput()->get('task', '', 'cmd'); + $app = Factory::getApplication(); + $task = $app->getInput()->get('task', '', 'cmd'); if ($task === 'save2copy') { $data['filter_id'] = 0; @@ -179,12 +180,77 @@ public function save($data) $alias = OutputFilter::stringURLSafe($title); } - $data['alias'] = $this->getUniqueAlias($alias); + // Generate unique title + alias + list($title, $alias) = $this->generateNewTitleAndAlias($title, $alias); + + $data['title'] = $title; + $data['alias'] = $alias; } return parent::save($data); } + /** + * Generate a new unique title and alias for a copied filter. + * Follows the same logic as Joomla's core content models. + * + * @param string $title The original title. + * @param string $alias The original alias. + * + * @return array Array with [newTitle, newAlias]. + * + * @since 5.3 + */ + protected function generateNewTitleAndAlias(string $title, string $alias): array + { + $db = $this->getDatabase(); + + // Strip existing numeric suffix from title (e.g., "Test (2)" => "Test") + if (preg_match('/^(.*?)(?:\s\((\d+)\))?$/', $title, $matches)) { + $baseTitle = trim($matches[1]); + } else { + $baseTitle = trim($title); + } + + $baseAlias = trim($alias ?: OutputFilter::stringURLSafe($title)); + + // Escape for LIKE + $likeTitle = $db->quote($db->escape($baseTitle, true) . '%', false); + + // Get all existing titles that start with the base title + $query = $db->getQuery(true) + ->select($db->quoteName('title')) + ->from($db->quoteName('#__finder_filters')) + ->where($db->quoteName('title') . ' LIKE ' . $likeTitle); + + $existingTitles = $db->setQuery($query)->loadColumn(); + + // Collect all numeric suffixes + $maxNum = 0; + foreach ($existingTitles as $existing) { + if (preg_match('/^\Q' . $baseTitle . '\E(?:\s\((\d+)\))?$/', $existing, $matches)) { + $num = isset($matches[1]) ? (int) $matches[1] : 1; + if ($num > $maxNum) { + $maxNum = $num; + } + } + } + + // Next numeric suffix + $nextNum = $maxNum + 1; + + // Build the new title + $newTitle = $baseTitle; + if ($nextNum > 1) { + $newTitle .= ' (' . $nextNum . ')'; + } + + // Build a unique alias + $newAlias = $this->getUniqueAlias($baseAlias); + + return [$newTitle, $newAlias]; + } + /** * Ensure a unique alias in the table by incrementing with dash style. * From e35e259fa41e92ae8916a7c8bec50ec6c3170ea5 Mon Sep 17 00:00:00 2001 From: Travis Risner Date: Fri, 17 Oct 2025 07:49:27 -0500 Subject: [PATCH 06/11] add alias to list view --- .../components/com_finder/src/Model/FilterModel.php | 6 ------ .../components/com_finder/tmpl/filters/default.php | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/administrator/components/com_finder/src/Model/FilterModel.php b/administrator/components/com_finder/src/Model/FilterModel.php index fbf17d6cb118d..45019e5fdc44c 100644 --- a/administrator/components/com_finder/src/Model/FilterModel.php +++ b/administrator/components/com_finder/src/Model/FilterModel.php @@ -205,7 +205,6 @@ protected function generateNewTitleAndAlias(string $title, string $alias): array { $db = $this->getDatabase(); - // Strip existing numeric suffix from title (e.g., "Test (2)" => "Test") if (preg_match('/^(.*?)(?:\s\((\d+)\))?$/', $title, $matches)) { $baseTitle = trim($matches[1]); } else { @@ -214,10 +213,8 @@ protected function generateNewTitleAndAlias(string $title, string $alias): array $baseAlias = trim($alias ?: OutputFilter::stringURLSafe($title)); - // Escape for LIKE $likeTitle = $db->quote($db->escape($baseTitle, true) . '%', false); - // Get all existing titles that start with the base title $query = $db->getQuery(true) ->select($db->quoteName('title')) ->from($db->quoteName('#__finder_filters')) @@ -225,7 +222,6 @@ protected function generateNewTitleAndAlias(string $title, string $alias): array $existingTitles = $db->setQuery($query)->loadColumn(); - // Collect all numeric suffixes $maxNum = 0; foreach ($existingTitles as $existing) { if (preg_match('/^\Q' . $baseTitle . '\E(?:\s\((\d+)\))?$/', $existing, $matches)) { @@ -236,10 +232,8 @@ protected function generateNewTitleAndAlias(string $title, string $alias): array } } - // Next numeric suffix $nextNum = $maxNum + 1; - // Build the new title $newTitle = $baseTitle; if ($nextNum > 1) { $newTitle .= ' (' . $nextNum . ')'; diff --git a/administrator/components/com_finder/tmpl/filters/default.php b/administrator/components/com_finder/tmpl/filters/default.php index a75c842ed8ec5..4941a7c12fde3 100644 --- a/administrator/components/com_finder/tmpl/filters/default.php +++ b/administrator/components/com_finder/tmpl/filters/default.php @@ -59,6 +59,9 @@ + + + @@ -103,6 +106,9 @@ + + alias; ?> + created_by_alias ?: $item->user_name; ?> From c6dcbe2ec4c918c9556b17c9958bc0e3f5244977 Mon Sep 17 00:00:00 2001 From: Travis Risner Date: Fri, 17 Oct 2025 08:09:57 -0500 Subject: [PATCH 07/11] cs --- administrator/components/com_finder/tmpl/filters/default.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/administrator/components/com_finder/tmpl/filters/default.php b/administrator/components/com_finder/tmpl/filters/default.php index 4941a7c12fde3..8a02f45bbce5c 100644 --- a/administrator/components/com_finder/tmpl/filters/default.php +++ b/administrator/components/com_finder/tmpl/filters/default.php @@ -108,7 +108,7 @@ alias; ?> - + created_by_alias ?: $item->user_name; ?> From 7093f24f458432358d5f31e288136c637ec87ca3 Mon Sep 17 00:00:00 2001 From: Travis Risner Date: Fri, 17 Oct 2025 08:17:06 -0500 Subject: [PATCH 08/11] Update administrator/components/com_finder/src/Model/FilterModel.php Co-authored-by: Richard Fath --- administrator/components/com_finder/src/Model/FilterModel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/administrator/components/com_finder/src/Model/FilterModel.php b/administrator/components/com_finder/src/Model/FilterModel.php index 45019e5fdc44c..744cb52667f7b 100644 --- a/administrator/components/com_finder/src/Model/FilterModel.php +++ b/administrator/components/com_finder/src/Model/FilterModel.php @@ -163,7 +163,7 @@ public function getTotal() * * @return boolean True on success, false on failure. * - * @since 5.3 + * @since __DEPLOY_VERSION__ */ public function save($data) { From 7a4917d802887e24a59ae094d9c549dd6244326c Mon Sep 17 00:00:00 2001 From: Travis Risner Date: Fri, 17 Oct 2025 08:17:19 -0500 Subject: [PATCH 09/11] Update administrator/components/com_finder/src/Model/FilterModel.php Co-authored-by: Richard Fath --- administrator/components/com_finder/src/Model/FilterModel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/administrator/components/com_finder/src/Model/FilterModel.php b/administrator/components/com_finder/src/Model/FilterModel.php index 744cb52667f7b..a0655f3d96310 100644 --- a/administrator/components/com_finder/src/Model/FilterModel.php +++ b/administrator/components/com_finder/src/Model/FilterModel.php @@ -199,7 +199,7 @@ public function save($data) * * @return array Array with [newTitle, newAlias]. * - * @since 5.3 + * @since __DEPLOY_VERSION__ */ protected function generateNewTitleAndAlias(string $title, string $alias): array { From bc8a30eacc224c3c37ddb92c98df02d09e33276d Mon Sep 17 00:00:00 2001 From: Travis Risner Date: Fri, 17 Oct 2025 08:17:31 -0500 Subject: [PATCH 10/11] Update administrator/components/com_finder/src/Model/FilterModel.php Thanks! Co-authored-by: Richard Fath --- administrator/components/com_finder/src/Model/FilterModel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/administrator/components/com_finder/src/Model/FilterModel.php b/administrator/components/com_finder/src/Model/FilterModel.php index a0655f3d96310..db86e5d72e91e 100644 --- a/administrator/components/com_finder/src/Model/FilterModel.php +++ b/administrator/components/com_finder/src/Model/FilterModel.php @@ -273,7 +273,7 @@ protected function getUniqueAlias(string $base): string * * @return boolean True if it exists, false otherwise. * - * @since 5.3 + * @since __DEPLOY_VERSION__ */ protected function aliasExists(string $alias): bool { From 7bf36081d43ddf4bb63c6cd79e3bb717fcb7fe71 Mon Sep 17 00:00:00 2001 From: Travis Risner Date: Fri, 17 Oct 2025 08:17:44 -0500 Subject: [PATCH 11/11] Update administrator/components/com_finder/src/Model/FilterModel.php Co-authored-by: Richard Fath --- administrator/components/com_finder/src/Model/FilterModel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/administrator/components/com_finder/src/Model/FilterModel.php b/administrator/components/com_finder/src/Model/FilterModel.php index db86e5d72e91e..9c20bbda16f69 100644 --- a/administrator/components/com_finder/src/Model/FilterModel.php +++ b/administrator/components/com_finder/src/Model/FilterModel.php @@ -253,7 +253,7 @@ protected function generateNewTitleAndAlias(string $title, string $alias): array * * @return string A unique alias. * - * @since 5.3 + * @since __DEPLOY_VERSION__ */ protected function getUniqueAlias(string $base): string {