diff --git a/district/forms.py b/district/forms.py index d8188b69b16929a1d01cd11a0ff6dd588b45f78e..b5da4b38e70f84ea8e1db5c25ecf1519e9cd6331 100644 --- a/district/forms.py +++ b/district/forms.py @@ -1,39 +1,91 @@ from django import forms from wagtail.admin.forms import WagtailAdminPageForm from wagtail.core.models.collections import Collection -from wagtail.core.models.sites import Site from .jekyll_import import perform_import class JekyllImportForm(WagtailAdminPageForm): - # TODO resolve circular import and make ModelChoiceField - article_root_page_id = forms.IntegerField() - collection = forms.ModelChoiceField(queryset=Collection.objects.all()) - dry_run = forms.BooleanField(initial=True) - jekyll_repo_folder = forms.CharField(max_length=512) + do_import = forms.BooleanField( + initial=False, required=False, label="Provést import z Jekyllu" + ) + collection = forms.ModelChoiceField( + queryset=Collection.objects.all(), required=False, label="Kolekce obrázků" + ) + dry_run = forms.BooleanField( + initial=True, + required=False, + label="Jenom na zkoušku", + help_text="Žádné články se neuloží, vypíše případné problémy či " + "již existující články - 'ostrému' importu existující " + "články nevadí, přeskočí je", + ) + use_git = forms.BooleanField( + initial=False, + required=False, + label="Použít Git", + help_text="Umožňuje jednodušší zpracování, ale vyžaduje nainstalovaný Git.", + ) + jekyll_repo_url = forms.URLField( + max_length=512, + required=False, + help_text="V GitHubu tlačítko Code -> a odkaz z Download zip" + "např. 'https://github.com/pirati-web/cb.pirati.cz/archive/refs/heads/gh-pages.zip'," + "pokud máte nainstalovaný Git, zvolte Použít Git a vložte" + "jednoduše URL repozitáře " + "např. 'https://github.com/pirati-web/cb.pirati.cz'", + ) + + # def __init__(self): + # pass def clean(self): cleaned_data = super().clean() - error_list = perform_import( - article_root_page_id=self.cleaned_data["article_root_page_id"], - collection=self.cleaned_data["collection"], - path=self.cleaned_data["jekyll_repo_folder"], - dry_run=self.cleaned_data["dry_run"], - ) + if not cleaned_data.get("do_import"): + return cleaned_data + else: + if not cleaned_data.get("collection"): + self.add_error("collection", "Pro import je toto pole povinné") + if not cleaned_data.get("jekyll_repo_url"): + self.add_error("jekyll_repo_url", "Pro import je toto pole povinné") + + if cleaned_data.get("use_git"): + if cleaned_data.get("jekyll_repo_url", "").endswith(".zip"): + self.add_error( + "jekyll_repo_url", "Vložte odkaz pouze na repozitář, ne na zip" + ) + else: + if not cleaned_data.get("jekyll_repo_url", "").endswith(".zip"): + self.add_error("jekyll_repo_url", "Odkaz nesměřuje na soubor '.zip'") + + if len(self.errors): + return cleaned_data + + if cleaned_data.get("dry_run"): + error_list = perform_import( + article_parent_page=self.instance, + collection=self.cleaned_data["collection"], + url=self.cleaned_data["jekyll_repo_url"], + dry_run=True, + use_git=self.cleaned_data["use_git"], + ) + # self.instance.import_error_list = error_list - for error in error_list: - self.add_error("jekyll_repo_folder", error) + for error in error_list: + self.add_error("jekyll_repo_url", error) return cleaned_data def save(self, commit=True): - perform_import( - article_root_page_id=self.cleaned_data["article_root_page_id"], - collection=self.cleaned_data["collection"], - path=self.cleaned_data["jekyll_repo_folder"], - dry_run=self.cleaned_data["dry_run"], - ) + if self.cleaned_data.get("do_import") and not self.cleaned_data["dry_run"]: + error_list = perform_import( + article_parent_page=self.instance, + collection=self.cleaned_data["collection"], + url=self.cleaned_data["jekyll_repo_url"], + dry_run=False, + use_git=self.cleaned_data["use_git"], + ) + self.instance.import_error_list = error_list return super().save(commit=commit) diff --git a/district/jekyll_import.py b/district/jekyll_import.py index 50170891d6450b37fd49e95ec235ce815cc5f3fc..0e82e3ce89be48a0140149d923ad7bab1ebdfe89 100644 --- a/district/jekyll_import.py +++ b/district/jekyll_import.py @@ -1,8 +1,12 @@ import os import re +import urllib import xml.etree.ElementTree as ET +import zipfile from io import StringIO +from shutil import rmtree from sys import stdout +from typing import List import markdown.serializers import yaml @@ -132,6 +136,13 @@ def get_collection(): return params["kolekce"] +def get_path(url: str, use_git: bool) -> str: + if use_git: + return clone_repo(url) + else: + return download_repo_as_zip(url) + + def get_or_create_image(path, file_path, collection): file_path = file_path.lstrip("/") if Image.objects.filter(title=file_path).exists(): @@ -149,16 +160,60 @@ def get_title_from_site_config(site_config: dict) -> str: return "" -def perform_import(article_root_page_id, collection, path, dry_run): - from district.models import DistrictArticlesPage +def clone_repo(url: str) -> str: + """ + Naclonuje repo do tmp s využitím gitu a vrátí cestu k němu. + Pokud URL končí lomítkem, odebereme ho, a vezmeme jako název repozitáře + string za posledním lomítkem jako název repa. To použijeme i pro promazání + takového adresáře, pokud už existuje. + """ + path = "/tmp/" + if url.endswith("/"): + url = url[:-1] + repo_name = url.split("/")[-1] + repo_path = os.path.join(path, repo_name) + + if os.path.exists(repo_path): + rmtree(repo_path) + + os.chdir(path) + os.system("git clone {}".format(url)) + + return repo_path + + +def download_repo_as_zip(url: str) -> str: + """ + Stáhne .zip repa, extrahuje a vrátí cestu k extrahovanému repu. + Hodně nešikovné je, že extrahovaná složka má ještě suffix "-gh-pages" + a to nevím, jestli platí vždy... regex taky pro název repa také není optimální, + ale ve finále nehraje moc roli, pokud vrátí cokoliv použitelného pro file name. + """ + path = "/tmp/" + repo_name = re.search("pirati-web/(.*)/archive/", url).group(1) + zip_path = "{}{}.zip".format(path, repo_name) + + if os.path.exists(zip_path): + os.remove(zip_path) + + urllib.request.urlretrieve(url, zip_path) + + with zipfile.ZipFile(zip_path, "r") as zip_ref: + zip_ref.extractall(path) + + return os.path.join(path, "{}-gh-pages".format(repo_name)) + + +def perform_import( + article_parent_page, collection, url: str, dry_run: bool, use_git: bool +) -> "List[str]": error_list = [] - articles = DistrictArticlesPage.objects.get(pk=article_root_page_id) params["kolekce"] = collection - site = articles.get_site() + site = article_parent_page.get_site() - path = params["path"] = path + path = params["path"] = get_path(url=url, use_git=use_git) site_config = get_site_config(path) title_suffix = get_title_from_site_config(site_config) @@ -176,7 +231,7 @@ def perform_import(article_root_page_id, collection, path, dry_run): if ext == "md": article, error = import_post( - path, fname, articles, title_suffix, dry_run + path, fname, article_parent_page, title_suffix, dry_run ) if error: diff --git a/district/management/commands/district_import_jekyll.py b/district/management/commands/district_import_jekyll.py index fc3525e22b859a126d2b403d66b736596cc6d8b2..ddfd464ab64c201d6edac161baeecfe64f115168 100644 --- a/district/management/commands/district_import_jekyll.py +++ b/district/management/commands/district_import_jekyll.py @@ -36,10 +36,5 @@ class Command(BaseCommand): ) def handle(self, *args, **options): - perform_import( - article_root_page_id=options["clanky_id"], - collection=Collection.objects.get(pk=options["kolekce_id"]), - path=options["path"], - site=None, - dry_run=options["dry-run"], - ) + pass + # perform_import() # TODO diff --git a/district/models.py b/district/models.py index 5460eedec5def317bc5f5dfab18bb6f534da263e..ab4a3cbd4bfc58625781c3e323da796d9dcedccc 100644 --- a/district/models.py +++ b/district/models.py @@ -231,18 +231,6 @@ class DistrictHomePage(MenuMixin, MetadataPageMixin, CalendarMixin, Page): CommentPanel(), ] - import_panels = [ - MultiFieldPanel( - [ - FieldPanel("article_root_page_id"), - FieldPanel("collection"), - FieldPanel("dry_run"), - FieldPanel("jekyll_repo_folder"), - ], - "import z Jekyll repozitáře", - ), - ] - ### EDIT HANDLERS edit_handler = TabbedInterface( [ @@ -269,8 +257,6 @@ class DistrictHomePage(MenuMixin, MetadataPageMixin, CalendarMixin, Page): ### OTHERS - base_form_class = JekyllImportForm - class Meta: verbose_name = "Místní sdružení" @@ -388,6 +374,29 @@ class DistrictArticlesPage(SubpageMixin, MetadataPageMixin, Page): settings_panels = [CommentPanel()] + import_panels = [ + MultiFieldPanel( + [ + FieldPanel("do_import"), + FieldPanel("collection"), + FieldPanel("dry_run"), + FieldPanel("use_git"), + FieldPanel("jekyll_repo_url"), + ], + "import z Jekyll repozitáře", + ), + ] + + ### EDIT HANDLERS + + edit_handler = TabbedInterface( + [ + ObjectList(content_panels, heading="Obsah"), + ObjectList(settings_panels, heading="Nastavení"), + ObjectList(import_panels, heading="Import"), + ] + ) + ### RELATIONS parent_page_types = ["district.DistrictHomePage"] @@ -395,6 +404,8 @@ class DistrictArticlesPage(SubpageMixin, MetadataPageMixin, Page): ### OTHERS + base_form_class = JekyllImportForm + class Meta: verbose_name = "Aktuality" diff --git a/district/wagtail_hooks.py b/district/wagtail_hooks.py index ea3324d401ca66f0a0c822b8b60ade28a8417ab8..ed88031ab12442bf50703aa33ce17dd655091299 100644 --- a/district/wagtail_hooks.py +++ b/district/wagtail_hooks.py @@ -1,3 +1,19 @@ +from wagtail.admin import messages +from wagtail.core import hooks + +from .models import DistrictHomePage + + +@hooks.register("after_edit_page") +@hooks.register("after_edit_page") +def handle_page_import(request, page): # def after_create_page(request, page): + """Block awesome page deletion and show a message.""" + + if request.method == "POST" and page.specific_class in [DistrictHomePage]: + if getattr(page, "import_messages", None): + messages.warning(request, str(page.import_error_list)) + + # import re # # from wagtail.core import hooks