Mon CV

Résoudre les conflits de synchronisation avec DropboxConflictsSolver

31 mars 2013

Nous sommes de plus en plus nombreux à utiliser Dropbox pour travailler à plusieurs sur des documents. C’est génial, le service est presque transparent … sauf qu’un jour, forcément, vous vous retrouverez avec des dossiers ou des fichiers en conflit.

Exemple, vous stockez un budget au format xls, que plusieurs personnes vont éditer simultanément … et enregistrer. Et là c’est le drame ! Dropbox ne sait plus quel est le bon fichier, ne sait pas les fusionner. Il va donc créer des fichiers avec une tag “en conflit” ou en anglais “conflicted”.

Tant que vous n’en avez que quelques uns, cela ce gère assez facilement à la main … mais à longue on peut vite se faire dépasser par ces multiples doublons.

J’ai cherché des solutions, et comme d’habitude, je n’ai pas trouvé tout à fait ce que je voulais. Du coup je me suis lancé dans une application en Applescript pour détecter les éléments en conflit et leur appliquer un traitement, au choix :

  1. les ouvrir dans le Finder,
  2. les mettre à la Corbeille,
  3. les déplacer dans un dossier (si possible hors Dropbox) de votre choix.

La première ligne de mon code est un commande shell qui recherche les fichiers potentiellement en conflit. Je me suis limité à rechercher selon les tag ‘conflicted’ (en anglais) et ‘en conflit’ (en français). Si vous travaillez avec des personnes ayant le programme installé dans une autre langue, potentiellement des conflits peuvent être générés dans cette nouvelle langue … et il faudra donc adapter cette première ligne de code en conséquence.

Pour pousser le vice, j’utilise des notifications Growl, que vous pouvez rediriger vers votre centre de notifications. J’ai également ajouté un support pour une notification en cas de mise à jour de cette application.

Vous pouvez télécharger l’application complète ici : https://github.com/yvangodard/DropboxConflictsSolver/archive/master.zip et me faire remonter les bugs, corrections ajouts sur  https://github.com/yvangodard/DropboxConflictsSolver. S’il y a des volontaires, je peux rapidement ajouter le support multilingue de l’application.

Voici l’extrait central du code …

do shell script "LIST_CONFLITS=$(mktemp /tmp/liste_conflicted.XXXXX); basename $LIST_CONFLITS"
set LIST_CONFLITS to "/tmp/" & result
set command to "find -L ~/Dropbox \\( -path \"*.dropbox.cache*\" -prune \\) -o \\( -path \"*(Copie de * en conflit [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]*\" -print \\) >> " & LIST_CONFLITS & " ; find -L ~/Dropbox \\( -path \"*.dropbox.cache*\" -prune \\) -o \\( -path \"*(Copie en conflit de * [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]*\" -print \\) >> " & LIST_CONFLITS & " ; find -L ~/Dropbox \\( -path \"*.dropbox.cache*\" -prune \\) -o \\( -path \"*(*'s conflicted copy [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]*\" -print \\) >> " & LIST_CONFLITS
do shell script command
set command2 to "cat " & LIST_CONFLITS & " | awk '{if (x[$0] != \"\") next ; print $0 ; x[$0]=$0}' | sort -u  >> " & LIST_CONFLITS & "new ; rm " & LIST_CONFLITS & " ; mv " & LIST_CONFLITS & "new " & LIST_CONFLITS
do shell script command2
do shell script "cat " & LIST_CONFLITS
set Liste to result
set founditems to paragraphs of Liste as list
set NumberOfConflicts to count items in founditems
if NumberOfConflicts = 0 then
	display dialog "Vous n'avez aucun élément en conflit dans votre dossier Dropbox." with icon 1 buttons {"Terminer"} default button {"Terminer"}
	set MessageGrowl to "Vous n'aviez aucun élément en conflit dans votre dossier Dropbox." & return & "Traitement terminé."
else if NumberOfConflicts > 0 then
	set ButtunZero to button returned of (display dialog "Voulez-vous AFFICHER DANS VOTRE FINDER ou TRAITER (déplacer ou supprimer) les éléments en conflit détectés ?" with icon 1 buttons {"Afficher dans le Finder", "Traiter", "Annuler"} default button "Annuler")
	if ButtunZero = "Annuler" then
	else if ButtunZero = "Afficher dans le Finder" then
		repeat with currFile in founditems
			set currFilePosix to POSIX file currFile
			tell application "Finder" to make new Finder window to currFilePosix
		end repeat
		if NumberOfConflicts = 1 then
			set MessageGrowl to "Traitement effectué, " & NumberOfConflicts & " élément ouvert dans votre Finder, à vous de jouer !"
		else if NumberOfConflicts > 1 then
			set MessageGrowl to "Traitement effectué, " & NumberOfConflicts & " éléments ouverts dans votre Finder, à vous de jouer !"
		end if
	else if ButtunZero = "Traiter" then
		if NumberOfConflicts = 1 then
			set ButtunPressed to button returned of (display alert "Vous avez " & NumberOfConflicts & " élément en conflit dans votre dossier Dropbox :" message Liste buttons {"Supprimer", "Déplacer", "Annuler"} default button "Annuler" as critical)
			if ButtunPressed = "Annuler" then
				set MessageGrowl to "Erreur :" & return & "Vous avez annulé le processus de traitement."
			else if ButtunPressed = "Déplacer" then
				display dialog "Choisissez le dossier de destination dans la fenêtre de dialogue suivante." with icon 1 buttons {"Continuer"} default button {"Continuer"}
				set DestinationFolder to choose folder ["Choisissez le dossier de destination."]
				do shell script "mv " & quoted form of Liste & " " & quoted form of (POSIX path of DestinationFolder)
				display alert "Le déplacement de l'élément a été effectué dans le dossier suivant :" message (POSIX path of DestinationFolder) buttons {"Terminer"} default button "Terminer" as critical
				set MessageGrowl to "Traitement effectué, 1 élément déplacé dans :" & return & (POSIX path of DestinationFolder)
			else if ButtunPressed = "Supprimer" then
				do shell script "mv " & quoted form of Liste & " ~/.Trash/"
				display dialog "Le déplacement a correctement été effectué dans votre corbeille." with icon 1 buttons {"Terminer"} default button {"Terminer"}
				set MessageGrowl to "Traitement effectué, 1 élément déplacé dans votre corbeille."
			end if
		else
			set ButtunPressed to button returned of (display dialog "Vous avez " & NumberOfConflicts & " éléments en conflit dans votre dossier Dropbox." & return & ¬
				"Que voulez-vous faire ?" with icon 1 buttons {"Tout supprimer", "Tout déplacer", "Choisir individuellement"} default button "Choisir individuellement")
			if ButtunPressed = "Tout supprimer" then
				set numero to "0"
				repeat until numero = NumberOfConflicts
					set numero to numero + 1
					set FichierEnCours to paragraph numero of Liste
					do shell script "mv " & quoted form of FichierEnCours & " ~/.Trash/"
				end repeat
				display dialog "Le déplacement des éléments a été correctement effectué dans votre corbeille." with icon 1 buttons {"Terminer"} default button {"Terminer"}
				set MessageGrowl to "Traitement effectué, " & NumberOfConflicts & " éléments déplacés dans votre corbeille."
			else if ButtunPressed = "Tout déplacer" then
				display dialog "Choisissez le dossier de destination dans la fenêtre de dialogue suivante." with icon 1 buttons {"Continuer"} default button {"Continuer"}
				set DestinationFolder to choose folder ["Choisissez le dossier de destination."]
				set numero to "0"
				repeat until numero = NumberOfConflicts
					set numero to numero + 1
					set FichierEnCours to paragraph numero of Liste
					do shell script "mv " & quoted form of FichierEnCours & " " & quoted form of (POSIX path of DestinationFolder)
				end repeat
				display alert "Le déplacement des éléments a été effectué dans le dossier suivant :" message (POSIX path of DestinationFolder) buttons {"Terminer"} default button "Terminer" as critical
				set MessageGrowl to "Traitement effectué, " & NumberOfConflicts & " éléments déplacés dans :" & return & (POSIX path of DestinationFolder)
			else if ButtunPressed = "Choisir individuellement" then
				set numero to "0"
				repeat until numero = NumberOfConflicts
					set numero to numero + 1
					set FichierEnCours to paragraph numero of Liste
					set ButtunPressed to button returned of (display alert "Traitement de l'élément n°" & numero & return & ¬
						"Que voulez-vous faire pour l'élément suivant ?" message FichierEnCours buttons {"Supprimer", "Déplacer", "Annuler"} default button "Annuler" as critical)
					if ButtunPressed = "Annuler" then
					else if ButtunPressed = "Déplacer" then
						display dialog "Choisissez le dossier de destination dans la fenêtre de dialogue suivante." with icon 1 buttons {"Continuer"} default button {"Continuer"}
						set DestinationFolder to choose folder ["Choisissez le dossier de destination."]
						do shell script "mv " & quoted form of FichierEnCours & " " & quoted form of (POSIX path of DestinationFolder)
					else if ButtunPressed = "Supprimer" then
						do shell script "mv " & quoted form of FichierEnCours & " ~/.Trash/"
					end if
				end repeat
				set MessageGrowl to "Traitement mixte de " & NumberOfConflicts & " éléments en conflit effectué."
			end if
		end if
	end if
end if
do shell script "rm " & LIST_CONFLITS
tell application "System Events"
	set isRunning to (count of (every process whose bundle identifier is "com.Growl.GrowlHelperApp")) > 0
end tell
if isRunning then
	tell application id "com.Growl.GrowlHelperApp"
		set the allNotificationsList to ¬
			{"Dropbox Conflicts Solver"}
		set the enabledNotificationsList to ¬
			{"Dropbox Conflicts Solver"}
		register as application ¬
			"Dropbox Conflicts Solver" all notifications allNotificationsList ¬
			default notifications enabledNotificationsList ¬
			icon of application "Dropbox.app"
		notify with name ¬
			"Dropbox Conflicts Solver" title ¬
			"Dropbox" description ¬
			MessageGrowl application name "Dropbox Conflicts Solver"
	end tell
end if

N’hésitez pas à me faire vos retours, vos suggestions.

En stock, j’ai également créé un petit installeur qui ajoute une recherche périodique automatique (via LaunchAgent), vous pouvez le télécharger ici.

Posted in Apple et MacintoshTags: