Introduction aux bookmarklets Asana

Auteur de la publication : @ShunS

Version originale (en anglais) : Introduction to Asana bookmarklets

Marquer comme terminées ou non terminées toutes les tâches d’un projet, développer/réduire toutes les sections d’un projet, développer tous les commentaires d’une tâche… Effectuez toutes ces actions groupées en un clic.

Screen Recording 2022-07-25 at 16.21.00 (1)

0. Qu’est-ce qu’un bookmarklet ?

Les bookmarklets sont des extraits de code JavaScript qui sont ajoutés aux favoris dans le navigateur ; il suffit de cliquer dessus pour les exécuter. Pour ajouter des extraits de code JavaScript à vos favoris, placez-les entre les symboles javascript:(function() { et })();.

Le front-end de tout site Web, y compris Asana, est principalement constitué de trois langages : HTML (langage de balisage pour structurer le contenu), CSS (langage pour spécifier la mise en page et le style) et JavaScript (langage de programmation).

Les paires de balises HTML construisent des unités structurées (nœuds), qui peuvent être exploitées par JavaScript. Cette interface est appelée DOM (Document Object Model).

1. Ajouter des bookmarklets

Accédez à la page Asana bookmarklets et faites glisser les liens en bleu vers la barre des favoris de votre navigateur pour les y ajouter.

L’idéal est de créer un dossier pour que les bookmarklets ne prennent pas trop de place dans votre barre des favoris. Si vous préférez quand même les ajouter directement à la barre des favoris, modifiez leur nom pour ne conserver que l’émoticône.

Si vous ajoutez manuellement les bookmarklets, suivez les étapes ci-dessous :

  1. Ajoutez une page Web quelconque à vos favoris.

  2. Au moment de l’ajout, cliquez sur « Autres… », sélectionnez le dossier, puis modifiez le nom et l’URL.

  3. Ajoutez l’intitulé du bookmarklet dans le champ « Nom » et le code commençant par javascript: dans « URL ».

Si vous souhaitez séparer votre liste des bookmarklets en plusieurs sections, je vous recommande d’utiliser Chrome Bookmarks Separator (séparateurs sur Chrome).

Il vous suffit alors d’ouvrir un onglet Asana et de cliquer sur un bookmarklet enregistré pour déclencher l’action associée.

2. Exemples de bookmarklets

Accédez à la page Asana bookmarklets pour consulter le code source et l’explication des bookmarklets présentés.

2-1. Actions liées aux tâches

2-1-1. :speech_balloon: Effacer l’activité

Effectuez les deux actions « Afficher l’intégralité des commentaires » et « Masquer les liens d’activités associées » ci-dessous.

javascript:(function() {
	const expandLink = document.querySelector('.TaskStoryFeed-expandLink');
	if (expandLink && expandLink.textContent.match(/\d/)) expandLink.click();
	document.querySelectorAll('.TruncatedRichText-expand').forEach(link => link.click());
	document.querySelectorAll('.TaskStoryFeed-expandMiniStoriesLink').forEach(link => link.click());
	document.querySelectorAll('.BacklinkMiniStory').forEach(line => {line.parentNode.style.display = 'none';});
})();

2-1-2. :arrow_up_down: Afficher l’intégralité des commentaires

Déclenche le clic sur tous les liens « X commentaires de plus » et « En voir plus » dans l’activité
de la tâche.

javascript:(function() {
	const expandLink = document.querySelector('.TaskStoryFeed-expandLink');
	if (expandLink && expandLink.textContent.match(/\d/)) expandLink.click();
	document.querySelectorAll('.TruncatedRichText-expand').forEach(link => link.click());
	document.querySelectorAll('.TaskStoryFeed-expandMiniStoriesLink').forEach(link => link.click());
})();

2-1-3. ↔ Masquer les liens d’activités associées

Permet de masquer tous les « <user_name> a mentionné cette tâche dans une autre tâche : <task_link> » dans l’activité de la tâche. Cette action s’avère utile lorsqu’une tâche est mentionnée dans un trop grand nombre de tâches, ce qui rend les autres commentaires et pièces jointes difficiles à trouver.

javascript:(function() {
	document.querySelectorAll('.BacklinkMiniStory').forEach(line => {line.parentNode.style.display = 'none';});
})();

2-1-4. ⫚ Masquer les sous-tâches terminées

Permet de masquer toutes les sous-tâches terminées dans le volet des détails de tâche sur la droite. Cliquez à nouveau sur le bookmarklet pour les afficher à nouveau.

Si vous avez de nombreuses sous-tâches, je vous recommande également d’utiliser l’extension Chrome Asana Load More.

javascript:(function() {
	const completedSubtaskRows = document.querySelectorAll('.SubtaskTaskRow--completed');
	completedSubtaskRows.forEach(row => {
		row.parentNode.parentNode.style.display = row.parentNode.parentNode.style.display? '': 'none';
	});
})();

2-2. Actions liées à une liste de tâches (ex. : dans un projet)

:exclamation:Remarque : si vous avez beaucoup de tâches et que certaines ne s’affichent pas dans la fenêtre, faites défiler la page vers le bas pour charger toutes les tâches avant d’exécuter les bookmarklets suivants.

2-2-1. :arrow_forward:︎ Activer/désactiver les sections

Permet de réduire/développer les sections dans la vue Liste.

javascript:(function() {
	const firstButtonIcon = document.querySelector('.TaskGroupHeader-toggleButton .Icon');
	if (!firstButtonIcon) return;
	const firstTriangleClassName = firstButtonIcon.classList.contains('DownTriangleIcon')? 'DownTriangleIcon': 'RightTriangleIcon';
	document.querySelectorAll(`.TaskGroupHeader-toggleButton .${firstTriangleClassName}`).forEach(buttonIcon => buttonIcon.parentNode.click());
})();

2-2-2. ▷ Activer/désactiver les sous-tâches

Permet de réduire/développer les sous-tâches dans la vue Liste en cliquant sur les petits symboles :arrow_forward: indiquant l’existence de sous-tâches. S’il y a beaucoup de sous-tâches, le bookmarklet clique sur chaque lien « Charger plus de sous-tâches » (cela peut prendre un certain temps).

javascript:(function() {
	const firstSubtaskButton = document.querySelector('.ProjectSpreadsheetGridRow-subtaskToggleButton');
	const firstTriangleClassName = firstSubtaskButton.firstElementChild.classList.contains('DownTriangleIcon')? 'DownTriangleIcon': 'RightTriangleIcon';
 
	const taskPlaceholderHTMLCollection = document.getElementsByClassName('SpreadsheetTaskRowScrollPlaceholder');
	const taskGroup = document.querySelector('.TaskGroup');
	const buttonAtTheBottom = document.querySelector('.SpreadsheetPotGridContents-addSectionButton');
	setTimeout(function () {if (buttonAtTheBottom) buttonAtTheBottom.scrollIntoView();}, 30);
	setTimeout(function () {taskGroup.style.display = 'none';}, 60);
 
	let monitorTaskStructure = setInterval(() => {
		if (taskPlaceholderHTMLCollection.length == 0) {
			document.querySelectorAll('.ProjectSpreadsheetGridRow-subtaskToggleButton').forEach(function (buttonIcon) {
				if (buttonIcon.firstElementChild.classList.contains(firstTriangleClassName)) buttonIcon.click();
			});
			taskGroup.style.display = '';
			clearInterval(monitorTaskStructure);
			const loadMoreLinkHTMLCollection = document.getElementsByClassName('SpreadsheetTaskList-showMoreLink');
			setTimeout(() => {
				let clickingLoadMoreLinks = setInterval(function() {
					if (!loadMoreLinkHTMLCollection.length) {
						clearInterval(clickingLoadMoreLinks);
					} else {
						loadMoreLinkHTMLCollection[0].scrollIntoView();
						loadMoreLinkHTMLCollection[0].click();
					}
				}, 100);
			}, 200);
		}
	}, 100);
})();

2-2-3. :white_check_mark: Terminer toutes les tâches

Permet de marquer toutes les tâches non terminées visibles dans la vue Liste comme étant terminées. S’il y a beaucoup de tâches, le bookmarklet masque temporairement toutes les tâches
et clique sur 50 tâches à la fois.

javascript:(function() {
	const taskPlaceholderHTMLCollection = document.getElementsByClassName('SpreadsheetTaskRowScrollPlaceholder');
	if (!taskPlaceholderHTMLCollection.length) {
		document.querySelectorAll('.TaskRowCompletionStatus-taskCompletionIcon--incomplete').forEach(incompleteIcon => incompleteIcon.parentNode.click());
	} else {
		const taskGroup = document.querySelector('.TaskGroup');
		const buttonAtTheBottom = document.querySelector('.SpreadsheetPotGridContents-addSectionButton');
		setTimeout(function () {if (buttonAtTheBottom) buttonAtTheBottom.scrollIntoView();}, 30);
		setTimeout(function () {
			taskGroup.style.display = 'none';
			const progressIndicator = document.createElement('span');
			progressIndicator.setAttribute('id', 'progressIndicator');
			progressIndicator.textContent = 'Processing';
			taskGroup.parentNode.appendChild(progressIndicator);
		}, 60);
 
		let monitorTaskStructure = setInterval(() => {
			if (taskPlaceholderHTMLCollection.length == 0) {
				clearInterval(monitorTaskStructure);
				const progressIndicator = document.querySelector('#progressIndicator');
				const allTasks = Array.from(document.querySelectorAll('.TaskRowCompletionStatus-taskCompletionIcon--incomplete'));
				const numProcesses = Math.floor(allTasks.length / 50) + 1;
				let counter = 0;
				let loopTasks = setInterval(() => {				
					progressIndicator.textContent = `Processing (${counter}/${numProcesses})`;
 
					for (let i = 50 * counter; i < Math.min(allTasks.length, 50 * (counter + 1)); i++) {
						allTasks[i].parentNode.click();
						if (i == allTasks.length - 1) {
							clearInterval(loopTasks);
							progressIndicator.remove();
							taskGroup.style.display = '';
						}
					}
					counter += 1;
				}, 500);
			}
		}, 100);
	}
})();

2-2-4. :ballot_box_with_check: Marquer toutes les tâches comme non terminées

Permet de marquer toutes les tâches terminées visibles dans la vue Liste comme étant non terminées. S’il y a beaucoup de tâches, le bookmarklet masque temporairement toutes les tâches
et clique sur 50 tâches à la fois.

javascript:(function() {
	const taskPlaceholderHTMLCollection = document.getElementsByClassName('SpreadsheetTaskRowScrollPlaceholder');
	if (!taskPlaceholderHTMLCollection.length) {
		document.querySelectorAll('.TaskRowCompletionStatus-taskCompletionIcon--complete').forEach(incompleteIcon => incompleteIcon.parentNode.click());
	} else {
		const taskGroup = document.querySelector('.TaskGroup');
		const buttonAtTheBottom = document.querySelector('.SpreadsheetPotGridContents-addSectionButton');
		setTimeout(function () {if (buttonAtTheBottom) buttonAtTheBottom.scrollIntoView();}, 30);
		setTimeout(function () {
			taskGroup.style.display = 'none';
			const progressIndicator = document.createElement('span');
			progressIndicator.setAttribute('id', 'progressIndicator');
			progressIndicator.textContent = 'Processing';
			taskGroup.parentNode.appendChild(progressIndicator);
		}, 60);
 
		let monitorTaskStructure = setInterval(() => {
			if (taskPlaceholderHTMLCollection.length == 0) {
				clearInterval(monitorTaskStructure);
				const progressIndicator = document.querySelector('#progressIndicator');
				const allTasks = Array.from(document.querySelectorAll('.TaskRowCompletionStatus-taskCompletionIcon--complete'));
				const numProcesses = Math.floor(allTasks.length / 50) + 1;
				let counter = 0;
				let loopTasks = setInterval(() => {				
					progressIndicator.textContent = `Processing (${counter}/${numProcesses})`;
					for (let i = 50 * counter; i < Math.min(allTasks.length, 50 * (counter + 1)); i++) {
						allTasks[i].parentNode.click();
						if (i == allTasks.length - 1) {
							clearInterval(loopTasks);
							progressIndicator.remove();
							taskGroup.style.display = '';
						}
					}
					counter += 1;
				}, 500);
			}
		}, 100);
	}
})();

2-3. Autres actions

2-3-1. ☰ Redimensionner la barre latérale

Permet de doubler la largeur de la barre latérale (en la faisant passer à 480 px) pour rendre
les noms de projets longs plus lisibles. Cliquez à nouveau sur le bookmarklet pour revenir à la largeur par défaut.

Cette option a été inspirée par le bookmarklet partagé par @anon91858240 sur https://forum.asana.com/t/sidebar-adjust-size-width/27297/14 !

javascript:(function() {
	const widerWidth = '480px';
	const asanaSidebar = document.querySelector('.AsanaMain-sidebar');
	if (!asanaSidebar) return; 
 
	var newStyle = document.querySelector('#asanaSidebarBookmarkletStyle');
	if (!newStyle) {	
		newStyle = document.createElement('style');
		newStyle.id = 'asanaSidebarBookmarkletStyle';
		document.head.appendChild(newStyle);
	}
 
	if (asanaSidebar.style.width == widerWidth) {
		asanaSidebar.style.width = '240px';
		newStyle.innerText = '';
	} else {
		asanaSidebar.style.width = widerWidth;
		newStyle.innerText = `.AsanaMain-sidebar.AsanaMain-sidebar--isCollapsed {margin-left: -${widerWidth} !important}`;
	}
})();
 

3. Défis

3-1. Affichage simplifié

Lorsqu’un projet comporte de nombreuses tâches, Asana n’affiche qu’une vue simplifiée des tâches les plus éloignées de la zone d’affichage. Seuls les noms des tâches sont alors affichés afin d’économiser des ressources (mémoire et consommation d’énergie, entre autres). Lorsque l’on fait défiler l’écran vers ces tâches, les données sont à nouveau chargées.

Exemple : si je fais défiler le projet depuis le bas de la page, seul le nom des tâches est affiché pour celles situées en haut de la page, et ce pendant un court instant. Moins d’une seconde plus tard, les autres informations sur les tâches sont entièrement affichées.

Si je cible toutes les tâches comme dans la section 2-2, toutes les tâches sont temporairement masquées pour faire croire à Asana qu’il y a suffisamment d’espace pour afficher toutes les tâches. Une fois qu’Asana affiche à nouveau toutes les tâches sous leur forme complète, l’automatisation s’exécute et le masquage s’annule.

3-2. Atténuer le chargement

Si nous réalisons trop d’actions en même temps, nous surchargeons le serveur Asana. Lorsque nous utilisons l’API Asana, ou lorsque nous effectuons des actions groupées sur une sélection multiple de tâches, la limite est d’environ 50 tâches. J’ai donc décidé de ne traiter que 50 tâches à la fois dans les bookmarklets « Terminer toutes les tâches/Marquer toutes les tâches comme non terminées » présentés ci-dessus.

4. Comparaison avec d’autres méthodes

4-1. Comparaison avec d’autres méthodes JavaScript

Comparons différentes méthodes d’automatisation sur Asana. Les méthodes figurant à gauche
sont plus complexes et plus performantes, tandis que celles figurant à droite sont plus simples. Les bookmarklets offrent une solution assez simple, car il n’est nécessaire d’écrire qu’un centième ou un dixième des lignes de code nécessaires au développement d’une extension Chrome. Cependant, une certaine longueur de code reste nécessaire pour répondre aux défis abordés dans la section 3.

Méthodes : OAuth Jeton d’accès personnel Extensions Chrome Bookmarklets Barre d’adresse du navigateur
Exemples : Se connecter à Asana Academy avec un compte Asana Extension Asana officielle Présentés dans cet article Obtenir la liste des espaces de travail
Authentification : Configurer un serveur Coder en dur un jeton d’accès personnel ou l’enregistrer dans des variables d’environnement Utiliser les cookies du navigateur en affichant l’identifiant de l’utilisateur Cookie (idem) Cookie (idem)
Accès : Accorder l’accès Utiliser un jeton d’accès personnel Installer à partir du Chrome Web Store Ajouter aux favoris Saisir l’URL dans la barre d’adresse
Peut exécuter : Actions via l’API Actions via l’API Actions via l’API+DOM Actions via DOM Actions GET via l’API (obtention d’informations)
Convient pour : Outils à grande échelle Écriture de scripts pour utilisation personnelle Distribution Distribution et simplicité de création Utilisation la plus simple
Ne peut pas exécuter : Actions via DOM Actions via DOM (Certaines choses, évidemment) Réaliser des tâches invisibles Actions POST/PUT/DELETE (modification d’informations)
Gestion de grandes quantités de données : Pagination Pagination Pagination Affichage simplifié Pagination (ne peut être automatisée)

Exemple : Terminer toutes les tâches d’un projet

Utiliser les bookmarklets est la méthode la plus simple et rapide.

  • Avec l’API, nous envoyons d’abord une requête GET au point de terminaison /projects/<project_gid>/tasks pour récupérer les tâches du projet. Ensuite, nous parcourons les tâches en boucle et envoyons des requêtes PUT afin de marquer chaque tâche comme terminée. Deux séries d’appels API sont nécessaires : la première pour récupérer les informations, la deuxième pour les mettre à jour.

  • Avec les bookmarklets, nous parcourons directement les tâches affichées et cliquons dessus.

4-2. Comparaison entre JavaScript et CSS

Il est possible d’utiliser le CSS pour personnaliser la mise en forme d’Asana. Les principales différences de CSS sont les suivantes :

  • Pas besoin de s’authentifier, car il n’y a pas d’interaction avec l’API.

  • Il est toujours possible de changer la mise en forme des éléments HTML.

  • Absence de réaction dynamique lorsque certains changements se produisent ou lorsque l’utilisateur effectue une action spécifique.

Comment utiliser la personnalisation CSS : installez des extensions Chrome, telles que Stylish ou User CSS. Vous pouvez soit utiliser des paramètres CSS personnalisés créés par d’autres utilisateurs à partir de bibliothèques telles que userstyles.org, soit créer vos propres règles CSS.

Exemple : Modifier la largeur de la barre latérale

  • Le bookmarklet 2-3-1 mentionné ci-dessus agrandit/réduit la barre latérale lorsque l’on clique dessus.

  • Si vous voulez que la largeur de la barre latérale soit toujours modifiée, mieux vaut personnaliser le CSS.

5. Remarques importantes

5-1. Rétroconception

Conformément aux Conditions générales de l’API Asana, la rétroconception est interdite. Cependant, il est généralement possible d’identifier les éléments DOM en faisant un clic droit et en sélectionnant « Inspect » (Inspecter). D’après moi, cette action ne permet en aucun cas d’accéder aux informations confidentielles d’Asana.

5-2. Possibles modifications de mise en forme

Asana peut à tout moment modifier les ID et les noms de classe que nous utilisons pour identifier les éléments HTML dans l’outil Asana, auquel cas certains bookmarklets ci-dessus cesseront de fonctionner. Si je remarque de tels changements, je mettrai à jour le code, mais n’hésitez pas à m’en informer en commentaire.

5-3. Clause de non-responsabilité

J’ai développé et testé les bookmarklets avec le plus grand soin, mais je ne peux être tenu responsable des problèmes qui pourraient en résulter.

6. Remerciements

La version originale de cette publication a été rédigée en japonais : Asanaブックマークレットのご紹介