From d596861a2e9a1c6af4d8c6133c9b300a21d87664 Mon Sep 17 00:00:00 2001 From: marc Date: Sun, 23 Mar 2025 21:46:04 +0100 Subject: [PATCH] Migrated links, lyrics and properties --- flake.nix | 1 + folkugat_web/api/tema/__init__.py | 2 +- folkugat_web/api/tema/editor.py | 75 ------ folkugat_web/api/tema/index.py | 209 ++------------- folkugat_web/api/tema/links.py | 160 ++++++++++++ folkugat_web/api/tema/lyrics.py | 80 ++++++ folkugat_web/api/tema/properties.py | 73 ++++++ .../templates/fragments/pdf_viewer.html | 3 + .../templates/fragments/tema/editor/link.html | 4 +- .../fragments/tema/editor/link_file.html | 6 +- .../fragments/tema/editor/link_url.html | 8 +- .../fragments/tema/editor/lyric.html | 6 +- .../fragments/tema/editor/property.html | 4 +- .../assets/templates/fragments/tema/link.html | 4 +- .../templates/fragments/tema/links.html | 2 +- .../templates/fragments/tema/lyric.html | 4 +- .../templates/fragments/tema/property.html | 4 +- folkugat_web/dal/sql/_connection.py | 3 +- folkugat_web/dal/sql/playlists/ddl.py | 6 - folkugat_web/dal/sql/sessions/ddl.py | 6 - folkugat_web/dal/sql/temes/conversion.py | 28 +- folkugat_web/dal/sql/temes/ddl.py | 57 ++++- folkugat_web/dal/sql/temes/links.py | 137 ++++++++++ folkugat_web/dal/sql/temes/lyrics.py | 127 ++++++++++ folkugat_web/dal/sql/temes/properties.py | 127 ++++++++++ folkugat_web/dal/sql/temes/query.py | 3 +- folkugat_web/dal/sql/temes/write.py | 29 ++- folkugat_web/fragments/tema.py | 239 ------------------ folkugat_web/fragments/tema/__init__.py | 84 ++++++ folkugat_web/fragments/tema/links.py | 88 +++++++ folkugat_web/fragments/tema/lyrics.py | 25 ++ folkugat_web/fragments/tema/properties.py | 27 ++ folkugat_web/fragments/temes.py | 17 +- folkugat_web/model/__init__.py | 3 - folkugat_web/model/_base.py | 39 --- folkugat_web/model/temes.py | 79 +----- folkugat_web/services/files.py | 2 +- folkugat_web/services/temes/links.py | 29 ++- folkugat_web/services/temes/lyrics.py | 30 +++ folkugat_web/services/temes/properties.py | 30 +++ folkugat_web/services/temes/search.py | 79 ++++-- folkugat_web/services/temes/write.py | 128 +--------- folkugat_web/typing.py | 2 +- folkugat_web/utils.py | 29 ++- scripts/00_migrate_links.py | 53 ++++ scripts/01_migrate_lyrics.py | 48 ++++ scripts/02_migrate_properties.py | 48 ++++ 47 files changed, 1403 insertions(+), 844 deletions(-) create mode 100644 folkugat_web/api/tema/links.py create mode 100644 folkugat_web/api/tema/lyrics.py create mode 100644 folkugat_web/api/tema/properties.py create mode 100644 folkugat_web/dal/sql/temes/links.py create mode 100644 folkugat_web/dal/sql/temes/lyrics.py create mode 100644 folkugat_web/dal/sql/temes/properties.py delete mode 100644 folkugat_web/fragments/tema.py create mode 100644 folkugat_web/fragments/tema/__init__.py create mode 100644 folkugat_web/fragments/tema/links.py create mode 100644 folkugat_web/fragments/tema/lyrics.py create mode 100644 folkugat_web/fragments/tema/properties.py delete mode 100644 folkugat_web/model/_base.py create mode 100644 folkugat_web/services/temes/lyrics.py create mode 100644 folkugat_web/services/temes/properties.py create mode 100644 scripts/00_migrate_links.py create mode 100644 scripts/01_migrate_lyrics.py create mode 100644 scripts/02_migrate_properties.py diff --git a/flake.nix b/flake.nix index 55de1c8..c3cadb8 100644 --- a/flake.nix +++ b/flake.nix @@ -59,6 +59,7 @@ shellHook = '' export PYTHONPATH=${pythonEnv}/${python.sitePackages}:$PYTHONPATH + export PYTHONPATH=$(pwd):$PYTHONPATH ''; }; }); diff --git a/folkugat_web/api/tema/__init__.py b/folkugat_web/api/tema/__init__.py index aba1bbe..962bbf4 100644 --- a/folkugat_web/api/tema/__init__.py +++ b/folkugat_web/api/tema/__init__.py @@ -1 +1 @@ -from . import editor, index +from . import editor, index, links, lyrics, properties diff --git a/folkugat_web/api/tema/editor.py b/folkugat_web/api/tema/editor.py index 4ae8578..1fa193c 100644 --- a/folkugat_web/api/tema/editor.py +++ b/folkugat_web/api/tema/editor.py @@ -11,78 +11,3 @@ def title_editor( tema_id: int, ): return tema.title_editor(request=request, logged_in=logged_in, tema_id=tema_id) - - -@router.get("/api/tema/{tema_id}/editor/lyric/{lyric_id}") -def lyric_editor( - request: Request, - logged_in: auth.RequireLogin, - tema_id: int, - lyric_id: int, -): - return tema.lyric_editor( - request=request, - logged_in=logged_in, - tema_id=tema_id, - lyric_id=lyric_id, - ) - - -@router.get("/api/tema/{tema_id}/editor/link/{link_id}") -def link_editor( - request: Request, - logged_in: auth.RequireLogin, - tema_id: int, - link_id: int, -): - return tema.link_editor( - request=request, - logged_in=logged_in, - tema_id=tema_id, - link_id=link_id, - ) - - -@router.get("/api/tema/{tema_id}/editor/link/{link_id}/url") -def link_editor_url_input( - request: Request, - logged_in: auth.RequireLogin, - tema_id: int, - link_id: int, -): - return tema.link_editor_url( - request=request, - logged_in=logged_in, - tema_id=tema_id, - link_id=link_id, - ) - - -@router.get("/api/tema/{tema_id}/editor/link/{link_id}/file") -def link_editor_file_input( - request: Request, - logged_in: auth.RequireLogin, - tema_id: int, - link_id: int, -): - return tema.link_editor_file( - request=request, - logged_in=logged_in, - tema_id=tema_id, - link_id=link_id, - ) - - -@router.get("/api/tema/{tema_id}/editor/property/{property_id}") -def property_editor( - request: Request, - logged_in: auth.RequireLogin, - tema_id: int, - property_id: int, -): - return tema.property_editor( - request=request, - logged_in=logged_in, - tema_id=tema_id, - property_id=property_id, - ) diff --git a/folkugat_web/api/tema/index.py b/folkugat_web/api/tema/index.py index b6984d2..d73fd19 100644 --- a/folkugat_web/api/tema/index.py +++ b/folkugat_web/api/tema/index.py @@ -1,15 +1,18 @@ from typing import Annotated -from fastapi import Request, UploadFile -from fastapi.params import File, Form, Param +from fastapi import HTTPException, Request +from fastapi.params import Form from fastapi.responses import HTMLResponse from folkugat_web.api import router from folkugat_web.fragments import tema, temes -from folkugat_web.model import temes as model -from folkugat_web.services import auth, files +from folkugat_web.services import auth +from folkugat_web.services.temes import links as links_service +from folkugat_web.services.temes import lyrics as lyrics_service +from folkugat_web.services.temes import properties as properties_service +from folkugat_web.services.temes import query as temes_q from folkugat_web.services.temes import write as temes_w -from folkugat_web.services.temes.links import guess_link_type from folkugat_web.templates import templates +from folkugat_web.utils import FnChain @router.get("/tema/{tema_id}") @@ -27,7 +30,17 @@ def page(request: Request, logged_in: auth.LoggedIn, tema_id: int): @router.get("/api/tema/{tema_id}") def contingut(request: Request, logged_in: auth.LoggedIn, tema_id: int): - return temes.tema(request, tema_id, logged_in) + tema = temes_q.get_tema_by_id(tema_id) + if not tema: + raise HTTPException(status_code=404, detail="Could not find tune") + tema = ( + FnChain.transform(tema) | + temes_q.tema_compute_stats | + links_service.add_links_to_tema | + lyrics_service.add_lyrics_to_tema | + properties_service.add_properties_to_tema + ).result() + return temes.tema(request, logged_in, tema) @router.delete("/api/tema/{tema_id}") @@ -62,190 +75,6 @@ def set_title( return tema.title(request=request, tema=new_tema, logged_in=logged_in) -@router.get("/api/tema/{tema_id}/lyric/{lyric_id}") -def lyric(request: Request, logged_in: auth.LoggedIn, tema_id: int, lyric_id: int): - return tema.lyric(request=request, logged_in=logged_in, tema_id=tema_id, lyric_id=lyric_id) - - -@router.put("/api/tema/{tema_id}/lyric/{lyric_id}") -def set_lyric( - request: Request, - logged_in: auth.RequireLogin, - tema_id: int, - lyric_id: int, - title: Annotated[str, Form()], - lyric: Annotated[str, Form()], -): - new_lyric = model.Lyrics(id=lyric_id, title=title, content=lyric.strip()) - _ = temes_w.update_lyric(tema_id=tema_id, lyric_id=lyric_id, lyric=new_lyric) - return tema.lyric(request=request, logged_in=logged_in, tema_id=tema_id, lyric_id=lyric_id) - - -@router.post("/api/tema/{tema_id}/lyric") -def add_lyric( - request: Request, - logged_in: auth.RequireLogin, - tema_id: int, -): - new_tema = temes_w.add_lyric(tema_id=tema_id) - lyric_id = new_tema.lyrics[-1].id - if lyric_id is None: - raise RuntimeError("Invalid lyric_id on newly created lyric!") - return tema.lyric_editor( - request=request, - logged_in=logged_in, - tema_id=tema_id, - lyric_id=lyric_id, - ) - - -@router.delete("/api/tema/{tema_id}/lyric/{lyric_id}") -def delete_lyric( - tema_id: int, - lyric_id: int, -): - _ = temes_w.delete_lyric(tema_id=tema_id, lyric_id=lyric_id) - return HTMLResponse() - - -@router.get("/api/tema/{tema_id}/link/{link_id}") -def link(request: Request, logged_in: auth.LoggedIn, tema_id: int, link_id: int): - return tema.link(request=request, logged_in=logged_in, tema_id=tema_id, link_id=link_id) - - -@router.put("/api/tema/{tema_id}/link/{link_id}") -async def set_link( - request: Request, - logged_in: auth.RequireLogin, - tema_id: int, - link_id: int, - content_type: Annotated[model.ContentType, Form()], - title: Annotated[str | None, Form()] = None, - url: Annotated[str | None, Form()] = None, - upload_file: Annotated[UploadFile | None, File()] = None, -): - if upload_file: - url = await files.store_file(tema_id=tema_id, upload_file=upload_file) - - link_type = guess_link_type(url or '') - new_link = model.Link( - id=link_id, - content_type=content_type, - link_type=link_type, - url=url or '', - title=title or '', - ) - _ = temes_w.update_link(tema_id=tema_id, link_id=link_id, link=new_link) - return tema.link(request=request, logged_in=logged_in, tema_id=tema_id, link_id=link_id) - - -@router.post("/api/tema/{tema_id}/link") -def add_link( - request: Request, - logged_in: auth.RequireLogin, - tema_id: int, -): - new_tema = temes_w.add_link(tema_id=tema_id) - link_id = new_tema.links[-1].id - if link_id is None: - raise RuntimeError("Invalid link_id on newly created link!") - return tema.link_editor( - request=request, - logged_in=logged_in, - tema_id=tema_id, - link_id=link_id, - ) - - -@router.delete("/api/tema/{tema_id}/link/{link_id}") -def delete_link( - _: auth.RequireLogin, - tema_id: int, - link_id: int, -): - _tema = temes_w.delete_link(tema_id=tema_id, link_id=link_id) - return HTMLResponse( - headers={ - "HX-Trigger": f"reload-tema-{tema_id}-score" - } - ) - - -@router.get("/api/tema/{tema_id}/link/{link_id}/icon") -def link_icon( - request: Request, - logged_in: auth.LoggedIn, - tema_id: int, - link_id: int, - content_type: Annotated[model.ContentType, Param()], - url: Annotated[str, Param()], -): - return tema.link_icon( - request=request, - logged_in=logged_in, - tema_id=tema_id, - link_id=link_id, - url=url, - content_type=content_type, - ) - - -@router.get("/api/tema/{tema_id}/score") -def get_score( - request: Request, - logged_in: auth.LoggedIn, - tema_id: int, -): - return tema.score( - request=request, - logged_in=logged_in, - tema_id=tema_id, - ) - - -@router.get("/api/tema/{tema_id}/property/{property_id}") -def property_(request: Request, logged_in: auth.LoggedIn, tema_id: int, property_id: int): - return tema.property_(request=request, logged_in=logged_in, tema_id=tema_id, property_id=property_id) - - -@router.put("/api/tema/{tema_id}/property/{property_id}") -def set_property( - request: Request, - logged_in: auth.RequireLogin, - tema_id: int, - property_id: int, - field: Annotated[model.PropertyField, Form()], - value: Annotated[str, Form()], -): - new_property = model.Property(id=property_id, field=field, value=value.strip()) - _ = temes_w.update_property(tema_id=tema_id, property_id=property_id, prop=new_property) - return tema.property_(request=request, logged_in=logged_in, tema_id=tema_id, property_id=property_id) - - -@router.post("/api/tema/{tema_id}/property") -def add_property( - request: Request, - logged_in: auth.RequireLogin, - tema_id: int, -): - new_tema = temes_w.add_property(tema_id=tema_id) - property_id = new_tema.properties[-1].id - if property_id is None: - raise RuntimeError("Invalid property_id on newly created property!") - return tema.property_editor( - request=request, - logged_in=logged_in, - tema_id=tema_id, - property_id=property_id, - ) - - -@router.delete("/api/tema/{tema_id}/property/{property_id}") -def delete_property(_: auth.RequireLogin, tema_id: int, property_id: int): - _tema = temes_w.delete_property(tema_id=tema_id, property_id=property_id) - return HTMLResponse() - - @router.put("/api/tema/{tema_id}/visible") def set_visible(request: Request, logged_in: auth.RequireLogin, tema_id: int): new_tema = temes_w.set_visibility(tema_id=tema_id, hidden=False) diff --git a/folkugat_web/api/tema/links.py b/folkugat_web/api/tema/links.py new file mode 100644 index 0000000..34b1262 --- /dev/null +++ b/folkugat_web/api/tema/links.py @@ -0,0 +1,160 @@ +from typing import Annotated + +from fastapi import HTTPException, Request, UploadFile +from fastapi.params import File, Form, Param +from fastapi.responses import HTMLResponse +from folkugat_web.api import router +from folkugat_web.fragments.tema import links as links_fragments +from folkugat_web.model import temes as model +from folkugat_web.services import auth, files +from folkugat_web.services.temes import links as links_service + + +@router.get("/api/tema/{tema_id}/link/{link_id}") +def link(request: Request, logged_in: auth.LoggedIn, tema_id: int, link_id: int): + link = links_service.get_link_by_id(link_id=link_id, tema_id=tema_id) + if not link: + raise HTTPException(status_code=404, detail="Could not find link!") + return links_fragments.link(request=request, logged_in=logged_in, link=link) + + +@router.put("/api/tema/{tema_id}/link/{link_id}") +async def set_link( + request: Request, + logged_in: auth.RequireLogin, + tema_id: int, + link_id: int, + content_type: Annotated[model.ContentType, Form()], + title: Annotated[str | None, Form()] = None, + url: Annotated[str | None, Form()] = None, + upload_file: Annotated[UploadFile | None, File()] = None, +): + if upload_file: + url = await files.store_file(tema_id=tema_id, upload_file=upload_file) + + link_type = links_service.guess_link_type(url or '') + new_link = model.Link( + id=link_id, + tema_id=tema_id, + content_type=content_type, + link_type=link_type, + url=(url or '').strip(), + title=(title or '').strip(), + ) + links_service.update_link(link=new_link) + return links_fragments.link(request=request, logged_in=logged_in, link=new_link) + + +@router.post("/api/tema/{tema_id}/link") +def create_link( + request: Request, + logged_in: auth.RequireLogin, + tema_id: int, +): + link = links_service.create_link(tema_id=tema_id) + return links_fragments.link_editor( + request=request, + logged_in=logged_in, + link=link, + ) + + +@router.delete("/api/tema/{tema_id}/link/{link_id}") +def delete_link( + _logged_in: auth.RequireLogin, + tema_id: int, + link_id: int, +): + links_service.delete_link(link_id=link_id, tema_id=tema_id) + return HTMLResponse( + headers={ + "HX-Trigger": f"reload-tema-{tema_id}-score" + } + ) + + +@router.get("/api/tema/{tema_id}/link/{link_id}/icon") +def link_icon( + request: Request, + logged_in: auth.LoggedIn, + tema_id: int, + link_id: int, + content_type: Annotated[model.ContentType, Param()], + url: Annotated[str, Param()], +): + link = model.Link( + id=link_id, + tema_id=tema_id, + content_type=content_type, + link_type=links_service.guess_link_type(url), + url=url, + ) + return links_fragments.link_icon( + request=request, + logged_in=logged_in, + link=link, + ) + + +@router.get("/api/tema/{tema_id}/score") +def get_score( + request: Request, + logged_in: auth.LoggedIn, + tema_id: int, +): + return links_fragments.score( + request=request, + logged_in=logged_in, + tema_id=tema_id, + ) + + +@router.get("/api/tema/{tema_id}/editor/link/{link_id}") +def link_editor( + request: Request, + logged_in: auth.RequireLogin, + tema_id: int, + link_id: int, +): + link = links_service.get_link_by_id(link_id=link_id, tema_id=tema_id) + if not link: + raise HTTPException(status_code=404, detail="Could not find link!") + return links_fragments.link_editor( + request=request, + logged_in=logged_in, + link=link, + ) + + +@router.get("/api/tema/{tema_id}/editor/link/{link_id}/url") +def link_editor_url_input( + request: Request, + logged_in: auth.RequireLogin, + tema_id: int, + link_id: int, +): + link = links_service.get_link_by_id(link_id=link_id, tema_id=tema_id) + if not link: + raise HTTPException(status_code=404, detail="Could not find link!") + return links_fragments.link_editor_url( + request=request, + logged_in=logged_in, + link=link, + ) + + +@router.get("/api/tema/{tema_id}/editor/link/{link_id}/file") +def link_editor_file_input( + request: Request, + logged_in: auth.RequireLogin, + tema_id: int, + link_id: int, +): + link = links_service.get_link_by_id(link_id=link_id, tema_id=tema_id) + if not link: + raise HTTPException(status_code=404, detail="Could not find link!") + return links_fragments.link_editor_file( + request=request, + logged_in=logged_in, + link=link, + ) diff --git a/folkugat_web/api/tema/lyrics.py b/folkugat_web/api/tema/lyrics.py new file mode 100644 index 0000000..26ef07d --- /dev/null +++ b/folkugat_web/api/tema/lyrics.py @@ -0,0 +1,80 @@ +import dataclasses +from typing import Annotated + +from fastapi import HTTPException, Request +from fastapi.params import Form +from fastapi.responses import HTMLResponse +from folkugat_web.api import router +from folkugat_web.fragments.tema import lyrics as lyrics_fragments +from folkugat_web.services import auth +from folkugat_web.services.temes import lyrics as lyrics_service + + +@router.get("/api/tema/{tema_id}/lyric/{lyric_id}") +def lyric( + request: Request, + logged_in: auth.LoggedIn, + tema_id: int, + lyric_id: int, +): + lyric = lyrics_service.get_lyric_by_id(lyric_id=lyric_id, tema_id=tema_id) + if not lyric: + raise HTTPException(status_code=404, detail="Could not find lyric!") + return lyrics_fragments.lyric(request=request, logged_in=logged_in, lyric=lyric) + + +@router.put("/api/tema/{tema_id}/lyric/{lyric_id}") +def set_lyric( + request: Request, + logged_in: auth.RequireLogin, + tema_id: int, + lyric_id: int, + title: Annotated[str, Form()], + content: Annotated[str, Form()], +): + lyric = lyrics_service.get_lyric_by_id(lyric_id=lyric_id, tema_id=tema_id) + if not lyric: + raise HTTPException(status_code=404, detail="Could not find lyric!") + new_lyric = dataclasses.replace(lyric, title=title, content=content.strip()) + lyrics_service.update_lyric(lyric=new_lyric) + return lyrics_fragments.lyric(request=request, logged_in=logged_in, lyric=new_lyric) + + +@router.post("/api/tema/{tema_id}/lyric") +def add_lyric( + request: Request, + logged_in: auth.RequireLogin, + tema_id: int, +): + lyric = lyrics_service.create_lyric(tema_id=tema_id) + return lyrics_fragments.lyric_editor( + request=request, + logged_in=logged_in, + lyric=lyric, + ) + + +@router.delete("/api/tema/{tema_id}/lyric/{lyric_id}") +def delete_lyric( + tema_id: int, + lyric_id: int, +): + lyrics_service.delete_lyric(lyric_id=lyric_id, tema_id=tema_id) + return HTMLResponse() + + +@router.get("/api/tema/{tema_id}/editor/lyric/{lyric_id}") +def lyric_editor( + request: Request, + logged_in: auth.RequireLogin, + tema_id: int, + lyric_id: int, +): + lyric = lyrics_service.get_lyric_by_id(lyric_id=lyric_id, tema_id=tema_id) + if not lyric: + raise HTTPException(status_code=404, detail="Could not find lyric!") + return lyrics_fragments.lyric_editor( + request=request, + logged_in=logged_in, + lyric=lyric, + ) diff --git a/folkugat_web/api/tema/properties.py b/folkugat_web/api/tema/properties.py new file mode 100644 index 0000000..7a45637 --- /dev/null +++ b/folkugat_web/api/tema/properties.py @@ -0,0 +1,73 @@ +import dataclasses +from typing import Annotated + +from fastapi import HTTPException, Request +from fastapi.params import Form +from fastapi.responses import HTMLResponse +from folkugat_web.api import router +from folkugat_web.fragments.tema import properties as properties_fragments +from folkugat_web.model import temes as model +from folkugat_web.services import auth +from folkugat_web.services.temes import properties as properties_service + + +@router.get("/api/tema/{tema_id}/property/{property_id}") +def property_(request: Request, logged_in: auth.LoggedIn, tema_id: int, property_id: int): + property = properties_service.get_property_by_id(property_id=property_id, tema_id=tema_id) + if not property: + raise HTTPException(status_code=404, detail="Could not find lyric!") + return properties_fragments.property_(request=request, logged_in=logged_in, property=property) + + +@router.put("/api/tema/{tema_id}/property/{property_id}") +def set_property( + request: Request, + logged_in: auth.RequireLogin, + tema_id: int, + property_id: int, + field: Annotated[model.PropertyField, Form()], + value: Annotated[str, Form()], +): + property = properties_service.get_property_by_id(property_id=property_id, tema_id=tema_id) + if not property: + raise HTTPException(status_code=404, detail="Could not find lyric!") + new_property = dataclasses.replace(property, field=field, value=value.strip()) + properties_service.update_property(property=new_property) + return properties_fragments.property_(request=request, logged_in=logged_in, property=new_property) + + +@router.post("/api/tema/{tema_id}/property") +def add_property( + request: Request, + logged_in: auth.RequireLogin, + tema_id: int, +): + property = properties_service.create_property(tema_id=tema_id) + return properties_fragments.property_editor( + request=request, + logged_in=logged_in, + property=property, + ) + + +@router.delete("/api/tema/{tema_id}/property/{property_id}") +def delete_property(_: auth.RequireLogin, tema_id: int, property_id: int): + properties_service.delete_property(property_id=property_id, tema_id=tema_id) + return HTMLResponse() + + +@router.get("/api/tema/{tema_id}/editor/property/{property_id}") +def property_editor( + request: Request, + logged_in: auth.RequireLogin, + tema_id: int, + property_id: int, +): + property = properties_service.get_property_by_id(property_id=property_id, tema_id=tema_id) + if not property: + raise HTTPException(status_code=404, detail="Could not find lyric!") + return properties_fragments.property_editor( + request=request, + logged_in=logged_in, + property=property, + ) diff --git a/folkugat_web/assets/templates/fragments/pdf_viewer.html b/folkugat_web/assets/templates/fragments/pdf_viewer.html index d1089c0..c6d29fa 100644 --- a/folkugat_web/assets/templates/fragments/pdf_viewer.html +++ b/folkugat_web/assets/templates/fragments/pdf_viewer.html @@ -37,4 +37,7 @@
? / ?
+
+ Obre el PDF +
diff --git a/folkugat_web/assets/templates/fragments/tema/editor/link.html b/folkugat_web/assets/templates/fragments/tema/editor/link.html index daa00ad..59aa0b3 100644 --- a/folkugat_web/assets/templates/fragments/tema/editor/link.html +++ b/folkugat_web/assets/templates/fragments/tema/editor/link.html @@ -36,14 +36,14 @@

-