diff options
| -rw-r--r-- | blog_admin/views.py | 6 | ||||
| -rw-r--r-- | ignis/admin.py | 4 | ||||
| -rw-r--r-- | ignis/migrations/0001_initial.py | 33 | ||||
| -rw-r--r-- | ignis/migrations/0002_alter_object_data.py | 18 | ||||
| -rw-r--r-- | ignis/migrations/0003_remove_objectdirectory_name_objectdirectory_slug.py | 25 | ||||
| -rw-r--r-- | ignis/migrations/0004_remove_objectdirectory_slug_objectdirectory_name.py | 22 | ||||
| -rw-r--r-- | ignis/migrations/0005_remove_objectdirectory_name_and_more.py | 22 | ||||
| -rw-r--r-- | ignis/migrations/0006_rename_post_slug_objectdirectory_name.py | 18 | ||||
| -rw-r--r-- | ignis/models.py | 19 | ||||
| -rw-r--r-- | ignis/objectstorage.py | 35 | ||||
| -rw-r--r-- | ignis/urls.py | 3 | ||||
| -rw-r--r-- | ignis/views.py | 63 | ||||
| -rw-r--r-- | static/images/site/repositories.png | bin | 0 -> 139288 bytes | |||
| -rw-r--r-- | templates/blog/post.html | 8 | ||||
| -rw-r--r-- | templates/blog_admin/edit_post.html | 53 | ||||
| -rw-r--r-- | templates/blog_admin/new_post.html | 45 | ||||
| -rw-r--r-- | templates/dev_status/home.html | 23 |
17 files changed, 371 insertions, 26 deletions
diff --git a/blog_admin/views.py b/blog_admin/views.py index ab087e62..75b58507 100644 --- a/blog_admin/views.py +++ b/blog_admin/views.py @@ -5,8 +5,6 @@ from django.contrib.auth.models import User from django.contrib import messages from blog.models import Post, Category, Tag import re -from io import BytesIO -from PIL import Image # Create your views here. @@ -48,6 +46,8 @@ def new_post(request): title = request.POST.get('title') body = request.POST.get('body') body = re.sub(r'<p><br></p>', '', body) + body = re.sub(r'<p class="ql-align-justify"><br></p>', '', body) + body = re.sub(r'<p class="ql-align-center"><br></p>', '', body) category = request.POST.get('category') tags = request.POST.get('tags') slug = request.POST.get('slug') @@ -86,6 +86,8 @@ def edit_post(request, slug): title = request.POST.get('title') body = request.POST.get('body') body = re.sub(r'<p><br></p>', '', body) + body = re.sub(r'<p class="ql-align-justify"><br></p>', '', body) + body = re.sub(r'<p class="ql-align-center"><br></p>', '', body) category = request.POST.get('category') tags = request.POST.get('tags') slug = request.POST.get('slug') diff --git a/ignis/admin.py b/ignis/admin.py index 8c38f3f3..0e97a0e9 100644 --- a/ignis/admin.py +++ b/ignis/admin.py @@ -1,3 +1,7 @@ from django.contrib import admin # Register your models here. +from .models import Object, ObjectDirectory + +admin.site.register(Object) +admin.site.register(ObjectDirectory)
\ No newline at end of file diff --git a/ignis/migrations/0001_initial.py b/ignis/migrations/0001_initial.py new file mode 100644 index 00000000..1cf08fda --- /dev/null +++ b/ignis/migrations/0001_initial.py @@ -0,0 +1,33 @@ +# Generated by Django 4.0.6 on 2022-11-22 06:31 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='ObjectDirectory', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255)), + ], + ), + migrations.CreateModel( + name='Object', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('md5', models.CharField(max_length=32)), + ('metadata', models.CharField(max_length=255)), + ('data', models.BinaryField()), + ('created', models.DateTimeField(auto_now_add=True)), + ('location', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='ignis.objectdirectory')), + ], + ), + ] diff --git a/ignis/migrations/0002_alter_object_data.py b/ignis/migrations/0002_alter_object_data.py new file mode 100644 index 00000000..e4685b4c --- /dev/null +++ b/ignis/migrations/0002_alter_object_data.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.6 on 2022-11-22 06:35 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ignis', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='object', + name='data', + field=models.TextField(), + ), + ] diff --git a/ignis/migrations/0003_remove_objectdirectory_name_objectdirectory_slug.py b/ignis/migrations/0003_remove_objectdirectory_name_objectdirectory_slug.py new file mode 100644 index 00000000..72fea363 --- /dev/null +++ b/ignis/migrations/0003_remove_objectdirectory_name_objectdirectory_slug.py @@ -0,0 +1,25 @@ +# Generated by Django 4.0.6 on 2022-11-22 06:40 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('blog', '0004_alter_post_post_image'), + ('ignis', '0002_alter_object_data'), + ] + + operations = [ + migrations.RemoveField( + model_name='objectdirectory', + name='name', + ), + migrations.AddField( + model_name='objectdirectory', + name='slug', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.post'), + preserve_default=False, + ), + ] diff --git a/ignis/migrations/0004_remove_objectdirectory_slug_objectdirectory_name.py b/ignis/migrations/0004_remove_objectdirectory_slug_objectdirectory_name.py new file mode 100644 index 00000000..e8a01d37 --- /dev/null +++ b/ignis/migrations/0004_remove_objectdirectory_slug_objectdirectory_name.py @@ -0,0 +1,22 @@ +# Generated by Django 4.0.6 on 2022-11-22 06:41 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ignis', '0003_remove_objectdirectory_name_objectdirectory_slug'), + ] + + operations = [ + migrations.RemoveField( + model_name='objectdirectory', + name='slug', + ), + migrations.AddField( + model_name='objectdirectory', + name='name', + field=models.CharField(default='root', max_length=255), + ), + ] diff --git a/ignis/migrations/0005_remove_objectdirectory_name_and_more.py b/ignis/migrations/0005_remove_objectdirectory_name_and_more.py new file mode 100644 index 00000000..d2193140 --- /dev/null +++ b/ignis/migrations/0005_remove_objectdirectory_name_and_more.py @@ -0,0 +1,22 @@ +# Generated by Django 4.0.6 on 2022-11-22 06:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ignis', '0004_remove_objectdirectory_slug_objectdirectory_name'), + ] + + operations = [ + migrations.RemoveField( + model_name='objectdirectory', + name='name', + ), + migrations.AddField( + model_name='objectdirectory', + name='post_slug', + field=models.CharField(max_length=255, null=True), + ), + ] diff --git a/ignis/migrations/0006_rename_post_slug_objectdirectory_name.py b/ignis/migrations/0006_rename_post_slug_objectdirectory_name.py new file mode 100644 index 00000000..7781a552 --- /dev/null +++ b/ignis/migrations/0006_rename_post_slug_objectdirectory_name.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.6 on 2022-11-22 07:30 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('ignis', '0005_remove_objectdirectory_name_and_more'), + ] + + operations = [ + migrations.RenameField( + model_name='objectdirectory', + old_name='post_slug', + new_name='name', + ), + ] diff --git a/ignis/models.py b/ignis/models.py index 71a83623..9c99de5d 100644 --- a/ignis/models.py +++ b/ignis/models.py @@ -1,3 +1,22 @@ from django.db import models +from blog.models import Post # Create your models here. +class Object(models.Model): + md5 = models.CharField(max_length=32) + metadata = models.CharField(max_length=255) + data = models.TextField() + created = models.DateTimeField(auto_now_add=True) + location = models.OneToOneField( + 'ObjectDirectory', + on_delete=models.CASCADE, + ) + + def __str__(self): + return self.md5 + +class ObjectDirectory(models.Model): + name = models.CharField(max_length=255, null=True) + + def __str__(self): + return self.name diff --git a/ignis/objectstorage.py b/ignis/objectstorage.py new file mode 100644 index 00000000..84d3a5c0 --- /dev/null +++ b/ignis/objectstorage.py @@ -0,0 +1,35 @@ +from .models import Object, ObjectDirectory + +class ObjectStorage: + def create_directory(self, name): + if not ObjectDirectory.objects.filter(name=name).exists(): + ObjectDirectory.objects.create(name=name) + + def rename_directory(self, old_name, new_name): + if not ObjectDirectory.objects.filter(name=old_name).exists(): + ObjectDirectory.objects.create(name=old_name) + ObjectDirectory.objects.filter(name=old_name).update(name=new_name) + + def delete_directory(self, name): + Object.objects.filter(location=ObjectDirectory.objects.get(name=name)).delete() + ObjectDirectory.objects.filter(name=name).delete() + + def create_object(self, md5, metadata, data, name): + if not ObjectDirectory.objects.filter(name=name).exists(): + ObjectDirectory.objects.create(name=name) + Object.objects.create(md5=md5, metadata=metadata, data=data, location=ObjectDirectory.objects.get(name=name)) + + def delete_object(self, slug, md5): + Object.objects.filter(location=ObjectDirectory.objects.get(name=slug), md5=md5).delete() + + def get_object(self, slug, md5): + return Object.objects.get(location=ObjectDirectory.objects.get(name=slug), md5=md5) + + def object_exists(self, slug, md5): + return Object.objects.filter(location=ObjectDirectory.objects.get(name=slug), md5=md5).exists() + + def get_directory_contents(self, name): + return Object.objects.filter(location=ObjectDirectory.objects.get(name=name)) + + def get_directories(self): + return ObjectDirectory.objects.all() diff --git a/ignis/urls.py b/ignis/urls.py index b3b1eace..c35845d0 100644 --- a/ignis/urls.py +++ b/ignis/urls.py @@ -5,4 +5,7 @@ app_name = 'ignis' urlpatterns = [ path('/tex', views.tex, name='tex'), path('/post_image/<int:post_id>/', views.post_image, name='post_image'), + path('/upload', views.upload_image, name='upload_image'), + path('/image/<str:slug>/<str:md5>', views.get_image, name='get_image'), + path('/su/mvdir', views.mvdir, name='mvdir'), ] diff --git a/ignis/views.py b/ignis/views.py index ffbce848..d904217c 100644 --- a/ignis/views.py +++ b/ignis/views.py @@ -1,10 +1,13 @@ -from django.shortcuts import render from PIL import Image from io import BytesIO from django.http import HttpResponse from django.views.decorators.csrf import csrf_exempt import base64 from blog.models import Post +from .objectstorage import ObjectStorage +import base64 +import _md5 +import json # Create your views here. @csrf_exempt @@ -37,3 +40,61 @@ def post_image(request, post_id): # convert base64 data src to image image = base64.b64decode(pi.split(',')[1]) return HttpResponse(image, content_type='image/png') + +@csrf_exempt +def get_image(request, slug, md5): + object_storage = ObjectStorage() + image = object_storage.get_object(slug, md5) + return HttpResponse(base64.b64decode(image.data), content_type=image.metadata) + +def upload_image(request): + if request.method == 'POST': + if not request.user.is_authenticated and not request.user.is_staff: + return HttpResponse('Unauthorized', status=401) + if not request.FILES.get('image'): + return HttpResponse('No image provided!', status=400) + if not request.POST.get('slug'): + return HttpResponse('No slug provided!', status=400) + + # upload image to object storage + image = request.FILES['image'] + slug = request.POST.get('slug') + object_storage = ObjectStorage() + object_storage.create_directory(slug) + + + image_data = image.read() + metadata = image.content_type + + image_hash = _md5.md5(image_data).hexdigest() + data = base64.b64encode(image_data).decode('utf-8') + + if not object_storage.object_exists(slug, image_hash): + object_storage.create_object(md5=image_hash, metadata=metadata, data=data, name=slug) + + # return json response + response = { + 'url': "/ignis/image/{}/{}".format(slug, image_hash) + } + + return HttpResponse(json.dumps(response), content_type='application/json', status=200) + + +def mvdir(request): + if not request.user.is_authenticated and not request.user.is_staff: + return HttpResponse('Unauthorized', status=401) + object_storage = ObjectStorage() + + # get from query params + old_name = request.GET.get('old') + new_name = request.GET.get('new') + + if not old_name or not new_name: + return HttpResponse('No name provided!', status=400) + + if old_name == "": + object_storage.create_directory(new_name) + return HttpResponse(json.dumps({'status': 'success'}), content_type='application/json', status=200) + else: + object_storage.rename_directory(old_name, new_name) + return HttpResponse(json.dumps({'status': 'success'}), content_type='application/json', status=200) diff --git a/static/images/site/repositories.png b/static/images/site/repositories.png Binary files differnew file mode 100644 index 00000000..b8162a03 --- /dev/null +++ b/static/images/site/repositories.png diff --git a/templates/blog/post.html b/templates/blog/post.html index edc7d32a..b4055ac4 100644 --- a/templates/blog/post.html +++ b/templates/blog/post.html @@ -1,9 +1,9 @@ {% extends 'blog/partials/base.html' %} {% block content %} -<link + <link rel="stylesheet" - href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/monokai-sublime.min.css" + href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/monokai-sublime.min.css" /> -<link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet" /> +{% comment %}<link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet" /> {% endcomment %} <div class="main"> <article> {% load subdomain %} @@ -72,7 +72,7 @@ {% endif %} </div> </article> - <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script> + {% comment %} <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script> {% endcomment %} <script> function editComment(id) { document.getElementById('comment-body-' + id).style.display = 'none'; diff --git a/templates/blog_admin/edit_post.html b/templates/blog_admin/edit_post.html index a6191e7c..b50848e3 100644 --- a/templates/blog_admin/edit_post.html +++ b/templates/blog_admin/edit_post.html @@ -32,12 +32,12 @@ value="{{ blog_title }}" /> </div> - <br> + {% comment %} <br> {% endcomment %} <div class="form-group"> - <label for="slug">Slug</label> + {% comment %} <label for="slug">Slug</label> {% endcomment %} <input style="display: inline-block" - type="text" + type="hidden" class="form-control" id="slug" name="slug" @@ -138,7 +138,6 @@ </section> </div> -<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-mml-svg.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script> <script src="https://cdn.quilljs.com/1.3.6/quill.min.js"></script> <script> @@ -232,6 +231,52 @@ "formula-block": function () { insertFormula(quill, true); }, + image: function () { + // open image upload dialog + let fileInput = this.container.querySelector('input.ql-image[type=file]'); + if (fileInput == null) { + fileInput = document.createElement('input'); + fileInput.setAttribute('type', 'file'); + fileInput.setAttribute('accept', 'image/png, image/gif, image/jpeg, image/bmp, image/x-icon'); + fileInput.classList.add('ql-image'); + fileInput.addEventListener('change', () => { + if (fileInput.files != null && fileInput.files[0] != null) { + let range = this.quill.getSelection(true); + const url = "/ignis/upload"; + const formData = new FormData(); + formData.append("image", fileInput.files[0]); + formData.append("slug", document.getElementById("slug").value); + // append csrf token in header + const headers = new Headers(); + headers.append("X-CSRFToken", "{{ csrf_token }}"); + + fetch(url, { + method: "POST", + headers: headers, + body: formData, + }).then((response) => response.json()).then((result) => { + this.quill.updateContents(new Delta() + .retain(range.index) + .delete(range.length) + .insert({ image: result.url }) + , Quill.sources.USER); + fileInput.value = ""; + }).catch((error) => { + console.error("Error:", error); + }); + + /* let reader = new FileReader(); + reader.onload = (e) => { + // this.quill.insertEmbed(range.index, "image", result.url); + // this.quill.setSelection(range.index + 1); + } + reader.readAsDataURL(fileInput.files[0]); */ + } + }); + this.container.appendChild(fileInput); + } + fileInput.click(); + }, }, }, }, diff --git a/templates/blog_admin/new_post.html b/templates/blog_admin/new_post.html index ecff5f82..7972cc87 100644 --- a/templates/blog_admin/new_post.html +++ b/templates/blog_admin/new_post.html @@ -26,12 +26,12 @@ value="{{ blog_title }}" /> </div> - <br> + {% comment %} <br> {% endcomment %} <div class="form-group"> - <label for="slug">Slug</label> + {% comment %} <label for="slug">Slug</label> {% endcomment %} <input style="display: inline-block" - type="text" + type="hidden" class="form-control" id="slug" name="slug" @@ -224,6 +224,45 @@ "formula-block": function () { insertFormula(quill, true); }, + image: function () { + // open image upload dialog + let fileInput = this.container.querySelector('input.ql-image[type=file]'); + if (fileInput == null) { + fileInput = document.createElement('input'); + fileInput.setAttribute('type', 'file'); + fileInput.setAttribute('accept', 'image/png, image/gif, image/jpeg, image/bmp, image/x-icon'); + fileInput.classList.add('ql-image'); + fileInput.addEventListener('change', () => { + if (fileInput.files != null && fileInput.files[0] != null) { + let range = this.quill.getSelection(true); + const url = "/ignis/upload"; + const formData = new FormData(); + formData.append("image", fileInput.files[0]); + formData.append("slug", document.getElementById("slug").value); + // append csrf token in header + const headers = new Headers(); + headers.append("X-CSRFToken", "{{ csrf_token }}"); + + fetch(url, { + method: "POST", + headers: headers, + body: formData, + }).then((response) => response.json()).then((result) => { + this.quill.updateContents(new Delta() + .retain(range.index) + .delete(range.length) + .insert({ image: result.url }) + , Quill.sources.USER); + fileInput.value = ""; + }).catch((error) => { + console.error("Error:", error); + }); + } + }); + this.container.appendChild(fileInput); + } + fileInput.click(); + }, }, }, }, diff --git a/templates/dev_status/home.html b/templates/dev_status/home.html index 770aad59..962d85ad 100644 --- a/templates/dev_status/home.html +++ b/templates/dev_status/home.html @@ -1,17 +1,17 @@ {% extends 'blog/partials/base.html' %} {% block content %} <div class="main"> <div class="area"> - <p class="titlebar">Repositories (<em>Showing {{ repo_length }} repositories</em>) </p> - <div class="area-content area-bg"> - {% if not user.is_authenticated %} - <p style="margin: 0 auto 10px auto;"> - You are not logged in. You can only view public repositories. Please login or register - using the sidebar on the left to view both public and private repositories. - </p> - {% endif %} - + {% load static %} + {% load times %} + {% if not user.is_authenticated %} + <p style="padding: 15px 10px;" class="alert"> + You are not logged in. You can only view public repositories. Please login or register + using the sidebar on the left to view both public and private repositories. + </p> + {% endif %} + <div style="background-image: url({% static 'images/site/repositories.png' %}); width: 720px; height: 173px; background-repeat: no-repeat; background-position: center;"> {% comment %} Search and Filter {% endcomment %} - <table style="margin: 0 auto;"> + <table style="margin: 0 auto; position: relative; top: 125px;"> <form action="" method="get"> <tr> <td> @@ -63,8 +63,7 @@ </div> <hr> <div class='repositories'> - {% load static %} - {% load times %} + <table style="width: 720px; table-layout: fixed; border-spacing: 12px; border-collapse: separate;"> {% for repo in repos %} <tr style="vertical-align: middle;"> |
