diff --git a/betty/assets/locale/ar/betty.po b/betty/assets/locale/ar/betty.po index 142a11c86..4ff077a32 100644 --- a/betty/assets/locale/ar/betty.po +++ b/betty/assets/locale/ar/betty.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Betty VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2025-02-02 10:13+0000\n" +"POT-Creation-Date: 2025-02-09 01:22+0000\n" "PO-Revision-Date: 2024-11-30 19:00+0000\n" "Last-Translator: Bart Feenstra \n" "Language: ar\n" @@ -275,6 +275,9 @@ msgstr "" msgid "Clear all caches" msgstr "" +msgid "Close" +msgstr "" + msgid "Conference" msgstr "" @@ -302,7 +305,7 @@ msgstr "" msgid "Correspondence" msgstr "" -msgid "Cotton Candy is Betty's default theme." +msgid "Cotton Candy is Betty's legacy theme." msgstr "" #, python-brace-format @@ -439,6 +442,9 @@ msgstr "" msgid "Enrich your ancestry with information from Wikipedia" msgstr "" +msgid "Entity type" +msgstr "" + msgid "Event" msgstr "" @@ -482,6 +488,9 @@ msgstr "" msgid "Files" msgstr "" +msgid "Filters" +msgstr "" + msgid "Find out more about this image on Wikimedia Commons." msgstr "" @@ -514,6 +523,9 @@ msgstr "" msgid "Generating your site to {output_directory}." msgstr "" +msgid "Go to the front page" +msgstr "" + msgid "HTTP API Documentation" msgstr "" @@ -535,6 +547,9 @@ msgstr "" msgid "I'm sorry, dear, but it seems there are no sources." msgstr "" +msgid "I'm sorry, dear, but it seems there is nothing to show." +msgstr "" + msgid "I'm sorry, dear, but it seems this page does not exist." msgstr "" @@ -558,6 +573,9 @@ msgstr "" msgid "Invalid YAML: {error}." msgstr "" +msgid "Keywords" +msgstr "" + msgid "Language" msgstr "" @@ -641,6 +659,9 @@ msgstr "زواج" msgid "Media" msgstr "" +msgid "Media references" +msgstr "" + msgid "Menu" msgstr "" @@ -683,6 +704,9 @@ msgstr "" msgid "Other attendees" msgstr "" +msgid "Other places contained by this one" +msgstr "" + msgid "Page not found" msgstr "" @@ -785,9 +809,16 @@ msgstr "مراجع" msgid "Region" msgstr "" +msgid "Reset" +msgstr "" + msgid "Residence" msgstr "" +#, python-brace-format +msgid "Results ({{{ betty-search-results-count }}})" +msgstr "" + msgid "Retirement" msgstr "" @@ -795,6 +826,9 @@ msgstr "" msgid "Saved your project to {configuration_file}." msgstr "" +msgid "Search" +msgstr "" + msgid "Serve a generated site" msgstr "" @@ -823,6 +857,9 @@ msgstr "" msgid "Subject" msgstr "" +msgid "Subjects" +msgstr "" + #, python-brace-format msgid "" "The Gramps {gramps_entity_reference} entity has a " @@ -914,6 +951,9 @@ msgstr "" msgid "This field is required." msgstr "" +msgid "This information is unavailable to protect people's privacy." +msgstr "" + msgid "This must be a boolean." msgstr "" @@ -953,9 +993,21 @@ msgstr "" msgid "Timeline" msgstr "الخط الزمني" +msgid "Toggle menu" +msgstr "" + +msgid "Toggle search" +msgstr "" + +msgid "Toggle translations" +msgstr "" + msgid "Town" msgstr "" +msgid "Translations" +msgstr "" + msgid "Trees" msgstr "" @@ -1055,7 +1107,7 @@ msgstr "" msgid "" "Your project has no theme enabled. This means your site's pages may look " -"bare. Try the \"cotton-candy\" extension." +"bare. Try the \"raspberry-mint\" extension." msgstr "" #, python-brace-format @@ -1122,6 +1174,10 @@ msgstr "" msgid "in %(place)s" msgstr "" +#, python-format +msgid "in %(source)s" +msgstr "" + msgid "" "npm (https://www.npmjs.com/) must be available for features that require " "Node.js packages to be installed. Ensure that the `npm` executable is " @@ -1156,6 +1212,9 @@ msgstr "" msgid "show more" msgstr "" +msgid "sometime" +msgstr "" + #, python-brace-format msgid "sometime after around {start_date}" msgstr "" diff --git a/betty/assets/locale/betty.pot b/betty/assets/locale/betty.pot index 33abeb92d..b4ab9acb6 100644 --- a/betty/assets/locale/betty.pot +++ b/betty/assets/locale/betty.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Betty VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2025-02-02 10:13+0000\n" +"POT-Creation-Date: 2025-02-09 01:22+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -263,6 +263,9 @@ msgstr "" msgid "Clear all caches" msgstr "" +msgid "Close" +msgstr "" + msgid "Conference" msgstr "" @@ -290,7 +293,7 @@ msgstr "" msgid "Correspondence" msgstr "" -msgid "Cotton Candy is Betty's default theme." +msgid "Cotton Candy is Betty's legacy theme." msgstr "" #, python-brace-format @@ -427,6 +430,9 @@ msgstr "" msgid "Enrich your ancestry with information from Wikipedia" msgstr "" +msgid "Entity type" +msgstr "" + msgid "Event" msgstr "" @@ -470,6 +476,9 @@ msgstr "" msgid "Files" msgstr "" +msgid "Filters" +msgstr "" + msgid "Find out more about this image on Wikimedia Commons." msgstr "" @@ -502,6 +511,9 @@ msgstr "" msgid "Generating your site to {output_directory}." msgstr "" +msgid "Go to the front page" +msgstr "" + msgid "HTTP API Documentation" msgstr "" @@ -523,6 +535,9 @@ msgstr "" msgid "I'm sorry, dear, but it seems there are no sources." msgstr "" +msgid "I'm sorry, dear, but it seems there is nothing to show." +msgstr "" + msgid "I'm sorry, dear, but it seems this page does not exist." msgstr "" @@ -546,6 +561,9 @@ msgstr "" msgid "Invalid YAML: {error}." msgstr "" +msgid "Keywords" +msgstr "" + msgid "Language" msgstr "" @@ -629,6 +647,9 @@ msgstr "" msgid "Media" msgstr "" +msgid "Media references" +msgstr "" + msgid "Menu" msgstr "" @@ -671,6 +692,9 @@ msgstr "" msgid "Other attendees" msgstr "" +msgid "Other places contained by this one" +msgstr "" + msgid "Page not found" msgstr "" @@ -773,9 +797,16 @@ msgstr "" msgid "Region" msgstr "" +msgid "Reset" +msgstr "" + msgid "Residence" msgstr "" +#, python-brace-format +msgid "Results ({{{ betty-search-results-count }}})" +msgstr "" + msgid "Retirement" msgstr "" @@ -783,6 +814,9 @@ msgstr "" msgid "Saved your project to {configuration_file}." msgstr "" +msgid "Search" +msgstr "" + msgid "Serve a generated site" msgstr "" @@ -811,6 +845,9 @@ msgstr "" msgid "Subject" msgstr "" +msgid "Subjects" +msgstr "" + #, python-brace-format msgid "" "The Gramps {gramps_entity_reference} entity has a " @@ -890,6 +927,9 @@ msgstr "" msgid "This field is required." msgstr "" +msgid "This information is unavailable to protect people's privacy." +msgstr "" + msgid "This must be a boolean." msgstr "" @@ -929,9 +969,21 @@ msgstr "" msgid "Timeline" msgstr "" +msgid "Toggle menu" +msgstr "" + +msgid "Toggle search" +msgstr "" + +msgid "Toggle translations" +msgstr "" + msgid "Town" msgstr "" +msgid "Translations" +msgstr "" + msgid "Trees" msgstr "" @@ -1031,7 +1083,7 @@ msgstr "" msgid "" "Your project has no theme enabled. This means your site's pages may look " -"bare. Try the \"cotton-candy\" extension." +"bare. Try the \"raspberry-mint\" extension." msgstr "" #, python-brace-format @@ -1098,6 +1150,10 @@ msgstr "" msgid "in %(place)s" msgstr "" +#, python-format +msgid "in %(source)s" +msgstr "" + msgid "" "npm (https://www.npmjs.com/) must be available for features that require " "Node.js packages to be installed. Ensure that the `npm` executable is " @@ -1128,6 +1184,9 @@ msgstr "" msgid "show more" msgstr "" +msgid "sometime" +msgstr "" + #, python-brace-format msgid "sometime after around {start_date}" msgstr "" diff --git a/betty/assets/locale/de-DE/betty.po b/betty/assets/locale/de-DE/betty.po index bcddb4c9d..50c26998f 100644 --- a/betty/assets/locale/de-DE/betty.po +++ b/betty/assets/locale/de-DE/betty.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Betty VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2025-02-02 10:13+0000\n" +"POT-Creation-Date: 2025-02-09 01:22+0000\n" "PO-Revision-Date: 2024-11-29 18:58+0000\n" "Last-Translator: Ettore Atalan \n" "Language: de_DE\n" @@ -292,6 +292,9 @@ msgstr "" msgid "Clear all caches" msgstr "" +msgid "Close" +msgstr "" + msgid "Conference" msgstr "Konferenz" @@ -319,8 +322,8 @@ msgstr "" msgid "Correspondence" msgstr "Schriftverkehr" -msgid "Cotton Candy is Betty's default theme." -msgstr "Cotton Candy ist Betty's Standard-Thema." +msgid "Cotton Candy is Betty's legacy theme." +msgstr "" #, python-brace-format msgid "Could not extract {file_path} as a gzip file (*.gz)." @@ -470,6 +473,9 @@ msgstr "Verlobung" msgid "Enrich your ancestry with information from Wikipedia" msgstr "" +msgid "Entity type" +msgstr "" + msgid "Event" msgstr "Ereignis" @@ -513,6 +519,9 @@ msgstr "" msgid "Files" msgstr "Dateien" +msgid "Filters" +msgstr "" + msgid "Find out more about this image on Wikimedia Commons." msgstr "Mehr über dieses Bild erfährst du auf Wikimedia Commons." @@ -545,6 +554,9 @@ msgstr "Erzeuge statische, öffentliche Dateien..." msgid "Generating your site to {output_directory}." msgstr "Erzeuge deine Site nach {output_directory}." +msgid "Go to the front page" +msgstr "" + msgid "HTTP API Documentation" msgstr "HTTP API Dokumentation" @@ -566,6 +578,9 @@ msgstr "Es tut mir leid, aber sieht so aus, als ob es keine Orte gibt." msgid "I'm sorry, dear, but it seems there are no sources." msgstr "Es tut mir leid, aber sieht so aus, als ob es keine Quellen gibt." +msgid "I'm sorry, dear, but it seems there is nothing to show." +msgstr "" + msgid "I'm sorry, dear, but it seems this page does not exist." msgstr "" @@ -589,6 +604,9 @@ msgstr "Invalid JSON: {error}." msgid "Invalid YAML: {error}." msgstr "Invalid YAML: {error}." +msgid "Keywords" +msgstr "" + msgid "Language" msgstr "Sprache" @@ -672,6 +690,9 @@ msgstr "Hochzeit" msgid "Media" msgstr "Medien" +msgid "Media references" +msgstr "" + msgid "Menu" msgstr "Menü" @@ -714,6 +735,9 @@ msgstr "Veranstalter" msgid "Other attendees" msgstr "Andere Teilnehmer" +msgid "Other places contained by this one" +msgstr "" + msgid "Page not found" msgstr "" @@ -823,9 +847,16 @@ msgstr "Referenzen" msgid "Region" msgstr "" +msgid "Reset" +msgstr "" + msgid "Residence" msgstr "Wohnort" +#, python-brace-format +msgid "Results ({{{ betty-search-results-count }}})" +msgstr "" + msgid "Retirement" msgstr "Ruhestand" @@ -833,6 +864,9 @@ msgstr "Ruhestand" msgid "Saved your project to {configuration_file}." msgstr "" +msgid "Search" +msgstr "" + msgid "Serve a generated site" msgstr "" @@ -861,6 +895,9 @@ msgstr "" msgid "Subject" msgstr "Betreff" +msgid "Subjects" +msgstr "" + #, python-brace-format msgid "" "The Gramps {gramps_entity_reference} entity has a " @@ -949,6 +986,9 @@ msgstr "" msgid "This field is required." msgstr "Diese Feld ist erforderlich." +msgid "This information is unavailable to protect people's privacy." +msgstr "" + msgid "This must be a boolean." msgstr "Es muss ein Boolean sein." @@ -990,9 +1030,21 @@ msgstr "" msgid "Timeline" msgstr "Zeitschiene" +msgid "Toggle menu" +msgstr "" + +msgid "Toggle search" +msgstr "" + +msgid "Toggle translations" +msgstr "" + msgid "Town" msgstr "" +msgid "Translations" +msgstr "" + msgid "Trees" msgstr "Bäume" @@ -1101,7 +1153,7 @@ msgstr "" msgid "" "Your project has no theme enabled. This means your site's pages may look " -"bare. Try the \"cotton-candy\" extension." +"bare. Try the \"raspberry-mint\" extension." msgstr "" #, python-brace-format @@ -1168,6 +1220,10 @@ msgstr "von {start_date} bis {end_date}" msgid "in %(place)s" msgstr "in %(place)s" +#, python-format +msgid "in %(source)s" +msgstr "" + msgid "" "npm (https://www.npmjs.com/) must be available for features that require " "Node.js packages to be installed. Ensure that the `npm` executable is " @@ -1201,6 +1257,9 @@ msgstr "zeige weniger" msgid "show more" msgstr "zeige mehr" +msgid "sometime" +msgstr "" + #, python-brace-format msgid "sometime after around {start_date}" msgstr "irgendwann nach etwa {start_date}" diff --git a/betty/assets/locale/fr-FR/betty.po b/betty/assets/locale/fr-FR/betty.po index 015d464b0..24e24b592 100644 --- a/betty/assets/locale/fr-FR/betty.po +++ b/betty/assets/locale/fr-FR/betty.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2025-02-02 10:13+0000\n" +"POT-Creation-Date: 2025-02-09 01:22+0000\n" "PO-Revision-Date: 2024-11-29 18:58+0000\n" "Last-Translator: Bart Feenstra \n" "Language: fr_FR\n" @@ -270,6 +270,9 @@ msgstr "" msgid "Clear all caches" msgstr "" +msgid "Close" +msgstr "" + msgid "Conference" msgstr "" @@ -297,7 +300,7 @@ msgstr "" msgid "Correspondence" msgstr "Correspondance" -msgid "Cotton Candy is Betty's default theme." +msgid "Cotton Candy is Betty's legacy theme." msgstr "" #, python-brace-format @@ -434,6 +437,9 @@ msgstr "Fiançailles" msgid "Enrich your ancestry with information from Wikipedia" msgstr "" +msgid "Entity type" +msgstr "" + msgid "Event" msgstr "Evénement" @@ -477,6 +483,9 @@ msgstr "" msgid "Files" msgstr "" +msgid "Filters" +msgstr "" + msgid "Find out more about this image on Wikimedia Commons." msgstr "" @@ -509,6 +518,9 @@ msgstr "" msgid "Generating your site to {output_directory}." msgstr "" +msgid "Go to the front page" +msgstr "" + msgid "HTTP API Documentation" msgstr "" @@ -530,6 +542,9 @@ msgstr "" msgid "I'm sorry, dear, but it seems there are no sources." msgstr "" +msgid "I'm sorry, dear, but it seems there is nothing to show." +msgstr "" + msgid "I'm sorry, dear, but it seems this page does not exist." msgstr "" @@ -553,6 +568,9 @@ msgstr "" msgid "Invalid YAML: {error}." msgstr "" +msgid "Keywords" +msgstr "" + msgid "Language" msgstr "Langage" @@ -636,6 +654,9 @@ msgstr "Mariage" msgid "Media" msgstr "Média" +msgid "Media references" +msgstr "" + msgid "Menu" msgstr "Menu" @@ -678,6 +699,9 @@ msgstr "" msgid "Other attendees" msgstr "Autres participants" +msgid "Other places contained by this one" +msgstr "" + msgid "Page not found" msgstr "" @@ -780,9 +804,16 @@ msgstr "Références" msgid "Region" msgstr "" +msgid "Reset" +msgstr "" + msgid "Residence" msgstr "Résidence" +#, python-brace-format +msgid "Results ({{{ betty-search-results-count }}})" +msgstr "" + msgid "Retirement" msgstr "Retraite" @@ -790,6 +821,9 @@ msgstr "Retraite" msgid "Saved your project to {configuration_file}." msgstr "" +msgid "Search" +msgstr "" + msgid "Serve a generated site" msgstr "" @@ -818,6 +852,9 @@ msgstr "" msgid "Subject" msgstr "Sujet" +msgid "Subjects" +msgstr "" + #, python-brace-format msgid "" "The Gramps {gramps_entity_reference} entity has a " @@ -897,6 +934,9 @@ msgstr "" msgid "This field is required." msgstr "" +msgid "This information is unavailable to protect people's privacy." +msgstr "" + msgid "This must be a boolean." msgstr "" @@ -940,9 +980,21 @@ msgstr "" msgid "Timeline" msgstr "Chronologie" +msgid "Toggle menu" +msgstr "" + +msgid "Toggle search" +msgstr "" + +msgid "Toggle translations" +msgstr "" + msgid "Town" msgstr "" +msgid "Translations" +msgstr "" + msgid "Trees" msgstr "" @@ -1044,7 +1096,7 @@ msgstr "" msgid "" "Your project has no theme enabled. This means your site's pages may look " -"bare. Try the \"cotton-candy\" extension." +"bare. Try the \"raspberry-mint\" extension." msgstr "" #, python-brace-format @@ -1127,6 +1179,10 @@ msgstr "du {start_date} au {end_date} OK2" msgid "in %(place)s" msgstr "à %(place)s" +#, python-format +msgid "in %(source)s" +msgstr "" + msgid "" "npm (https://www.npmjs.com/) must be available for features that require " "Node.js packages to be installed. Ensure that the `npm` executable is " @@ -1157,6 +1213,9 @@ msgstr "voir moins" msgid "show more" msgstr "voir plus" +msgid "sometime" +msgstr "" + #, python-brace-format msgid "sometime after around {start_date}" msgstr "environ après le {start_date}" diff --git a/betty/assets/locale/he/betty.po b/betty/assets/locale/he/betty.po index 211c75450..aab728a34 100644 --- a/betty/assets/locale/he/betty.po +++ b/betty/assets/locale/he/betty.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Betty VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2025-02-02 10:13+0000\n" +"POT-Creation-Date: 2025-02-09 01:22+0000\n" "PO-Revision-Date: 2024-11-30 19:00+0000\n" "Last-Translator: Bart Feenstra \n" "Language: he\n" @@ -266,6 +266,9 @@ msgstr "" msgid "Clear all caches" msgstr "" +msgid "Close" +msgstr "" + msgid "Conference" msgstr "" @@ -293,7 +296,7 @@ msgstr "" msgid "Correspondence" msgstr "" -msgid "Cotton Candy is Betty's default theme." +msgid "Cotton Candy is Betty's legacy theme." msgstr "" #, python-brace-format @@ -430,6 +433,9 @@ msgstr "" msgid "Enrich your ancestry with information from Wikipedia" msgstr "" +msgid "Entity type" +msgstr "" + msgid "Event" msgstr "" @@ -473,6 +479,9 @@ msgstr "" msgid "Files" msgstr "" +msgid "Filters" +msgstr "" + msgid "Find out more about this image on Wikimedia Commons." msgstr "" @@ -505,6 +514,9 @@ msgstr "" msgid "Generating your site to {output_directory}." msgstr "" +msgid "Go to the front page" +msgstr "" + msgid "HTTP API Documentation" msgstr "" @@ -526,6 +538,9 @@ msgstr "" msgid "I'm sorry, dear, but it seems there are no sources." msgstr "" +msgid "I'm sorry, dear, but it seems there is nothing to show." +msgstr "" + msgid "I'm sorry, dear, but it seems this page does not exist." msgstr "" @@ -549,6 +564,9 @@ msgstr "" msgid "Invalid YAML: {error}." msgstr "" +msgid "Keywords" +msgstr "" + msgid "Language" msgstr "" @@ -632,6 +650,9 @@ msgstr "" msgid "Media" msgstr "" +msgid "Media references" +msgstr "" + msgid "Menu" msgstr "" @@ -674,6 +695,9 @@ msgstr "" msgid "Other attendees" msgstr "" +msgid "Other places contained by this one" +msgstr "" + msgid "Page not found" msgstr "" @@ -776,9 +800,16 @@ msgstr "" msgid "Region" msgstr "" +msgid "Reset" +msgstr "" + msgid "Residence" msgstr "" +#, python-brace-format +msgid "Results ({{{ betty-search-results-count }}})" +msgstr "" + msgid "Retirement" msgstr "" @@ -786,6 +817,9 @@ msgstr "" msgid "Saved your project to {configuration_file}." msgstr "" +msgid "Search" +msgstr "" + msgid "Serve a generated site" msgstr "" @@ -814,6 +848,9 @@ msgstr "" msgid "Subject" msgstr "" +msgid "Subjects" +msgstr "" + #, python-brace-format msgid "" "The Gramps {gramps_entity_reference} entity has a " @@ -893,6 +930,9 @@ msgstr "" msgid "This field is required." msgstr "" +msgid "This information is unavailable to protect people's privacy." +msgstr "" + msgid "This must be a boolean." msgstr "" @@ -932,9 +972,21 @@ msgstr "" msgid "Timeline" msgstr "" +msgid "Toggle menu" +msgstr "" + +msgid "Toggle search" +msgstr "" + +msgid "Toggle translations" +msgstr "" + msgid "Town" msgstr "" +msgid "Translations" +msgstr "" + msgid "Trees" msgstr "" @@ -1034,7 +1086,7 @@ msgstr "" msgid "" "Your project has no theme enabled. This means your site's pages may look " -"bare. Try the \"cotton-candy\" extension." +"bare. Try the \"raspberry-mint\" extension." msgstr "" #, python-brace-format @@ -1101,6 +1153,10 @@ msgstr "" msgid "in %(place)s" msgstr "" +#, python-format +msgid "in %(source)s" +msgstr "" + msgid "" "npm (https://www.npmjs.com/) must be available for features that require " "Node.js packages to be installed. Ensure that the `npm` executable is " @@ -1131,6 +1187,9 @@ msgstr "" msgid "show more" msgstr "" +msgid "sometime" +msgstr "" + #, python-brace-format msgid "sometime after around {start_date}" msgstr "" diff --git a/betty/assets/locale/nl-NL/betty.po b/betty/assets/locale/nl-NL/betty.po index 845befeea..49fc20b78 100644 --- a/betty/assets/locale/nl-NL/betty.po +++ b/betty/assets/locale/nl-NL/betty.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2025-02-02 10:13+0000\n" +"POT-Creation-Date: 2025-02-09 01:22+0000\n" "PO-Revision-Date: 2024-12-08 20:00+0000\n" "Last-Translator: Bart Feenstra \n" "Language: nl_NL\n" @@ -310,6 +310,9 @@ msgstr "Stad" msgid "Clear all caches" msgstr "Leeg alle caches" +msgid "Close" +msgstr "Sluiten" + msgid "Conference" msgstr "Conferentie" @@ -339,8 +342,8 @@ msgstr "" msgid "Correspondence" msgstr "Correspondentie" -msgid "Cotton Candy is Betty's default theme." -msgstr "Cotton Candy is Betty's standaardthema." +msgid "Cotton Candy is Betty's legacy theme." +msgstr "" #, python-brace-format msgid "Could not extract {file_path} as a gzip file (*.gz)." @@ -495,6 +498,9 @@ msgstr "Verloving" msgid "Enrich your ancestry with information from Wikipedia" msgstr "Verrijk je familiegeschiedenis met informatie van Wikipedia" +msgid "Entity type" +msgstr "Entiteitstype" + msgid "Event" msgstr "Gebeurtenis" @@ -538,6 +544,9 @@ msgstr "Bestandsverwijzingen" msgid "Files" msgstr "Bestanden" +msgid "Filters" +msgstr "Filters" + msgid "Find out more about this image on Wikimedia Commons." msgstr "Vind meer informatie over deze afbeelding op Wikimedia Commons." @@ -572,6 +581,9 @@ msgstr "Statische publieke bestanden aan het genereren..." msgid "Generating your site to {output_directory}." msgstr "Bezig je site te genereren naar {output_directory}." +msgid "Go to the front page" +msgstr "" + msgid "HTTP API Documentation" msgstr "HTTP API-documentatie" @@ -593,6 +605,9 @@ msgstr "Het spijt me, schat, maar het lijkt erop dat er geen plaatsen zijn." msgid "I'm sorry, dear, but it seems there are no sources." msgstr "Het spijt me, schat, maar het lijkt erop dat er geen bronnen zijn." +msgid "I'm sorry, dear, but it seems there is nothing to show." +msgstr "Het spijt me, schat, maar het lijkt erop dat er niets is om te laten zien." + msgid "I'm sorry, dear, but it seems this page does not exist." msgstr "Het spijt me, lieverd, maar het lijkt erop dat deze pagina niet bestaat." @@ -618,6 +633,9 @@ msgstr "Ongeldige JSON: {error}." msgid "Invalid YAML: {error}." msgstr "Ongeldige YAML: {error}." +msgid "Keywords" +msgstr "" + msgid "Language" msgstr "Taal" @@ -701,6 +719,9 @@ msgstr "Huwelijk" msgid "Media" msgstr "Media" +msgid "Media references" +msgstr "" + msgid "Menu" msgstr "Menu" @@ -746,6 +767,9 @@ msgstr "Organisator" msgid "Other attendees" msgstr "Overige aanwezigen" +msgid "Other places contained by this one" +msgstr "" + msgid "Page not found" msgstr "Pagina niet gevonden" @@ -859,9 +883,16 @@ msgstr "Referenties" msgid "Region" msgstr "Regio" +msgid "Reset" +msgstr "Reset" + msgid "Residence" msgstr "Woonplaats" +#, python-brace-format +msgid "Results ({{{ betty-search-results-count }}})" +msgstr "Resultaten ({{{ betty-search-results-count }}})" + msgid "Retirement" msgstr "Pensioen" @@ -869,6 +900,9 @@ msgstr "Pensioen" msgid "Saved your project to {configuration_file}." msgstr "Configuratie naar \"{configuration_file}\" opgeslagen." +msgid "Search" +msgstr "Zoeken" + msgid "Serve a generated site" msgstr "Serveer een gegenereerde site" @@ -897,6 +931,9 @@ msgstr "Straat" msgid "Subject" msgstr "Onderwerp" +msgid "Subjects" +msgstr "" + #, python-brace-format msgid "" "The Gramps {gramps_entity_reference} entity has a " @@ -991,6 +1028,9 @@ msgstr "" msgid "This field is required." msgstr "Dit veld is vereist." +msgid "This information is unavailable to protect people's privacy." +msgstr "" + msgid "This must be a boolean." msgstr "Dit moet een booleaan zijn." @@ -1030,9 +1070,21 @@ msgstr "De gegevens van deze bron zijn niet beschikbaar vanwege privacyredenen." msgid "Timeline" msgstr "Tijdlijn" +msgid "Toggle menu" +msgstr "" + +msgid "Toggle search" +msgstr "" + +msgid "Toggle translations" +msgstr "" + msgid "Town" msgstr "Town" +msgid "Translations" +msgstr "Vertalingen" + msgid "Trees" msgstr "Stambomen" @@ -1146,7 +1198,7 @@ msgstr "" msgid "" "Your project has no theme enabled. This means your site's pages may look " -"bare. Try the \"cotton-candy\" extension." +"bare. Try the \"raspberry-mint\" extension." msgstr "" "Er is geen thema ingeschakeld voor je project. Dit betekent dat de " "pagina's van je site er leeg uit kunnen zien. Probeer de \"cotton-" @@ -1216,6 +1268,10 @@ msgstr "vanaf {start_date} tot en met {end_date}" msgid "in %(place)s" msgstr "te %(place)s" +#, python-format +msgid "in %(source)s" +msgstr "" + msgid "" "npm (https://www.npmjs.com/) must be available for features that require " "Node.js packages to be installed. Ensure that the `npm` executable is " @@ -1249,6 +1305,9 @@ msgstr "toon minder" msgid "show more" msgstr "toon meer" +msgid "sometime" +msgstr "" + #, python-brace-format msgid "sometime after around {start_date}" msgstr "ergens na ongeveer {start_date}" diff --git a/betty/assets/locale/uk/betty.po b/betty/assets/locale/uk/betty.po index 4526c617b..c2a81342a 100644 --- a/betty/assets/locale/uk/betty.po +++ b/betty/assets/locale/uk/betty.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Betty 0.4\n" "Report-Msgid-Bugs-To: illia@maier.page\n" -"POT-Creation-Date: 2025-02-02 10:13+0000\n" +"POT-Creation-Date: 2025-02-09 01:22+0000\n" "PO-Revision-Date: 2024-11-30 19:00+0000\n" "Last-Translator: Illia Maier \n" "Language: uk\n" @@ -313,6 +313,9 @@ msgstr "Місто" msgid "Clear all caches" msgstr "Очистити всі кеші" +msgid "Close" +msgstr "" + msgid "Conference" msgstr "Конференція" @@ -342,8 +345,8 @@ msgstr "" msgid "Correspondence" msgstr "Листування" -msgid "Cotton Candy is Betty's default theme." -msgstr "Тема за замовчуванням для Betty — Cotton Candy." +msgid "Cotton Candy is Betty's legacy theme." +msgstr "" #, python-brace-format msgid "Could not extract {file_path} as a gzip file (*.gz)." @@ -495,6 +498,9 @@ msgstr "Заручини" msgid "Enrich your ancestry with information from Wikipedia" msgstr "Збагатити своїх предків з інформацією з Вікіпедії" +msgid "Entity type" +msgstr "" + msgid "Event" msgstr "Подія" @@ -538,6 +544,9 @@ msgstr "Посилання на файли" msgid "Files" msgstr "Файли" +msgid "Filters" +msgstr "" + msgid "Find out more about this image on Wikimedia Commons." msgstr "Дізнайтеся більше про це зображення на Wikimedia Commons." @@ -572,6 +581,9 @@ msgstr "Генерація статичних публічних файлів... msgid "Generating your site to {output_directory}." msgstr "Генерація вашого сайту в {output_directory}." +msgid "Go to the front page" +msgstr "" + msgid "HTTP API Documentation" msgstr "документація HTTP API" @@ -593,6 +605,9 @@ msgstr "Вибачте, любий, але, здається, немає міс msgid "I'm sorry, dear, but it seems there are no sources." msgstr "Вибачте, любий, але, здається, немає джерел." +msgid "I'm sorry, dear, but it seems there is nothing to show." +msgstr "" + msgid "I'm sorry, dear, but it seems this page does not exist." msgstr "Вибачте, любий, але, здається, ця сторінка не існує." @@ -616,6 +631,9 @@ msgstr "Недійсний JSON: {error}." msgid "Invalid YAML: {error}." msgstr "Недійсний YAML: {error}." +msgid "Keywords" +msgstr "" + msgid "Language" msgstr "Мова" @@ -701,6 +719,9 @@ msgstr "Шлюб" msgid "Media" msgstr "Медіа" +msgid "Media references" +msgstr "" + msgid "Menu" msgstr "Меню" @@ -746,6 +767,9 @@ msgstr "Організатор" msgid "Other attendees" msgstr "Інші учасники" +msgid "Other places contained by this one" +msgstr "" + msgid "Page not found" msgstr "Сторінку не знайдено" @@ -859,9 +883,16 @@ msgstr "Референції" msgid "Region" msgstr "Регіон" +msgid "Reset" +msgstr "" + msgid "Residence" msgstr "Проживання" +#, python-brace-format +msgid "Results ({{{ betty-search-results-count }}})" +msgstr "" + msgid "Retirement" msgstr "Вихід на пенсію" @@ -869,6 +900,9 @@ msgstr "Вихід на пенсію" msgid "Saved your project to {configuration_file}." msgstr "Проєкт збережено у {configuration_file}." +msgid "Search" +msgstr "" + msgid "Serve a generated site" msgstr "Запустити згенерований сайт" @@ -897,6 +931,9 @@ msgstr "Вулиця" msgid "Subject" msgstr "Предмет" +msgid "Subjects" +msgstr "" + #, python-brace-format msgid "" "The Gramps {gramps_entity_reference} entity has a " @@ -992,6 +1029,9 @@ msgstr "Деталі цієї цитати недоступні для захи msgid "This field is required." msgstr "Це поле є обов’язковим." +msgid "This information is unavailable to protect people's privacy." +msgstr "" + msgid "This must be a boolean." msgstr "Це має бути булеве значення." @@ -1031,9 +1071,21 @@ msgstr "Деталі цього джерела недоступні для за msgid "Timeline" msgstr "Хронологія" +msgid "Toggle menu" +msgstr "" + +msgid "Toggle search" +msgstr "" + +msgid "Toggle translations" +msgstr "" + msgid "Town" msgstr "Містечко" +msgid "Translations" +msgstr "" + msgid "Trees" msgstr "Дерева" @@ -1146,7 +1198,7 @@ msgstr "" msgid "" "Your project has no theme enabled. This means your site's pages may look " -"bare. Try the \"cotton-candy\" extension." +"bare. Try the \"raspberry-mint\" extension." msgstr "" "У вашому проєкті не ввімкнено тему. Це означає, що сторінки вашого сайту " "можуть виглядати пусто. Спробуйте розширення \"cotton-candy\"." @@ -1215,6 +1267,10 @@ msgstr "з {start_date} до {end_date}" msgid "in %(place)s" msgstr "в %(place)s" +#, python-format +msgid "in %(source)s" +msgstr "" + msgid "" "npm (https://www.npmjs.com/) must be available for features that require " "Node.js packages to be installed. Ensure that the `npm` executable is " @@ -1249,6 +1305,9 @@ msgstr "показати менше" msgid "show more" msgstr "показати більше" +msgid "sometime" +msgstr "" + #, python-brace-format msgid "sometime after around {start_date}" msgstr "десь приблизно після {start_date}" diff --git a/betty/project/__init__.py b/betty/project/__init__.py index 9faf2bbc7..acf82f9ed 100644 --- a/betty/project/__init__.py +++ b/betty/project/__init__.py @@ -379,7 +379,7 @@ async def _init_extensions(self) -> ProjectExtensions: if theme_count == 0: logging.getLogger().warning( _( - 'Your project has no theme enabled. This means your site\'s pages may look bare. Try the "cotton-candy" extension.' + 'Your project has no theme enabled. This means your site\'s pages may look bare. Try the "raspberry-mint" extension.' ).localize(await self.app.localizer) ) diff --git a/betty/project/extension/cotton_candy/__init__.py b/betty/project/extension/cotton_candy/__init__.py index f0a76d4ea..e182acc08 100644 --- a/betty/project/extension/cotton_candy/__init__.py +++ b/betty/project/extension/cotton_candy/__init__.py @@ -74,7 +74,7 @@ class CottonCandy( _plugin_id = "cotton-candy" _plugin_label = static("Cotton Candy") - _plugin_description = _("Cotton Candy is Betty's default theme.") + _plugin_description = _("Cotton Candy is Betty's legacy theme.") @private def __init__( diff --git a/betty/project/extension/demo/__init__.py b/betty/project/extension/demo/__init__.py index fa2622fa1..d94348dae 100644 --- a/betty/project/extension/demo/__init__.py +++ b/betty/project/extension/demo/__init__.py @@ -11,11 +11,11 @@ from betty.locale.localizable import static from betty.plugin import ShorthandPluginBase from betty.project.extension import Extension -from betty.project.extension.cotton_candy import CottonCandy from betty.project.extension.demo.project import load_ancestry from betty.project.extension.deriver import Deriver from betty.project.extension.http_api_doc import HttpApiDoc from betty.project.extension.maps import Maps +from betty.project.extension.raspberry_mint import RaspberryMint from betty.project.extension.trees import Trees from betty.project.extension.wikipedia import Wikipedia from betty.project.load import LoadAncestryEvent @@ -38,10 +38,10 @@ class Demo(ShorthandPluginBase, Extension): @classmethod def depends_on(cls) -> set[PluginIdentifier[Extension]]: return { - CottonCandy, Deriver, HttpApiDoc, Maps, + RaspberryMint, Trees, Wikipedia, } diff --git a/betty/project/extension/demo/project.py b/betty/project/extension/demo/project.py index 75473f285..c86dfe5d9 100644 --- a/betty/project/extension/demo/project.py +++ b/betty/project/extension/demo/project.py @@ -4,6 +4,7 @@ from __future__ import annotations +import tarfile from random import choice from typing import TYPE_CHECKING @@ -37,13 +38,13 @@ from betty.plugin.config import PluginInstanceConfiguration from betty.project import Project from betty.project.config import LocaleConfiguration, ProjectConfiguration -from betty.project.extension.cotton_candy import CottonCandy -from betty.project.extension.cotton_candy.config import CottonCandyConfiguration from betty.project.extension.demo.copyright_notice import Streetmix +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.project.extension.raspberry_mint.config import RaspberryMintConfiguration from betty.typing import internal +from pathlib import Path if TYPE_CHECKING: - from pathlib import Path from betty.machine_name import MachineName from collections.abc import Mapping, Sequence from betty.app import App @@ -75,8 +76,8 @@ async def create_project(app: App, project_directory_path: Path) -> Project: extensions=[ PluginInstanceConfiguration(Demo), PluginInstanceConfiguration( - CottonCandy, - configuration=CottonCandyConfiguration( + RaspberryMint, + configuration=RaspberryMintConfiguration( featured_entities=[ EntityReference(Place, "betty-demo-amsterdam"), EntityReference(Person, "betty-demo-liberta-lankester"), diff --git a/betty/project/extension/raspberry_mint/__init__.py b/betty/project/extension/raspberry_mint/__init__.py new file mode 100644 index 000000000..12ee2b79e --- /dev/null +++ b/betty/project/extension/raspberry_mint/__init__.py @@ -0,0 +1,194 @@ +""" +Provide the Raspberry Mint theme. +""" + +from __future__ import annotations + +import json +from pathlib import Path +from typing import TYPE_CHECKING, final, Self + +import aiofiles +from typing_extensions import override + +from betty.html import CssProvider +from betty.jinja2 import ( + Jinja2Provider, + Filters, +) +from betty.locale.localizable import static, call, plain +from betty.locale.localizer import DEFAULT_LOCALIZER +from betty.os import link_or_copy +from betty.plugin import ShorthandPluginBase +from betty.project.extension import Theme, Extension, ConfigurableExtension +from betty.project.extension._theme import jinja2_filters +from betty.project.extension._theme.search import generate_search_index +from betty.project.extension.maps import Maps +from betty.project.extension.raspberry_mint.config import RaspberryMintConfiguration +from betty.project.extension.trees import Trees +from betty.project.extension.webpack import Webpack +from betty.project.extension.webpack.build import EntryPointProvider +from betty.project.generate import GenerateSiteEvent +from betty.typing import private + +if TYPE_CHECKING: + from betty.project import Project + from betty.plugin import PluginIdentifier + from betty.event_dispatcher import EventHandlerRegistry + from collections.abc import Sequence + + +async def _generate_logo(event: GenerateSiteEvent) -> None: + await link_or_copy( + event.project.logo, + event.project.configuration.www_directory_path + / ("logo" + event.project.logo.suffix), + ) + + +_RESULT_CONTAINER_TEMPLATE = plain(""" +
  • + {{{ betty-search-result }}} +
  • +""") + +_RESULTS_CONTAINER_TEMPLATE = call( + lambda localizer: '

      ' + + localizer._("Results ({{{ betty-search-results-count }}})") + + "

      {{{ betty-search-results }}}
    " +) + + +async def _generate_search_index(event: GenerateSiteEvent) -> None: + await generate_search_index( + event.project, + _RESULT_CONTAINER_TEMPLATE, + _RESULTS_CONTAINER_TEMPLATE, + job_context=event.job_context, + ) + + +async def _generate_webmanifest(event: GenerateSiteEvent) -> None: + project = event.project + extensions = await project.extensions + webmanifest = json.dumps( + { + "name": project.configuration.title.localize(DEFAULT_LOCALIZER), + "icons": [ + {"src": "/logo" + project.logo.suffix}, + ], + "lang": project.configuration.locales.default.locale, + "theme_color": extensions[RaspberryMint].configuration.secondary_color.hex, + "background_color": "#ffffff", + "display": "fullscreen", + } + ) + async with aiofiles.open( + project.configuration.www_directory_path / "betty.webmanifest", "w" + ) as f: + await f.write(webmanifest) + + +@final +class RaspberryMint( + ShorthandPluginBase, + Theme, + CssProvider, + ConfigurableExtension[RaspberryMintConfiguration], + Jinja2Provider, + EntryPointProvider, +): + """ + The Raspberry Mint theme. + """ + + _plugin_id = "raspberry-mint" + _plugin_label = static("Raspberry Mint") + + @private + def __init__( + self, + project: Project, + public_css_paths: Sequence[str], + *, + configuration: RaspberryMintConfiguration, + ): + super().__init__(project, configuration=configuration) + self._public_css_paths = public_css_paths + + @override + @classmethod + async def new_for_project(cls, project: Project) -> Self: + static_url_generator = await project.static_url_generator + return cls( + project, + [static_url_generator.generate("/css/raspberry-mint.css")], + configuration=cls.new_default_configuration(), + ) + + @override + async def bootstrap(self) -> None: + await super().bootstrap() + try: + await self._assert_configuration() + except BaseException: + await self.shutdown() + raise + + async def _assert_configuration(self) -> None: + await self.configuration.featured_entities.validate( + self.project.entity_type_repository + ) + + @override + def register_event_handlers(self, registry: EventHandlerRegistry) -> None: + registry.add_handler( + GenerateSiteEvent, + _generate_logo, + _generate_search_index, + _generate_webmanifest, + ) + + @override + @classmethod + def depends_on(cls) -> set[PluginIdentifier[Extension]]: + return {Webpack} + + @override + @classmethod + def comes_after(cls) -> set[PluginIdentifier[Extension]]: + return {Maps, Trees} + + @override + @classmethod + def assets_directory_path(cls) -> Path: + return Path(__file__).parent / "assets" + + @override + @classmethod + def webpack_entry_point_directory_path(cls) -> Path: + return Path(__file__).parent / "webpack" + + @override + def webpack_entry_point_cache_keys(self) -> Sequence[str]: + return ( + self.project.configuration.root_path, + self._configuration.primary_color.hex, + self._configuration.secondary_color.hex, + self._configuration.tertiary_color.hex, + ) + + @override + @property + def public_css_paths(self) -> Sequence[str]: + return self._public_css_paths + + @override + @classmethod + def new_default_configuration(cls) -> RaspberryMintConfiguration: + return RaspberryMintConfiguration() + + @override + @property + def filters(self) -> Filters: + return jinja2_filters(self._project) diff --git a/betty/project/extension/raspberry_mint/assets/public/localized/.error/401.html.j2 b/betty/project/extension/raspberry_mint/assets/public/localized/.error/401.html.j2 new file mode 100644 index 000000000..1de5f6011 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/public/localized/.error/401.html.j2 @@ -0,0 +1,11 @@ +{% extends 'base.html.j2' %} +{% set page_title = _('Unauthorized') %} +{% block page_content %} +
    +
    +
    +

    {% trans %}I'm sorry, dear, but it seems you're not logged in.{% endtrans %}

    +
    +
    +
    +{% endblock %} diff --git a/betty/project/extension/raspberry_mint/assets/public/localized/.error/403.html.j2 b/betty/project/extension/raspberry_mint/assets/public/localized/.error/403.html.j2 new file mode 100644 index 000000000..d4bde2010 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/public/localized/.error/403.html.j2 @@ -0,0 +1,11 @@ +{% extends 'base.html.j2' %} +{% set page_title = _('Access denied') %} +{% block page_content %} +
    +
    +
    +

    {% trans %}I'm sorry, dear, but it seems you're not allowed to view this page.{% endtrans %}

    +
    +
    +
    +{% endblock %} diff --git a/betty/project/extension/raspberry_mint/assets/public/localized/.error/404.html.j2 b/betty/project/extension/raspberry_mint/assets/public/localized/.error/404.html.j2 new file mode 100644 index 000000000..e0096b195 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/public/localized/.error/404.html.j2 @@ -0,0 +1,11 @@ +{% extends 'base.html.j2' %} +{% set page_title = _('Page not found') %} +{% block page_content %} +
    +
    +
    +

    {% trans %}I'm sorry, dear, but it seems this page does not exist.{% endtrans %}

    +
    +
    +
    +{% endblock %} diff --git a/betty/project/extension/raspberry_mint/assets/public/localized/index.html.j2 b/betty/project/extension/raspberry_mint/assets/public/localized/index.html.j2 new file mode 100644 index 000000000..823229a5a --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/public/localized/index.html.j2 @@ -0,0 +1,18 @@ +{% extends 'base.html.j2' %} +{% set page_title %} + {{ project.configuration.title | localize }} +{% endset %} +{% block page_summary %} +{% if 'demo' in project.extensions %} + {% include '-front-demo-summary.html.j2' %} +{% endif %} +{% endblock %} +{% block page_content %} +{% if 'demo' in project.extensions %} + {% include '-front-demo-content.html.j2' %} +{% endif %} +{% set featured_entity_references = project.extensions['raspberry-mint'].configuration.featured_entities %} +{% if featured_entity_references %} + {% include '-front-featured-entities.html.j2' %} +{% endif %} +{% endblock %} diff --git a/betty/project/extension/raspberry_mint/assets/templates/-front-demo-content.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/-front-demo-content.html.j2 new file mode 100644 index 000000000..c43682394 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/-front-demo-content.html.j2 @@ -0,0 +1,14 @@ +
    +
    +
    +

    + {% set liberta_lankester_label -%} + Liberta 'Betty' Lankester + {%- endset %} + {% trans liberta_lankester_label = liberta_lankester_label -%} + Betty was named after {{ liberta_lankester_label }}, and this website shows a small sample of her family history. You can browse the pages about her and some of her family to get an idea of what a Betty site looks like. + {%- endtrans %} +

    +
    +
    +
    diff --git a/betty/project/extension/raspberry_mint/assets/templates/-front-demo-summary.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/-front-demo-summary.html.j2 new file mode 100644 index 000000000..2f96e27b3 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/-front-demo-summary.html.j2 @@ -0,0 +1,5 @@ +

    + {% trans -%} + Betty is an application that takes a family tree and builds a website out of it, much like the one you are viewing right now. The more information your genealogical research contains, the more interactivity Betty can add to your site, such as media galleries, maps, and browsable family trees. + {%- endtrans %} +

    diff --git a/betty/project/extension/raspberry_mint/assets/templates/-front-featured-entities.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/-front-featured-entities.html.j2 new file mode 100644 index 000000000..1c48931f2 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/-front-featured-entities.html.j2 @@ -0,0 +1,14 @@ +
    +
    + {% for featured_entity_reference in featured_entity_references %} +
    + {% with entity = project.ancestry[featured_entity_reference.entity_type][featured_entity_reference.entity_id] %} + {% include [ + 'entity/card--' + entity.plugin_id() + '.html.j2', + 'entity/card.html.j2', + ] %} + {% endwith %} +
    + {% endfor %} +
    +
    diff --git a/betty/project/extension/raspberry_mint/assets/templates/accordion.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/accordion.html.j2 new file mode 100644 index 000000000..c925002d1 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/accordion.html.j2 @@ -0,0 +1,19 @@ +{% if accordion_items | length > 0 %} +
    + {% for item in accordion_items %} +
    + <{{ accordion_heading_element }} class="accordion-header{% if accordion_heading_class is defined %} {{ accordion_heading_class }}{% endif %}"> + + +
    +
    + {{ item['body'] }} +
    +
    +
    + {% endfor %} +
    +{% endif %} diff --git a/betty/project/extension/raspberry_mint/assets/templates/base.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/base.html.j2 new file mode 100644 index 000000000..c86579052 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/base.html.j2 @@ -0,0 +1,24 @@ +{% do 'raspberry-mint' | webpack_entry_point_js %} + + + + {% include 'head.html.j2' %} + + + + +{% include 'header.html.j2' %} +
    +{% include 'intro.html.j2' %} +{% block page_content required %}{% endblock %} +{% include 'section/external-links.html.j2' %} +{% include 'outro.html.j2' %} +{% include 'footer.html.j2' %} +{% include 'linked-data.html.j2' %} +{% include 'stylesheets.html.j2' %} +{% include 'webpack-entry-loader.html.j2' %} +{% include 'scripts.html.j2' %} + + diff --git a/betty/project/extension/raspberry_mint/assets/templates/button.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/button.html.j2 new file mode 100644 index 000000000..a148ae21a --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/button.html.j2 @@ -0,0 +1 @@ + diff --git a/betty/project/extension/raspberry_mint/assets/templates/checkbox.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/checkbox.html.j2 new file mode 100644 index 000000000..d4cc11e10 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/checkbox.html.j2 @@ -0,0 +1,7 @@ +{% if html_id is not defined %} + {% set html_id = generate_html_id() %} +{% endif %} +
    + + +
    diff --git a/betty/project/extension/raspberry_mint/assets/templates/checkboxes.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/checkboxes.html.j2 new file mode 100644 index 000000000..35fe9ec3c --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/checkboxes.html.j2 @@ -0,0 +1,13 @@ +
    + {{ checkboxes_label }} + {% for checkbox in checkboxes %} + {% with checkbox_checked = checkbox['checked'], + checkbox_label = checkbox['label'], + checkbox_value = checkbox['value'], + html_class = checkbox['class'], + html_id = checkbox['id'] + %} + {% include 'checkbox.html.j2' %} + {% endwith %} + {% endfor %} +
    \ No newline at end of file diff --git a/betty/project/extension/raspberry_mint/assets/templates/entity/appearances.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/entity/appearances.html.j2 new file mode 100644 index 000000000..b5dacb8e5 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/entity/appearances.html.j2 @@ -0,0 +1,22 @@ +{% set referees = file_references | map(attribute='referee') | select('public') | list %} +{% if referees | length > 0 %} +
    +
    +
    +
    +
    +

    + {% trans %}Appearances{% endtrans %} + {% with url = page_resource | localized_url ~ '#appearances' %} + {% include 'permalink.html.j2' %} + {% endwith %} +

    + {% with entities = referees %} + {% include 'entity/list.html.j2' %} + {% endwith %} +
    +
    +
    +
    +
    +{% endif %} diff --git a/betty/project/extension/raspberry_mint/assets/templates/entity/card.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/entity/card.html.j2 new file mode 100644 index 000000000..7080df2b8 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/entity/card.html.j2 @@ -0,0 +1,23 @@ +
    +

    + {% with embedded = True %} + {% include [ + 'entity/label--' + entity.plugin_id() + '.html.j2', + 'entity/label.html.j2', + ] %} + {% endwith %} +

    +
    + {% with embedded = True %} + {% include 'entity/summary--' + entity.plugin_id() + '.html.j2' ignore missing %} + {% endwith %} + Read more +
    + {% if entity is has_file_references %} + {% set image_references = entity | associated_file_references | selectattr('file', 'public') | selectattr('file.media_type', 'image_supported_media_type') | list %} + {% if image_references | length > 0 %} + {% set image_reference = image_references[0] %} + {{ image_reference.file.label | localize }} + {% endif %} + {% endif %} +
    diff --git a/betty/project/extension/raspberry_mint/assets/templates/entity/event-dimensions.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/entity/event-dimensions.html.j2 new file mode 100644 index 000000000..f346b9567 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/entity/event-dimensions.html.j2 @@ -0,0 +1,35 @@ +{%- set embedded = embedded | default(False) -%} +{%- set citation_context = entity_contexts['citation'] -%} +{%- set place_context = entity_contexts['place'] -%} +{%- set formatted_date = '' -%} +{%- if event.date -%} + {%- set formatted_date = event.date | format_datey -%} +{%- endif -%} + +{%- set formatted_place = '' -%} +{%- if event.place and event.place != place_context -%} + {%- set formatted_place -%} + {%- with entity = event.place, date_context=event.date -%} + {%- include 'entity/label--place.html.j2' -%} + {%- endwith -%} + {%- endset -%} +{%- endif -%} + +{%- if formatted_date and formatted_place -%} + {%- trans date = formatted_date, place=formatted_place -%} + {{ date }} in {{ place }} + {%- endtrans -%} +{%- elif formatted_date -%} + {{ formatted_date }} +{%- elif formatted_place -%} + {%- trans place = formatted_place -%} + in {{ place }} + {%- endtrans -%} +{%- else -%} + {% trans %}sometime{% endtrans %} +{%- endif -%} +{%- if not embedded -%} + {% with citations = event.citations | reject('eq', citation_context) %} + {% include 'reference.html.j2' %} + {% endwith %} +{%- endif -%} diff --git a/betty/project/extension/raspberry_mint/assets/templates/entity/label--citation.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/entity/label--citation.html.j2 new file mode 100644 index 000000000..6ec068e72 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/entity/label--citation.html.j2 @@ -0,0 +1,57 @@ +{# Citation formatting is inspired by the MLA style guide(https://style.mla.org/) #} +{% set citation_context = entity_contexts['citation'] %} +{% if entity.source and entity.source.public %} + {% if entity.source.public %} + {% if entity.source.author %} + {% set formatted_source_author %} + {{ entity.source.author | localize }}. + {% endset %} + {% endif %} + {% set formatted_source_label %} + "{% with entity = entity.source %}{% include ['entity/label--source.html.j2', 'entity/label.html.j2'] %}{% endwith %}". + {% endset %} + {% if entity.source.publisher %} + {% set formatted_source_publisher %} + {{ entity.source.publisher | localize }}{% if entity.public and (entity.location or entity.date) %},{% else %}.{% endif %} + {% endset %} + {% endif %} + {% else %} + {% set formatted_source_label %} + {% include 'entity/private.html.j2' %} + {% endset %} + {% endif %} +{% endif %} +{% if entity.public %} + {% if entity.location %} + {% set formatted_citation_location %} + {%- set location = entity.location | localize -%} + {%- if citation_context != entity and entity is persistent_entity_id -%} + + {%- endif -%} + {{ location }} + {%- if citation_context != entity and entity is persistent_entity_id -%} + + {%- endif -%}. + {% endset %} + {% endif %} + {% if entity.date %} + {% set formatted_citation_date %} + {% trans date = entity.date | format_datey %}Accessed {{ date }}{% endtrans %}. + {% endset %} + {% endif %} +{% else %} + {% set formatted_citation_label %} + + {%- include 'entity/private.html.j2' -%} + +{% endset %} +{% endif -%} + +{{ [ + formatted_source_author, + formatted_source_label, + formatted_source_publisher, + formatted_citation_location, + formatted_citation_date, + formatted_citation_label, +] | map('default', '') | map('trim') | reject('eq', '') | join(' ') | trim }} \ No newline at end of file diff --git a/betty/project/extension/raspberry_mint/assets/templates/entity/label--event.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/entity/label--event.html.j2 new file mode 100644 index 000000000..d8f8a066a --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/entity/label--event.html.j2 @@ -0,0 +1,31 @@ +{%- set embedded = embedded | default(False) -%} +{%- set person_context = entity_contexts['person'] -%} +{%- macro person_label(entity) -%} + {% include 'entity/label--person.html.j2' %} +{%- endmacro -%} +{%- if entity.name -%} + {%- set formatted_event = entity.name | localize | html_lang | safe -%} +{%- else -%} + {%- set formatted_event = entity.event_type.plugin_label() | localize | html_lang | safe -%} +{%- endif -%} +{%- if entity is persistent_entity_id and not embedded -%} + {%- set formatted_event -%} + {{ formatted_event }} + {%- endset -%} +{%- endif -%} +{%- set subjects = entity.presences | select('public') | selectattr('role', 'presence_role_plugin', 'subject') | map(attribute='person') | select('public') | list -%} +{%- set non_context_subjects = subjects | reject('eq', person_context) | list -%} +{%- set formatted_subjects = non_context_subjects | map(person_label) | join(', ') %} +{%- if non_context_subjects | length == 0 -%} + {{ formatted_event }} +{%- else -%} + {%- if person_context in subjects -%} + {% trans event = formatted_event, subjects = formatted_subjects -%} + {{ event }} with {{ subjects }} + {%- endtrans %} + {%- else -%} + {% trans event = formatted_event, subjects = formatted_subjects -%} + {{ event }} of {{ subjects }} + {%- endtrans %} + {%- endif -%} +{%- endif -%} diff --git a/betty/project/extension/raspberry_mint/assets/templates/entity/label--person-name.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/entity/label--person-name.html.j2 new file mode 100644 index 000000000..0eab00ffb --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/entity/label--person-name.html.j2 @@ -0,0 +1,15 @@ +{%- set embedded = embedded | default(False) -%} +{%- set person_context = entity_contexts['person'] -%} +{%- set label_as_link = not embedded and person_context != entity.person and entity.person is persistent_entity_id and entity.person is public -%} +{%- if label_as_link -%} + +{%- endif -%} +{% include 'entity/person-name-label.html.j2' %} +{%- if label_as_link -%} + +{%- endif -%} +{%- if entity is public and not embedded -%} + {%- with citations = entity.citations -%} + {%- include 'reference.html.j2' -%} + {% endwith %} +{%- endif -%} diff --git a/betty/project/extension/raspberry_mint/assets/templates/entity/label--person.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/entity/label--person.html.j2 new file mode 100644 index 000000000..cc8b43a08 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/entity/label--person.html.j2 @@ -0,0 +1,20 @@ +{% set embedded = embedded | default(False) %} +{% set person_context = entity_contexts['person'] %} +{% set label_as_link = not embedded and person_context != entity and entity is persistent_entity_id and entity is public %} +{% if label_as_link -%} + +{%- endif %} +{% if entity.private -%} + {% include 'entity/private.html.j2' %} +{%- else -%} + {%- if entity.names | length -%} + {% with entity = entity.names | select('public') | first | default(entity.names | first) %} + {% include 'entity/person-name-label.html.j2' %} + {% endwith %} + {%- else -%} + n.n. + {%- endif -%} +{%- endif -%} +{%- if label_as_link -%} + +{%- endif -%} diff --git a/betty/project/extension/raspberry_mint/assets/templates/entity/label--place.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/entity/label--place.html.j2 new file mode 100644 index 000000000..42d095c1c --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/entity/label--place.html.j2 @@ -0,0 +1,20 @@ +{% set embedded = embedded | default(False) %} +{% set place_context = entity_contexts['place'] %} +{% set label_as_link = not embedded and place_context != entity and entity is persistent_entity_id and entity is public %} +{%- set names = entity.names | select_has_dates(date_context | default(none)) | list -%} +{%- if names | length == 0 -%} + {%- set names = entity.names -%} +{%- endif -%} +{%- if names -%} + {%- set name = (names | first).name -%} +{%- else -%} + {%- set name = entity.label -%} +{%- endif -%} + +{%- if label_as_link -%} + +{%- endif -%} +{{ name | localize | html_lang }} +{%- if label_as_link -%} + +{%- endif -%} diff --git a/betty/project/extension/raspberry_mint/assets/templates/entity/label.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/entity/label.html.j2 new file mode 100644 index 000000000..e866154a3 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/entity/label.html.j2 @@ -0,0 +1,12 @@ +{%- set embedded = embedded | default(False) -%} +{% if entity is public %} + {%- if not embedded and entity is public and entity is persistent_entity_id -%} + + {%- endif -%} + {{ entity.label | localize | html_lang }} + {%- if not embedded and entity is public and entity is persistent_entity_id -%} + + {%- endif -%} +{% else %} + {% include 'entity/private.html.j2' %} +{%- endif -%} diff --git a/betty/project/extension/raspberry_mint/assets/templates/entity/list--event.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/entity/list--event.html.j2 new file mode 100644 index 000000000..b98b83043 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/entity/list--event.html.j2 @@ -0,0 +1,3 @@ +{% with events = entities %} + {% include 'timeline.html.j2' %} +{% endwith %} diff --git a/betty/project/extension/raspberry_mint/assets/templates/entity/list.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/entity/list.html.j2 new file mode 100644 index 000000000..391d34611 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/entity/list.html.j2 @@ -0,0 +1,16 @@ +{% set entities = entities | select('public') | list %} +{% set ns = namespace(entities_and_names = []) %} +{% for entity in entities %} + {% do ns.entities_and_names.append((entity, entity.label | localize)) %} +{% endfor %} +{% set entities = ns.entities_and_names | sort(attribute=1) | map(attribute=0) | list %} +{% if entities | length > 0 %} +
      + {% for entity in entities %} +
    • + {% include ['entity/label--' + entity.plugin_id() + '.html.j2', 'entity/label.html.j2'] %} + {% include 'entity/summary--' + entity.plugin_id() + '.html.j2' ignore missing %} +
    • + {% endfor %} +
    +{% endif %} diff --git a/betty/project/extension/raspberry_mint/assets/templates/entity/page--citation.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/entity/page--citation.html.j2 new file mode 100644 index 000000000..180cc72fd --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/entity/page--citation.html.j2 @@ -0,0 +1,12 @@ +{% extends 'entity/page.html.j2' %} +{% block page_content %} + {% include 'section/wikipedia.html.j2' %} + + {% with facts = entity.facts %} + {% include 'section/facts.html.j2' %} + {% endwith %} + + {% with file_references = entity.file_references %} + {% include 'section/media.html.j2' %} + {% endwith %} +{% endblock %} diff --git a/betty/project/extension/raspberry_mint/assets/templates/entity/page--event.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/entity/page--event.html.j2 new file mode 100644 index 000000000..7d9ebee9f --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/entity/page--event.html.j2 @@ -0,0 +1,88 @@ +{% extends 'entity/page.html.j2' %} +{% block page_content %} + {% if entity.place is not none %} + {% with places = [entity.place] %} + {% include 'section/map.html.j2' %} + {% endwith %} + {% endif %} + + {% set ns = namespace(subjects = [], witnesses = [], other_attendees = []) %} + {% for presence in entity.presences | select('public') %} + {% if presence.role is presence_role_plugin('subject') %} + {% set ns.subjects = ns.subjects + [presence.person] %} + {% endif %} + {% endfor %} + {% if ns.subjects | length > 0 %} +
    +
    +
    +
    +

    + {% trans %}Subjects{% endtrans %} + {% with url = page_resource | localized_url ~ '#attendees-subject' %} + {% include 'permalink.html.j2' %} + {% endwith %} +

    + {% with entities = ns.subjects %} + {% include ['entity/list--person.html.j2', 'entity/list.html.j2'] %} + {% endwith %} +
    +
    +
    +
    + {% endif %} + + {% for presence in entity.presences | select('public') %} + {% if presence.role is presence_role_plugin('witness') %} + {% set ns.witnesses = ns.witnesses + [presence.person] %} + {% endif %} + {% endfor %} + {% if ns.witnesses | length > 0 %} +
    +
    +
    +
    +

    + {% trans %}Witnesses{% endtrans %} + {% with url = page_resource | localized_url ~ '#attendees-witness' %} + {% include 'permalink.html.j2' %} + {% endwith %} +

    + {% with entities = ns.witnesses %} + {% include ['entity/list--person.html.j2', 'entity/list.html.j2'] %} + {% endwith %} +
    +
    +
    +
    + {% endif %} + + {% for presence in entity.presences | select('public') %} + {% if not presence.role is presence_role_plugin('subject') and not presence.role is presence_role_plugin('witness') %} + {% set ns.other_attendees = ns.other_attendees + [presence.person] %} + {% endif %} + {% endfor %} + {% if ns.other_attendees | length > 0 %} +
    +
    +
    +
    +

    + {% trans %}Other attendees{% endtrans %} + {% with url = page_resource | localized_url ~ '#attendees-other' %} + {% include 'permalink.html.j2' %} + {% endwith %} +

    + {% with entities = ns.other_attendees %} + {% include ['entity/list--person.html.j2', 'entity/list.html.j2'] %} + {% endwith %} +
    +
    +
    +
    + {% endif %} + + {% with file_references = entity | associated_file_references %} + {% include 'section/media.html.j2' %} + {% endwith %} +{% endblock %} diff --git a/betty/project/extension/raspberry_mint/assets/templates/entity/page--file.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/entity/page--file.html.j2 new file mode 100644 index 000000000..07a9aa0ec --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/entity/page--file.html.j2 @@ -0,0 +1,97 @@ +{% extends 'entity/page.html.j2' %} +{% set page_title = entity.label | localize %} +{% block page_content %} + {% if entity.media_type and (entity.media_type.type == 'image' or entity.media_type | str == 'application/pdf') %} +
    +
    +
    +
    +
    + + {% for size, breakpoint in [(1300, 900), (900, 500)] %} + + {% endfor %} + {{ entity.label | localize }} + +
    {{ entity.label | localize }}
    +
    +
    +
    +
    +
    + {% endif %} + +
    +
    +
    + +
    + {% if entity.copyright_notice is not none or entity.license is not none %} +
    + {% if entity.copyright_notice is not none %} +
    +

    {% trans %}Copyright{% endtrans %}

    +

    + {% if entity.copyright_notice.url is not none %} + + {% endif %} + {{ entity.copyright_notice.summary | localize | html_lang }} + {% if entity.copyright_notice.url is not none %} + + {% endif %} +

    +
    + {% endif %} + {% if entity.license is not none %} +
    +

    {% trans %}License{% endtrans %}

    +

    + {% if entity.license.url is not none %} + + {% endif %} + {{ entity.license.summary | localize | html_lang }} + {% if entity.license.url is not none %} + + {% endif %} +

    +
    + {% endif %} +
    + {% endif %} +
    +
    + + {% with notes = entity.notes %} + {% include 'section/notes.html.j2' %} + {% endwith %} + + {% with file_references = entity.referees %} + {% include 'entity/appearances.html.j2' %} + {% endwith %} + + {# These are the citations for the file itself. #} + {% set citations = entity.citations | select('public') | list %} + {% if citations | length > 0 %} +
    +
    +
    +
    +

    + {% trans %}Media references{% endtrans %} + {% with url = page_resource | localized_url ~ '#references'%} + {% include 'permalink.html.j2' %} + {% endwith %} +

    +
      + {% for entity in citations %} +
    • {% include 'entity/label--citation.html.j2' %}
    • + {% endfor %} +
    +
    +
    +
    +
    + {% endif %} +{% endblock %} diff --git a/betty/project/extension/raspberry_mint/assets/templates/entity/page--person.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/entity/page--person.html.j2 new file mode 100644 index 000000000..5157606ae --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/entity/page--person.html.j2 @@ -0,0 +1,20 @@ +{% extends 'entity/page.html.j2' %} +{% block page_content %} + {% include 'section/wikipedia.html.j2' %} + + {% with places = entity.presences | select('public') | selectattr('event', 'public') | map(attribute='event.place') | reject('none') | unique | list %} + {% include 'section/map.html.j2' %} + {% endwith %} + + {% include 'section/notes.html.j2' %} + + {% include 'section/family.html.j2' %} + + {% with events = entity | person_timeline_events %} + {% include 'section/timeline.html.j2' %} + {% endwith %} + + {% with file_references = entity | associated_file_references %} + {% include 'section/media.html.j2' %} + {% endwith %} +{% endblock %} diff --git a/betty/project/extension/raspberry_mint/assets/templates/entity/page--place.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/entity/page--place.html.j2 new file mode 100644 index 000000000..ce5610e11 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/entity/page--place.html.j2 @@ -0,0 +1,53 @@ +{% extends 'entity/page.html.j2' %} +{% block page_content %} + {% include 'section/wikipedia.html.j2' %} + + {% set enclosees = entity.walk_enclosees | map(attribute='enclosee') | select('entity_plugin', 'place') | select('public') | list %} + + {% with places = enclosees + [entity] %} + {% include 'section/map.html.j2' %} + {% endwith %} + + {% if enclosees | length > 0 %} + {% with entities = enclosees %} +
    +
    +
    +
    + {% set accordion_item_body %} + {% with entities = enclosees %} + {% include 'entity/list.html.j2' %} + {% endwith %} + {% endset %} + {% with accordion_heading_element = 'h2', + accordion_id = 'enclosees', + accordion_items = [{ + 'header': _('Other places contained by this one'), + 'body': accordion_item_body, + }] + %} + {% include 'accordion.html.j2' %} + {% endwith %} +
    +
    +
    +
    + {% endwith %} + {% endif %} + + {% include 'section/notes.html.j2' %} + + + {% set ns = namespace(events = []) %} + {% do ns.events.extend(entity.events) %} + {% for enclosee in enclosees %} + {% do ns.events.extend(enclosee.events) %} + {% endfor %} + {% with events = ns.events %} + {% include 'section/timeline.html.j2' %} + {% endwith %} + + {% with file_references = entity | associated_file_references %} + {% include 'section/media.html.j2' %} + {% endwith %} +{% endblock %} diff --git a/betty/project/extension/raspberry_mint/assets/templates/entity/page--source.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/entity/page--source.html.j2 new file mode 100644 index 000000000..96ded0505 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/entity/page--source.html.j2 @@ -0,0 +1,17 @@ +{% extends 'entity/page.html.j2' %} +{% block page_content %} + {% include 'section/wikipedia.html.j2' %} + + {% include 'section/notes.html.j2' %} + + {% set sources = [entity] + entity.walk_contains | select('public') | list %} + + {% set facts = sources | map(attribute='citations') | flatten | map(attribute='facts') | flatten | unique | list %} + {% include 'section/facts.html.j2' %} + + {% set have_file_references = sources %} + {% set have_file_references = have_file_references + sources | map(attribute='citations') | flatten | list %} + {% with file_references = have_file_references | selectattr('public') | map(attribute='file_references') | flatten | unique %} + {% include 'section/media.html.j2' %} + {% endwith %} +{% endblock %} diff --git a/betty/project/extension/raspberry_mint/assets/templates/entity/page-list--event.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/entity/page-list--event.html.j2 new file mode 100644 index 000000000..d4058dc03 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/entity/page-list--event.html.j2 @@ -0,0 +1,17 @@ +{% extends 'entity/page-list-base.html.j2' %} +{% block page_content %} + {{ super() }} + +
    +
    +
    + {% set events = entities | select('public') | list %} + {% if events | length > 0 %} + {% include 'timeline.html.j2' %} + {% else %} +

    {% trans %}I'm sorry, dear, but it seems there is nothing to show.{% endtrans %}

    + {% endif %} +
    +
    +
    +{% endblock %} diff --git a/betty/project/extension/raspberry_mint/assets/templates/entity/page-list--place.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/entity/page-list--place.html.j2 new file mode 100644 index 000000000..dd98ae08a --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/entity/page-list--place.html.j2 @@ -0,0 +1,23 @@ +{% extends 'entity/page-list-base.html.j2' %} +{% set entities = entities | select('public') | list %} +{% block page_content %} + {{ super() }} + +
    + {% with places = entities %} + {% include 'section/map.html.j2' %} + {% endwith %} + +
    +
    +
    + {% if entities | length > 0 %} + {% include ['entity/list--' + entity_type.plugin_id() + '.html.j2', 'entity/list.html.j2'] %} + {% else %} +

    {% trans %}I'm sorry, dear, but it seems there is nothing to show.{% endtrans %}

    + {% endif %} +
    +
    +
    +
    +{% endblock %} diff --git a/betty/project/extension/raspberry_mint/assets/templates/entity/page-list-base.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/entity/page-list-base.html.j2 new file mode 100644 index 000000000..0327cdf3c --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/entity/page-list-base.html.j2 @@ -0,0 +1,8 @@ +{% extends 'base.html.j2' %} +{% set page_title = entity_type.plugin_label_plural() | localize %} +{% do breadcrumbs.append( + page_title, + page_resource | localized_url(absolute=true) +) %} +{% block page_content %} +{% endblock %} diff --git a/betty/project/extension/raspberry_mint/assets/templates/entity/page-list.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/entity/page-list.html.j2 new file mode 100644 index 000000000..69c5d3586 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/entity/page-list.html.j2 @@ -0,0 +1,17 @@ +{% extends 'entity/page-list-base.html.j2' %} +{% block page_content %} + {{ super() }} + +
    +
    +
    + {% set entities = entities | select('public') | list %} + {% if entities | length > 0 %} + {% include ['entity/list--' + entity_type.plugin_id() + '.html.j2', 'entity/list.html.j2'] %} + {% else %} +

    {% trans %}I'm sorry, dear, but it seems there is nothing to show.{% endtrans %}

    + {% endif %} +
    +
    +
    +{% endblock %} diff --git a/betty/project/extension/raspberry_mint/assets/templates/entity/page.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/entity/page.html.j2 new file mode 100644 index 000000000..9f9abd877 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/entity/page.html.j2 @@ -0,0 +1,13 @@ +{% extends 'base.html.j2' %} +{% set page_title = entity.label | localize %} +{% set entity_contexts = entity_contexts(entity) %} +{% do breadcrumbs.append( + entity_type.plugin_label_plural() | localize, + entity_type | localized_url(absolute=true) +) %} +{% do breadcrumbs.append( + page_title, + page_resource | localized_url(absolute=true) +) %} +{% block page_content %} +{% endblock %} diff --git a/betty/project/extension/raspberry_mint/assets/templates/entity/person-name-label.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/entity/person-name-label.html.j2 new file mode 100644 index 000000000..e2c114c0f --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/entity/person-name-label.html.j2 @@ -0,0 +1,11 @@ +{% if entity.private -%} + {% include 'entity/private.html.j2' %} +{%- else -%} + {%- if entity.individual is not none -%} + {{ entity.individual }} + {%- else -%} + … + {%- endif -%} + {%- if entity.affiliation is not none %} {{ entity.affiliation }} + {%- endif -%} +{%- endif -%} diff --git a/betty/project/extension/raspberry_mint/assets/templates/entity/place-enclosure-label.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/entity/place-enclosure-label.html.j2 new file mode 100644 index 000000000..573077b35 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/entity/place-enclosure-label.html.j2 @@ -0,0 +1,16 @@ +{%- macro place_enclosure_label(place, place_context = none, date_context = none) -%} + {%- with entity = place -%} + {%- include ['entity/label--place.html.j2', 'entity/label.html.j2'] -%} + {%- endwith -%} + {{ _place_enclosure_label(place, place_context, date_context) }} +{%- endmacro -%} + +{%- macro _place_enclosure_label(place, place_context = none, date_context = none) -%} + {%- set encloser_enclosure = place.enclosers | negotiate_has_dates(date_context | default(none)) -%} + {%- if encloser_enclosure is not none and (place_context is none or place_context != encloser_enclosure.encloser) -%} + {%- with entity = encloser_enclosure.encloser -%} + , {% include 'entity/label--place.html.j2' -%} + {%- endwith -%} + {{ _place_enclosure_label(encloser_enclosure.encloser, place_context) }} + {%- endif -%} +{%- endmacro -%} diff --git a/betty/project/extension/raspberry_mint/assets/templates/entity/private.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/entity/private.html.j2 new file mode 100644 index 000000000..17baf6b21 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/entity/private.html.j2 @@ -0,0 +1 @@ +{% trans %}private{% endtrans %} \ No newline at end of file diff --git a/betty/project/extension/raspberry_mint/assets/templates/entity/source-containment-label.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/entity/source-containment-label.html.j2 new file mode 100644 index 000000000..1841a66e8 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/entity/source-containment-label.html.j2 @@ -0,0 +1,15 @@ +{%- macro source_containment_label(source, source_context = none) -%} + {%- with entity = source -%} + {%- include ['entity/label--source.html.j2', 'entity/label.html.j2'] -%} + {%- endwith -%} + {{ _source_containment_label(source, source_context) }} +{%- endmacro -%} + +{%- macro _source_containment_label(source, source_context = none) -%} + {%- if source.contained_by is not none and (source_context is none or source_context != source.contained_by) -%} + {%- with entity = source.contained_by -%} + , {% include ['entity/label--source.html.j2', 'entity/label.html.j2'] -%} + {%- endwith -%} + {{ _source_containment_label(source.contained_by, source_context) }} + {%- endif -%} +{%- endmacro -%} diff --git a/betty/project/extension/raspberry_mint/assets/templates/entity/summary--citation.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/entity/summary--citation.html.j2 new file mode 100644 index 000000000..2dabb605f --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/entity/summary--citation.html.j2 @@ -0,0 +1,9 @@ +{% from 'entity/source-containment-label.html.j2' import source_containment_label %} +{%- set embedded = embedded | default(False) -%} +{%- set source_context = entity_contexts['source'] -%} +
    +

    {{ entity.type.plugin_label() | localize | html_lang }}

    + {%- trans source = source_containment_label(entity.source, source_context) -%} + in {{ source }} + {%- endtrans -%} +
    diff --git a/betty/project/extension/raspberry_mint/assets/templates/entity/summary--event.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/entity/summary--event.html.j2 new file mode 100644 index 000000000..a1fb23d26 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/entity/summary--event.html.j2 @@ -0,0 +1,5 @@ +
    + {% with event = entity %} + {% include 'entity/event-dimensions.html.j2' %} + {% endwith %} +
    diff --git a/betty/project/extension/raspberry_mint/assets/templates/entity/summary--file.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/entity/summary--file.html.j2 new file mode 100644 index 000000000..dcdff3a84 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/entity/summary--file.html.j2 @@ -0,0 +1 @@ +

    {% trans %}Media{% endtrans %}

    diff --git a/betty/project/extension/raspberry_mint/assets/templates/entity/summary--person-name.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/entity/summary--person-name.html.j2 new file mode 100644 index 000000000..b75c33a68 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/entity/summary--person-name.html.j2 @@ -0,0 +1 @@ +

    {% trans %}Person name{% endtrans %}

    diff --git a/betty/project/extension/raspberry_mint/assets/templates/entity/summary--person.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/entity/summary--person.html.j2 new file mode 100644 index 000000000..db942ea5a --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/entity/summary--person.html.j2 @@ -0,0 +1,67 @@ +{%- macro _person_name_label(entity) -%} + {% include 'entity/label--person-name.html.j2' %} +{%- endmacro -%} +{%- macro _embedded_person_name_label(entity) -%} + {% set embedded = True %} + {% include 'entity/label--person-name.html.j2' %} +{%- endmacro -%} + +{%- if not embedded is defined -%} + {%- set embedded = False -%} +{%- endif -%} +
    + {%- if entity.private -%} +

    {%- trans -%}This information is unavailable to protect people's privacy.{%- endtrans -%}

    + {%- else -%} + {% if entity.gender.plugin_id() != 'unknown' %} +

    {{ entity.gender.plugin_label() | localize | html_lang }}

    + {% endif %} + {% set alternative_names = (entity.names | select('public') | list)[1:] %} + {%- if alternative_names -%} + {%- trans names = alternative_names | map(_embedded_person_name_label if embedded else _person_name_label) | list | join(', ') -%}Also known as {{ names }}{%- endtrans -%} + {%- endif -%} + {% set events = entity.presences | selectattr('role', 'presence_role_plugin', 'subject') | map(attribute='event') | reject('none') | select('public') | list %} + {% set start_of_life_events = events | select('start_of_life_event') | list %} + {%- if start_of_life_events | length -%} + {% set start_of_life_event = start_of_life_events | first %} + {%- set formatted_start -%} + {%- with event = start_of_life_event -%} + {%- include 'entity/event-dimensions.html.j2' -%} + {%- endwith -%} + {%- endset -%} + {%- endif -%} + {% set end_of_life_events = events | select('end_of_life_event') | list %} + {%- if end_of_life_events | length -%} + {% set end_of_life_event = end_of_life_events | first %} + {%- set formatted_end -%} + {%- with event = end_of_life_event -%} + {%- include 'entity/event-dimensions.html.j2' -%} + {%- endwith -%} + {%- endset -%} + {%- endif -%} + {%- if formatted_start is defined and formatted_start or formatted_end is defined and formatted_end -%} +
    + {%- if formatted_start is defined and formatted_start -%} +
    +
    + {{ start_of_life_event.event_type.plugin_label() | localize | html_lang }} +
    +
    + {{ formatted_start }} +
    +
    + {%- endif -%} + {%- if formatted_end is defined and formatted_end -%} +
    +
    + {{ end_of_life_event.event_type.plugin_label() | localize | html_lang }} +
    +
    + {{ formatted_end }} +
    +
    + {%- endif -%} +
    + {%- endif -%} + {%- endif -%} +
    diff --git a/betty/project/extension/raspberry_mint/assets/templates/entity/summary--place.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/entity/summary--place.html.j2 new file mode 100644 index 000000000..6b601aa5a --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/entity/summary--place.html.j2 @@ -0,0 +1,17 @@ +{% from 'entity/place-enclosure-label.html.j2' import place_enclosure_label %} +{%- set embedded = embedded | default(False) -%} +{%- set place_context = entity_contexts['place'] -%} +
    + {%- if entity.place_type.plugin_id() != 'unknown' -%} +

    {{ entity.place_type.plugin_label() | localize | html_lang }}

    + {%- endif -%} + {%- set encloser_enclosure = entity.enclosers | negotiate_has_dates(date_context | default(none)) -%} + {%- if encloser_enclosure -%} + {%- set encloser_label = place_enclosure_label(entity, place_context) -%} + {%- if encloser_label -%} + {%- trans place = encloser_label | html_lang -%} + in {{ place }} + {%- endtrans -%} + {%- endif -%} + {%- endif -%} +
    diff --git a/betty/project/extension/raspberry_mint/assets/templates/entity/summary--source.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/entity/summary--source.html.j2 new file mode 100644 index 000000000..1c7d53d9e --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/entity/summary--source.html.j2 @@ -0,0 +1,11 @@ +{% from 'entity/source-containment-label.html.j2' import source_containment_label %} +{%- set embedded = embedded | default(False) -%} +{%- set source_context = entity_contexts['source'] -%} +
    +

    {{ entity.type.plugin_label() | localize | html_lang }}

    + {%- if entity.contained_by -%} + {%- trans source = source_containment_label(entity) -%} + in {{ source }} + {%- endtrans -%} + {%- endif -%} +
    diff --git a/betty/project/extension/raspberry_mint/assets/templates/footer.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/footer.html.j2 new file mode 100644 index 000000000..978b9bcfe --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/footer.html.j2 @@ -0,0 +1,35 @@ +
    +
    +
    +
    +

    + {{ project.copyright_notice.summary | localize | html_lang }} +

    +

    + {{ project.license.summary | localize | html_lang }} +

    +
    +
    + {% if job_context is defined %} +

    + {% trans last_modified = localizer.format_datetime_datetime(job_context.start) -%} + Last modified on {{ last_modified }} + {%- endtrans %} +

    + {% endif %} + {% if 'http-api-doc' in project.extensions %} +

    + {% trans %}API documentation{% endtrans %} +

    + {% endif %} +
    +
    +
    +
    + + A family history as told by Betty + +
    +
    +
    +
    diff --git a/betty/project/extension/raspberry_mint/assets/templates/header.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/header.html.j2 new file mode 100644 index 000000000..8dab1bf00 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/header.html.j2 @@ -0,0 +1,190 @@ +{% set ns = namespace(primary_navigation_links = {}) %} +{% for entity_type_configuration in project.configuration.entity_types.values() | selectattr('generate_html_list') %} + {% set entity_type = project.entity_type_repository.get(entity_type_configuration.id) %} + {% set entity_type_label -%} + {% if entity_type.plugin_id() == 'event' %} + {% trans %}Timeline{% endtrans %} + {% else %} + {{ entity_type.plugin_label_plural() | localize }} + {% endif %} + {%- endset %} + {% do ns.primary_navigation_links.update({entity_type | localized_url: entity_type_label}) %} +{% endfor %} + +
    +
    + +
    +
    + + + diff --git a/betty/project/extension/raspberry_mint/assets/templates/intro.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/intro.html.j2 new file mode 100644 index 000000000..9ca35df75 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/intro.html.j2 @@ -0,0 +1,26 @@ +
    +
    +
    +
    +

    {{ page_title | html_lang }}

    + {% block page_summary %} + {% if page_resource is entity_plugin %} + {% with entity = page_resource %} + {% include 'entity/summary--' + entity.plugin_id() + '.html.j2' ignore missing %} + {% endwith %} + {% endif %} + {% endblock %} +
    + {% if page_resource is has_file_references %} + {% set page_image_reference = page_resource.file_references | selectattr('file', 'public') | selectattr('file.media_type', 'image_supported_media_type') | first %} + {% if page_image_reference is defined %} +
    + + {{ page_image_reference.file.label | localize }} + +
    + {% endif %} + {% endif %} +
    +
    +
    diff --git a/betty/project/extension/raspberry_mint/assets/templates/outro.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/outro.html.j2 new file mode 100644 index 000000000..2044452d2 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/outro.html.j2 @@ -0,0 +1,21 @@ +{% if citer | length > 0 %} +
    +
    +
    +

    + {% trans %}References{% endtrans %} + {% with url = page_resource | localized_url ~ '#references' %} + {% include 'permalink.html.j2' %} + {% endwith %} +

    +
      + {% for number, entity in citer %} +
    1. + {% include 'entity/label--citation.html.j2' %} +
    2. + {% endfor %} +
    +
    +
    +
    +{% endif %} diff --git a/betty/project/extension/raspberry_mint/assets/templates/permalink.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/permalink.html.j2 new file mode 100644 index 000000000..21bffbef1 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/permalink.html.j2 @@ -0,0 +1,9 @@ +{% set label = _('Permanent link to this section.') %} + + + diff --git a/betty/project/extension/raspberry_mint/assets/templates/reference.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/reference.html.j2 new file mode 100644 index 000000000..c8941aac5 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/reference.html.j2 @@ -0,0 +1,5 @@ +{%- set ns = namespace(numbered_citations = []) -%} +{%- for citation in citations -%} + {% set ns.numbered_citations = ns.numbered_citations + [(citer.cite(citation), reference)] %} +{%- endfor -%} +{%- for number, citation in ns.numbered_citations | sort(attribute=0) %} [{{ number }}]{%- endfor -%} diff --git a/betty/project/extension/raspberry_mint/assets/templates/search/result--file.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/search/result--file.html.j2 new file mode 100644 index 000000000..6bdf40b54 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/search/result--file.html.j2 @@ -0,0 +1,10 @@ +
    + {{ entity.label | localize }} + {% include 'entity/summary--file.html.j2' %} +
    +{% if entity.media_type and entity.media_type.type== 'image' %} +
    + {{ entity.description | localize }} +
    + {% endif %} +View {{ entity.label | localize | html_lang }} diff --git a/betty/project/extension/raspberry_mint/assets/templates/search/result--person.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/search/result--person.html.j2 new file mode 100644 index 000000000..7dcac1253 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/search/result--person.html.j2 @@ -0,0 +1 @@ +{% include 'search/result-with-image.html.j2' %} diff --git a/betty/project/extension/raspberry_mint/assets/templates/search/result--place.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/search/result--place.html.j2 new file mode 100644 index 000000000..7dcac1253 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/search/result--place.html.j2 @@ -0,0 +1 @@ +{% include 'search/result-with-image.html.j2' %} diff --git a/betty/project/extension/raspberry_mint/assets/templates/search/result--source.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/search/result--source.html.j2 new file mode 100644 index 000000000..7dcac1253 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/search/result--source.html.j2 @@ -0,0 +1 @@ +{% include 'search/result-with-image.html.j2' %} diff --git a/betty/project/extension/raspberry_mint/assets/templates/search/result-with-image.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/search/result-with-image.html.j2 new file mode 100644 index 000000000..f4bfcaa05 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/search/result-with-image.html.j2 @@ -0,0 +1,12 @@ +{% set embedded=True %} +
    + {% include ['entity/label--' + entity.plugin_id() + '.html.j2', 'entity/label.html.j2'] %} + {% include 'entity/summary--' + entity.plugin_id() + '.html.j2' ignore missing %} +
    +{% set image_reference = entity.file_references | selectattr('file', 'public') | selectattr('file.media_type', 'image_supported_media_type') | first %} +{% if image_reference is defined %} +
    + {{ image_reference.file.label | localize }} +
    +{% endif %} +View {{ entity.label | localize | html_lang }} diff --git a/betty/project/extension/raspberry_mint/assets/templates/search/result.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/search/result.html.j2 new file mode 100644 index 000000000..b7f06ac0d --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/search/result.html.j2 @@ -0,0 +1,6 @@ +{% set embedded=True %} +
    + {% include ['entity/label--' + entity.plugin_id() + '.html.j2', 'entity/label.html.j2'] %} + {% include 'entity/summary--' + entity.plugin_id() + '.html.j2' ignore missing %} +
    +View {{ entity.label | localize | html_lang }} diff --git a/betty/project/extension/raspberry_mint/assets/templates/section/external-links.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/section/external-links.html.j2 new file mode 100644 index 000000000..856b85e3a --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/section/external-links.html.j2 @@ -0,0 +1,30 @@ +{% if page_resource is has_links %} + {% set links = page_resource.links | list %} + {% set links = (links | select_localizeds(include_unspecified=true) | list + links | selectattr('locale', 'none') | list) | unique | list %} + {% if links | length > 0 %} +
    + +
    + {% endif %} +{% endif %} \ No newline at end of file diff --git a/betty/project/extension/raspberry_mint/assets/templates/section/facts.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/section/facts.html.j2 new file mode 100644 index 000000000..069ce2fb0 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/section/facts.html.j2 @@ -0,0 +1,20 @@ +{% set facts = facts | select('public') | list %} +{% if facts | length > 0 %} +
    +
    +
    +
    +

    + {% trans %}Facts{% endtrans %} + {% with url = page_resource | localized_url ~ '#facts'%} + {% include 'permalink.html.j2' %} + {% endwith %} +

    + {% with entities = facts %} + {% include 'entity/list.html.j2' %} + {% endwith %} +
    +
    +
    +
    +{% endif %} diff --git a/betty/project/extension/raspberry_mint/assets/templates/section/family.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/section/family.html.j2 new file mode 100644 index 000000000..fab6bee0a --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/section/family.html.j2 @@ -0,0 +1,104 @@ +{%- set parents = entity.parents | list -%} +{%- set siblings = entity.siblings | list -%} +{%- set children = entity.children | list -%} + +{%- set has_family = parents | length > 0 or siblings | length > 0 or children | length > 0 -%} + +{% macro person_label(person) -%} + {%- with entity = person, entity_contexts=entity_contexts(entity) -%} + {%- include 'entity/label--person.html.j2' -%} + {%- endwith -%} +{%- endmacro %} + +{%- if has_family -%} +
    +
    +

    + {% trans %}Family{% endtrans %} + {% with url = page_resource | localized_url ~ '#family'%} + {% include 'permalink.html.j2' %} + {% endwith %} +

    + {% set family_section_count = 0 %} + {% if parents | length > 0 or siblings | length > 0 %} + {% set family_section_count = family_section_count + 1 %} +
    +
    +

    + {% if parents | length > 0 %} + {% trans parent_labels = parents | map(person_label) | join(', ') %}They are the child of {{ parent_labels }}.{% endtrans %} + {% endif %} + + {% if siblings | length > 0 %} + {%- trans sibling_count = siblings | length -%} + They grew up with a sibling. + {%- pluralize -%} + They grew up with {{ sibling_count }} siblings. + {%- endtrans -%} + {% endif %} +

    +
    + {% set public_siblings = siblings | select('public') | list %} + {% for public_sibling in public_siblings %} +
    +
    +
    + {%- with entity = public_sibling, entity_contexts=entity_contexts(entity) -%} + {%- include 'entity/label--person.html.j2' -%} + {%- include 'entity/summary--person.html.j2' -%} + {%- endwith -%} +
    +
    +
    + {% endfor %} +
    + {% endif %} + + {% if children | length > 0 %} + {% for family_parents, family_children in entity | person_descendant_families %} + {% set family_section_count = family_section_count + 1 %} + {% if family_section_count > 1 %} +
    + {% endif %} +
    + {% set family_parents = family_parents | reject('eq', entity) | list %} +
    +

    + {%- if family_parents | length > 0 -%} + {%- trans child_count = family_children | length, co_parent_labels = family_parents | map(person_label) | join(', ') -%} + They had a child with {{ co_parent_labels }}. + {%- pluralize -%} + They had {{ child_count }} children with {{ co_parent_labels }}. + {%- endtrans -%} + {%- else -%} + {%- trans child_count = family_children | length -%} + They had a child. + {%- pluralize -%} + They had {{ child_count }} children. + {%- endtrans -%} + {%- endif -%} +

    +
    + {% set public_family_children = family_children | select('public') | list %} + {% for public_family_child in public_family_children %} +
    +
    +
    + {%- with entity = public_family_child, entity_contexts=entity_contexts(entity) -%} + {%- include 'entity/label--person.html.j2' -%} + {%- include 'entity/summary--person.html.j2' -%} + {%- endwith -%} +
    +
    +
    + {% endfor %} +
    + {% endfor %} + {% endif %} +
    +
    + + {% with person = entity %} + {% include 'section/tree.html.j2' %} + {% endwith %} +{%- endif -%} diff --git a/betty/project/extension/raspberry_mint/assets/templates/section/map.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/section/map.html.j2 new file mode 100644 index 000000000..7fbfb00e3 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/section/map.html.j2 @@ -0,0 +1,6 @@ +{% set places = places | select('public') | rejectattr('coordinates', 'none') | list %} +{% if places | length > 0 %} +
    + {% include 'map.html.j2' %} +
    +{% endif %} diff --git a/betty/project/extension/raspberry_mint/assets/templates/section/media.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/section/media.html.j2 new file mode 100644 index 000000000..c84ef1119 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/section/media.html.j2 @@ -0,0 +1,37 @@ +{% set file_references = file_references | selectattr('file', 'public') | list %} +{% if file_references | length > 0 %} +
    +
    +
    +
    +
    +

    + {% trans %}Media{% endtrans %} + {% with url = page_resource | localized_url ~ '#media' %} + {% include 'permalink.html.j2' %} + {% endwith %} +

    +
    +
    +
    + {% for file_reference in file_references %} +
    + {% if file_reference.file.media_type is image_supported_media_type %} +
    + + + {{ file_reference.file.label | localize }} + +
    {{ file_reference.file.label | localize | html_lang }}
    +
    + {% else %} +

    {{ file_reference.file.label | localize | html_lang }}

    + {% endif %} + View {{ file_reference.file.label | localize | html_lang }} +
    + {% endfor %} +
    +
    +
    +
    +{% endif %} diff --git a/betty/project/extension/raspberry_mint/assets/templates/section/notes.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/section/notes.html.j2 new file mode 100644 index 000000000..a133eebae --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/section/notes.html.j2 @@ -0,0 +1,20 @@ +{% set notes = entity.notes | select('public') | list %} +{% if notes | length > 0 %} +
    +
    +
    +
    +

    + {% trans %}Notes{% endtrans %} + {% with url = page_resource | localized_url ~ '#notes'%} + {% include 'permalink.html.j2' %} + {% endwith %} +

    + {% for note in notes %} + {{ note.text | localize | paragraphs }} + {% endfor %} +
    +
    +
    +
    +{% endif %} diff --git a/betty/project/extension/raspberry_mint/assets/templates/section/timeline.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/section/timeline.html.j2 new file mode 100644 index 000000000..d2911cfb0 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/section/timeline.html.j2 @@ -0,0 +1,20 @@ +{%- set timeline -%} + {% include 'timeline.html.j2' %} +{%- endset -%} +{%- if timeline -%} +
    +
    +
    +
    +

    + {% trans %}Timeline{% endtrans %} + {% with url = page_resource | localized_url ~ '#timeline' %} + {% include 'permalink.html.j2' %} + {% endwith %} +

    + {{ timeline }} +
    +
    +
    +
    +{%- endif -%} diff --git a/betty/project/extension/raspberry_mint/assets/templates/section/tree.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/section/tree.html.j2 new file mode 100644 index 000000000..702a2e268 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/section/tree.html.j2 @@ -0,0 +1,3 @@ +
    + {% include 'tree.html.j2' %} +
    diff --git a/betty/project/extension/raspberry_mint/assets/templates/section/wikipedia.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/section/wikipedia.html.j2 new file mode 100644 index 000000000..697a3a2d8 --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/section/wikipedia.html.j2 @@ -0,0 +1,24 @@ +{% if 'wikipedia' in project.extensions %} + {% set content -%} + {% with resource = entity %} + {% include 'wikipedia.html.j2' %} + {% endwith %} + {%- endset %} + {% if content %} +
    +
    +
    +
    +

    + {% trans %}About{% endtrans %} + {% with url = page_resource | localized_url ~ '#wikipedia' %} + {% include 'permalink.html.j2' %} + {% endwith %} +

    + {{ content }} +
    +
    +
    +
    + {% endif %} +{% endif %} diff --git a/betty/project/extension/raspberry_mint/assets/templates/submit.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/submit.html.j2 new file mode 100644 index 000000000..2a2c1ea7c --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/submit.html.j2 @@ -0,0 +1 @@ + diff --git a/betty/project/extension/raspberry_mint/assets/templates/timeline.html.j2 b/betty/project/extension/raspberry_mint/assets/templates/timeline.html.j2 new file mode 100644 index 000000000..aba59f84f --- /dev/null +++ b/betty/project/extension/raspberry_mint/assets/templates/timeline.html.j2 @@ -0,0 +1,23 @@ +{% set person_context = entity_contexts['person'] %} +{% set events = events | rejectattr('date', 'none') | selectattr('date.comparable') | select('public') | list | sort(attribute='date') %} +{% if events | length > 0 %} +
      + {% for entity in events %} + {% set ns = namespace(is_attendee = False, is_subject = False) %} + {% if person_context is not none %} + {% for presence in entity.presences %} + {% if presence.person == person_context %} + {% set ns.is_attendee = True %} + {% if presence.role is presence_role_plugin('subject') %} + {% set ns.is_subject = True %} + {% endif %} + {% endif %} + {% endfor %} + {% endif %} + + {% include 'entity/label--event.html.j2' %} + {% include 'entity/summary--event.html.j2' %} + + {% endfor %} +
    +{% endif %} diff --git a/betty/project/extension/raspberry_mint/config.py b/betty/project/extension/raspberry_mint/config.py new file mode 100644 index 000000000..2a5213819 --- /dev/null +++ b/betty/project/extension/raspberry_mint/config.py @@ -0,0 +1,92 @@ +""" +Provide configuration for the Raspberry Mint extension. +""" + +from __future__ import annotations + +from typing import Sequence, TYPE_CHECKING + +from typing_extensions import override + +from betty.assertion import assert_record, OptionalField +from betty.config import Configuration +from betty.model.config import EntityReference, EntityReferenceSequence +from betty.project.extension._theme import ColorConfiguration + +if TYPE_CHECKING: + from betty.serde.dump import Dump, DumpMapping + from betty.model import UserFacingEntity, Entity + + +class RaspberryMintConfiguration(Configuration): + """ + Provide configuration for the :py:class:`betty.project.extension.raspberry_mint.RaspberryMint` extension. + """ + + DEFAULT_PRIMARY_COLOR = "#b3446c" + DEFAULT_SECONDARY_COLOR = "#3eb489" + DEFAULT_TERTIARY_COLOR = "#ffbd22" + + def __init__( + self, + *, + featured_entities: ( + Sequence[EntityReference[UserFacingEntity & Entity]] | None + ) = None, + primary_color: str = DEFAULT_PRIMARY_COLOR, + secondary_color: str = DEFAULT_SECONDARY_COLOR, + tertiary_color: str = DEFAULT_TERTIARY_COLOR, + ): + super().__init__() + self._featured_entities = EntityReferenceSequence["UserFacingEntity & Entity"]( + featured_entities or () + ) + self._primary_color = ColorConfiguration(primary_color) + self._secondary_color = ColorConfiguration(secondary_color) + self._tertiary_color = ColorConfiguration(tertiary_color) + + @property + def featured_entities(self) -> EntityReferenceSequence[UserFacingEntity & Entity]: + """ + The entities featured on the front page. + """ + return self._featured_entities + + @property + def primary_color(self) -> ColorConfiguration: + """ + The primary color. + """ + return self._primary_color + + @property + def secondary_color(self) -> ColorConfiguration: + """ + The secondary color. + """ + return self._secondary_color + + @property + def tertiary_color(self) -> ColorConfiguration: + """ + The tertiary color. + """ + return self._tertiary_color + + @override + def load(self, dump: Dump) -> None: + assert_record( + OptionalField("featured_entities", self.featured_entities.load), + OptionalField("primary_color", self.primary_color.load), + OptionalField("secondary_color", self.secondary_color.load), + OptionalField("tertiary_color", self.tertiary_color.load), + )(dump) + + @override + def dump(self) -> DumpMapping[Dump]: + return { + "featured_entities": self.featured_entities.dump(), + "primary_color": self.primary_color.dump(), + "secondary_color": self.secondary_color.dump(), + "tertiary_color": self.tertiary_color.dump(), + } diff --git a/betty/project/extension/raspberry_mint/webpack/css/background.scss b/betty/project/extension/raspberry_mint/webpack/css/background.scss new file mode 100644 index 000000000..0df75fce2 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/background.scss @@ -0,0 +1,3 @@ +.background-embellished { + background: repeating-radial-gradient(at 100% 0, var(--bs-body-bg), var(--betty-background-contrast-color), var(--bs-body-bg) 125%); +} diff --git a/betty/project/extension/raspberry_mint/webpack/css/color-modes.scss b/betty/project/extension/raspberry_mint/webpack/css/color-modes.scss new file mode 100644 index 000000000..f31ff4264 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/color-modes.scss @@ -0,0 +1,60 @@ +@mixin common {; + --betty-focus-color: #{$tertiary-300}; + --betty-focus-contrast-color: #{$black}; + --betty-selection-color: #{$tertiary-100}; + --betty-selection-contrast-color: #{$black}; + + background-color: var(--bs-body-bg); + color: var(--bs-body-color); +} + +@mixin light { + --betty-background-contrast-color: #{$gray-100}; + --betty-border-color: #{$gray-200}; + --betty-heading-embellishment-color: #{$primary-200}; + --betty-link-hover-decoration-color: #{$secondary-800}; + --betty-link-decoration-color: #{$secondary-600}; +} + +@mixin dark { + --bs-body-bg: #{$body-bg-dark}; + --bs-body-color: #{$body-color-dark}; + --bs-body-color-rgb: #{to-rgb($body-color-dark)}; + --bs-card-color: #{$card-color-dark} !important; + --bs-link-color: #{$secondary-200}; + --bs-link-color-rgb: #{to-rgb($secondary-200)}; + --bs-link-hover-color: #{$secondary-100}; + --betty-background-contrast-color: #{$gray-900}; + --betty-border-color: #{$gray-800}; + --betty-heading-embellishment-color: #{$primary-800}; + --betty-link-hover-decoration-color: #{$secondary-200}; + --betty-link-decoration-color: #{$secondary-400}; +} + +:root, [data-bs-theme="light"] { + @include common; + @include light; +} + +[data-bs-theme="light-secondary"] { + @include common; + @include light; + + --bs-body-bg: #{$secondary-100}; + --betty-background-contrast-color: #{$secondary-200}; + --betty-border-color: #{$secondary-300}; +} + +[data-bs-theme="dark"] { + @include common; + @include dark; +} + +[data-bs-theme="dark-secondary"] { + @include common; + @include dark; + + --bs-body-bg: #{$secondary-900}; + --betty-background-contrast-color: #{$secondary-800}; + --betty-border-color: #{$secondary-700}; +} \ No newline at end of file diff --git a/betty/project/extension/raspberry_mint/webpack/css/component/accordion.scss b/betty/project/extension/raspberry_mint/webpack/css/component/accordion.scss new file mode 100644 index 000000000..d7e4bc705 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/component/accordion.scss @@ -0,0 +1,17 @@ +.accordion-button { + margin-bottom: 0; + + &:focus { + background-color: var(--betty-focus-color); + box-shadow: 0 0 var(--betty-focus-color), 0 2px var(--betty-focus-contrast-color); + color: var(--betty-focus-contrast-color) !important; + text-decoration-color: var(--betty-focus-contrast-color); + } +} + +.accordion-body { + @extend .mb-3; + + border-right: $border-width solid $black; + margin-right: calc((1.5em - $border-width) / 2); +} diff --git a/betty/project/extension/raspberry_mint/webpack/css/component/button.scss b/betty/project/extension/raspberry_mint/webpack/css/component/button.scss new file mode 100644 index 000000000..96ea78c85 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/component/button.scss @@ -0,0 +1,32 @@ +.btn { + &:focus { + background-color: var(--betty-focus-color); + border-color: var(--betty-focus-contrast-color); + color: var(--betty-focus-contrast-color); + } +} + +@mixin button { + --bs-btn-active-bg: #{$primary-800}; + --bs-btn-active-border-color: #{$primary-800}; + --bs-btn-active-color: #{$white}; + --bs-btn-hover-bg: #{$primary-700}; + --bs-btn-hover-border-color: #{$primary-700}; + --bs-btn-hover-color: #{$white}; +} + +.btn.btn-primary { + @include button; + + --bs-btn-bg: #{$primary}; + --bs-btn-border-color: #{$primary}; + --bs-btn-color: #{$white}; +} + +.btn.btn-secondary { + @include button; + + --bs-btn-bg: #{$white}; + --bs-btn-border-color: #{$primary}; + --bs-btn-color: #{$primary}; +} \ No newline at end of file diff --git a/betty/project/extension/raspberry_mint/webpack/css/component/card.scss b/betty/project/extension/raspberry_mint/webpack/css/component/card.scss new file mode 100644 index 000000000..130fe9789 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/component/card.scss @@ -0,0 +1,10 @@ +.card { + border: none; + box-shadow: $border-width $border-width $gray-300; + + .stretched-link { + &:focus { + border: $border-width solid var(--betty-focus-color); + } + } +} diff --git a/betty/project/extension/raspberry_mint/webpack/css/component/close.scss b/betty/project/extension/raspberry_mint/webpack/css/component/close.scss new file mode 100644 index 000000000..ad7154d07 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/component/close.scss @@ -0,0 +1,9 @@ +.btn-close { + border: 3px solid $white; + + &:focus { + background-color: var(--betty-focus-color); + border-color: var(--betty-focus-contrast-color); + color: var(--betty-focus-contrast-color); + } +} diff --git a/betty/project/extension/raspberry_mint/webpack/css/component/form-check.scss b/betty/project/extension/raspberry_mint/webpack/css/component/form-check.scss new file mode 100644 index 000000000..71f4b0372 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/component/form-check.scss @@ -0,0 +1,17 @@ +.form-check { + &:focus-within { + .form-check-label { + background-color: var(--betty-focus-color); + box-shadow: 0 0 var(--betty-focus-color), 0 2px var(--betty-focus-contrast-color); + color: var(--betty-focus-contrast-color); + text-decoration-color: var(--betty-focus-contrast-color); + outline: none; + + } + + .form-check-input { + background-color: var(--betty-focus-color); + border-color: var(--betty-focus-contrast-color); + } + } +} \ No newline at end of file diff --git a/betty/project/extension/raspberry_mint/webpack/css/component/form-control-text.scss b/betty/project/extension/raspberry_mint/webpack/css/component/form-control-text.scss new file mode 100644 index 000000000..4354c843e --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/component/form-control-text.scss @@ -0,0 +1,17 @@ +.form-control-text { + &:focus-within { + .form-label { + background-color: var(--betty-focus-color); + box-shadow: 0 0 var(--betty-focus-color), 0 2px var(--betty-focus-contrast-color); + color: var(--betty-focus-contrast-color); + text-decoration-color: var(--betty-focus-contrast-color); + outline: none; + + } + + .form-control { + background-color: var(--betty-focus-color); + border-color: var(--betty-focus-contrast-color); + } + } +} \ No newline at end of file diff --git a/betty/project/extension/raspberry_mint/webpack/css/component/form.scss b/betty/project/extension/raspberry_mint/webpack/css/component/form.scss new file mode 100644 index 000000000..9a57b8dfd --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/component/form.scss @@ -0,0 +1,4 @@ +// Use `fieldset legend` to be more specific than Bootstrap's own `legend`. +fieldset legend { + @extend .h6; +} diff --git a/betty/project/extension/raspberry_mint/webpack/css/component/header.scss b/betty/project/extension/raspberry_mint/webpack/css/component/header.scss new file mode 100644 index 000000000..14bdabb70 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/component/header.scss @@ -0,0 +1,69 @@ +@use '../mixins/text'; + +.header-site-logo { + height: 1.5em; + margin-bottom: $spacer * .25; + margin-top: $spacer * .25; + width: 1.5em; +} + +.header-site-title { + color: var(--bs-body-color); + font-weight: bold; + margin-bottom: $spacer * .25; + margin-top: $spacer * .25; + text-decoration: none; + + &:focus { + @include text.link-focus; + } +} + +.header-primary-navigation li a { + color: var(--bs-body-color); + display: inline-block; + padding: $spacer * .25; + text-decoration: none; + + &:hover { + background-color: var(--bs-body-color); + color: var(--bs-body-bg); + } +} + +.header-entry-point { + @include font-size($h5-font-size); + + background-position: center; + background-repeat: no-repeat; + text-indent: -999rem; + width: 1lh; +} + +#nav-menu .header-entry-point { + height: 2lh; +} + +.header-entry-point-menu { + background-image: #{icon-primary-navigation($white)}; + + &:focus { + background-image: #{icon-primary-navigation($black)}; + } +} + +.header-entry-point-search { + background-image: #{icon-search($white)}; + + &:focus { + background-image: #{icon-search($black)}; + } +} + +.header-entry-point-language { + background-image: #{icon-language($white)}; + + &:focus { + background-image: #{icon-language($black)}; + } +} \ No newline at end of file diff --git a/betty/project/extension/raspberry_mint/webpack/css/component/image.scss b/betty/project/extension/raspberry_mint/webpack/css/component/image.scss new file mode 100644 index 000000000..c3ede29e6 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/component/image.scss @@ -0,0 +1,8 @@ +.image { + background-color: var(--betty-background-contrast-color); + border: $border-width solid var(--betty-border-color); + + // Ensure vector images such as SVGs are not stretched. + object-fit: cover; + width: 100%; +} diff --git a/betty/project/extension/raspberry_mint/webpack/css/component/map.scss b/betty/project/extension/raspberry_mint/webpack/css/component/map.scss new file mode 100644 index 000000000..cb1c11b25 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/component/map.scss @@ -0,0 +1,19 @@ +.map-section { + /* Maps are full width, so leave enough vertical space for scrolling. */ + height: 50vh; + + .map { + margin-left: -$border-width; + margin-right: -$border-width; + } +} + +.map { + border: $border-width solid var(--betty-border-color); + + + &:focus { + border-color: var(--betty-focus-color); + outline: none; + } +} diff --git a/betty/project/extension/raspberry_mint/webpack/css/component/media.scss b/betty/project/extension/raspberry_mint/webpack/css/component/media.scss new file mode 100644 index 000000000..572318cd2 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/component/media.scss @@ -0,0 +1,11 @@ +.media { + .media-file { + .image { + aspect-ratio: 1; + } + + &:focus-within .image { + border-color: var(--betty-focus-color); + } + } +} diff --git a/betty/project/extension/raspberry_mint/webpack/css/component/modal.scss b/betty/project/extension/raspberry_mint/webpack/css/component/modal.scss new file mode 100644 index 000000000..cef38ffc1 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/component/modal.scss @@ -0,0 +1,18 @@ +@use "sass:map"; +@import "bootstrap/scss/mixins/breakpoints"; + +@mixin -modal-fullscreen() { + --bs-modal-header-padding: #{$container-padding-x * 0.5}; + --bs-modal-padding: #{$container-padding-x * 0.5}; +} + +@each $breakpoint in map.keys($grid-breakpoints) { + $infix: breakpoint-infix($breakpoint, $grid-breakpoints); + $postfix: if($infix != "", $infix + "-down", ""); + + @include media-breakpoint-down($breakpoint) { + .modal-fullscreen#{$postfix} { + @include -modal-fullscreen; + } + } +} diff --git a/betty/project/extension/raspberry_mint/webpack/css/component/permalink.scss b/betty/project/extension/raspberry_mint/webpack/css/component/permalink.scss new file mode 100644 index 000000000..ad3d58558 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/component/permalink.scss @@ -0,0 +1,25 @@ +.permalink { + box-shadow: none; + display: inline-block; + + &:hover .permalink-wrapper { + background-color: var(--bs-link-hover-color); + } + + &:focus { + background-color: var(--betty-focus-color); + box-shadow: 0 0 var(--betty-focus-color), 0 2px var(--betty-focus-contrast-color); + + .permalink-wrapper { + background-color: var(--betty-focus-contrast-color); + } + } + + .permalink-wrapper { + background-color: var(--bs-link-color); + display: block; + height: 0.75em; + mask-image: icon-link($black); + width: 0.75em; + } +} diff --git a/betty/project/extension/raspberry_mint/webpack/css/component/references.scss b/betty/project/extension/raspberry_mint/webpack/css/component/references.scss new file mode 100644 index 000000000..7dc12cfb6 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/component/references.scss @@ -0,0 +1,5 @@ +.reference { + &:target { + background-color: var(--betty-focus-color); + } +} diff --git a/betty/project/extension/raspberry_mint/webpack/css/component/search.scss b/betty/project/extension/raspberry_mint/webpack/css/component/search.scss new file mode 100644 index 000000000..7a17fc510 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/component/search.scss @@ -0,0 +1,23 @@ +.search-result { + position: relative; + + &:focus-within { + .link { + background-color: var(--betty-focus-color); + color: var(--betty-focus-contrast-color); + text-decoration-color: var(--betty-focus-contrast-color); + outline: none; + } + } + + .search-result-preview .image { + aspect-ratio: 1; + width: 50px; + } +} + +@include media-breakpoint-up(lg) { + .search-result .search-result-preview .image { + width: 100px; + } +} diff --git a/betty/project/extension/raspberry_mint/webpack/css/component/section.scss b/betty/project/extension/raspberry_mint/webpack/css/component/section.scss new file mode 100644 index 000000000..c57f90144 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/component/section.scss @@ -0,0 +1,30 @@ +.section { + position: relative; + + &:target::before { + background-color: var(--betty-focus-color); + content: ""; + display: block; + height: 100%; + left: $border-width * -2; + position: absolute; + width: $border-width; + } +} + +.section-heading { + padding-left: $border-width * 4; + position: relative; + z-index:1; + + &::before { + background-color: var(--betty-heading-embellishment-color); + content:""; + display: block; + height: 1lh; + left: 0; + position: absolute; + width: $border-width * 2; + z-index:-1; + } +} \ No newline at end of file diff --git a/betty/project/extension/raspberry_mint/webpack/css/component/timeline.scss b/betty/project/extension/raspberry_mint/webpack/css/component/timeline.scss new file mode 100644 index 000000000..e26228619 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/component/timeline.scss @@ -0,0 +1,84 @@ +.timeline { + --timeline-column-gap: 0.75em; + + display: grid; + gap: $spacer * 2 var(--timeline-column-gap); + grid-auto-columns: max-content; + grid-template-columns: $border-width 1fr; + list-style: none; + padding-left: 1em; + + &::before { + content: ""; + grid-column: 1; + grid-row: 1 / span calc(var(--timeline-item-count) * 2); + background: $black; + } + + li { + position: relative; + grid-column: 2; + grid-row: span 2; + + &::before, &::after { + aspect-ratio: 1; + border-radius: 50%; + content: ""; + left: calc(-1 * (var(--timeline-column-gap) + $border-width / 2)); + position: absolute; + transform: translate(-50%); + } + + &::before { + background: $white; + border: $border-width solid $black; + top: 0; + width: $border-width * 9; + } + + &.timeline-attendee::after { + background: $primary-200; + top: $border-width * 3; + width: $border-width * 3; + } + + &.timeline-attendee--subject::after { /* stylelint-disable-line selector-class-pattern */ + background: $primary; + } + } +} + + +@include media-breakpoint-up(lg) { + .timeline { + grid-template-columns: 1fr $border-width 1fr; + pading-left: 0; + + &::before { + grid-column: 2; + grid-row: 1 / span var(--timeline-item-count); + } + + li { + &::before, &::after { + transform: translate(-50%); + } + + &:nth-child(odd) { + grid-column: 1; + + &::before, &::after { + left: calc(100% + var(--timeline-column-gap) + $border-width / 2); + } + } + + &:nth-child(even) { + grid-column: 3; + } + + &:nth-child(2) { + grid-row: 2/4; + } + } + } +} diff --git a/betty/project/extension/raspberry_mint/webpack/css/component/tree.scss b/betty/project/extension/raspberry_mint/webpack/css/component/tree.scss new file mode 100644 index 000000000..c787eef1b --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/component/tree.scss @@ -0,0 +1,13 @@ +.tree-section { + /* Trees are full width, so leave enough vertical space for scrolling. */ + height: 50vh; + + .tree { + margin-left: -$border-width; + margin-right: -$border-width; + } +} + +.tree { + border: $border-width solid var(--betty-border-color); +} diff --git a/betty/project/extension/raspberry_mint/webpack/css/custom.scss b/betty/project/extension/raspberry_mint/webpack/css/custom.scss new file mode 100644 index 000000000..76d774124 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/custom.scss @@ -0,0 +1,37 @@ +/* Bootstrap styles */ +@import "bootstrap/scss/accordion"; +@import "bootstrap/scss/buttons"; +@import "bootstrap/scss/card"; +@import "bootstrap/scss/close"; +@import "bootstrap/scss/forms/labels"; +@import "bootstrap/scss/forms/form-text"; +@import "bootstrap/scss/forms/form-control"; +@import "bootstrap/scss/forms/form-check"; +@import "bootstrap/scss/modal"; +@import "bootstrap/scss/transitions"; + +/* Raspberry Mint styles */ +@import "./background"; +@import "./color-modes"; +@import "./font"; +@import "./page"; +@import "./text"; +@import "./component/accordion"; +@import "./component/button"; +@import "./component/card"; +@import "./component/close"; +@import "./component/form"; +@import "./component/form-check"; +@import "./component/form-control-text"; +@import "./component/header"; +@import "./component/image"; +@import "./component/map"; +@import "./component/media"; +@import "./component/modal"; +@import "./component/permalink"; +@import "./component/references"; +@import "./component/search"; +@import "./component/section"; +@import "./component/timeline"; +@import "./component/tree"; +@import "./entity"; diff --git a/betty/project/extension/raspberry_mint/webpack/css/entity.scss b/betty/project/extension/raspberry_mint/webpack/css/entity.scss new file mode 100644 index 000000000..89dda389b --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/entity.scss @@ -0,0 +1,36 @@ +.aka { + .person-label { + font-style: italic; + } +} + +a.file-view-original { + background-image: icon-expand($primary); + background-position-y: center; + background-repeat: no-repeat; + background-size: 1lh 1lh; + display: inline-block; + margin-top: $spacer / 2; + padding-left: 1.25lh; + + &:focus { + background-image: icon-expand($black); + } +} + +.file-usage { + background-color: $gray-100; +} + +.entity-list { + list-style: none; + padding-left: 0; + + li { + padding: $spacer / 2; + + &:nth-child(odd) { + background-color: $gray-100; + } + } +} \ No newline at end of file diff --git a/betty/project/extension/raspberry_mint/webpack/css/font.scss b/betty/project/extension/raspberry_mint/webpack/css/font.scss new file mode 100644 index 000000000..7cf383313 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/font.scss @@ -0,0 +1,20 @@ +@import "@fontsource/noto-sans/300.css"; +@import "@fontsource/noto-sans/300-italic.css"; +@import "@fontsource/noto-sans/400.css"; +@import "@fontsource/noto-sans/400-italic.css"; +@import "@fontsource/noto-sans/500.css"; +@import "@fontsource/noto-sans/500-italic.css"; +@import "@fontsource/noto-sans/600.css"; +@import "@fontsource/noto-sans/600-italic.css"; +@import "@fontsource/noto-sans/700.css"; +@import "@fontsource/noto-sans/700-italic.css"; +@import "@fontsource/noto-sans-arabic/300.css"; +@import "@fontsource/noto-sans-arabic/400.css"; +@import "@fontsource/noto-sans-arabic/500.css"; +@import "@fontsource/noto-sans-arabic/600.css"; +@import "@fontsource/noto-sans-arabic/700.css"; +@import "@fontsource/noto-sans-hebrew/300.css"; +@import "@fontsource/noto-sans-hebrew/400.css"; +@import "@fontsource/noto-sans-hebrew/500.css"; +@import "@fontsource/noto-sans-hebrew/600.css"; +@import "@fontsource/noto-sans-hebrew/700.css"; diff --git a/betty/project/extension/raspberry_mint/webpack/css/functions.scss b/betty/project/extension/raspberry_mint/webpack/css/functions.scss new file mode 100644 index 000000000..add412b14 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/functions.scss @@ -0,0 +1 @@ +@import "./functions/icons"; diff --git a/betty/project/extension/raspberry_mint/webpack/css/functions/icons.scss b/betty/project/extension/raspberry_mint/webpack/css/functions/icons.scss new file mode 100644 index 000000000..5c7ee1791 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/functions/icons.scss @@ -0,0 +1,39 @@ +@function -to-rgb($color) { + @return "rgb(#{to-rgb($color)})" +} + +@function icon-chevron-collapse($color) { + @return url('data:image/svg+xml;utf8,'); +} + +@function icon-chevron-expand($color) { + @return url('data:image/svg+xml;utf8,'); +} + +@function icon-close($color) { + @return url('data:image/svg+xml;utf8,'); +} + +@function icon-language($color) { + @return url('data:image/svg+xml;utf8,'); +} + +@function icon-link($color) { + @return url('data:image/svg+xml;utf8,'); +} + +@function icon-primary-navigation($color) { + @return url('data:image/svg+xml;utf8,'); +} + +@function icon-search($color) { + @return url('data:image/svg+xml;utf8,'); +} + +@function icon-arrow-right($color) { + @return url('data:image/svg+xml;utf8,'); +} + +@function icon-expand($color) { + @return url('data:image/svg+xml;utf8,'); +} diff --git a/betty/project/extension/raspberry_mint/webpack/css/main.scss b/betty/project/extension/raspberry_mint/webpack/css/main.scss new file mode 100644 index 000000000..e09f3d332 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/main.scss @@ -0,0 +1,34 @@ +/* This file follows "Option B" from https://getbootstrap.com/docs/5.3/customize/sass/ */ + +// 1. Include functions first (so you can manipulate colors, SVGs, calc, etc) +@import "bootstrap/scss/functions"; +@import "./functions"; + +// 2. Include any default variable overrides here +@import "./variables"; + +// 3. Include remainder of required Bootstrap stylesheets (including any separate color mode stylesheets) +@import "bootstrap/scss/variables"; + +// 4. Include any default map overrides here +@import "./maps"; + +// 5. Include remainder of required parts +@import "bootstrap/scss/maps"; +@import "bootstrap/scss/mixins"; +@import "bootstrap/scss/root"; + +// 6. Optionally include any other parts as needed +@import "bootstrap/scss/utilities"; +@import "bootstrap/scss/reboot"; +@import "bootstrap/scss/type"; +@import "bootstrap/scss/images"; +@import "bootstrap/scss/containers"; +@import "bootstrap/scss/grid"; +@import "bootstrap/scss/helpers"; + +// 7. Optionally include utilities API last to generate classes based on the Sass map in `_utilities.scss` +@import "bootstrap/scss/utilities/api"; + +// 8. Add additional custom code here +@import "./custom"; diff --git a/betty/project/extension/raspberry_mint/webpack/css/maps.scss b/betty/project/extension/raspberry_mint/webpack/css/maps.scss new file mode 100644 index 000000000..4b66f4f4b --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/maps.scss @@ -0,0 +1,7 @@ +$theme-colors: ( + "primary": $primary, + "secondary": $secondary, + "secondary-dark": $secondary-900, + "secondary-light": $secondary-100, + "danger": $tertiary, +); diff --git a/betty/project/extension/raspberry_mint/webpack/css/mixins/text.scss b/betty/project/extension/raspberry_mint/webpack/css/mixins/text.scss new file mode 100644 index 000000000..6628cccc2 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/mixins/text.scss @@ -0,0 +1,18 @@ +@mixin link() { + text-decoration-color: var(--betty-link-decoration-color); + text-decoration-thickness: 2px; + + &:hover { + color: var(--bs-link-hover-color); + text-decoration-color: var(--betty-link-hover-decoration-color); + } +} + +@mixin link-focus() { + background-color: var(--betty-focus-color); + color: var(--betty-focus-contrast-color); + outline: none; + text-decoration-color: var(--betty-focus-contrast-color); + text-decoration-style: underline; + text-decoration-thickness: 2px; +} diff --git a/betty/project/extension/raspberry_mint/webpack/css/page.scss b/betty/project/extension/raspberry_mint/webpack/css/page.scss new file mode 100644 index 000000000..01c13245f --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/page.scss @@ -0,0 +1,17 @@ +.page-intro-title { + margin-bottom: 0; +} + +.page-intro-image-link { + aspect-ratio: 1; + max-width: 300px; + width: 100%; + + .page-intro-image { + aspect-ratio: 1; + } + + &:focus .page-intro-image { + border-color: var(--betty-focus-color); + } +} \ No newline at end of file diff --git a/betty/project/extension/raspberry_mint/webpack/css/text.scss b/betty/project/extension/raspberry_mint/webpack/css/text.scss new file mode 100644 index 000000000..6c545a42e --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/text.scss @@ -0,0 +1,44 @@ +@use './mixins/text'; + +.link { + color: var(--bs-link-color); + text-decoration: $link-hover-decoration; + + @include text.link; +} + +a:not(.no-link) { + @include text.link; + + &:focus { + @include text.link-focus; + } +} + +a.view-more { + background-image: icon-arrow-right($primary); + background-position-y: center; + background-repeat: no-repeat; + background-size: 1lh 1lh; + display: inline-block; + margin-top: $spacer * 0.5; + padding-left: 1.25lh; + + &:focus { + background-image: icon-arrow-right($black); + } +} + +.card a.view-more.stretched-link:focus { + border: none; +} + +::selection { + background-color: var(--betty-selection-color); + color: var(--betty-selection-contrast-color) !important; +} + +.private { + cursor: help; + text-decoration: dotted underline 2px; +} diff --git a/betty/project/extension/raspberry_mint/webpack/css/variables.scss b/betty/project/extension/raspberry_mint/webpack/css/variables.scss new file mode 100644 index 000000000..afa0df869 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/variables.scss @@ -0,0 +1,14 @@ +@import "./variables/colors"; +@import "./variables/border"; +@import "./variables/spacing"; +@import "./variables/focus"; +@import "./variables/form"; +@import "./variables/font"; +@import "./variables/text"; + +// Components +@import "./variables/close"; +@import "./variables/accordion"; +@import "./variables/form-check"; +@import "./variables/form-control"; +@import "./variables/modal"; diff --git a/betty/project/extension/raspberry_mint/webpack/css/variables/accordion.scss b/betty/project/extension/raspberry_mint/webpack/css/variables/accordion.scss new file mode 100644 index 000000000..dc5c7707d --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/variables/accordion.scss @@ -0,0 +1,9 @@ +$accordion-button-active-bg: none; +$accordion-button-active-color: inherit; +$accordion-button-active-icon: icon-chevron-collapse($accordion-icon-color); +$accordion-button-icon: icon-chevron-expand($accordion-icon-color); +$accordion-button-padding-y: 0; +$accordion-button-padding-x: 0; +$accordion-border-width: 0; +$accordion-icon-transform: rotateX(-180deg); +$accordion-icon-width: 1.5em; diff --git a/betty/project/extension/raspberry_mint/webpack/css/variables/border.scss b/betty/project/extension/raspberry_mint/webpack/css/variables/border.scss new file mode 100644 index 000000000..7da616fae --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/variables/border.scss @@ -0,0 +1,7 @@ +$border-radius: 0; +$border-radius-sm: 0; +$border-radius-lg: 0; +$border-radius-xl: 0; +$border-radius-xxl: 0; +$border-radius-pill: 0; +$border-width: 3px; diff --git a/betty/project/extension/raspberry_mint/webpack/css/variables/close.scss b/betty/project/extension/raspberry_mint/webpack/css/variables/close.scss new file mode 100644 index 000000000..a4c0a87f0 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/variables/close.scss @@ -0,0 +1,6 @@ +$btn-close-bg: #{icon-close($black)}; +$btn-close-focus-opacity: 1; +$btn-close-hover-opacity: 1; +$btn-close-opacity: 1; +$btn-close-padding-x: 0; +$btn-close-width: 2rem; diff --git a/betty/project/extension/raspberry_mint/webpack/css/variables/colors-config.scss.j2 b/betty/project/extension/raspberry_mint/webpack/css/variables/colors-config.scss.j2 new file mode 100644 index 000000000..513d88e15 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/variables/colors-config.scss.j2 @@ -0,0 +1,3 @@ +$primary: {{ project.extensions['raspberry-mint'].configuration.primary_color.hex }}; +$secondary: {{ project.extensions['raspberry-mint'].configuration.secondary_color.hex }}; +$tertiary: {{ project.extensions['raspberry-mint'].configuration.tertiary_color.hex }}; diff --git a/betty/project/extension/raspberry_mint/webpack/css/variables/colors.scss b/betty/project/extension/raspberry_mint/webpack/css/variables/colors.scss new file mode 100644 index 000000000..73146ebab --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/variables/colors.scss @@ -0,0 +1,70 @@ +@use "sass:color"; + +/** Base colors */ +@import "./colors-config"; + +$gray: #808080; + +/* Primary shades */ +$primary-100: color.mix(white, $primary, 80%); +$primary-200: color.mix(white, $primary, 60%); +$primary-300: color.mix(white, $primary, 40%); +$primary-400: color.mix(white, $primary, 20%); +$primary-500: $primary; +$primary-600: color.mix(black, $primary, 20%); +$primary-700: color.mix(black, $primary, 40%); +$primary-800: color.mix(black, $primary, 60%); +$primary-900: color.mix(black, $primary, 80%); + +/* Secondary shades */ +$secondary-100: color.mix(white, $secondary, 80%); +$secondary-200: color.mix(white, $secondary, 60%); +$secondary-300: color.mix(white, $secondary, 40%); +$secondary-400: color.mix(white, $secondary, 20%); +$secondary-500: $secondary; +$secondary-600: color.mix(black, $secondary, 20%); +$secondary-700: color.mix(black, $secondary, 40%); +$secondary-800: color.mix(black, $secondary, 60%); +$secondary-900: color.mix(black, $secondary, 80%); + +/* Tertiary shades */ +$tertiary-100: color.mix(white, $tertiary, 80%); +$tertiary-200: color.mix(white, $tertiary, 60%); +$tertiary-300: color.mix(white, $tertiary, 40%); +$tertiary-400: color.mix(white, $tertiary, 20%); +$tertiary-500: $tertiary; +$tertiary-600: color.mix(black, $tertiary, 20%); +$tertiary-700: color.mix(black, $tertiary, 40%); +$tertiary-800: color.mix(black, $tertiary, 60%); +$tertiary-900: color.mix(black, $tertiary, 80%); + +/* Gray shades */ +$gray-100: color.mix(white, $gray, 80%); +$gray-200: color.mix(white, $gray, 60%); +$gray-300: color.mix(white, $gray, 40%); +$gray-400: color.mix(white, $gray, 20%); +$gray-500: $gray; +$gray-600: color.mix(black, $gray, 20%); +$gray-700: color.mix(black, $gray, 40%); +$gray-800: color.mix(black, $gray, 60%); +$gray-900: color.mix(black, $gray, 80%); + +/* Black and white */ +$black: #000; +$white: #fff; + +/* Overrides */ +$accordion-icon-color: $black; +$accordion-icon-active-color: $black; +$body-bg: $white; +$body-bg-dark: $black; +$body-color: $black; +$body-color-dark: $white; +$card-bg: $gray-100; +$card-bg-dark: $white; +$card-color: $black; +$card-color-dark: $white; +$link-color: $secondary-800; +$link-hover-color: $secondary-900; +$link-hover-underline-color: $secondary-800; +$link-underline-color: $secondary-600; diff --git a/betty/project/extension/raspberry_mint/webpack/css/variables/focus.scss b/betty/project/extension/raspberry_mint/webpack/css/variables/focus.scss new file mode 100644 index 000000000..6323dedf6 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/variables/focus.scss @@ -0,0 +1,4 @@ +$focus-ring-width: 0; +$focus-ring-opacity: 1; +$focus-ring-blur: 0; +$focus-ring-box-shadow: 0; diff --git a/betty/project/extension/raspberry_mint/webpack/css/variables/font.scss b/betty/project/extension/raspberry_mint/webpack/css/variables/font.scss new file mode 100644 index 000000000..1d233aaa8 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/variables/font.scss @@ -0,0 +1,3 @@ +$font-family-base: "Noto Sans", "Noto Sans Arabic", "Noto Sans Hebrew", system-ui, -apple-system, "Segoe UI", "Roboto", "Helvetica Neue", "Liberation Sans", "Arial", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; +$font-size-base: 1.3rem; +$small-font-size: 0.9em; diff --git a/betty/project/extension/raspberry_mint/webpack/css/variables/form-check.scss b/betty/project/extension/raspberry_mint/webpack/css/variables/form-check.scss new file mode 100644 index 000000000..c6a41e2b0 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/variables/form-check.scss @@ -0,0 +1,6 @@ +$form-check-input-border: $border-width solid $form-element-border-color; +$form-check-input-border-radius: 0; +$form-check-input-checked-color: $black; +$form-check-input-checked-bg-color: white; +$form-check-input-checked-border-color: $form-element-border-color; +$form-check-input-width: 1.5em; diff --git a/betty/project/extension/raspberry_mint/webpack/css/variables/form-control.scss b/betty/project/extension/raspberry_mint/webpack/css/variables/form-control.scss new file mode 100644 index 000000000..48b677ba2 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/variables/form-control.scss @@ -0,0 +1,4 @@ +$input-border-color: $form-element-border-color; +$input-focus-bg: var(--betty-focus-color); +$input-focus-border-color: var(--betty-focus-contrast-color); +$input-placeholder-color: $gray-500; diff --git a/betty/project/extension/raspberry_mint/webpack/css/variables/form.scss b/betty/project/extension/raspberry_mint/webpack/css/variables/form.scss new file mode 100644 index 000000000..ed871dc14 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/variables/form.scss @@ -0,0 +1 @@ +$form-element-border-color: $gray-900; diff --git a/betty/project/extension/raspberry_mint/webpack/css/variables/modal.scss b/betty/project/extension/raspberry_mint/webpack/css/variables/modal.scss new file mode 100644 index 000000000..8be6674f0 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/variables/modal.scss @@ -0,0 +1,7 @@ +@use "sass:map"; + +$modal-backdrop-opacity: .7; +$modal-content-border-width: 0; +$modal-header-border-width: 0; +$modal-header-padding-x: $spacer; +$modal-header-padding-y: $spacer * .25; diff --git a/betty/project/extension/raspberry_mint/webpack/css/variables/spacing.scss b/betty/project/extension/raspberry_mint/webpack/css/variables/spacing.scss new file mode 100644 index 000000000..732a04cc5 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/variables/spacing.scss @@ -0,0 +1 @@ +$spacer: 2rem; diff --git a/betty/project/extension/raspberry_mint/webpack/css/variables/text.scss b/betty/project/extension/raspberry_mint/webpack/css/variables/text.scss new file mode 100644 index 000000000..519260cb6 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/css/variables/text.scss @@ -0,0 +1 @@ +$link-hover-decoration: underline; diff --git a/betty/project/extension/raspberry_mint/webpack/main.ts b/betty/project/extension/raspberry_mint/webpack/main.ts new file mode 100644 index 000000000..1c77ee70b --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/main.ts @@ -0,0 +1,8 @@ +'use strict' + +import './css/main.scss' +import "bootstrap/js/dist/collapse" +import "bootstrap/js/dist/modal" +import { Search } from './search.ts' + +new Search() diff --git a/betty/project/extension/raspberry_mint/webpack/package.json b/betty/project/extension/raspberry_mint/webpack/package.json new file mode 100644 index 000000000..d9c5df707 --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/package.json @@ -0,0 +1,13 @@ +{ + "engines": { + "node": ">= 20" + }, + "dependencies": { + "@popperjs/core": "^2.11.8", + "bootstrap": "^5.3.3", + "@fontsource/noto-sans": "^5.1.1", + "@fontsource/noto-sans-arabic": "^5.1.1", + "@fontsource/noto-sans-hebrew": "^5.1.1", + "ionicons": "^7.4.0" + } +} diff --git a/betty/project/extension/raspberry_mint/webpack/search.ts b/betty/project/extension/raspberry_mint/webpack/search.ts new file mode 100644 index 000000000..a7606c2ad --- /dev/null +++ b/betty/project/extension/raspberry_mint/webpack/search.ts @@ -0,0 +1,122 @@ +interface IndexEntry { + text: string + result: string + entityTypeId: string +} + +interface Index { + index: IndexEntry[] + resultContainerTemplate: string + resultsContainerTemplate: string +} + +class Search { + private readonly searchFormElement: HTMLElement + private readonly searchFormQueryElement: HTMLInputElement + private readonly searchFormFilterResetElement: HTMLElement + private readonly resultsContainer: HTMLElement + private index: Index | null = null + private filterEntityTypeHtmlIds: Record + + public constructor() { + this.searchFormElement = this.getElementById('search-form') + this.searchFormQueryElement = this.getElementById('search-form-query') as HTMLInputElement + this.searchFormFilterResetElement = this.getElementById('search-form-filter-reset') + this.resultsContainer = this.getElementById('search-results-container') + this.searchFormElement.addEventListener( + 'submit', + (e) => { + void (async (): Promise => { + await this.search(this.searchFormQueryElement.value, this.getFilterEntityTypeIds()) + })() + e.preventDefault() + e.stopPropagation() + } + ) + this.searchFormFilterResetElement.addEventListener('click', () => { + this.resetFilters() + }) + this.filterEntityTypeHtmlIds = JSON.parse(this.searchFormElement.dataset.bettySearchFormFilterEntityType) as Record + } + + private getElementById(id: string): HTMLElement { + const element = document.getElementById(id) + if (!element) { + throw new Error(`Cannot find element with ID #${id}`) + } + return element + } + + private resetFilters(): void { + this.resetFilterEntityType() + } + + private getFilterEntityTypeElements(): HTMLInputElement[] { + return this.searchFormElement.querySelectorAll('.search-form-filter-entity-type input') + } + + private getFilterEntityTypeIds(): string[] { + const filterEntityTypeIds = [] + for (const entityTypeFilterElement of this.getFilterEntityTypeElements()) { + if (entityTypeFilterElement.checked) { + filterEntityTypeIds.push(this.filterEntityTypeHtmlIds[entityTypeFilterElement.id]) + } + } + return filterEntityTypeIds + } + + private resetFilterEntityType(): void { + for (const entityTypeFilterElement of this.getFilterEntityTypeElements()) { + entityTypeFilterElement.checked = true + } + } + + private setSearchEntries(index: Index, entries: IndexEntry[]): void { + this.resultsContainer.innerHTML = this.renderResults(index, entries) + } + + private async getIndex(): Promise { + if (this.index === null) { + const response = await fetch(this.searchFormElement.dataset.bettySearchFormIndex) + this.index = await response.json() as Index + } + return this.index + } + + private async search(query: string, entityTypeIds: string[]): Promise { + const queryParts = query.toLowerCase().split(/\s/).filter(queryPart => queryPart.trim().length) + const index = await this.getIndex() + if (queryParts.length) { + this.setSearchEntries(index, index.index.filter((entry) => this.match(queryParts, entityTypeIds, entry))) + } else { + this.setSearchEntries(index, []) + } + } + + private match(queryParts: string[], entityTypeIds: string[], entry: IndexEntry): boolean { + if (!entityTypeIds.includes(entry.entityTypeId)) { + return false + } + for (const queryPart of queryParts) { + if (!entry.text.includes(queryPart)) { + return false + } + } + return true + } + + private renderResults(index: Index, entries: IndexEntry[]): string { + return index.resultsContainerTemplate + .replace('{{{ betty-search-results }}}', entries.map((entry) => this.renderResult(entry)).join('')) + .replace('{{{ betty-search-results-count }}}', entries.length) + } + + private renderResult(entry: IndexEntry): string { + return this.index?.resultContainerTemplate + .replace('{{{ betty-search-result }}}', entry.result) + } +} + +export { + Search, +} diff --git a/betty/project/extension/webpack/webpack/webpack.config.js b/betty/project/extension/webpack/webpack/webpack.config.js index 4d8895817..f816e90f5 100644 --- a/betty/project/extension/webpack/webpack/webpack.config.js +++ b/betty/project/extension/webpack/webpack/webpack.config.js @@ -179,6 +179,13 @@ const webpackConfiguration = { generator: { filename: 'images/[hash][ext]' } + }, + { + test: /.*\.woff|woff2/, + type: 'asset/resource', + generator: { + filename: 'fonts/[hash][ext]' + } } ] } diff --git a/betty/tests/project/extension/raspberry_mint/__init__.py b/betty/tests/project/extension/raspberry_mint/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/betty/tests/project/extension/raspberry_mint/assets/__init__.py b/betty/tests/project/extension/raspberry_mint/assets/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/__init__.py b/betty/tests/project/extension/raspberry_mint/assets/templates/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/entity/__init__.py b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_appearances.py b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_appearances.py new file mode 100644 index 000000000..ba95fbc40 --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_appearances.py @@ -0,0 +1,39 @@ +from pathlib import Path + +from betty.ancestry.event import Event +from betty.ancestry.file import File +from betty.ancestry.file_reference import FileReference +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.test_utils.jinja2 import TemplateFileTestBase + + +class Test(TemplateFileTestBase): + extensions = {RaspberryMint} + template = "entity/appearances.html.j2" + + async def test_minimal(self) -> None: + expected = "" + async with self.assert_template_file( + data={"file_references": [], "page_resource": "/sut.html"} + ) as (actual, _): + assert actual == expected + + async def test_with_public_referees(self) -> None: + referee = Event(id="E0") + file = File(Path(__file__)) + file_reference = FileReference(referee, file) + async with self.assert_template_file( + data={"file_references": [file_reference], "page_resource": "/sut.html"} + ) as (actual, _): + assert "/sut.html#appearances" in actual + assert "/event/E0/index.html" in actual + + async def test_without_public_referees(self) -> None: + referee = Event(id="E0", private=True) + file = File(Path(__file__)) + file_reference = FileReference(referee, file) + expected = "" + async with self.assert_template_file( + data={"file_references": [file_reference], "page_resource": "/sut.html"} + ) as (actual, _): + assert actual == expected diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_event_dimensions.py b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_event_dimensions.py new file mode 100644 index 000000000..c1f4f7038 --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_event_dimensions.py @@ -0,0 +1,113 @@ +from betty.ancestry.citation import Citation +from betty.ancestry.event import Event +from betty.ancestry.event_type.event_types import Birth +from betty.ancestry.name import Name +from betty.ancestry.place import Place +from betty.ancestry.source import Source +from betty.date import Date +from betty.jinja2 import EntityContexts +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.test_utils.jinja2 import TemplateFileTestBase + + +class Test(TemplateFileTestBase): + extensions = {RaspberryMint} + template = "entity/event-dimensions.html.j2" + + async def test_minimal(self) -> None: + event = Event(event_type=Birth()) + async with self.assert_template_file( + data={ + "event": event, + } + ) as (actual, _): + assert actual == "sometime" + + async def test_with_date(self) -> None: + event = Event( + event_type=Birth(), + date=Date(1970), + ) + expected = "1970" + async with self.assert_template_file( + data={ + "event": event, + } + ) as (actual, _): + assert actual == expected + + async def test_with_place(self) -> None: + event = Event(event_type=Birth()) + event.place = Place( + id="P0", + names=[Name("The Place")], + ) + expected = 'in The Place' + async with self.assert_template_file( + data={ + "event": event, + } + ) as (actual, _): + assert actual == expected + + async def test_with_place_is_place_context(self) -> None: + event = Event(event_type=Birth()) + place = Place( + id="P0", + names=[Name("The Place")], + ) + event.place = place + async with self.assert_template_file( + data={ + "event": event, + "entity_contexts": await EntityContexts.new(place), + } + ) as (actual, _): + assert actual == "sometime" + + async def test_with_date_and_place(self) -> None: + event = Event( + event_type=Birth(), + date=Date(1970), + ) + event.place = Place( + id="P0", + names=[Name("The Place")], + ) + expected = '1970 in The Place' + async with self.assert_template_file( + data={ + "event": event, + } + ) as (actual, _): + assert actual == expected + + async def test_with_citation(self) -> None: + event = Event(event_type=Birth()) + event.citations.add(Citation(source=Source(name="The Source"))) + expected = 'sometime [1]' + async with self.assert_template_file( + data={ + "event": event, + } + ) as (actual, _): + assert actual == expected + + async def test_embedded(self) -> None: + event = Event( + event_type=Birth(), + date=Date(1970), + ) + event.place = Place( + id="P0", + names=[Name("The Place")], + ) + event.citations.add(Citation(source=Source(name="The Source"))) + expected = '1970 in The Place' + async with self.assert_template_file( + data={ + "event": event, + "embedded": True, + } + ) as (actual, _): + assert actual == expected diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_label.py b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_label.py new file mode 100644 index 000000000..a10def666 --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_label.py @@ -0,0 +1,41 @@ +from __future__ import annotations + +from betty.ancestry.event import Event +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.test_utils.jinja2 import TemplateFileTestBase + + +class Test(TemplateFileTestBase): + extensions = {RaspberryMint} + template = "entity/label.html.j2" + + async def test_minimal(self) -> None: + entity = Event() + expected = 'Unknown' + async with self.assert_template_file( + data={ + "entity": entity, + } + ) as (actual, _): + assert actual == expected + + async def test_with_persistent_id(self) -> None: + entity = Event(id="E0") + expected = 'Unknown' + async with self.assert_template_file( + data={ + "entity": entity, + } + ) as (actual, _): + assert actual == expected + + async def test_embedded(self) -> None: + entity = Event(id="E0") + expected = 'Unknown' + async with self.assert_template_file( + data={ + "entity": entity, + "embedded": True, + } + ) as (actual, _): + assert actual == expected diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_label__citation.py b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_label__citation.py new file mode 100644 index 000000000..d2054ca2a --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_label__citation.py @@ -0,0 +1,129 @@ +from __future__ import annotations + +from betty.ancestry.citation import Citation +from betty.ancestry.source import Source +from betty.date import Date +from betty.jinja2 import EntityContexts +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.test_utils.jinja2 import TemplateFileTestBase + + +class Test(TemplateFileTestBase): + extensions = {RaspberryMint} + template = "entity/label--citation.html.j2" + + async def test_minimal(self) -> None: + source = Source() + citation = Citation(source=source) + expected = f'"Source {source.id}".' + async with self.assert_template_file( + data={ + "entity": citation, + } + ) as (actual, _): + assert actual == expected + + async def test_with_persistent_id(self) -> None: + source = Source() + citation = Citation(id="C0", source=source) + expected = f'"Source {source.id}".' + async with self.assert_template_file( + data={ + "entity": citation, + } + ) as (actual, _): + assert actual == expected + + async def test_embedded(self) -> None: + source = Source() + citation = Citation(id="C0", source=source) + expected = f'"Source {source.id}".' + async with self.assert_template_file( + data={ + "entity": citation, + "embedded": True, + } + ) as (actual, _): + assert actual == expected + + async def test_with_private_source(self) -> None: + source = Source(private=True) + citation = Citation(source=source) + expected = 'private' + async with self.assert_template_file( + data={ + "entity": citation, + } + ) as (actual, _): + assert actual == expected + + async def test_with_source_author(self) -> None: + source = Source(author="Bart") + citation = Citation(source=source) + expected = ( + f'Bart. "Source {source.id}".' + ) + async with self.assert_template_file( + data={ + "entity": citation, + } + ) as (actual, _): + assert actual == expected + + async def test_with_source_publisher(self) -> None: + source = Source(publisher="Bart") + citation = Citation(source=source) + expected = ( + f'"Source {source.id}". Bart.' + ) + async with self.assert_template_file( + data={ + "entity": citation, + } + ) as (actual, _): + assert actual == expected + + async def test_with_location(self) -> None: + source = Source() + citation = Citation(source=source, location="Somewhere") + expected = f'"Source {source.id}". Somewhere.' + async with self.assert_template_file( + data={ + "entity": citation, + } + ) as (actual, _): + assert actual == expected + + async def test_with_citation_context(self) -> None: + source = Source() + citation = Citation(id="C0", source=source, location="Somewhere") + expected = f'"Source {source.id}". Somewhere.' + async with self.assert_template_file( + data={ + "entity": citation, + "entity_contexts": await EntityContexts.new(citation), + } + ) as (actual, _): + assert actual == expected + + async def test_with_date(self) -> None: + source = Source() + citation = Citation(source=source, date=Date(1970, 1, 1)) + expected = f'"Source {source.id}". Accessed January 1, 1970.' + async with self.assert_template_file( + data={ + "entity": citation, + } + ) as (actual, _): + assert actual == expected + + async def test_private(self) -> None: + source = Source() + citation = Citation(source=source, private=True) + expected = f'"Source {source.id}". private' + async with self.assert_template_file( + data={ + "entity": citation, + } + ) as (actual, _): + assert actual == expected diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_label__event.py b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_label__event.py new file mode 100644 index 000000000..48960ceb5 --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_label__event.py @@ -0,0 +1,111 @@ +from betty.ancestry.event import Event +from betty.ancestry.event_type.event_types import Birth, Marriage +from betty.ancestry.person import Person +from betty.ancestry.presence import Presence +from betty.ancestry.presence_role.presence_roles import Subject +from betty.jinja2 import EntityContexts +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.test_utils.jinja2 import TemplateFileTestBase + + +class Test(TemplateFileTestBase): + extensions = {RaspberryMint} + template = "entity/label--event.html.j2" + + async def test_minimal(self) -> None: + event = Event(event_type=Birth()) + expected = "Birth" + async with self.assert_template_file( + data={ + "entity": event, + } + ) as (actual, _): + assert actual == expected + + async def test_with_name(self) -> None: + event = Event( + event_type=Birth(), + name="Something happened!", + ) + expected = 'Something happened!' + async with self.assert_template_file( + data={ + "entity": event, + } + ) as (actual, _): + assert actual == expected + + async def test_with_persistent_id(self) -> None: + event_id = "EVENT1" + event = Event( + id=event_id, + event_type=Birth(), + name="Something happened!", + ) + expected = 'Something happened!' + async with self.assert_template_file( + data={ + "entity": event, + } + ) as (actual, _): + assert actual == expected + + async def test_with_embedded(self) -> None: + event_id = "EVENT1" + event = Event( + id=event_id, + event_type=Birth(), + name="Something happened!", + ) + expected = 'Something happened!' + async with self.assert_template_file( + data={ + "entity": event, + "embedded": True, + } + ) as (actual, _): + assert actual == expected + + async def test_with_single_subject_as_person_context(self) -> None: + event = Event(event_type=Marriage()) + context_subject = Person() + Presence(context_subject, Subject(), event) + expected = "Marriage" + async with self.assert_template_file( + data={ + "entity": event, + "entity_contexts": await EntityContexts.new(context_subject), + } + ) as (actual, _): + assert actual == expected + + async def test_with_subjects_and_subject_as_person_context(self) -> None: + event = Event(event_type=Marriage()) + context_subject = Person() + other_subject = Person() + Presence(context_subject, Subject(), event) + Presence(other_subject, Subject(), event) + expected = ( + 'Marriage with n.n.' + ) + async with self.assert_template_file( + data={ + "entity": event, + "entity_contexts": await EntityContexts.new(context_subject), + } + ) as (actual, _): + assert actual == expected + + async def test_with_subjects_without_person_context(self) -> None: + event = Event(event_type=Marriage()) + context_subject = Person() + other_subject = Person() + Presence(context_subject, Subject(), event) + Presence(other_subject, Subject(), event) + expected = 'Marriage of n.n., n.n.' + async with self.assert_template_file( + data={ + "entity": event, + } + ) as (actual, _): + assert actual == expected diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_label__person.py b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_label__person.py new file mode 100644 index 000000000..f9cbb7bac --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_label__person.py @@ -0,0 +1,98 @@ +from betty.ancestry.person import Person +from betty.ancestry.person_name import PersonName +from betty.jinja2 import EntityContexts +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.test_utils.jinja2 import TemplateFileTestBase + + +class Test(TemplateFileTestBase): + extensions = {RaspberryMint} + template = "entity/label--person.html.j2" + + async def test_minimal(self) -> None: + person = Person() + expected = 'n.n.' + async with self.assert_template_file( + data={ + "entity": person, + } + ) as (actual, _): + assert actual == expected + + async def test_with_name(self) -> None: + person = Person() + PersonName( + person=person, + individual="Jane", + affiliation="Dough", + ) + expected = "Jane Dough" + async with self.assert_template_file( + data={ + "entity": person, + } + ) as (actual, _): + assert actual == expected + + async def test_with_persistent_id(self) -> None: + person = Person(id="P0") + expected = 'n.n.' + async with self.assert_template_file( + data={ + "entity": person, + } + ) as (actual, _): + assert actual == expected + + async def test_embedded(self) -> None: + person = Person(id="P0") + expected = 'n.n.' + async with self.assert_template_file( + data={ + "entity": person, + "embedded": True, + } + ) as (actual, _): + assert actual == expected + + async def test_private(self) -> None: + person = Person(id="P0", private=True) + PersonName( + person=person, + individual="Jane", + affiliation="Dough", + ) + expected = 'private' + async with self.assert_template_file( + data={ + "entity": person, + } + ) as (actual, _): + assert actual == expected + + async def test_with_private_name(self) -> None: + person = Person() + PersonName( + person=person, + individual="Jane", + affiliation="Dough", + private=True, + ) + expected = 'private' + async with self.assert_template_file( + data={ + "entity": person, + } + ) as (actual, _): + assert actual == expected + + async def test_person_is_context(self) -> None: + person = Person(id="P0") + expected = 'n.n.' + async with self.assert_template_file( + data={ + "entity": person, + "entity_contexts": await EntityContexts.new(person), + } + ) as (actual, _): + assert actual == expected diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_label__person_name.py b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_label__person_name.py new file mode 100644 index 000000000..194164ce3 --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_label__person_name.py @@ -0,0 +1,106 @@ +from betty.ancestry.citation import Citation +from betty.ancestry.person import Person +from betty.ancestry.person_name import PersonName +from betty.ancestry.source import Source +from betty.jinja2 import EntityContexts +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.test_utils.jinja2 import TemplateFileTestBase + + +class Test(TemplateFileTestBase): + extensions = {RaspberryMint} + template = "entity/label--person-name.html.j2" + + async def test_minimal_with_individual_name(self) -> None: + person = Person() + person_name = PersonName(person=person, individual="Jane") + expected = "Jane" + async with self.assert_template_file( + data={ + "entity": person_name, + } + ) as (actual, _): + assert actual == expected + + async def test_minimal_with_affiliation_name(self) -> None: + person = Person() + person_name = PersonName(person=person, affiliation="Dough") + expected = "… Dough" + async with self.assert_template_file( + data={ + "entity": person_name, + } + ) as (actual, _): + assert actual == expected + + async def test_with_person_with_persistent_id(self) -> None: + person = Person(id="P0") + person_name = PersonName(person=person, individual="Jane") + expected = 'Jane' + async with self.assert_template_file( + data={ + "entity": person_name, + } + ) as (actual, _): + assert actual == expected + + async def test_embedded(self) -> None: + source = Source() + citation = Citation(source=source) + person = Person(id="P0") + person_name = PersonName(person=person, individual="Jane", citations=[citation]) + expected = "Jane" + async with self.assert_template_file( + data={ + "entity": person_name, + "embedded": True, + } + ) as (actual, _): + assert actual == expected + + async def test_private(self) -> None: + person = Person(id="P0") + person_name = PersonName(person=person, individual="Jane", private=True) + expected = 'private' + async with self.assert_template_file( + data={ + "entity": person_name, + } + ) as (actual, _): + assert actual == expected + + async def test_with_private_person(self) -> None: + person = Person(id="P0", private=True) + person_name = PersonName(person=person, individual="Jane") + expected = 'private' + async with self.assert_template_file( + data={ + "entity": person_name, + } + ) as (actual, _): + assert actual == expected + + async def test_person_is_context(self) -> None: + person = Person(id="P0") + person_name = PersonName(person=person, individual="Jane") + expected = "Jane" + async with self.assert_template_file( + data={ + "entity": person_name, + "entity_contexts": await EntityContexts.new(person), + } + ) as (actual, _): + assert actual == expected + + async def test_with_citations(self) -> None: + source = Source() + citation = Citation(source=source) + person = Person() + person_name = PersonName(person=person, individual="Jane", citations=[citation]) + expected = 'Jane [1]' + async with self.assert_template_file( + data={ + "entity": person_name, + } + ) as (actual, _): + assert actual == expected diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_label__place.py b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_label__place.py new file mode 100644 index 000000000..ab06bca99 --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_label__place.py @@ -0,0 +1,93 @@ +from __future__ import annotations + + +from betty.ancestry.name import Name +from betty.ancestry.place import Place +from betty.date import DateRange, Date +from betty.jinja2 import EntityContexts +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.test_utils.jinja2 import TemplateFileTestBase + + +class Test(TemplateFileTestBase): + extensions = {RaspberryMint} + template = "entity/label--place.html.j2" + + async def test_minimal(self) -> None: + place = Place() + expected = f'Place {place.id}' + async with self.assert_template_file( + data={ + "entity": place, + } + ) as (actual, _): + assert actual == expected + + async def test_with_persistent_id(self) -> None: + place = Place(id="P0") + expected = 'Place P0' + async with self.assert_template_file( + data={ + "entity": place, + } + ) as (actual, _): + assert actual == expected + + async def test_with_name(self) -> None: + place = Place(names=[Name("The Place")]) + expected = 'The Place' + async with self.assert_template_file( + data={ + "entity": place, + } + ) as (actual, _): + assert actual == expected + + async def test_embedded(self) -> None: + place = Place( + id="P0", + names=[Name("The Place")], + ) + expected = 'The Place' + async with self.assert_template_file( + data={ + "entity": place, + "embedded": True, + } + ) as (actual, _): + assert actual == expected + + async def test_with_place_context(self) -> None: + place = Place(id="P0") + + expected = 'Place P0' + async with self.assert_template_file( + data={ + "entity": place, + "entity_contexts": await EntityContexts.new(place), + } + ) as (actual, _): + assert actual == expected + + async def test_with_date_context(self) -> None: + place = Place( + names=[ + Name( + "The Old Place", + date=DateRange(None, Date(1969, 12, 31)), + ), + Name( + "The New Place", + date=DateRange(Date(1970, 1, 1)), + ), + ], + ) + + expected = 'The New Place' + async with self.assert_template_file( + data={ + "entity": place, + "date_context": Date(1970, 1, 1), + } + ) as (actual, _): + assert actual == expected diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_list.py b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_list.py new file mode 100644 index 000000000..3423e68b6 --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_list.py @@ -0,0 +1,36 @@ +from betty.ancestry.event import Event +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.test_utils.jinja2 import TemplateFileTestBase + + +class Test(TemplateFileTestBase): + extensions = {RaspberryMint} + template = "entity/list.html.j2" + + async def test_minimal(self) -> None: + expected = "" + async with self.assert_template_file( + data={ + "entities": [], + } + ) as (actual, _): + assert actual == expected + + async def test_without_public_entities(self) -> None: + entity = Event(private=True) + expected = "" + async with self.assert_template_file( + data={ + "entities": [entity], + } + ) as (actual, _): + assert actual == expected + + async def test_with_public_entities(self) -> None: + entity = Event(id="E0") + async with self.assert_template_file( + data={ + "entities": [entity], + } + ) as (actual, _): + assert "/event/E0/index.html" in actual diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_place_enclosure_label.py b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_place_enclosure_label.py new file mode 100644 index 000000000..fd099c19e --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_place_enclosure_label.py @@ -0,0 +1,48 @@ +from betty.ancestry.enclosure import Enclosure +from betty.ancestry.place import Place +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.test_utils.jinja2 import TemplateStringTestBase + + +class Test(TemplateStringTestBase): + extensions = {RaspberryMint} + + async def test_minimal(self) -> None: + place = Place() + expected = f'Place {place.id}' + async with self.assert_template_string( + "{%- from 'entity/place-enclosure-label.html.j2' import place_enclosure_label -%}{{ place_enclosure_label(place) }}", + data={ + "place": place, + }, + ) as (actual, _): + assert actual == expected + + async def test_with_encloser(self) -> None: + encloser_encloser_place = Place() + encloser_place = Place() + Enclosure(encloser_place, encloser_encloser_place) + place = Place() + Enclosure(place, encloser_place) + expected = f'Place {place.id}, Place {encloser_place.id}, Place {encloser_encloser_place.id}' + async with self.assert_template_string( + "{%- from 'entity/place-enclosure-label.html.j2' import place_enclosure_label -%}{{ place_enclosure_label(place) }}", + data={ + "place": place, + }, + ) as (actual, _): + assert actual == expected + + async def test_with_place_context(self) -> None: + encloser_place = Place() + place = Place() + Enclosure(place, encloser_place) + expected = f'Place {place.id}' + async with self.assert_template_string( + "{%- from 'entity/place-enclosure-label.html.j2' import place_enclosure_label -%}{{ place_enclosure_label(place, place_context) }}", + data={ + "place": place, + "place_context": encloser_place, + }, + ) as (actual, _): + assert actual == expected diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_private.py b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_private.py new file mode 100644 index 000000000..77db89d6d --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_private.py @@ -0,0 +1,11 @@ +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.test_utils.jinja2 import TemplateFileTestBase + + +class Test(TemplateFileTestBase): + extensions = {RaspberryMint} + template = "entity/private.html.j2" + + async def test_minimal(self) -> None: + async with self.assert_template_file(): + pass diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_source_containment_label.py b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_source_containment_label.py new file mode 100644 index 000000000..a7f71f568 --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_source_containment_label.py @@ -0,0 +1,44 @@ +from betty.ancestry.source import Source +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.test_utils.jinja2 import TemplateStringTestBase + + +class Test(TemplateStringTestBase): + extensions = {RaspberryMint} + + async def test_minimal(self) -> None: + source = Source() + expected = f'Source {source.id}' + async with self.assert_template_string( + "{%- from 'entity/source-containment-label.html.j2' import source_containment_label -%}{{ source_containment_label(source) }}", + data={ + "source": source, + }, + ) as (actual, _): + assert actual == expected + + async def test_with_contained_by(self) -> None: + contained_by_contained_by_source = Source() + contained_by_source = Source(contained_by=contained_by_contained_by_source) + source = Source(contained_by=contained_by_source) + expected = f'Source {source.id}, Source {contained_by_source.id}, Source {contained_by_contained_by_source.id}' + async with self.assert_template_string( + "{%- from 'entity/source-containment-label.html.j2' import source_containment_label -%}{{ source_containment_label(source) }}", + data={ + "source": source, + }, + ) as (actual, _): + assert actual == expected + + async def test_with_source_context(self) -> None: + contained_by_source = Source() + source = Source(contained_by=contained_by_source) + expected = f'Source {source.id}' + async with self.assert_template_string( + "{%- from 'entity/source-containment-label.html.j2' import source_containment_label -%}{{ source_containment_label(source, source_context) }}", + data={ + "source": source, + "source_context": contained_by_source, + }, + ) as (actual, _): + assert actual == expected diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_summary__citation.py b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_summary__citation.py new file mode 100644 index 000000000..810c9b43d --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_summary__citation.py @@ -0,0 +1,29 @@ +from betty.ancestry.citation import Citation +from betty.ancestry.source import Source +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.test_utils.jinja2 import TemplateFileTestBase + + +class Test(TemplateFileTestBase): + extensions = {RaspberryMint} + template = "entity/summary--citation.html.j2" + + async def test_minimal(self) -> None: + source = Source() + citation = Citation(source=source) + async with self.assert_template_file( + data={ + "entity": citation, + } + ) as (actual, _): + assert source.id in actual + + async def test_with_private_source(self) -> None: + source = Source(private=True) + citation = Citation(source=source) + async with self.assert_template_file( + data={ + "entity": citation, + } + ) as (actual, _): + assert source.id not in actual diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_summary__event.py b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_summary__event.py new file mode 100644 index 000000000..57fa20854 --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_summary__event.py @@ -0,0 +1,17 @@ +from betty.ancestry.event import Event +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.test_utils.jinja2 import TemplateFileTestBase + + +class Test(TemplateFileTestBase): + extensions = {RaspberryMint} + template = "entity/summary--event.html.j2" + + async def test_minimal(self) -> None: + event = Event() + async with self.assert_template_file( + data={ + "entity": event, + } + ) as (actual, _): + assert actual diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_summary__file.py b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_summary__file.py new file mode 100644 index 000000000..73faaab41 --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_summary__file.py @@ -0,0 +1,19 @@ +from pathlib import Path + +from betty.ancestry.file import File +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.test_utils.jinja2 import TemplateFileTestBase + + +class Test(TemplateFileTestBase): + extensions = {RaspberryMint} + template = "entity/summary--file.html.j2" + + async def test_minimal(self) -> None: + file = File(Path(__file__)) + async with self.assert_template_file( + data={ + "entity": file, + } + ) as (actual, _): + assert actual diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_summary__person.py b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_summary__person.py new file mode 100644 index 000000000..d709f0394 --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_summary__person.py @@ -0,0 +1,148 @@ +from betty.ancestry.citation import Citation +from betty.ancestry.event import Event +from betty.ancestry.event_type.event_types import Birth, Death +from betty.ancestry.gender.genders import NonBinary +from betty.ancestry.person import Person +from betty.ancestry.person_name import PersonName +from betty.ancestry.presence import Presence +from betty.ancestry.presence_role.presence_roles import Subject +from betty.ancestry.source import Source +from betty.locale.localizer import DEFAULT_LOCALIZER +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.test_utils.jinja2 import TemplateFileTestBase + + +class Test(TemplateFileTestBase): + extensions = {RaspberryMint} + template = "entity/summary--person.html.j2" + + async def test_minimal(self) -> None: + person = Person() + async with self.assert_template_file( + data={ + "entity": person, + } + ) as (actual, _): + assert actual == '
    ' + + async def test_embedded(self) -> None: + source = Source() + person = Person() + PersonName( + person=person, individual="Jane", citations=[Citation(source=source)] + ) + birth = Event(event_type=Birth(), citations=[Citation(source=source)]) + death = Event(event_type=Death(), citations=[Citation(source=source)]) + Presence(person, Subject(), birth) + Presence(person, Subject(), death) + async with self.assert_template_file( + data={ + "entity": person, + "embedded": True, + } + ) as (actual, _): + assert birth.id not in actual + assert death.id not in actual + assert "#reference" not in actual + + async def test_private(self) -> None: + source = Source() + person = Person(private=True) + PersonName(person=person, individual="Primary Name") + individual_name = "Jane" + PersonName( + person=person, + individual=individual_name, + citations=[Citation(source=source)], + ) + birth = Event(event_type=Birth(), citations=[Citation(source=source)]) + death = Event(event_type=Death(), citations=[Citation(source=source)]) + Presence(person, Subject(), birth) + Presence(person, Subject(), death) + async with self.assert_template_file( + data={ + "entity": person, + } + ) as (actual, _): + assert individual_name not in actual + assert ( + birth.event_type.plugin_label().localize(DEFAULT_LOCALIZER) + not in actual + ) + assert ( + death.event_type.plugin_label().localize(DEFAULT_LOCALIZER) + not in actual + ) + assert "#reference" not in actual + + async def test_with_public_alternative_name(self) -> None: + source = Source() + person = Person() + PersonName(person=person, individual="Primary Name") + individual_name = "Jane" + PersonName( + person=person, + individual=individual_name, + citations=[Citation(source=source)], + ) + async with self.assert_template_file( + data={ + "entity": person, + } + ) as (actual, _): + assert individual_name in actual + assert "#reference" in actual + + async def test_with_private_alternative_name(self) -> None: + source = Source() + person = Person() + PersonName(person=person, individual="Primary Name") + individual_name = "Jane" + PersonName( + person=person, + individual=individual_name, + citations=[Citation(source=source)], + private=True, + ) + async with self.assert_template_file( + data={ + "entity": person, + } + ) as (actual, _): + assert individual_name not in actual + assert "#reference" not in actual + + async def test_with_start_of_life_event(self) -> None: + source = Source() + person = Person() + birth = Event(event_type=Birth(), citations=[Citation(source=source)]) + Presence(person, Subject(), birth) + async with self.assert_template_file( + data={ + "entity": person, + } + ) as (actual, _): + assert birth.event_type.plugin_label().localize(DEFAULT_LOCALIZER) in actual + assert "#reference" in actual + + async def test_with_end_of_life_event(self) -> None: + source = Source() + person = Person() + death = Event(event_type=Death(), citations=[Citation(source=source)]) + Presence(person, Subject(), death) + async with self.assert_template_file( + data={ + "entity": person, + } + ) as (actual, _): + assert death.event_type.plugin_label().localize(DEFAULT_LOCALIZER) in actual + assert "#reference" in actual + + async def test_with_gender(self) -> None: + person = Person(gender=NonBinary()) + async with self.assert_template_file( + data={ + "entity": person, + } + ) as (actual, _): + assert NonBinary.plugin_label().localize(DEFAULT_LOCALIZER) in actual diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_summary__person_name.py b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_summary__person_name.py new file mode 100644 index 000000000..af75d14e2 --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_summary__person_name.py @@ -0,0 +1,18 @@ +from betty.ancestry.person import Person +from betty.ancestry.person_name import PersonName +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.test_utils.jinja2 import TemplateFileTestBase + + +class Test(TemplateFileTestBase): + extensions = {RaspberryMint} + template = "entity/summary--person-name.html.j2" + + async def test_minimal(self) -> None: + person_name = PersonName(person=Person(), individual="Jane") + async with self.assert_template_file( + data={ + "entity": person_name, + } + ): + pass diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_summary__place.py b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_summary__place.py new file mode 100644 index 000000000..5299c0892 --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_summary__place.py @@ -0,0 +1,40 @@ +from betty.ancestry.enclosure import Enclosure +from betty.ancestry.place import Place +from betty.ancestry.place_type.place_types import Country +from betty.locale.localizer import DEFAULT_LOCALIZER +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.test_utils.jinja2 import TemplateFileTestBase + + +class Test(TemplateFileTestBase): + extensions = {RaspberryMint} + template = "entity/summary--place.html.j2" + + async def test_minimal(self) -> None: + place = Place() + async with self.assert_template_file( + data={ + "entity": place, + } + ) as (actual, _): + assert actual == '
    ' + + async def test_with_non_unknown_place_type(self) -> None: + place = Place(place_type=Country()) + async with self.assert_template_file( + data={ + "entity": place, + } + ) as (actual, _): + assert Country.plugin_label().localize(DEFAULT_LOCALIZER) in actual + + async def test_with_encloser(self) -> None: + encloser_place = Place() + place = Place() + Enclosure(place, encloser_place) + async with self.assert_template_file( + data={ + "entity": place, + } + ) as (actual, _): + assert encloser_place.id in actual diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_summary__source.py b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_summary__source.py new file mode 100644 index 000000000..e9940b1b8 --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/assets/templates/entity/test_summary__source.py @@ -0,0 +1,27 @@ +from betty.ancestry.source import Source +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.test_utils.jinja2 import TemplateFileTestBase + + +class Test(TemplateFileTestBase): + extensions = {RaspberryMint} + template = "entity/summary--source.html.j2" + + async def test_minimal(self) -> None: + source = Source() + async with self.assert_template_file( + data={ + "entity": source, + } + ) as (actual, _): + assert actual + + async def test_with_contained_by(self) -> None: + contained_by_source = Source() + source = Source(contained_by=contained_by_source) + async with self.assert_template_file( + data={ + "entity": source, + } + ) as (actual, _): + assert contained_by_source.id in actual diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/search/test_result.py b/betty/tests/project/extension/raspberry_mint/assets/templates/search/test_result.py new file mode 100644 index 000000000..3f675d464 --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/assets/templates/search/test_result.py @@ -0,0 +1,19 @@ +from betty.ancestry.person import Person +from betty.locale.localizer import DEFAULT_LOCALIZER +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.test_utils.jinja2 import TemplateFileTestBase + + +class Test(TemplateFileTestBase): + extensions = {RaspberryMint} + template = "search/result.html.j2" + + async def test_minimal(self) -> None: + entity = Person() + async with self.assert_template_file( + data={ + "entity": entity, + } + ) as (actual, _): + assert entity.label.localize(DEFAULT_LOCALIZER) in actual + assert entity.id in actual diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/search/test_result__file.py b/betty/tests/project/extension/raspberry_mint/assets/templates/search/test_result__file.py new file mode 100644 index 000000000..3fc1f6ae8 --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/assets/templates/search/test_result__file.py @@ -0,0 +1,36 @@ +from pathlib import Path +from PIL import Image +from betty.ancestry.file import File +from betty.locale.localizer import DEFAULT_LOCALIZER +from betty.media_type import MediaType +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.test_utils.jinja2 import TemplateFileTestBase + + +class Test(TemplateFileTestBase): + extensions = {RaspberryMint} + template = "search/result--file.html.j2" + + async def test_minimal(self) -> None: + entity = File(Path(__file__)) + async with self.assert_template_file( + data={ + "entity": entity, + } + ) as (actual, _): + assert entity.label.localize(DEFAULT_LOCALIZER) in actual + assert entity.id in actual + + async def test_with_image(self, tmp_path: Path) -> None: + image_path = tmp_path / "image.png" + image = Image.new("1", (1, 1)) + image.save(image_path) + entity = File(image_path, media_type=MediaType("image/png")) + async with self.assert_template_file( + data={ + "entity": entity, + } + ) as (actual, _): + assert entity.label.localize(DEFAULT_LOCALIZER) in actual + assert entity.id in actual + assert " None: + person = Person() + async with self.assert_template_file( + data={ + "entity": person, + } + ) as (actual, _): + assert person.label.localize(DEFAULT_LOCALIZER) in actual + assert person.id in actual diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/search/test_result__place.py b/betty/tests/project/extension/raspberry_mint/assets/templates/search/test_result__place.py new file mode 100644 index 000000000..40b526a65 --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/assets/templates/search/test_result__place.py @@ -0,0 +1,19 @@ +from betty.ancestry.place import Place +from betty.locale.localizer import DEFAULT_LOCALIZER +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.test_utils.jinja2 import TemplateFileTestBase + + +class Test(TemplateFileTestBase): + extensions = {RaspberryMint} + template = "search/result--place.html.j2" + + async def test_minimal(self) -> None: + place = Place() + async with self.assert_template_file( + data={ + "entity": place, + } + ) as (actual, _): + assert place.label.localize(DEFAULT_LOCALIZER) in actual + assert place.id in actual diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/search/test_result__source.py b/betty/tests/project/extension/raspberry_mint/assets/templates/search/test_result__source.py new file mode 100644 index 000000000..605f8af03 --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/assets/templates/search/test_result__source.py @@ -0,0 +1,19 @@ +from betty.ancestry.source import Source +from betty.locale.localizer import DEFAULT_LOCALIZER +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.test_utils.jinja2 import TemplateFileTestBase + + +class Test(TemplateFileTestBase): + extensions = {RaspberryMint} + template = "search/result--source.html.j2" + + async def test_minimal(self) -> None: + source = Source() + async with self.assert_template_file( + data={ + "entity": source, + } + ) as (actual, _): + assert source.label.localize(DEFAULT_LOCALIZER) in actual + assert source.id in actual diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/search/test_result_with_image.py b/betty/tests/project/extension/raspberry_mint/assets/templates/search/test_result_with_image.py new file mode 100644 index 000000000..d69aef246 --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/assets/templates/search/test_result_with_image.py @@ -0,0 +1,46 @@ +from pathlib import Path + +from PIL import Image + +from betty.ancestry.file import File +from betty.ancestry.file_reference import FileReference +from betty.ancestry.has_file_references import HasFileReferences +from betty.locale.localizer import DEFAULT_LOCALIZER +from betty.media_type import MediaType +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.test_utils.jinja2 import TemplateFileTestBase +from betty.test_utils.model import DummyEntity + + +class DummyEntityWithFileReferences(HasFileReferences, DummyEntity): + pass + + +class Test(TemplateFileTestBase): + extensions = {RaspberryMint} + template = "search/result-with-image.html.j2" + + async def test_minimal(self) -> None: + entity = DummyEntityWithFileReferences() + async with self.assert_template_file( + data={ + "entity": entity, + } + ) as (actual, _): + assert entity.label.localize(DEFAULT_LOCALIZER) in actual + assert entity.id in actual + + async def test_with_image(self, tmp_path: Path) -> None: + image_path = tmp_path / "image.png" + image = Image.new("1", (1, 1)) + image.save(image_path) + entity = DummyEntityWithFileReferences() + FileReference(entity, File(image_path, media_type=MediaType("image/png"))) + async with self.assert_template_file( + data={ + "entity": entity, + } + ) as (actual, _): + assert entity.label.localize(DEFAULT_LOCALIZER) in actual + assert entity.id in actual + assert " None: + entity = DummyEntityWithLinks() + async with self.assert_template_file( + data={ + "page_resource": entity, + } + ) as (actual, _): + assert actual == "" + + async def test_with_link_without_locale(self) -> None: + link_url = "https://example.com" + link_label = "An example site" + link = Link(link_url, label=link_label) + entity = DummyEntityWithLinks() + entity.links.append(link) + async with self.assert_template_file( + data={ + "page_resource": entity, + } + ) as (actual, _): + assert link_url in actual + assert link_label in actual + + async def test_with_link_with_matching_locale(self) -> None: + link_url = "https://example.com" + link_label = "An example site" + link = Link(link_url, label=link_label, locale=DEFAULT_LOCALE) + entity = DummyEntityWithLinks() + entity.links.append(link) + async with self.assert_template_file( + data={ + "page_resource": entity, + } + ) as (actual, _): + assert link_url in actual + assert link_label in actual + + async def test_with_link_without_matching_locale(self) -> None: + link_url = "https://example.com" + link_label = "An example site" + link = Link(link_url, label=link_label, locale="nl") + entity = DummyEntityWithLinks() + entity.links.append(link) + async with self.assert_template_file( + data={ + "page_resource": entity, + } + ) as (actual, _): + assert link_url not in actual + assert link_label not in actual diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/section/test_facts.py b/betty/tests/project/extension/raspberry_mint/assets/templates/section/test_facts.py new file mode 100644 index 000000000..b33e4f334 --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/assets/templates/section/test_facts.py @@ -0,0 +1,28 @@ +from betty.locale.localizer import DEFAULT_LOCALIZER +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.test_utils.jinja2 import TemplateFileTestBase +from betty.test_utils.model import DummyEntity + + +class Test(TemplateFileTestBase): + extensions = {RaspberryMint} + template = "section/facts.html.j2" + + async def test_minimal(self) -> None: + async with self.assert_template_file( + data={ + "facts": [], + "page_resource": "/index.html", + } + ) as (actual, _): + assert actual == "" + + async def test_with_fact(self) -> None: + fact = DummyEntity() + async with self.assert_template_file( + data={ + "facts": [fact], + "page_resource": "/index.html", + } + ) as (actual, _): + assert fact.label.localize(DEFAULT_LOCALIZER) in actual diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/section/test_family.py b/betty/tests/project/extension/raspberry_mint/assets/templates/section/test_family.py new file mode 100644 index 000000000..1659b56cf --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/assets/templates/section/test_family.py @@ -0,0 +1,111 @@ +from betty.ancestry.person import Person +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.project.extension.trees import Trees +from betty.test_utils.jinja2 import TemplateFileTestBase + + +class Test(TemplateFileTestBase): + extensions = {RaspberryMint, Trees} + template = "section/family.html.j2" + + async def test_minimal(self) -> None: + person = Person() + async with self.assert_template_file( + data={ + "entity": person, + "page_resource": "/index.html", + } + ) as (actual, _): + assert not actual + + async def test_with_parents(self) -> None: + parent = Person(id="P0") + person = Person(parents=[parent]) + async with self.assert_template_file( + data={ + "entity": person, + "page_resource": "/index.html", + } + ) as (actual, _): + assert parent.id in actual + + async def test_with_private_parents(self) -> None: + parent = Person(id="P0", private=True) + person = Person(parents=[parent]) + async with self.assert_template_file( + data={ + "entity": person, + "page_resource": "/index.html", + } + ) as (actual, _): + assert parent.id not in actual + + async def test_with_siblings(self) -> None: + parent = Person(id="P0") + sibling = Person(id="P1", parents=[parent]) + person = Person(parents=[parent]) + async with self.assert_template_file( + data={ + "entity": person, + "page_resource": "/index.html", + } + ) as (actual, _): + assert sibling.id in actual + + async def test_with_private_siblings(self) -> None: + parent = Person(id="P0") + sibling = Person(id="P1", parents=[parent], private=True) + person = Person(parents=[parent]) + async with self.assert_template_file( + data={ + "entity": person, + "page_resource": "/index.html", + } + ) as (actual, _): + assert sibling.id not in actual + + async def test_with_children(self) -> None: + child = Person(id="P0") + person = Person(children=[child]) + async with self.assert_template_file( + data={ + "entity": person, + "page_resource": "/index.html", + } + ) as (actual, _): + assert child.id in actual + + async def test_with_private_children(self) -> None: + child = Person(id="P0", private=True) + person = Person(children=[child]) + async with self.assert_template_file( + data={ + "entity": person, + "page_resource": "/index.html", + } + ) as (actual, _): + assert child.id not in actual + + async def test_with_co_parents(self) -> None: + child = Person() + co_parent = Person(id="P0", children=[child]) + person = Person(children=[child]) + async with self.assert_template_file( + data={ + "entity": person, + "page_resource": "/index.html", + } + ) as (actual, _): + assert co_parent.id in actual + + async def test_with_private_co_parents(self) -> None: + child = Person() + co_parent = Person(id="P0", children=[child], private=True) + person = Person(children=[child]) + async with self.assert_template_file( + data={ + "entity": person, + "page_resource": "/index.html", + } + ) as (actual, _): + assert co_parent.id not in actual diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/section/test_map.py b/betty/tests/project/extension/raspberry_mint/assets/templates/section/test_map.py new file mode 100644 index 000000000..1af343a7e --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/assets/templates/section/test_map.py @@ -0,0 +1,28 @@ +from geopy import Point + +from betty.ancestry.place import Place +from betty.project.extension.maps import Maps +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.test_utils.jinja2 import TemplateFileTestBase + + +class Test(TemplateFileTestBase): + extensions = {RaspberryMint, Maps} + template = "section/map.html.j2" + + async def test_minimal(self) -> None: + async with self.assert_template_file( + data={ + "places": [], + } + ) as (actual, _): + assert actual == "" + + async def test_with_places(self) -> None: + place = Place(coordinates=Point(1, 1)) + async with self.assert_template_file( + data={ + "places": [place], + } + ) as (actual, _): + assert actual diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/section/test_media.py b/betty/tests/project/extension/raspberry_mint/assets/templates/section/test_media.py new file mode 100644 index 000000000..acf76ead4 --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/assets/templates/section/test_media.py @@ -0,0 +1,52 @@ +from pathlib import Path + +from PIL import Image + +from betty.ancestry.file import File +from betty.ancestry.file_reference import FileReference +from betty.media_type import MediaType +from betty.model.association import TemporaryToOneResolver +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.test_utils.jinja2 import TemplateFileTestBase + + +class Test(TemplateFileTestBase): + extensions = {RaspberryMint} + template = "section/media.html.j2" + + async def test_minimal(self) -> None: + async with self.assert_template_file( + data={ + "file_references": [], + "page_resource": "/index.html", + } + ) as (actual, _): + assert actual == "" + + async def test_with_public_file_references(self, tmp_path: Path) -> None: + image_path = tmp_path / "image.png" + image = Image.new("1", (1, 1)) + image.save(image_path) + file = File(image_path, media_type=MediaType("image/png")) + file_reference = FileReference(TemporaryToOneResolver(), file) + async with self.assert_template_file( + data={ + "file_references": [file_reference], + "page_resource": "/index.html", + } + ) as (actual, _): + assert file.id in actual + + async def test_without_public_file_references(self, tmp_path: Path) -> None: + image_path = tmp_path / "image.png" + image = Image.new("1", (1, 1)) + image.save(image_path) + file = File(image_path, media_type=MediaType("image/png"), private=True) + file_reference = FileReference(TemporaryToOneResolver(), file) + async with self.assert_template_file( + data={ + "file_references": [file_reference], + "page_resource": "/index.html", + } + ) as (actual, _): + assert not actual diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/section/test_notes.py b/betty/tests/project/extension/raspberry_mint/assets/templates/section/test_notes.py new file mode 100644 index 000000000..40982dbbc --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/assets/templates/section/test_notes.py @@ -0,0 +1,41 @@ +from betty.ancestry.note import Note +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.test_utils.jinja2 import TemplateFileTestBase +from betty.tests.ancestry.test_has_notes import DummyHasNotes + + +class Test(TemplateFileTestBase): + extensions = {RaspberryMint} + template = "section/notes.html.j2" + + async def test_minimal(self) -> None: + entity = DummyHasNotes() + async with self.assert_template_file( + data={ + "entity": entity, + "page_resource": "/index.html", + } + ) as (actual, _): + assert not actual + + async def test_with_public_notes(self) -> None: + note_text = "Hello, world!" + entity = DummyHasNotes(notes=[Note(note_text)]) + async with self.assert_template_file( + data={ + "entity": entity, + "page_resource": "/index.html", + } + ) as (actual, _): + assert note_text in actual + + async def test_without_public_notes(self) -> None: + note_text = "Hello, world!" + entity = DummyHasNotes(notes=[Note(note_text, private=True)]) + async with self.assert_template_file( + data={ + "entity": entity, + "page_resource": "/index.html", + } + ) as (actual, _): + assert not actual diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/section/test_timeline.py b/betty/tests/project/extension/raspberry_mint/assets/templates/section/test_timeline.py new file mode 100644 index 000000000..67dc9f1c8 --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/assets/templates/section/test_timeline.py @@ -0,0 +1,28 @@ +from betty.ancestry.event import Event +from betty.date import Date +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.test_utils.jinja2 import TemplateFileTestBase + + +class Test(TemplateFileTestBase): + extensions = {RaspberryMint} + template = "section/timeline.html.j2" + + async def test_minimal(self) -> None: + async with self.assert_template_file( + data={ + "events": [], + "page_resource": "/index.html", + } + ) as (actual, _): + assert not actual + + async def test_with_events(self) -> None: + event = Event(id="E0", date=Date(1970, 1, 1)) + async with self.assert_template_file( + data={ + "events": [event], + "page_resource": "/index.html", + } + ) as (actual, _): + assert event.id in actual diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/section/test_tree.py b/betty/tests/project/extension/raspberry_mint/assets/templates/section/test_tree.py new file mode 100644 index 000000000..e1c67a78f --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/assets/templates/section/test_tree.py @@ -0,0 +1,18 @@ +from betty.ancestry.person import Person +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.project.extension.trees import Trees +from betty.test_utils.jinja2 import TemplateFileTestBase + + +class Test(TemplateFileTestBase): + extensions = {RaspberryMint, Trees} + template = "section/tree.html.j2" + + async def test(self) -> None: + person = Person() + async with self.assert_template_file( + data={ + "person": person, + } + ) as (actual, _): + assert actual diff --git a/betty/tests/project/extension/raspberry_mint/assets/templates/section/test_wikipedia.py b/betty/tests/project/extension/raspberry_mint/assets/templates/section/test_wikipedia.py new file mode 100644 index 000000000..12462a4b2 --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/assets/templates/section/test_wikipedia.py @@ -0,0 +1,38 @@ +from pytest_mock import MockerFixture +from betty.ancestry.link import Link +from betty.locale import DEFAULT_LOCALE +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.project.extension.wikipedia import Wikipedia +from betty.test_utils.jinja2 import TemplateFileTestBase +from betty.tests.project.test_load import DummyHasLinks +from betty.wikipedia import Summary + + +class Test(TemplateFileTestBase): + extensions = {RaspberryMint, Wikipedia} + template = "section/wikipedia.html.j2" + + async def test_minimal(self) -> None: + entity = DummyHasLinks() + async with self.assert_template_file( + data={ + "entity": entity, + "page_resource": "/index.html", + } + ) as (actual, _): + assert not actual + + async def test_with_summary(self, mocker: MockerFixture) -> None: + summary_content = "Hello, world!" + m_get_summary = mocker.patch("betty.wikipedia._Retriever.get_summary") + m_get_summary.return_value = Summary( + DEFAULT_LOCALE, "Example", "Example", summary_content + ) + entity = DummyHasLinks(links=[Link("https://en.wikipedia.org/wiki/Example")]) + async with self.assert_template_file( + data={ + "entity": entity, + "page_resource": "/index.html", + } + ) as (actual, _): + assert summary_content in actual diff --git a/betty/tests/project/extension/raspberry_mint/test___init__.py b/betty/tests/project/extension/raspberry_mint/test___init__.py new file mode 100644 index 000000000..61feb061e --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/test___init__.py @@ -0,0 +1,73 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +import pytest +from typing_extensions import override + +from betty.assertion.error import AssertionFailed +from betty.model import ENTITY_TYPE_REPOSITORY, Entity +from betty.model.config import EntityReference +from betty.plugin.proxy import ProxyPluginRepository +from betty.plugin.static import StaticPluginRepository +from betty.project import Project +from betty.project.config import EntityTypeConfiguration +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.project.generate import generate +from betty.test_utils.model import DummyUserFacingEntity +from betty.test_utils.project.extension import ExtensionTestBase +from betty.test_utils.project.extension.webpack.build import EntryPointProviderTestBase + +if TYPE_CHECKING: + from pytest_mock import MockerFixture + from betty.app import App + + +class TestRaspberryMint(EntryPointProviderTestBase, ExtensionTestBase[RaspberryMint]): + @override + def get_sut_class(self) -> type[RaspberryMint]: + return RaspberryMint + + async def test_filters(self, new_temporary_app: App) -> None: + async with Project.new_temporary(new_temporary_app) as project, project: + sut = await project.new_target(self.get_sut_class()) + assert len(sut.filters) + + async def test_public_css_paths(self, new_temporary_app: App) -> None: + async with Project.new_temporary(new_temporary_app) as project, project: + sut = await project.new_target(self.get_sut_class()) + assert len(sut.public_css_paths) + + async def test_bootstrap_should_validate_featured_entities_configuration( + self, new_temporary_app: App + ) -> None: + async with Project.new_temporary(new_temporary_app) as project, project: + sut = await RaspberryMint.new_for_project(project) + sut.configuration.featured_entities.replace( + EntityReference("non-existent-entity") + ) + with pytest.raises(AssertionFailed): + async with sut: + pass + + async def test_generate_html_list_for_third_party_entity( + self, mocker: MockerFixture, new_temporary_app: App + ) -> None: + mocker.patch( + "betty.model.ENTITY_TYPE_REPOSITORY", + new=ProxyPluginRepository[Entity]( + StaticPluginRepository(DummyUserFacingEntity), ENTITY_TYPE_REPOSITORY + ), + ) + async with Project.new_temporary(new_temporary_app) as project: + project.configuration.extensions.enable(RaspberryMint) + project.configuration.entity_types.replace( + EntityTypeConfiguration(DummyUserFacingEntity, generate_html_list=True) + ) + async with project: + await generate(project) + assert ( + project.configuration.www_directory_path + / DummyUserFacingEntity.plugin_id() + / "index.html" + ).is_file() diff --git a/betty/tests/project/extension/raspberry_mint/test_config.py b/betty/tests/project/extension/raspberry_mint/test_config.py new file mode 100644 index 000000000..e7bd406d6 --- /dev/null +++ b/betty/tests/project/extension/raspberry_mint/test_config.py @@ -0,0 +1,145 @@ +from __future__ import annotations + +from typing import Any, TYPE_CHECKING + +from betty.assertion.error import AssertionFailed +from betty.model import UserFacingEntity +from betty.model.config import EntityReference +from betty.plugin.static import StaticPluginRepository +from betty.project.extension.raspberry_mint.config import RaspberryMintConfiguration +from betty.test_utils.assertion.error import raises_error +from betty.test_utils.model import DummyEntity + +if TYPE_CHECKING: + from collections.abc import Mapping + from pytest_mock import MockerFixture + from betty.serde.dump import Dump, DumpMapping + + +class RaspberryMintConfigurationTestEntity(UserFacingEntity, DummyEntity): + pass + + +class TestRaspberryMintConfiguration: + def test_featured_entities_from___init__(self) -> None: + entity_reference = EntityReference(RaspberryMintConfigurationTestEntity) + sut = RaspberryMintConfiguration(featured_entities=[entity_reference]) + assert entity_reference in sut.featured_entities + + def test_primary_color_from___init__(self) -> None: + hex_value = "#000000" + sut = RaspberryMintConfiguration(primary_color=hex_value) + assert sut.primary_color.hex == hex_value + + def test_secondary_color_from___init__(self) -> None: + hex_value = "#000000" + sut = RaspberryMintConfiguration(secondary_color=hex_value) + assert sut.secondary_color.hex == hex_value + + def test_tertiary_color_from___init__(self) -> None: + hex_value = "#000000" + sut = RaspberryMintConfiguration(tertiary_color=hex_value) + assert sut.tertiary_color.hex == hex_value + + def test_load_with_minimal_configuration(self) -> None: + dump: Mapping[str, Any] = {} + RaspberryMintConfiguration().load(dump) + + def test_load_without_dict_should_error(self) -> None: + dump = None + with raises_error(error_type=AssertionFailed): + RaspberryMintConfiguration().load(dump) + + def test_load_with_featured_entities(self, mocker: MockerFixture) -> None: + mocker.patch( + "betty.model.ENTITY_TYPE_REPOSITORY", + new=StaticPluginRepository(RaspberryMintConfigurationTestEntity), + ) + entity_type = RaspberryMintConfigurationTestEntity + entity_id = "123" + dump: Dump = { + "featured_entities": [ + { + "entity_type": entity_type.plugin_id(), + "entity": entity_id, + }, + ], + } + sut = RaspberryMintConfiguration() + sut.load(dump) + assert sut.featured_entities[0].entity_type == entity_type.plugin_id() + assert sut.featured_entities[0].entity_id == entity_id + + def test_load_with_primary_color(self) -> None: + hex_value = "#000000" + dump: Dump = { + "primary_color": hex_value, + } + sut = RaspberryMintConfiguration() + sut.load(dump) + assert sut.primary_color.hex == hex_value + + def test_load_with_secondary_color(self) -> None: + hex_value = "#000000" + dump: Dump = { + "secondary_color": hex_value, + } + sut = RaspberryMintConfiguration() + sut.load(dump) + assert sut.secondary_color.hex == hex_value + + def test_load_with_tertiary_color(self) -> None: + hex_value = "#000000" + dump: Dump = { + "tertiary_color": hex_value, + } + sut = RaspberryMintConfiguration() + sut.load(dump) + assert sut.tertiary_color.hex == hex_value + + def test_dump_with_minimal_configuration(self) -> None: + sut = RaspberryMintConfiguration() + expected: DumpMapping[Dump] = { + "featured_entities": [], + "primary_color": RaspberryMintConfiguration.DEFAULT_PRIMARY_COLOR, + "secondary_color": RaspberryMintConfiguration.DEFAULT_SECONDARY_COLOR, + "tertiary_color": RaspberryMintConfiguration.DEFAULT_TERTIARY_COLOR, + } + assert sut.dump() == expected + + def test_dump_with_featured_entities(self) -> None: + entity_type = RaspberryMintConfigurationTestEntity + entity_id = "123" + sut = RaspberryMintConfiguration( + featured_entities=[EntityReference(entity_type, entity_id)], + ) + expected = [ + { + "entity_type": entity_type.plugin_id(), + "entity": entity_id, + }, + ] + dump = sut.dump() + assert isinstance(dump, dict) + assert expected == dump["featured_entities"] + + def test_dump_with_primary_color(self) -> None: + hex_value = "#000000" + sut = RaspberryMintConfiguration(primary_color=hex_value) + dump = sut.dump() + assert isinstance(dump, dict) + assert hex_value == dump["primary_color"] + + def test_dump_with_secondary_color(self) -> None: + hex_value = "#000000" + sut = RaspberryMintConfiguration(secondary_color=hex_value) + dump = sut.dump() + assert isinstance(dump, dict) + assert hex_value == dump["secondary_color"] + + def test_dump_with_tertiary_color(self) -> None: + hex_value = "#000000" + sut = RaspberryMintConfiguration(tertiary_color=hex_value) + dump = sut.dump() + assert isinstance(dump, dict) + assert hex_value == dump["tertiary_color"] diff --git a/betty/tests_playwright/project/extension/raspberry_mint/__init__.py b/betty/tests_playwright/project/extension/raspberry_mint/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/betty/tests_playwright/project/extension/raspberry_mint/test_search_ui.py b/betty/tests_playwright/project/extension/raspberry_mint/test_search_ui.py new file mode 100644 index 000000000..b52e3b72b --- /dev/null +++ b/betty/tests_playwright/project/extension/raspberry_mint/test_search_ui.py @@ -0,0 +1,70 @@ +from collections.abc import AsyncIterator + +import pytest +from playwright.async_api import expect, Page + +from betty import serve +from betty.ancestry.person import Person +from betty.ancestry.person_name import PersonName +from betty.app import App +from betty.project import Project +from betty.project.extension.raspberry_mint import RaspberryMint +from betty.project.generate import generate +from betty.serve import Server + + +class TestSearchUi: + INDIVIDUAL_NAME = "Janet" + + @pytest.fixture(scope="session") + async def served_project(self) -> AsyncIterator[tuple[Project, Server]]: + person_id = "I0001" + person = Person(id=person_id) + PersonName(individual=self.INDIVIDUAL_NAME, person=person) + async with ( + App.new_temporary() as app, + app, + Project.new_temporary(app) as project, + ): + project.configuration.extensions.enable(RaspberryMint) + project.ancestry[Person].add(person) + async with project: + await generate(project) + async with await serve.BuiltinProjectServer.new_for_project( + project + ) as server: + yield project, server + + @pytest.mark.asyncio(loop_scope="session") + async def test(self, page: Page, served_project: tuple[Project, Server]) -> None: + project, server = served_project + await page.goto(server.public_url) + + # Enter the search. + entry_point = page.locator(".header-entry-point-search").locator("visible=true") + await entry_point.click(force=True) + await expect(page.locator("#search-form")).to_be_visible() + + # Search for a person's name. + search_query = page.locator("#search-form-query") + await search_query.fill(self.INDIVIDUAL_NAME) + await page.locator(":focus").press("Enter") + + # Assert there is a search result. + search_results_container = page.locator("#search-results-container") + await expect(search_results_container).to_be_visible() + search_result = search_results_container.locator(".search-result") + await expect(search_result).to_have_count(1) + + # Follow the search result's link. + search_result_link = search_result.locator("a") + await search_result_link.evaluate( + "searchResultLink => searchResultLink.click()" + ) + + # Assert we're at the page linked to by the search result. + person = project.ancestry[Person]["I0001"] + await expect(page).to_have_url( + f"{server.public_url}/person/{person.id}/index.html" + ) + await page.close() diff --git a/documentation/usage/extension/cotton-candy.rst b/documentation/usage/extension/cotton-candy.rst index 16ca3c4e2..dde6ad840 100644 --- a/documentation/usage/extension/cotton-candy.rst +++ b/documentation/usage/extension/cotton-candy.rst @@ -1,6 +1,6 @@ The *Cotton Candy* extension ========================== -The ``cotton-candy`` extension provides Betty's default theme. +The ``cotton-candy`` extension provides Betty's legacy theme. .. important:: This extension requires :ref:`Node.js `. diff --git a/pyproject.toml b/pyproject.toml index 69a0ab434..e90f23041 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -146,6 +146,7 @@ betty = 'betty.cli:main' 'http-api-doc' = 'betty.project.extension.http_api_doc:HttpApiDoc' 'maps' = 'betty.project.extension.maps:Maps' 'privatizer' = 'betty.project.extension.privatizer:Privatizer' +'raspberry-mint' = 'betty.project.extension.raspberry_mint:RaspberryMint' 'trees' = 'betty.project.extension.trees:Trees' 'webpack' = 'betty.project.extension.webpack:Webpack' 'wikipedia' = 'betty.project.extension.wikipedia:Wikipedia'