From 00d89c7a16295c8aeebd944fc77b6fb977237a2d Mon Sep 17 00:00:00 2001
From: tcharlss <tcharlss@bravecassine.com>
Date: Thu, 2 Jun 2022 23:13:06 +0200
Subject: [PATCH] feat: La balise `#TRI` permet d'alterner le sens du tri.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* La balise `#TRI` produit dorénavant toujours un lien, même si l'item est exposé.
* Un clic sur un item exposé inverse le sens du tri
* L'item utilisé pour le tri est indiqué visuellement au moyen d'un picto (uniquement dans le privé)

Fix: #4877
---
 CHANGELOG.md                                  |  4 +
 ecrire/public/balises.php                     | 31 +++----
 ecrire/public/criteres.php                    |  5 +-
 ecrire/public/fonctions.php                   | 88 +++++++++++++++++++
 prive/themes/spip/images/tri-sens-asc-xx.svg  |  1 +
 prive/themes/spip/images/tri-sens-desc-xx.svg |  1 +
 prive/themes/spip/lists.css.html              | 24 +++++
 7 files changed, 135 insertions(+), 19 deletions(-)
 create mode 100644 prive/themes/spip/images/tri-sens-asc-xx.svg
 create mode 100644 prive/themes/spip/images/tri-sens-desc-xx.svg

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 125a61abf7..1a7a8734e7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,10 @@
 - #5118 Fix les viewbox erronnées lors de la copie locale des SVG sans viewbox
 - #5194 Améliorer le comportement du bouton "Ajourd'hui" dans le dateur en surlignant le jour courant + ajout option data-todayhighlight sur les input.date + fix option data-clearbtn
 
+### Added
+
+- #4877 La balise `#TRI` permet d'alterner le sens du critère `tri`
+
 ### Removed
 
 - spip-team/securite#3724 #5150 Suppression de la fonction `initialiser_sel()` (qui ne servait que pour la gestion de htpasswd déportée en plugin).
diff --git a/ecrire/public/balises.php b/ecrire/public/balises.php
index d9c30869eb..0c0110ae2b 100644
--- a/ecrire/public/balises.php
+++ b/ecrire/public/balises.php
@@ -2688,34 +2688,31 @@ function balise_TRI_dist($p, $liste = 'true') {
 		return $p;
 	}
 
-	$_champ = interprete_argument_balise(1, $p);
-	// si pas de champ, renvoyer le critere de tri utilise
-	if (!$_champ) {
-		$p->code = $boucle->modificateur['tri_champ'];
+	// Différentes infos relatives au tri présentes dans les modificateurs
+	$_tri_nom = $boucle->modificateur['tri_nom'] ; // nom du paramètre définissant le tri
+	$_tri_champ = $boucle->modificateur['tri_champ']; // champ actuel utilisé le tri
+	$_tri_sens = $boucle->modificateur['tri_sens']; // sens de tri actuel
+	$_tri_liste_sens_defaut = $boucle->modificateur['tri_liste_sens_defaut']; // sens par défaut pour chaque champ
+
+	$_champ_ou_sens = interprete_argument_balise(1, $p);
+	// si pas de champ, renvoyer le critère de tri actuel
+	if (!$_champ_ou_sens) {
+		$p->code = $_tri_champ;
 
 		return $p;
 	}
 	// forcer la jointure si besoin, et si le champ est statique
-	if (preg_match(",^'([\w.]+)'$,i", $_champ, $m)) {
+	if (preg_match(",^'([\w.]+)'$,i", $_champ_ou_sens, $m)) {
 		index_pile($b, $m[1], $p->boucles, '', null, true, false);
 	}
 
 	$_libelle = interprete_argument_balise(2, $p);
-	$_libelle = $_libelle ?: $_champ;
+	$_libelle = $_libelle ?: $_champ_ou_sens;
 
-	$_class = interprete_argument_balise(3, $p);
-	// si champ = ">" c'est un lien vers le tri croissant : de gauche a droite ==> 1
-	// si champ = "<" c'est un lien vers le tri decroissant : (sens inverse) == -1
-	$_issens = "in_array($_champ,array('>','<'))";
-	$_sens = "(strpos('< >',$_champ)-1)";
+	$_class = interprete_argument_balise(3, $p) ?? "''";
 
-	$_variable = "((\$s=$_issens)?'sens':'tri')." . $boucle->modificateur['tri_nom'];
-	$_url = "parametre_url(self(),$_variable,\$s?$_sens:$_champ)";
-	$_url = "parametre_url($_url,'var_memotri',strncmp(" . $boucle->modificateur['tri_nom'] . ",'session',7)==0?$_variable:'')";
-	$_on = '$s?(' . $boucle->modificateur['tri_sens'] . "==$_sens" . '):(' . $boucle->modificateur['tri_champ'] . "==$_champ)";
+	$p->code = "calculer_balise_tri($_champ_ou_sens, $_libelle, $_class, $_tri_nom, $_tri_champ, $_tri_sens, $_tri_liste_sens_defaut)";
 
-	$p->code = "lien_ou_expose($_url,$_libelle,$_on" . ($_class ? ",$_class" : '') . ')';
-	//$p->code = "''";
 	$p->interdire_scripts = false;
 
 	return $p;
diff --git a/ecrire/public/criteres.php b/ecrire/public/criteres.php
index fa0418cb85..4947412782 100644
--- a/ecrire/public/criteres.php
+++ b/ecrire/public/criteres.php
@@ -1824,18 +1824,19 @@ function critere_tri_dist($idb, &$boucles, $crit) {
 	// definition du champ par defaut
 	$_champ_defaut = !isset($crit->param[0][0]) ? "''"
 		: calculer_liste([$crit->param[0][0]], $idb, $boucles, $boucle->id_parent);
-	$_sens_defaut = !isset($crit->param[1][0]) ? '1'
+	$_liste_sens_defaut = !isset($crit->param[1][0]) ? '1'
 		: calculer_liste([$crit->param[1][0]], $idb, $boucles, $boucle->id_parent);
 	$_variable = !isset($crit->param[2][0]) ? "'$idb'"
 		: calculer_liste([$crit->param[2][0]], $idb, $boucles, $boucle->id_parent);
 
 	$_tri = "((\$t=(isset(\$Pile[0]['tri'.$_variable]))?\$Pile[0]['tri'.$_variable]:((strncmp($_variable,'session',7)==0 AND session_get('tri'.$_variable))?session_get('tri'.$_variable):$_champ_defaut))?tri_protege_champ(\$t):'')";
 
-	$_sens_defaut = "(is_array(\$s=$_sens_defaut)?(isset(\$s[\$st=$_tri])?\$s[\$st]:reset(\$s)):\$s)";
+	$_sens_defaut = "(is_array(\$s=$_liste_sens_defaut)?(isset(\$s[\$st=$_tri])?\$s[\$st]:reset(\$s)):\$s)";
 	$_sens = "((intval(\$t=(isset(\$Pile[0]['sens'.$_variable]))?\$Pile[0]['sens'.$_variable]:((strncmp($_variable,'session',7)==0 AND session_get('sens'.$_variable))?session_get('sens'.$_variable):$_sens_defaut))==-1 OR \$t=='inverse')?-1:1)";
 
 	$boucle->modificateur['tri_champ'] = $_tri;
 	$boucle->modificateur['tri_sens'] = $_sens;
+	$boucle->modificateur['tri_liste_sens_defaut'] = $_liste_sens_defaut;
 	$boucle->modificateur['tri_nom'] = $_variable;
 	// faut il inserer un test sur l'existence de $tri parmi les champs de la table ?
 	// evite des erreurs sql, mais peut empecher des tri sur jointure ...
diff --git a/ecrire/public/fonctions.php b/ecrire/public/fonctions.php
index 6ca2ea2eb8..d53f67333e 100644
--- a/ecrire/public/fonctions.php
+++ b/ecrire/public/fonctions.php
@@ -404,6 +404,94 @@ function calculer_rang_smart($titre, $objet_source, $id, $env) {
 	return recuperer_numero($titre);
 }
 
+/**
+ * Calcul de la balise #TRI
+ *
+ * @param string $champ_ou_sens
+ *     - soit le nom de champ sur lequel effectuer le nouveau tri
+ *     - soit `<` et `>` pour définir le sens du tri sur le champ actuel
+ * @param string $libelle
+ *     Texte du lien
+ * @param string $classe
+ *     Classe ajoutée au lien, telle que `ajax`
+ * @param string $tri_nom
+ *     Nom du paramètre définissant le tri
+ * @param string $tri_champ
+ *     Nom du champ actuel utilisé pour le tri
+ * @param string $tri_sens
+ *     Sens de tri actuel, 1 ou -1
+ * @param array|string|int $liste_tri_sens_defaut
+ *     Soit la liste des sens de tri par défaut pour chaque champ
+ *     Soit une valeur par défaut pour tous les champs (1, -1, inverse)
+ * @return string
+ *     HTML avec un lien cliquable
+ */
+function calculer_balise_tri(string $champ_ou_sens, string $libelle, string $classe, string $tri_nom, string $tri_champ, string $tri_sens, $liste_tri_sens_defaut): string {
+
+	$url = self('&');
+	$tri_sens = (int) $tri_sens;
+	$alias_sens = [
+		'<' => -1,
+		'>' => 1,
+		'inverse' => -1,
+	];
+
+	// Normaliser la liste des sens de tri par défaut
+	// On ajoute un jocker pour les champs non présents dans la liste
+	// avec la valeur du 1er item de la liste, idem critère {tri}
+	if (is_array($liste_tri_sens_defaut)) {
+		$liste_tri_sens_defaut['*'] = array_values($liste_tri_sens_defaut)[0];
+	} else {
+		$liste_tri_sens_defaut = [
+			'*' => (int) ($alias_sens[$liste_tri_sens_defaut] ?? $liste_tri_sens_defaut),
+		];
+	}
+
+	// Le nouveau sens de tri :
+	// Soit c'est un sens fixe donné en paramètre (< ou >)
+	$is_sens_fixe = array_key_exists($champ_ou_sens, $alias_sens);
+	if ($is_sens_fixe) {
+		$tri_sens_nouveau = $alias_sens[$champ_ou_sens];
+	// Soit c'est le champ utilisé actuellement pour le tri → on inverse le sens
+	} elseif ($champ_ou_sens === $tri_champ) {
+		$tri_sens_nouveau = $tri_sens * -1;
+	// Sinon c'est un nouveau champ, et on prend son tri par défaut
+	} else {
+		$tri_sens_nouveau = (int) ($liste_tri_sens_defaut[$champ_ou_sens] ?? $liste_tri_sens_defaut['*']);
+	}
+
+	// URL : ajouter le champ sur lequel porte le tri
+	if (!$is_sens_fixe) {
+		$param_tri = "tri$tri_nom";
+		$url = parametre_url($url, $param_tri, $champ_ou_sens);
+	}
+
+	// URL : n'ajouter le sens de tri que si nécessaire,
+	// c.à.d différent du sens par défaut pour le champ
+	$param_sens = "sens$tri_nom";
+	$tri_sens_defaut_champ = (int) ($liste_tri_sens_defaut[$champ_ou_sens] ?? $liste_tri_sens_defaut['*']);
+	if ($tri_sens_nouveau !== $tri_sens_defaut_champ) {
+		$url = parametre_url($url, $param_sens, $tri_sens_nouveau);
+	} else {
+		$url = parametre_url($url, $param_sens, '');
+	}
+
+	// Drapeau pour garder en session ?
+	$param_memo = (!$is_sens_fixe ? $param_tri : $param_sens);
+	$url = parametre_url($url, 'var_memotri', strncmp($tri_nom, 'session', 7) == 0 ? $param_memo : '');
+
+	// Classes : on indique le sens de tri et l'item exposé
+	$classe .= ' item-tri item-tri_' . ($tri_sens === 1 ? 'asc' : 'desc');
+	if ($champ_ou_sens === $tri_champ) {
+		$classe .= ' item-tri_actif';
+	}
+
+	// Lien
+	$balise = lien_ou_expose($url, $libelle, false, $classe);
+
+	return $balise;
+}
+
 
 /**
  * Proteger les champs passes dans l'url et utiliser dans {tri ...}
diff --git a/prive/themes/spip/images/tri-sens-asc-xx.svg b/prive/themes/spip/images/tri-sens-asc-xx.svg
new file mode 100644
index 0000000000..57ae3cb018
--- /dev/null
+++ b/prive/themes/spip/images/tri-sens-asc-xx.svg
@@ -0,0 +1 @@
+<svg role="img" xmlns="http://www.w3.org/2000/svg" width="16px" height="16px" viewBox="0 0 24 24" aria-labelledby="arrowUpIconTitle" stroke="#111" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none" color="#111"> <title id="arrowUpIconTitle">Arrow Up</title> <path d="M18 9l-6-6-6 6"/> <path d="M12 21V4"/> <path stroke-linecap="round" d="M12 3v1"/> </svg>
\ No newline at end of file
diff --git a/prive/themes/spip/images/tri-sens-desc-xx.svg b/prive/themes/spip/images/tri-sens-desc-xx.svg
new file mode 100644
index 0000000000..19bb3a65d0
--- /dev/null
+++ b/prive/themes/spip/images/tri-sens-desc-xx.svg
@@ -0,0 +1 @@
+<svg role="img" xmlns="http://www.w3.org/2000/svg" width="16px" height="16px" viewBox="0 0 24 24" aria-labelledby="arrowDownIconTitle" stroke="#111" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none" color="#111"> <title id="arrowDownIconTitle">Arrow Down</title> <path d="M6 15l6 6 6-6"/> <path d="M12 3v17"/> <path stroke-linecap="round" d="M12 21v-1"/> </svg>
\ No newline at end of file
diff --git a/prive/themes/spip/lists.css.html b/prive/themes/spip/lists.css.html
index 937f699146..a845cf25d9 100644
--- a/prive/themes/spip/lists.css.html
+++ b/prive/themes/spip/lists.css.html
@@ -716,6 +716,30 @@
 	text-align: var(--spip-left);
 }
 
+/* Balise TRI : picto indiquant le sens de tri sur l'item exposé */
+.item-tri_actif {
+	display: inline-flex;
+	align-items: center;
+}
+.item-tri_actif:after {
+	content: "";
+	display: inline-block;
+	width: 1em;
+	height: 1em;
+	margin-inline-start: 0.15em;
+	margin-inline-start: 0.15em;
+	background-color: currentColor !important;
+	mask-size: contain;
+	mask-position: center;
+	mask-repeat: no-repeat;
+}
+.item-tri_actif.item-tri_asc:after {
+	mask-image: url("#CHEMIN_IMAGE{tri-sens-asc-xx.svg}");
+}
+.item-tri_actif.item-tri_desc:after {
+	mask-image: url("#CHEMIN_IMAGE{tri-sens-desc-xx.svg}");
+}
+
 
 /**
  * =========
-- 
GitLab