Skip to content

Commit cc5531f

Browse files
ValJedharoun
andauthored
Feature/batch tagging (#4997)
* Pro 7746 batch tag images (#4963) * batch tagging UI * back tagging backend --------- Co-authored-by: Harouna Traoré <haroun@users.noreply.github.com> Co-authored-by: Jed <vjeudy@protonmail.com> * Pro 7916 batch tagging notifs (#4980) Co-authored-by: Harouna Traoré <haroun@users.noreply.github.com> * rework UI when no tags or none have been found (#4987) * Pro 8003 manage tags (#4992) * apos context menu expose show method to be called from template ref like we do with hide * updates manage tags button to open image tags manager, refresh images and tags when closing * Big get request (#4993) * adds middleware to convert post request containing __aposGetWithQuery in get request * uses new middleware in media manager * asks for first page when requesting images through IDs * Pro 8000 batch tagging reordering (#4998) * tags are properly sorted when opening popover, or going back to list. Not when checking or unchecking. * when using `__apostGetWithQuery` in body, we keep existing `req.query` instead of erasing it (to keep things like mode and locale). * don't get images when requesting tags only, lighter request. * search empty states wins over the no tags yet state. * hides create button if user has no create permission on tags. * improves tags watcher, works if a tag has its title or slug updated. * adds missing translations for batch tagging (#4995) * removes space * adds changelog * fix lint * removes unused translation key * adds batch operation button identifier for tests --------- Co-authored-by: haroun <1765606+haroun@users.noreply.github.com> Co-authored-by: Harouna Traoré <haroun@users.noreply.github.com>
1 parent dbc684c commit cc5531f

18 files changed

Lines changed: 807 additions & 389 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
* Adds keyboard shortcuts for manipulating widgets in areas. Includes Cut, Copy, Paste, Delete, and Duplicate.
1010
* Adds dynamic choices working with piece manager filters.
1111
* Allow `import.imageTags` (array of image tag IDs) to be passed to the rich text widget when importing (see https://docs.apostrophecms.org/reference/api/rich-text.html#importing-inline-images).
12+
* Adds a new way to make `GET` requests with a large query string. It can become a `POST` request containing the key `__aposGetWithQuery` in its body.
13+
A middleware checks for this key and converts the request back to a `GET` request with the right `req.query` property.
14+
* Adds a new batch operation to tag images.
1215

1316
### Changes
1417

modules/@apostrophecms/express/index.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,23 @@ module.exports = {
417417
bodyParserJson: bodyParser.json({
418418
limit: '16mb',
419419
...(self.options.bodyParser && self.options.bodyParser.json)
420-
})
420+
}),
421+
// Supports POST that are supposed to be GET requests
422+
// when the query string is too big, we convert it back to GET here.
423+
convertPostToGetWithQuery(req, res, next) {
424+
if (req.method === 'POST' && req.body?.__aposGetWithQuery) {
425+
req.method = 'GET';
426+
req.query = {
427+
...req.query,
428+
...req.body.__aposGetWithQuery
429+
};
430+
delete req.body;
431+
const [ url ] = req.url.split('?');
432+
req.url = `${url}?${qs.stringify(req.query)}`;
433+
}
434+
435+
return next();
436+
}
421437
};
422438
},
423439

modules/@apostrophecms/i18n/i18n/de.json

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@
6262
"batchPublishCompleted": "{{ count }} {{ type }} veröffentlicht.",
6363
"batchPublishProgress": "Veröffentlichung {{ type }}...",
6464
"batchRestoreCompleted": "{{ count }} {{ type }} wiederhergestellt.",
65+
"batchTagProgress": "Taggen von {{ count }} {{ type }}...",
66+
"batchUntagProgress": "Entfernen der Markierung von {{ count }} {{ type }}...",
6567
"batchRestoreProgress": "Wiederherstellung {{ type }}...",
6668
"breakpointPreviewClear": "Klarer Haltepunkt",
6769
"breakpointPreviewDesktop": "Desktop",
@@ -550,14 +552,19 @@
550552
"table": "Tabelle",
551553
"tableDescription": "Fügen Sie tabellarische Inhalte zu Ihrer Seite hinzu",
552554
"tableImport": "Tabelle importieren",
555+
"tag": "Tag",
553556
"tagAddTag": "Tag hinzufügen",
557+
"tagCreateHelp": "Das neue Tag wird auf Ihre Auswahl angewendet",
554558
"tagCreateNewTag": "Neuen Tag erstellen",
555-
"tagCreateTag": "Tag \"{{ tag }}\" erstellen",
556-
"tagNewTag": "Neuer Tag",
557-
"tagNoTagsFoundCreateOne": "Erstellen {{ tag }}?",
558-
"tagNoTagsFoundPerhaps": "Wir konnten keine passenden Tags finden. Vielleicht",
559+
"tagCreateNewTagName": "Neues Tag erstellen \"{{ tag }}\"",
560+
"tagCreateTag": "Tag Erstellen",
561+
"tagCreateTagName": "Erstellen {{ tag }}?",
562+
"tagExist": "Dieses Tag existiert bereits",
563+
"tagManage": "Tags verwalten",
564+
"tagNoResultFor": "Keine Ergebnisse für \"{{ tag }}\"",
565+
"tagNoTagsYet": "Noch keine Tags",
559566
"tagSearchApplyTags": "Tags anwenden",
560-
"tagSearchPlaceholder": "Tags...",
567+
"tagSearchPlaceholder": "Tag...",
561568
"tagYourImages": "Weise deinen Bildern Tags zu, um die Suche und das Filtern im Medienmanager zu erleichtern.",
562569
"tags": "Tags",
563570
"takeActionAndCreateNew": "{{ saveLabel }} und erstelle Neues",

modules/@apostrophecms/i18n/i18n/en.json

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@
6363
"batchPublishProgress": "Publishing {{ type }}...",
6464
"batchRestoreCompleted": "Restored {{ count }} {{ type }}.",
6565
"batchRestoreProgress": "Restoring {{ type }}...",
66+
"batchTagProgress": "Tagging {{ count }} {{ type }}...",
67+
"batchUntagProgress": "Untagging {{ count }} {{ type }}...",
6668
"breakpointPreviewClear": "Clear Breakpoint",
6769
"breakpointPreviewDesktop": "Desktop",
6870
"breakpointPreviewExit": "Exit",
@@ -550,14 +552,19 @@
550552
"table": "Table",
551553
"tableDescription": "Add tabular content to your page",
552554
"tableImport": "Import Table",
555+
"tag": "Tag",
553556
"tagAddTag": "Add Tag",
557+
"tagCreateHelp": "The new tag will be applied to your selection",
554558
"tagCreateNewTag": "Create new tag",
555-
"tagCreateTag": "Create tag \"{{ tag }}\"",
556-
"tagNewTag": "New Tag",
557-
"tagNoTagsFoundCreateOne": "create {{ tag }}?",
558-
"tagNoTagsFoundPerhaps": "We couldn't find any matching tags. Perhaps",
559+
"tagCreateNewTagName": "Create new Tag \"{{ tag }}\"",
560+
"tagCreateTag": "Create Tag",
561+
"tagCreateTagName": "create \"{{ tag }}\"",
562+
"tagExist": "This tag already exist",
563+
"tagManage": "Manage Tags",
564+
"tagNoResultFor": "No results for \"{{ tag }}\"",
565+
"tagNoTagsYet": "No tags yet",
559566
"tagSearchApplyTags": "Apply Tags",
560-
"tagSearchPlaceholder": "Tags...",
567+
"tagSearchPlaceholder": "Tag...",
561568
"tagYourImages": "Tag your images to make searching and filtering the media manager easier",
562569
"tags": "Tags",
563570
"takeActionAndCreateNew": "{{ saveLabel }} and Create New",

modules/@apostrophecms/i18n/i18n/es.json

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@
6363
"batchPublishProgress": "Publicando {{ type }}...",
6464
"batchRestoreCompleted": "Restaurado {{ count }} {{ type }}.",
6565
"batchRestoreProgress": "Restaurando {{ type }}...",
66+
"batchTagProgress": "Etiquetando {{ count }} {{ type }}...",
67+
"batchUntagProgress": "Desetiquetando {{ count }} {{ type }}...",
6668
"breakpointPreviewClear": "Punto de ruptura claro",
6769
"breakpointPreviewDesktop": "Escritorio",
6870
"breakpointPreviewExit": "Salir",
@@ -550,12 +552,17 @@
550552
"table": "Tabla",
551553
"tableDescription": "Agregar contenido tabular a tu página",
552554
"tableImport": "Importar tabla",
555+
"tag": "Etiqueta",
553556
"tagAddTag": "Agregar etiqueta",
557+
"tagCreateHelp": "La nueva etiqueta se aplicará a su selección",
554558
"tagCreateNewTag": "Crear nueva etiqueta",
555-
"tagCreateTag": "Crear etiqueta \"{{ tag }}\"",
556-
"tagNewTag": "Nueva etiqueta",
557-
"tagNoTagsFoundCreateOne": "¿Crear {{ tag }}?",
558-
"tagNoTagsFoundPerhaps": "No pudimos encontrar ninguna etiqueta coincidente. Quizás",
559+
"tagCreateNewTagName": "Crear nueva etiqueta \"{{ tag }}\"",
560+
"tagCreateTag": "Crear etiqueta",
561+
"tagCreateTagName": "crear \"{{ tag }}\"",
562+
"tagExist": "Esta etiqueta ya existe",
563+
"tagManage": "Administrar etiquetas",
564+
"tagNoResultFor": "No hay resultados para \"{{ tag }}\"",
565+
"tagNoTagsYet": "No hay etiquetas todavía",
559566
"tagSearchApplyTags": "Aplicar etiquetas",
560567
"tagSearchPlaceholder": "Etiquetas...",
561568
"tagYourImages": "Etiquete sus imágenes para facilitar la busqueda y filtrado en el gestor multimedia",

modules/@apostrophecms/i18n/i18n/fr.json

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@
6363
"batchPublishProgress": "Publication {{ type }}...",
6464
"batchRestoreCompleted": "Restauré {{ count }} {{ type }}.",
6565
"batchRestoreProgress": "Restaurer {{ type }}...",
66+
"batchTagProgress": "Taggage de {{ count }} {{ type }}...",
67+
"batchUntagProgress": "Dé-taggage de {{ count }} {{ type }}...",
6668
"breakpointPreviewClear": "Point d'arrêt clair",
6769
"breakpointPreviewDesktop": "Bureau",
6870
"breakpointPreviewExit": "Sortir",
@@ -550,12 +552,17 @@
550552
"table": "Tableau",
551553
"tableDescription": "Ajouter du contenu tabulaire à votre page",
552554
"tableImport": "Importer tableau",
555+
"tag": "Tag",
553556
"tagAddTag": "Ajouter un tag",
557+
"tagCreateHelp": "Le nouveau tag sera appliqué à votre sélection",
554558
"tagCreateNewTag": "Créer un nouveau tag",
555-
"tagCreateTag": "Créer un tag \"{{ tag }}\"",
556-
"tagNewTag": "Nouveau tag",
557-
"tagNoTagsFoundCreateOne": "créer {{ tag }} ?",
558-
"tagNoTagsFoundPerhaps": "Nous n'avons trouvé aucun tag correspondant. Peut-être",
559+
"tagCreateNewTagName": "Créer le tag \"{{ tag }}\"",
560+
"tagCreateTag": "Créer un tag",
561+
"tagCreateTagName": "créer \"{{ tag }}\"",
562+
"tagExist": "Ce tag existe déjà",
563+
"tagManage": "Gérer Tags",
564+
"tagNoResultFor": "Pas de résultats pour \"{{ tag }}\"",
565+
"tagNoTagsYet": "Pas encore de tags",
559566
"tagSearchApplyTags": "Appliquer des tags",
560567
"tagSearchPlaceholder": "Tags...",
561568
"tagYourImages": "Taguez vos images pour faciliter la recherche et le filtrage du gestionnaire de médias",

modules/@apostrophecms/i18n/i18n/it.json

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@
6363
"batchPublishProgress": "Pubblicazione {{ type }}...",
6464
"batchRestoreCompleted": "Ripristinati {{ count }} {{ type }}.",
6565
"batchRestoreProgress": "Ripristino {{ type }}...",
66+
"batchTagProgress": "Aggiungere tag a {{ count }} {{ type }}",
67+
"batchUntagProgress": "Rimozione dei tag da {{ count }} {{ type }}",
6668
"breakpointPreviewClear": "Chiaro punto di interruzione",
6769
"breakpointPreviewDesktop": "Desktop",
6870
"breakpointPreviewExit": "Esci",
@@ -550,14 +552,19 @@
550552
"table": "Tabella",
551553
"tableDescription": "Aggiungi una tabella alla tua pagina",
552554
"tableImport": "Importa tabella",
553-
"tagAddTag": "Aggiungi etichetta",
554-
"tagCreateNewTag": "Crea nuova etichetta",
555-
"tagCreateTag": "Crea etichetta \"{{ tag }}\"",
556-
"tagNewTag": "Nuova etichetta",
557-
"tagNoTagsFoundCreateOne": "crea {{ tag }}?",
558-
"tagNoTagsFoundPerhaps": "Non siamo riusciti a trovare etichette corrispondenti. Forse",
559-
"tagSearchApplyTags": "Applica etichette",
560-
"tagSearchPlaceholder": "Etichette...",
555+
"tag": "Tag",
556+
"tagAddTag": "Aggiungi tag",
557+
"tagCreateHelp": "Il nuovo tag verrà applicato alla tua selezione",
558+
"tagCreateNewTag": "Crea etichetta",
559+
"tagCreateNewTagName": "Create new Tag \"{{ tag }}\"",
560+
"tagCreateTag": "Creare tag",
561+
"tagCreateTagName": "creare \"{{ tag }}\"",
562+
"tagExist": "Questo tag esiste già",
563+
"tagManage": "Gestisci tag",
564+
"tagNoResultFor": "Nessun risultato per \"{{ tag }}\"",
565+
"tagNoTagsYet": "Nessun tag ancora",
566+
"tagSearchApplyTags": "Applica tag",
567+
"tagSearchPlaceholder": "Tag...",
561568
"tagYourImages": "Tagga le tue immagini per facilitare la ricerca e il filtraggio nel gestore dei media",
562569
"tags": "Tag",
563570
"takeActionAndCreateNew": "{{ saveLabel }} e crea nuovo",

modules/@apostrophecms/i18n/i18n/pt-BR.json

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@
6363
"batchPublishProgress": "Publicando {{ type }}...",
6464
"batchRestoreCompleted": "Restaurado {{ count }} {{ type }}.",
6565
"batchRestoreProgress": "Restaurando {{ type }}...",
66+
"batchTagProgress": "Marcando {{ count }} {{ type }}...",
67+
"batchUntagProgress": "Desmarcando {{ count }} {{ type }}...",
6668
"breakpointPreviewClear": "Ponto de interrupção claro",
6769
"breakpointPreviewDesktop": "Desktop",
6870
"breakpointPreviewExit": "Sair",
@@ -550,12 +552,17 @@
550552
"table": "Tabela",
551553
"tableDescription": "Adicionar conteúdo tabular à sua página",
552554
"tableImport": "Importar tabela",
555+
"tag": "Tag",
553556
"tagAddTag": "Adicionar Tag",
557+
"tagCreateHelp": "A nova tag será aplicada à sua seleção",
554558
"tagCreateNewTag": "Criar nova tag",
555-
"tagCreateTag": "Criar tag \"{{ tag }}\"",
556-
"tagNewTag": "Nova Tag",
557-
"tagNoTagsFoundCreateOne": "criar {{ tag }}?",
558-
"tagNoTagsFoundPerhaps": "Não conseguimos encontrar tags correspondentes. Talvez",
559+
"tagCreateNewTagName": "Criar nova tag \"{{ tag }}\"",
560+
"tagCreateTag": "Criar tag",
561+
"tagCreateTagName": "criar \"{{ tag }}\"",
562+
"tagExist": "Esta tag já existe",
563+
"tagManage": "Gerenciar tags",
564+
"tagNoResultFor": "Nenhum resultado para \"{{ tag }}\"",
565+
"tagNoTagsYet": "Nenhuma tag ainda",
559566
"tagSearchApplyTags": "Aplicar Tags",
560567
"tagSearchPlaceholder": "Tags...",
561568
"tagYourImages": "Adicione tags nas suas imagens para facilitar a pesquisa e a filtragem do gerenciador de mídia",

modules/@apostrophecms/i18n/i18n/sk.json

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@
6363
"batchPublishProgress": "Publikovanie {{ type }}...",
6464
"batchRestoreCompleted": "Obnovené {{ count }} {{ type }}.",
6565
"batchRestoreProgress": "Obnovenie {{ type }}...",
66+
"batchTagProgress": "Označovanie {{ count }} {{ type }}...",
67+
"batchUntagProgress": "Odznačenie {{ count }} {{ type }}...",
6668
"breakpointPreviewClear": "Bod prerušenia",
6769
"breakpointPreviewDesktop": "Desktop",
6870
"breakpointPreviewExit": "Odísť",
@@ -550,10 +552,15 @@
550552
"table": "Tabuľka",
551553
"tableDescription": "Pridať tabuľkový obsah na vašu stránku",
552554
"tableImport": "Importovať tabuľku",
555+
"tag": "Tag",
553556
"tagAddTag": "Pridať tag",
554-
"tagCreateNewTag": "Vytvoriť nový tag",
555-
"tagCreateTag": "Vytvoriť tag \"{{ tag }}\"",
556-
"tagNewTag": "Nový tag",
557+
"tagCreateHelp": "Nová tag sa použije na váš výber",
558+
"tagCreateNewTag": "Vytvoriť tag",
559+
"tagCreateNewTagName": "Vytvoriť novej tag \"{{ tag }}\"",
560+
"tagCreateTag": "Vytvoriť tag",
561+
"tagCreateTagName": "vytvoriť \"{{ tag }}\"",
562+
"tagExist": "Táto tag už existuje",
563+
"tagManage": "Spravovať Tags",
557564
"tagNoTagsFoundCreateOne": "vytvoriť {{ tag }}?",
558565
"tagNoTagsFoundPerhaps": "Nenašli sme žiadne zodpovedajúce tagy. Možno",
559566
"tagSearchApplyTags": "Použiť tagy",

modules/@apostrophecms/image/index.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,28 @@ module.exports = {
6363
relationshipSuggestionIcon: 'image-icon',
6464
relationshipSuggestionFields: []
6565
},
66+
batchOperations: {
67+
add: {
68+
tag: {
69+
label: 'apostrophe:tag',
70+
messages: {
71+
create: {
72+
progress: 'apostrophe:batchTagProgress'
73+
},
74+
add: {
75+
progress: 'apostrophe:batchTagProgress'
76+
},
77+
remove: {
78+
progress: 'apostrophe:batchUntagProgress'
79+
}
80+
},
81+
icon: 'label-icon',
82+
action: 'tag',
83+
permission: 'edit'
84+
}
85+
},
86+
order: [ 'tag', 'archive', 'restore' ]
87+
},
6688
utilityOperations: {
6789
remove: [ 'new' ]
6890
},
@@ -292,6 +314,56 @@ module.exports = {
292314
b = b * 100 / max;
293315
return Math.abs(a - b) < 1;
294316
}
317+
},
318+
async tag(req) {
319+
const title = self.apos.launder.string(req.body.title);
320+
const slug = self.apos.launder.string(req.body.slug);
321+
const operation = self.apos.launder.string(req.body.operation);
322+
323+
if (!Array.isArray(req.body._ids)) {
324+
throw self.apos.error('invalid', 'Missing _ids');
325+
}
326+
if (![ 'create', 'add', 'remove' ].includes(operation)) {
327+
throw self.apos.error('invalid', `"${operation}" is not a valid operation`);
328+
}
329+
330+
req.body._ids = req.body._ids.map(_id => {
331+
return self.inferIdLocaleAndMode(req, self.apos.launder.id(_id));
332+
});
333+
334+
const imageTagManager = self.apos.doc.getManager('@apostrophecms/image-tag');
335+
const tag = (operation === 'create')
336+
? await imageTagManager.insert(
337+
req,
338+
{
339+
...imageTagManager.newInstance(),
340+
title
341+
}
342+
)
343+
: await imageTagManager.find(req, { slug }).toObject();
344+
345+
return self.apos.modules['@apostrophecms/job'].runBatch(
346+
req,
347+
req.body._ids,
348+
async function(req, id) {
349+
const piece = await self.findOneForEditing(req, { _id: id });
350+
if (!piece) {
351+
throw self.apos.error('notfound');
352+
}
353+
354+
piece._tags = operation === 'remove'
355+
? piece._tags.filter(pieceTag => pieceTag.aposDocId !== tag.aposDocId)
356+
: piece._tags
357+
.filter(pieceTag => pieceTag.aposDocId !== tag.aposDocId)
358+
.concat(tag);
359+
360+
await self.update(req, piece);
361+
},
362+
{
363+
action: 'tag',
364+
docTypes: [ self.__meta.name ]
365+
}
366+
);
295367
}
296368
}
297369
}),

0 commit comments

Comments
 (0)