diff --git a/ecrire/action/editer_liens.php b/ecrire/action/editer_liens.php
index fb732293ef280d4ff3bd18f57f2b37c186f9bc52..35272ee6ea9ba1381f40a8b6cfaa73b17ccc23e0 100644
--- a/ecrire/action/editer_liens.php
+++ b/ecrire/action/editer_liens.php
@@ -396,14 +396,19 @@ function lien_insert($objet_source, $primary, $table_lien, $id, $objets, $qualif
 			}
 
 			if (lien_triables($table_lien)) {
-				$where = lien_where($primary, $id, $objet, $id_objet);
-				// si il y a deja un lien pour ce couple (avec un autre role?) on reprend le meme rang si non nul
-				if (!$rang = intval(sql_getfetsel('rang_lien', $table_lien, $where))) {
-					$where = lien_where($primary, '*', $objet, $id_objet);
-					$rang = intval(sql_getfetsel('max(rang_lien)', $table_lien, $where));
-					// si aucun lien n'a de rang, on en introduit pas, on garde zero
-					if ($rang > 0) {
-						$rang = intval($rang) + 1;
+				if (isset($qualif['rang_lien'])) {
+					$rang = $qualif['rang_lien'];
+				}
+				else {
+					$where = lien_where($primary, $id, $objet, $id_objet);
+					// si il y a deja un lien pour ce couple (avec un autre role?) on reprend le meme rang si non nul
+					if (!$rang = intval(sql_getfetsel('rang_lien', $table_lien, $where))) {
+						$where = lien_rang_where($table_lien, $primary, $id, $objet, $id_objet);
+						$rang = intval(sql_getfetsel('max(rang_lien)', $table_lien, $where));
+						// si aucun lien n'a de rang, on en introduit pas, on garde zero
+						if ($rang > 0) {
+							$rang = intval($rang) + 1;
+						}
 					}
 				}
 				$insertions['rang_lien'] = $rang;
@@ -437,8 +442,11 @@ function lien_insert($objet_source, $primary, $table_lien, $id, $objets, $qualif
 				and !sql_getfetsel($primary, $table_lien, $where)
 			) {
 				if (lien_triables($table_lien) and isset($insertions['rang_lien']) and intval($insertions['rang_lien'])) {
+					$where_meme_lien = lien_where($primary, $id, $objet, $id_objet);
+					$where_meme_lien = implode(' AND ', $where_meme_lien);
 					// on decale les liens de rang_lien>=la valeur inseree pour faire la place
-					$w = lien_where($primary, '*', $objet, $id_objet, ['rang_lien>=' . intval($insertions['rang_lien']),"$primary!=" . intval($id)]);
+					// sauf sur le meme lien avec un role eventuellement different
+					$w = lien_rang_where($table_lien, $primary, $id, $objet, $id_objet, ['rang_lien>=' . intval($insertions['rang_lien']), "NOT($where_meme_lien)"]);
 					sql_update($table_lien, ['rang_lien' => 'rang_lien+1'], $w);
 				}
 
@@ -462,7 +470,8 @@ function lien_insert($objet_source, $primary, $table_lien, $id, $objets, $qualif
 		}
 	}
 	// si on a fait des insertions, on reordonne les liens concernes
-	if ($ins > 0) {
+	// pas la peine si $qualif['rang_lien'] etait fournie, on va passer dans lien_set a suivre et donc finir le recomptage
+	if ($ins > 0 and empty($qualif['rang_lien'])) {
 		lien_ordonner($objet_source, $primary, $table_lien, $id, $objets);
 	}
 
@@ -483,24 +492,39 @@ function lien_ordonner($objet_source, $primary, $table_lien, $id, $objets) {
 		return;
 	}
 
+	$deja_reordonne = [];
+
 	foreach ($objets as $objet => $id_objets) {
 		if (!is_array($id_objets)) {
 			$id_objets = [$id_objets];
 		}
 
 		foreach ($id_objets as $id_objet) {
-			$objet = (($objet == '*') ? $objet : objet_type($objet)); # securite
-
-			$where = lien_where($primary, '*', $objet, $id_objet);
-			$liens = sql_allfetsel("$primary, id_objet, objet, rang_lien", $table_lien, $where, $primary, 'rang_lien');
-
-			$rangs = array_column($liens, 'rang_lien');
-			if (count($rangs) and (max($rangs) > 0 or min($rangs) < 0)) {
-				$rang = 1;
-				foreach ($liens as $lien) {
-					$where = lien_where($primary, $lien[$primary], $objet, $id_objet, ['rang_lien!=' . intval($rang)]);
-					sql_updateq($table_lien, ['rang_lien' => $rang], $where);
-					$rang++;
+			if (empty($deja_reordonne[$id][$objet][$id_objet])) {
+				$objet = (($objet == '*') ? $objet : objet_type($objet)); # securite
+
+				$where = lien_rang_where($table_lien, $primary, $id, $objet, $id_objet);
+				$liens = sql_allfetsel("$primary, id_objet, objet, rang_lien", $table_lien, $where, '', 'rang_lien');
+
+				$rangs = array_column($liens, 'rang_lien');
+				if (count($rangs) and (max($rangs) > 0 or min($rangs) < 0)) {
+					$rang = 1;
+					foreach ($liens as $lien) {
+						if (empty($deja_reordonne[$lien[$primary]][$lien['objet']][$lien['id_objet']])) {
+							$where = lien_where($primary, $lien[$primary], $lien['objet'], $lien['id_objet'], ['rang_lien!=' . intval($rang)]);
+							sql_updateq($table_lien, ['rang_lien' => $rang], $where);
+
+							if (empty($deja_reordonne[$lien[$primary]])) {
+								$deja_reordonne[$lien[$primary]] = [];
+							}
+							if (empty($deja_reordonne[$lien[$primary]][$lien['objet']])) {
+								$deja_reordonne[$lien[$primary]][$lien['objet']] = [];
+							}
+							$deja_reordonne[$lien[$primary]][$lien['objet']][$lien['id_objet']] = $rang;
+
+							$rang++;
+						}
+					}
 				}
 			}
 		}
@@ -590,6 +614,27 @@ function lien_where($primary, $id_source, $objet, $id_objet, $cond = []) {
 	return $where;
 }
 
+/**
+ * Fabriquer la condition where pour compter les rangs
+ * @param string $table_lien
+ * @param string $primary
+ * @param int|string|array $id_source
+ * @param string $objet
+ * @param int|string|array $id_objet
+ * @param array $cond
+ * @return array                        Liste des conditions
+ */
+function lien_rang_where($table_lien, $primary, $id_source, $objet, $id_objet, $cond = []) {
+
+	// si on veut compter les rangs autrement que le core ne le fait par defaut, fournir le where adhoc
+	if (function_exists($f = 'lien_rang_where_' . $table_lien)) {
+		return $f($primary, $id_source, $objet, $id_objet, $cond);
+	}
+
+	// par defaut c'est un rang compté pour tous les id_source d'un couple objet-id_objet
+	return lien_where($primary, '*', $objet, $id_objet, $cond);
+}
+
 /**
  * Sous fonction suppression
  * qui traite les liens pour un objet source dont la clé primaire
@@ -864,7 +909,10 @@ function lien_set($objet_source, $primary, $table_lien, $id, $objets, $qualif) {
 			if (lien_triables($table_lien) and isset($qualif['rang_lien'])) {
 				if (intval($qualif['rang_lien'])) {
 					// on decale les liens de rang_lien>=la valeur inseree pour faire la place
-					$w = lien_where($primary, '*', $objet, $id_objet, ['rang_lien>=' . intval($qualif['rang_lien']),"$primary!=" . intval($id)]);
+					// sauf sur le meme lien avec un role eventuellement different
+					$where_meme_lien = lien_where($primary, $id, $objet, $id_objet);
+					$where_meme_lien = implode(' AND ', $where_meme_lien);
+					$w = lien_rang_where($table_lien, $primary, $id, $objet, $id_objet, ['rang_lien>=' . intval($qualif['rang_lien']), "NOT($where_meme_lien)"]);
 					sql_update($table_lien, ['rang_lien' => 'rang_lien+1'], $w);
 				}
 				// tous les liens de même rôle recoivent le rang indiqué aussi