diff --git a/tinywiki/migrations/0001_initial.py b/tinywiki/migrations/0001_initial.py index a13484c..1aaacbc 100644 --- a/tinywiki/migrations/0001_initial.py +++ b/tinywiki/migrations/0001_initial.py @@ -1,46 +1,86 @@ -# Generated by Django 5.2.4 on 2025-09-15 00:26 - -import django.db.models.deletion -import tinywiki.models -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name='Image', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('slug', models.SlugField(max_length=255, verbose_name='slug')), - ('alt', models.CharField(max_length=511, verbose_name='alternative text')), - ('description', models.CharField(blank=True, max_length=1023, null=True, verbose_name='description')), - ('image', models.ImageField(upload_to='tinywiki/img', verbose_name='image file')), - ('uploaded_at', models.DateTimeField(auto_now_add=True, verbose_name='uploaded at')), - ('uploaded_by', models.ForeignKey(default=tinywiki.models.get_tinywiki_default_user, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='tinywiki_image_uploads', to=settings.AUTH_USER_MODEL, verbose_name='uploaded by')), - ], - ), - migrations.CreateModel( - name='Page', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('slug', models.SlugField(max_length=255, unique=True, verbose_name='slug')), - ('title', models.CharField(max_length=255, verbose_name='title')), - ('status_data', models.CharField(default='', max_length=15, verbose_name='status')), - ('content_type_data', models.CharField(choices=[('markdown', 'Markdown'), ('bbcode', 'BBCode')], default='bbcode', verbose_name='content type')), - ('content', models.TextField(verbose_name='Page content')), - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='created at')), - ('last_edited_at', models.DateTimeField(auto_now=True, verbose_name='last edited at')), - ('author', models.ForeignKey(default=tinywiki.models.get_tinywiki_default_user, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='tinywiki_athors', to=settings.AUTH_USER_MODEL, verbose_name='author')), - ('created_by', models.ForeignKey(default=tinywiki.models.get_tinywiki_default_user, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='tinywiki_created', to=settings.AUTH_USER_MODEL, verbose_name='created by')), - ('last_edited_by', models.ForeignKey(default=tinywiki.models.get_tinywiki_default_user, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='tinywiki_last_edited', to=settings.AUTH_USER_MODEL, verbose_name='last edited by')), - ], - ), - ] +# Generated by Django 5.2.9 on 2025-12-27 04:35 + +import django.db.models.deletion +import tinywiki.models +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='BuiltinImages', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('app', models.CharField(max_length=255, unique=True, verbose_name='app')), + ('version', models.PositiveIntegerField(verbose_name='version')), + ('prefix', models.CharField(verbose_name='prefix')), + ], + ), + migrations.CreateModel( + name='BuiltinPages', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('app', models.CharField(max_length=255, unique=True, verbose_name='app')), + ('version', models.PositiveIntegerField(verbose_name='version')), + ('prefix', models.CharField(verbose_name='prefix')), + ], + ), + migrations.CreateModel( + name='SidebarSection', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=255, verbose_name='title')), + ('priority', models.PositiveIntegerField(verbose_name='priority')), + ('is_visible', models.BooleanField(default=True, verbose_name='is visible')), + ], + ), + migrations.CreateModel( + name='Image', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('slug', models.SlugField(max_length=255, verbose_name='slug')), + ('alt', models.CharField(max_length=511, verbose_name='alternative text')), + ('description', models.CharField(blank=True, max_length=1023, null=True, verbose_name='description')), + ('image', models.ImageField(upload_to='tinywiki/img', verbose_name='image file')), + ('uploaded_at', models.DateTimeField(auto_now_add=True, verbose_name='uploaded at')), + ('uploaded_by', models.ForeignKey(default=tinywiki.models.get_tinywiki_default_user, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='tinywiki_image_uploads', to=settings.AUTH_USER_MODEL, verbose_name='uploaded by')), + ], + ), + migrations.CreateModel( + name='Page', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('slug', models.SlugField(max_length=255, unique=True, verbose_name='slug')), + ('title', models.CharField(max_length=255, verbose_name='title')), + ('status_data', models.CharField(choices=[('in_progress', 'in progress'), ('draft', 'draft'), ('published', 'published')], default='in_progress', max_length=15, verbose_name='status')), + ('content_type_data', models.CharField(choices=[('markdown', 'Markdown'), ('bbcode', 'BBCode')], default='bbcode', verbose_name='content type')), + ('content', models.TextField(verbose_name='Page content')), + ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='created at')), + ('last_edited_at', models.DateTimeField(auto_now=True, verbose_name='last edited at')), + ('author', models.ForeignKey(default=tinywiki.models.get_tinywiki_default_user, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='tinywiki_athors', to=settings.AUTH_USER_MODEL, verbose_name='author')), + ('created_by', models.ForeignKey(default=tinywiki.models.get_tinywiki_default_user, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='tinywiki_created', to=settings.AUTH_USER_MODEL, verbose_name='created by')), + ('last_edited_by', models.ForeignKey(default=tinywiki.models.get_tinywiki_default_user, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='tinywiki_last_edited', to=settings.AUTH_USER_MODEL, verbose_name='last edited by')), + ], + ), + migrations.CreateModel( + name='SidebarEntry', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=255, verbose_name='Title')), + ('is_visible', models.BooleanField(default=True, verbose_name='is visible')), + ('priority', models.PositiveIntegerField(verbose_name='Priority')), + ('wiki_slug', models.CharField(blank=True, max_length=255, null=True, verbose_name='Wiki slug')), + ('url', models.CharField(blank=True, max_length=512, null=True, verbose_name='Link URL')), + ('widget', models.TextField(blank=True, null=True, verbose_name='Widget')), + ('section', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='tinywiki.sidebarsection', verbose_name='Section')), + ], + ), + ] diff --git a/tinywiki/migrations/0002_initial_data.py b/tinywiki/migrations/0002_initial_data.py index 4aad33c..88d4da4 100644 --- a/tinywiki/migrations/0002_initial_data.py +++ b/tinywiki/migrations/0002_initial_data.py @@ -1,132 +1,136 @@ -# Generated by Django 5.2.4 on 2025-09-14 13:15 - -from django.db import migrations -from .. import settings -class Migration(migrations.Migration): - dependencies = [ - ('tinywiki', '0001_initial'), - ] - - @staticmethod - def init_tinywiki_user(apps, schema_editor): - from django.contrib.auth import get_user_model - get_user_model().objects.create_user(**settings.TINYWIKI_USER_CONFIG) - - @staticmethod - def init_tinywiki_groups_and_permissions(apps, schema_editor): - from ..models import Page - from django.contrib.auth.models import Group, Permission - from django.contrib.contenttypes.models import ContentType - - PERMISSIONS = [ - 'tinywiki-read-all', - 'tinywiki-delete', - 'tinywiki-create', - 'tinywiki-create-system', - 'tinywiki-edit', - 'tinywiki-edit-system', - 'tinywiki-edit-all', - 'tinywiki-delete-all', - 'tinywiki-delete-system', - ] - - GROUPS = [ - ('tinywiki-moderator',('tinywiki-read-all', - 'tinywiki-delete-all', - 'tinywiki-edit-all', - 'tinywiki-create')), - ('tinywiki-author',('tinywiki-create', - 'tinywiki-edit', - 'tinywiki-delete')), - ('tinywiki-reader',('tinywiki-read-all',)), - ('tinywiki-admin',('tinywiki-read-all', - 'tinywiki-create', - 'tinywiki-create-system', - 'tinywiki-delete-all', - 'tinywiki-delete-system', - 'tinywiki-edit-all', - 'tinywiki-edit-system')) - ] - - perm_mapping = {} - content_type = ContentType.objects.get_for_model(Page) - for perm in PERMISSIONS: - permission = Permission.objects.create(codename=perm,content_type=content_type) - perm_mapping[perm] = permission - - - for grp,perms in GROUPS: - group = Group.objects.create(name=grp) - for perm in perms: - group.permissions.add(perm_mapping[perm]) - - - def init_builtin_pages(apps,schema_editor)->None: - from ..models import Page,Image - from ..enums import WikiContentType,WikiPageStatus - from pathlib import Path - import json - - page_path = Path(__file__).resolve().parent / "pages" - json_file = page_path / "pages.json" - if json_file.is_file(): - with open(json_file,"rt",encoding="utf-8") as ifile: - data=json.loads(ifile.read()) - - - - for slug,spec in data.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) - - - def init_builtin_images(apps,schema_edit): - from pathlib import Path - from ..models import Image - from django.core.files.images import ImageFile - import json - - image_dir = Path(__file__).resolve().parent / "images" - images_json_file = image_dir/ "images.json" - - if not images_json_file.is_file(): - return - - with open(images_json_file,"rt",encoding="utf-8") as ifile: - images_data = json.loads(ifile.read()) - - for slug,_spec in images_data.items(): - spec = dict(_spec) - image_basename = spec['image'] - image_filename = image_dir / spec["image"] - spec['slug'] = slug - del spec['image'] - - 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() - - - def init_user_imges(apps,schema_edit)->None: - #TODO - pass - - def init_user_pages(apps,schema_edit)->None: - #TODO - pass - - operations = [ - migrations.RunPython(init_tinywiki_groups_and_permissions), - migrations.RunPython(init_tinywiki_user), - migrations.RunPython(init_builtin_images), - migrations.RunPython(init_builtin_pages), - ] +# Generated by Django 5.2.4 on 2025-09-14 13:15 + +from django.db import migrations +from .. import settings + + +class Migration(migrations.Migration): + dependencies = [ + ('tinywiki', '0001_initial'), + ] + + @staticmethod + def init_tinywiki_user(apps, schema_editor): + from django.contrib.auth import get_user_model + get_user_model().objects.create_user(**settings.TINYWIKI_USER_CONFIG) + + @staticmethod + def init_tinywiki_groups_and_permissions(apps, schema_editor): + from ..models import Page + from django.contrib.auth.models import Group, Permission + from django.contrib.contenttypes.models import ContentType + + PERMISSIONS = [ + 'tinywiki-read-all', + 'tinywiki-delete', + 'tinywiki-create', + 'tinywiki-create-system', + 'tinywiki-edit', + 'tinywiki-edit-system', + 'tinywiki-edit-all', + 'tinywiki-delete-all', + 'tinywiki-delete-system', + ] + + GROUPS = [ + ('tinywiki-moderator', ('tinywiki-read-all', + 'tinywiki-delete-all', + 'tinywiki-edit-all', + 'tinywiki-create')), + ('tinywiki-author', ('tinywiki-create', + 'tinywiki-edit', + 'tinywiki-delete')), + ('tinywiki-reader', ('tinywiki-read-all',)), + ('tinywiki-admin', ('tinywiki-read-all', + 'tinywiki-create', + 'tinywiki-create-system', + 'tinywiki-delete-all', + 'tinywiki-delete-system', + 'tinywiki-edit-all', + 'tinywiki-edit-system')) + ] + + perm_mapping = {} + content_type = ContentType.objects.get_for_model(Page) + for perm in PERMISSIONS: + permission = Permission.objects.create(codename=perm,content_type=content_type) + perm_mapping[perm] = permission + + for grp, perms in GROUPS: + group = Group.objects.create(name=grp) + for perm in perms: + group.permissions.add(perm_mapping[perm]) + + @staticmethod + def init_builtin_pages(apps, schema_editor): + from ..models import Page, BuiltinPages + from ..enums import WikiContentType, WikiPageStatus + from pathlib import Path + import json + + page_path = Path(__file__).resolve().parent / "pages" + json_file = page_path / "pages.json" + + if json_file.is_file(): + with open(json_file, "rt", encoding="utf-8") as ifile: + data = json.loads(ifile.read()) + + version = data["version"] + app = data["app"] + prefix = data['prefix'] + + 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) + + BuiltinPages.objects.create(app=app, prefix=prefix, version=version) + + @staticmethod + def init_builtin_images(apps, schema_edit): + from pathlib import Path + from ..models import Image, BuiltinImages + from django.core.files.images import ImageFile + import json + + image_dir = Path(__file__).resolve().parent / "images" + images_json_file = image_dir / "images.json" + + if not images_json_file.is_file(): + return + + with open(images_json_file, "rt", encoding="utf-8") as ifile: + images_data = json.loads(ifile.read()) + + version = images_data['version'] + app = images_data['app'] + prefix = images_data['prefix'] + + for slug, _spec in images_data['images'].items(): + spec = dict(_spec) + image_basename = spec['image'] + image_filename = image_dir / spec["image"] + spec['slug'] = slug + del spec['image'] + + 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) + + + operations = [ + migrations.RunPython(init_tinywiki_groups_and_permissions), + migrations.RunPython(init_tinywiki_user), + migrations.RunPython(init_builtin_images), + migrations.RunPython(init_builtin_pages), + ] diff --git a/tinywiki/migrations/0004_default_sidebar.py b/tinywiki/migrations/0003_default_sidebar.py similarity index 97% rename from tinywiki/migrations/0004_default_sidebar.py rename to tinywiki/migrations/0003_default_sidebar.py index 3b1ccc7..b977bd0 100644 --- a/tinywiki/migrations/0004_default_sidebar.py +++ b/tinywiki/migrations/0003_default_sidebar.py @@ -85,7 +85,7 @@ class Migration(migrations.Migration): SidebarEntry.objects.create(section=section, **item_spec) dependencies = [ - ('tinywiki', '0003_sidebarsection_alter_page_status_data_sidebarentry'), + ('tinywiki', '0002_initial_data'), ] operations = [ diff --git a/tinywiki/migrations/0003_sidebarsection_alter_page_status_data_sidebarentry.py b/tinywiki/migrations/0003_sidebarsection_alter_page_status_data_sidebarentry.py deleted file mode 100644 index 4c37c14..0000000 --- a/tinywiki/migrations/0003_sidebarsection_alter_page_status_data_sidebarentry.py +++ /dev/null @@ -1,41 +0,0 @@ -# Generated by Django 5.2.9 on 2025-12-23 15:57 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('tinywiki', '0002_initial_data'), - ] - - operations = [ - migrations.CreateModel( - name='SidebarSection', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('title', models.CharField(max_length=255, verbose_name='title')), - ('priority', models.PositiveIntegerField(verbose_name='priority')), - ('is_visible', models.BooleanField(default=True, verbose_name='is visible')), - ], - ), - migrations.AlterField( - model_name='page', - name='status_data', - field=models.CharField(choices=[('in_progress', 'in progress'), ('draft', 'draft'), ('published', 'published')], default='in_progress', max_length=15, verbose_name='status'), - ), - migrations.CreateModel( - name='SidebarEntry', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('title', models.CharField(max_length=255, verbose_name='Title')), - ('is_visible', models.BooleanField(default=True, verbose_name='is visible')), - ('priority', models.PositiveIntegerField(verbose_name='Priority')), - ('wiki_slug', models.CharField(blank=True, max_length=255, null=True, verbose_name='Wiki slug')), - ('url', models.CharField(blank=True, max_length=512, null=True, verbose_name='Link URL')), - ('widget', models.TextField(blank=True, null=True, verbose_name='Widget')), - ('section', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='tinywiki.sidebarsection', verbose_name='Section')), - ], - ), - ] diff --git a/tinywiki/migrations/images/images.json b/tinywiki/migrations/images/images.json index 90a17fb..2927c0b 100644 --- a/tinywiki/migrations/images/images.json +++ b/tinywiki/migrations/images/images.json @@ -1,7 +1,12 @@ { - "tw-image-01": { - "alt": "Foggy Mountain-tops in the dawn", - "image": "fabian-bachli-jQAe44MEIXU-unsplash.jpg", - "description": "Foto from [url=\"https://unsplash.com/de/@fabianbaechli?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash\"]Fabian Bächli[/url] on [url=\"https://unsplash.com/de/fotos/nebelige-berggipfel-in-der-morgendammerung-mit-sanften-wolken-jQAe44MEIXU?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash\"]Unsplash[/url]" + "version": 0, + "app": "tinywiki", + "prefix": "tw-", + "images": { + "tw-image-01": { + "alt": "Foggy Mountain-tops in the dawn", + "image": "fabian-bachli-jQAe44MEIXU-unsplash.jpg", + "description": "Foto from [url=\"https://unsplash.com/de/@fabianbaechli?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash\"]Fabian Bächli[/url] on [url=\"https://unsplash.com/de/fotos/nebelige-berggipfel-in-der-morgendammerung-mit-sanften-wolken-jQAe44MEIXU?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash\"]Unsplash[/url]" + } } } \ No newline at end of file diff --git a/tinywiki/migrations/pages/pages.json b/tinywiki/migrations/pages/pages.json index 27f40a5..4718aa6 100644 --- a/tinywiki/migrations/pages/pages.json +++ b/tinywiki/migrations/pages/pages.json @@ -1,26 +1,31 @@ { - "tw-license": { - "title": "TinyWiki License", - "content_type":"bbcode", - "status":"published", - "file":"license.bbcode" - }, - "tw-bootstrap-license": { - "title": "Bootstrap License", - "content_type": "bbcode", - "status":"published", - "file":"bs-license.bbcode" - }, - "tw-bbcode": { - "title": "BBCode used by TinyWiki", - "content_type": "bbcode", - "status": "published", - "file": "bbcode.bbcode" - }, - "tw-markdown": { - "title": "Markdown used by TinyWiki", - "content_type": "markdown", - "status": "draft", - "file": "markdown.md" + "version": 0, + "app": "tinywiki", + "prefix": "tw-", + "pages": { + "tw-license": { + "title": "TinyWiki License", + "content_type": "bbcode", + "status": "published", + "file": "license.bbcode" + }, + "tw-bootstrap-license": { + "title": "Bootstrap License", + "content_type": "bbcode", + "status": "published", + "file": "bs-license.bbcode" + }, + "tw-bbcode": { + "title": "BBCode used by TinyWiki", + "content_type": "bbcode", + "status": "published", + "file": "bbcode.bbcode" + }, + "tw-markdown": { + "title": "Markdown used by TinyWiki", + "content_type": "markdown", + "status": "draft", + "file": "markdown.md" + } } } \ No newline at end of file diff --git a/tinywiki/models.py b/tinywiki/models.py index 3cc20d6..ca163f1 100644 --- a/tinywiki/models.py +++ b/tinywiki/models.py @@ -60,7 +60,6 @@ class Page(models.Model): null=False, blank=False) - created_at = models.DateTimeField(_("created at"), auto_now_add=True) created_by = models.ForeignKey(get_user_model(), @@ -80,6 +79,7 @@ class Page(models.Model): @property def content_type(self)->WikiContentType: return WikiContentType.from_string(self.content_type_data) + @content_type.setter def content_type(self, content_type: str | WikiContentType): if isinstance(content_type, str): @@ -92,6 +92,7 @@ class Page(models.Model): @property def status(self)->WikiPageStatus: return WikiPageStatus.from_string(self.status_data) + @status.setter def status(self, status: str | WikiPageStatus): if isinstance(status, str): @@ -215,3 +216,19 @@ class SidebarEntry(models.Model): return mark_safe(f"