from pathlib import Path import json import zipfile import os import sys from .models import Image, Page, BuiltinImages, BuiltinPages, get_tinywiki_default_user from .enums import WikiContentType, WikiPageStatus from django.core.files.images import ImageFile from django.conf import settings as django_settings def import_builtin_pages(json_file: str | Path, user=None): """ Import builtin pages :param json_file: The json file which holds the data. :type json_file: str | Path :raises ValueError: If file does not exist or json_file is not a file. :return: `True` if pages were successfully imported, `False` otherwise :rvalue: bool """ if isinstance(json_file, str): json_file = Path(json_file).resolve() if not json_file.exists(): raise ValueError("File \"{json_file}\" does not exist!") if not json_file.isfile(): raise ValueError("json_file needs to be a valid file!") with open(json_file, "rt", encoding="utf-8") as ifile: data = json.loads(ifile.read()) app = data['app'] prefix = data['prefix'] version = data['version'] page_path = json_file.parent try: bp = BuiltinPages.objects.get(app=app) if version <= bp.version: print("Page already imported, skipping!") return False for slug, spec in data['pages'].items(): filename = page_path / spec['file'] with open(filename, "rt", encoding="utf-8") as ifile: content = ifile.read() try: p = Page.objects.get() p.title = spec['title'] p.status_data = WikiPageStatus.from_string(spec['status']).value p.content_type_data = WikiContentType.from_string(spec['content_type']).value, p.content = content p.author = user if user else get_tinywiki_default_user() p.save() except Page.DoesNotExist: Page.objects.create(slug=slug, title=spec['title'], status_data=WikiPageStatus.from_string(spec['status']).value, content_type_data=WikiContentType.from_string(spec['content_type']).value, content=content, author=user if user else get_tinywiki_default_user()) bp.prefix = prefix bp.version = version bp.save() return True except BuiltinPages.DoesNotExist: for slug, spec in data['pages'].items(): filename = page_path / spec['file'] with open(filename, "rt", encoding="utf-8") as ifile: content = ifile.read() Page.objects.create(slug=slug, title=spec['title'], status_data=WikiPageStatus.from_string(spec['status']).value, content_type_data=WikiContentType.from_string(spec['content_type']).value, content=content, author=user if user else get_tinywiki_default_user()) BuiltinPages.objects.create(app=app, prefix=prefix, version=version) return True def import_builtin_pages_from_zip(zip: str | Path, user=None): """ Import builtin pages :param zip: The zip file which holds the data. :type zip: str | Path :raises ValueError: If file does not exist or json_file is not a file. :return: `True` if pages were successfully imported, `False` otherwise :rvalue: bool """ if isinstance(zip, str): zip = Path(zip).resolve() if not zip.exists(): raise ValueError(f"File \"{zip}\" does not exist!") if not zip.isfile(): raise ValueError("zip needs to be a valid file!") if not zipfile.is_zipfile(zip): raise ValueError("zip needs to be a zip file!") with zipfile.ZipFile(zip, "r") as zf: with zf.open('pages.json') as json_file: data = json.loads(json_file.read().decode('utf-8')) app = data['app'] prefix = data['prefix'] version = data['version'] try: bp = BuiltinPages.objects.get(app=app) if version <= bp.version: print("Page already imported, skipping!") return False for slug, spec in data['pages'].items(): with zf.open(spec['file']) as ifile: content = ifile.read().decode('utf-8') try: p = Page.objects.get() p.title = spec['title'] p.status_data = WikiPageStatus.from_string(spec['status']).value p.content_type_data = WikiContentType.from_string(spec['content_type']).value, p.content = content p.author = user if user else get_tinywiki_default_user() p.save() except Page.DoesNotExist: Page.objects.create(slug=slug, title=spec['title'], status_data=WikiPageStatus.from_string(spec['status']).value, content_type_data=WikiContentType.from_string(spec['content_type']).value, content=content, author=user if user else get_tinywiki_default_user()) bp.prefix = prefix bp.version = version bp.save() return True except BuiltinPages.DoesNotExist: for slug, spec in data['pages'].items(): with zf.open(spec['file'], "rt", encoding="utf-8") as ifile: content = ifile.read() Page.objects.create(slug=slug, title=spec['title'], status_data=WikiPageStatus.from_string(spec['status']).value, content_type_data=WikiContentType.from_string(spec['content_type']).value, content=content, author=user if user else get_tinywiki_default_user()) BuiltinPages.objects.create(app=app, prefix=prefix, version=version) return True def import_builtin_images(json_file: str | Path, user=None): if isinstance(json_file, str): json_file = Path(json_file).resolve() if not json_file.exists(): raise ValueError(f"File \"{json_file}\" does not exist!") if not json_file.isfile(): raise ValueError("json_file needs to be a valid file!") image_dir = json_file.parent with open(json_file, "rt", encoding="utf-8") as ifile: data = json.loads(ifile.read()) version = data['version'] app = data['app'] prefix = data['prefix'] try: bi = BuiltinImages.objects.get(app=app) if bi.version <= data['version']: return False for slug, spec in data['images'].items(): try: img = Image.objects.get(slug=spec['slug']) continue except Image.DoesNotExist: image_basename = spec['image'] image_filename = image_dir / spec.pop("image") spec['slug'] = slug spec['user'] = user if user else get_tinywiki_default_user() img = Image(**spec) with open(image_filename, "rb") as ifile: img_file = ImageFile(ifile, image_basename) img.image.save(image_basename, img_file) img.save() bi.version = version bi.save() return True except BuiltinImages.DoesNotExist: for slug, spec in data['images'].items(): spec = spec image_basename = spec['image'] image_filename = image_dir / spec.pop("image") spec['slug'] = slug spec['user'] = user if user else get_tinywiki_default_user() img = Image(**spec) with open(image_filename, "rb") as ifile: img_file = ImageFile(ifile, image_basename) img.image.save(image_basename, img_file) img.save() BuiltinImages.objects.create(app=app, prefix=prefix, version=version) return True def import_builtin_images_from_zip(zip: str | Path, user=None): if not isinstance(zip, Path): zip = Path(zip).resolve() if not zip.exists(): raise ValueError(f"File \"{zip}\" does not exist!") if not zip.isfile(): raise ValueError("zip needs to be a valid file!") if not zipfile.is_zipfile(zip): raise ValueError("zip needs to be a zip file!") with zipfile.ZipFile(zip, "r", encoding="utf-8") as zf: data = json.loads(zf.open('images.json').read().decode('utf-8')) version = data['version'] app = data['app'] prefix = data['prefix'] try: bi = BuiltinImages.objects.get(app=app) if bi.version <= data['version']: return False for slug, spec in data['images'].items(): try: img = Image.objects.get(slug=spec['slug']) continue except Image.DoesNotExist: image_basename = os.path.basename(spec['image']) image_filename = spec.pop("image") spec['slug'] = slug spec['user'] = user if user else get_tinywiki_default_user() img = Image(**spec) with zf.open(image_filename) as ifile: img_file = ImageFile(ifile, image_basename) img.image.save(image_basename, img_file) img.save() bi.version = version bi.save() return True except BuiltinImages.DoesNotExist: for slug, spec in data['images'].items(): spec = spec image_basename = os.path.basename(spec['image']) image_filename = spec.pop("image") spec['slug'] = slug spec['user'] = user if user else get_tinywiki_default_user() img = Image(**spec) with zf.open(image_filename) as ifile: img_file = ImageFile(ifile, image_basename) img.image.save(image_basename, img_file) img.save() BuiltinImages.objects.create(app=app, prefix=prefix, version=version) return True def export_wiki_content(app: str, filename, prefix: str | None = None, page_version: int = 0, image_version: int = 0) -> bool: prefix = prefix try: bp = BuiltinPages.objects.get(app=app) if page_version < bp.version: page_version = bp.version + 1 if not prefix: prefix = bp.prefix except BuiltinPages.DoesNotExist: bp = None try: bi = BuiltinImages.objects.get(app=app) if image_version < bi.version: image_version = bi.version + 1 if not prefix: prefix = bp.prefix except BuiltinImages.DoesNotExist: bi = None if not prefix: raise RuntimeError("No slug prefix! Can not export!") pages = Page.objects.filter(slug__startswith=prefix) images = Image.objects.filter(slug__startswith=prefix) if not pages and not images: return False with zipfile.ZipFile(filename, mode="w", compression=zipfile.ZIP_DEFLATED, compresslevel=9) as zf: if pages: pages_data = { 'app': app, 'prefix': prefix, 'version': page_version, 'pages': {} } for p in pages: if WikiContentType.BBCODE == p.content_type: extension = 'bbcode' else: extension = 'md' page_file = f"pages/{p.slug}.{extension}" pages_data['pages'][p.slug] = { 'title': p.title, 'content_type': p.content_type.value, 'status': p.status.value, 'file': page_file, } zf.writestr(page_file, p.content) zf.writestr('pages.json', json.dumps(pages_data, ensure_ascii=False, indent=4)) if bp: bp.version = page_version bp.save() else: BuiltinPages.objects.create(app=app, prefix=prefix, version=page_version) if images: images_data = { 'app': app, 'prefix': prefix, 'version': image_version, 'images': {} } for i in images: if not os.path.isfile(i.image.path): continue arcname = f"images/{os.path.basename(i.image.path)}" images_data['images'][i.slug] = { 'slug': i.slug, 'alt': i.alt, 'file': arcname, 'description': i.description, } zf.write(i.image.path, arcname) zf.writestr('images.json', json.dumps(images_data, ensure_ascii=False, indent=4)) if bi: bi.version = image_version bi.save() else: BuiltinImages.objects.create(app=app, prefix=prefix, version=image_version) return True def export_tinywiki_wiki_content(filename=None): if filename is None: filename = Path(django_settings.MEDIA_ROOT) / "tinywiki-tinywiki.zip" export_wiki_content('tinywiki', filename, 'tw-')