diff options
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | blog_admin/views.py | 19 | ||||
| -rw-r--r-- | ignis/admin.py | 6 | ||||
| -rw-r--r-- | ignis/migrations/0008_postimage_repositoryimages_repositorytitles_and_more.py | 50 | ||||
| -rw-r--r-- | ignis/migrations/0009_rename_repositorytitles_repositorytitle_and_more.py | 20 | ||||
| -rw-r--r-- | ignis/migrations/0010_alter_postimage_post.py | 20 | ||||
| -rw-r--r-- | ignis/migrations/0011_postimage_name.py | 18 | ||||
| -rw-r--r-- | ignis/models.py | 31 | ||||
| -rw-r--r-- | ignis/objectstorage.py | 36 | ||||
| -rw-r--r-- | ignis/urls.py | 3 | ||||
| -rw-r--r-- | ignis/views.py | 142 | ||||
| -rw-r--r-- | templates/blog_admin/edit_post.html | 12 | ||||
| -rw-r--r-- | templates/blog_admin/new_post.html | 12 |
13 files changed, 243 insertions, 128 deletions
@@ -131,5 +131,5 @@ dmypy.json # .DS_Store for macOS .DS_Store -genimages +images .DS_Store diff --git a/blog_admin/views.py b/blog_admin/views.py index 75b58507..35ab6489 100644 --- a/blog_admin/views.py +++ b/blog_admin/views.py @@ -4,7 +4,10 @@ from users.models import UserProfile from django.contrib.auth.models import User from django.contrib import messages from blog.models import Post, Category, Tag +from ignis.models import PostImage import re +import random +import string # Create your views here. @@ -52,6 +55,7 @@ def new_post(request): tags = request.POST.get('tags') slug = request.POST.get('slug') post_image = request.FILES['post_image'] if 'post_image' in request.FILES else None + random_post_identifier = request.POST.get('random_post_identifier') if title and body and category and tags and slug and post_image: try: category = Category.objects.get(slug = category) @@ -65,16 +69,25 @@ def new_post(request): post_image = base64.b64encode(post_image) post.post_image = "data:image/png;base64," + post_image.decode('utf-8') post.save() + PostImage.objects.filter(temp_post_id = random_post_identifier).update(post = post) + PostImage.objects.filter(temp_post_id = random_post_identifier).update(temp_post_id = None) + # replace all random_post_identifier with post.id in post.body + post.body = post.body.replace(random_post_identifier, str(post.id)) + post.save() messages.success(request, 'Post created successfully!') return redirect('blog-admin:posts') except Exception as e: messages.error(request, 'Error: {}'.format(e), extra_tags='new_post_create_error') - return render(request, 'blog_admin/new_post.html', { 'title': 'New Post', 'categories': categories, 'blog_title': title, 'blog_body': body, 'blog_category': category, 'blog_tags': tags, 'blog_slug': slug }) + return render(request, 'blog_admin/new_post.html', { 'title': 'New Post', 'categories': categories, 'blog_title': title, 'blog_body': body, 'blog_category': category, 'blog_tags': tags, 'blog_slug': slug, 'random_post_identifier': random_post_identifier }) else: messages.error(request, 'Error: All fields are required!', extra_tags='new_post_create_error') - return render(request, 'blog_admin/new_post.html', { 'title': 'New Post', 'categories': categories, 'blog_title': title, 'blog_body': body, 'blog_category': category, 'blog_tags': tags, 'blog_slug': slug }) + return render(request, 'blog_admin/new_post.html', { 'title': 'New Post', 'categories': categories, 'blog_title': title, 'blog_body': body, 'blog_category': category, 'blog_tags': tags, 'blog_slug': slug, 'random_post_identifier': random_post_identifier }) else: - return render(request, 'blog_admin/new_post.html', { 'title': 'New Post', 'categories': categories }) + # new random temorary post identifier - 8 digit alphanumeric string + + random_post_identifier = ''.join(random.choices(string.ascii_uppercase + string.digits, k=8)) + random_post_identifier = 'rpi_' + random_post_identifier + return render(request, 'blog_admin/new_post.html', { 'title': 'New Post', 'categories': categories, 'random_post_identifier': random_post_identifier }) else: return redirect('blog:home') diff --git a/ignis/admin.py b/ignis/admin.py index 0e97a0e9..eb188185 100644 --- a/ignis/admin.py +++ b/ignis/admin.py @@ -1,7 +1,7 @@ from django.contrib import admin # Register your models here. -from .models import Object, ObjectDirectory +from .models import PostImage, RepositoryTitle -admin.site.register(Object) -admin.site.register(ObjectDirectory)
\ No newline at end of file +admin.site.register(PostImage) +admin.site.register(RepositoryTitle) diff --git a/ignis/migrations/0008_postimage_repositoryimages_repositorytitles_and_more.py b/ignis/migrations/0008_postimage_repositoryimages_repositorytitles_and_more.py new file mode 100644 index 00000000..8cd1b668 --- /dev/null +++ b/ignis/migrations/0008_postimage_repositoryimages_repositorytitles_and_more.py @@ -0,0 +1,50 @@ +# Generated by Django 4.0.6 on 2022-11-22 15:13 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('blog', '0004_alter_post_post_image'), + ('ignis', '0007_alter_object_location'), + ] + + operations = [ + migrations.CreateModel( + name='PostImage', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('image', models.ImageField(upload_to='images//post_images')), + ('temp_post_id', models.CharField(default=None, max_length=12, null=True)), + ('post', models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='blog.post')), + ], + ), + migrations.CreateModel( + name='RepositoryImages', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('image', models.ImageField(upload_to='images//repository_images')), + ], + ), + migrations.CreateModel( + name='RepositoryTitles', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('repository', models.CharField(max_length=100)), + ('image', models.ImageField(upload_to='images//repository_titles')), + ], + ), + migrations.DeleteModel( + name='Object', + ), + migrations.DeleteModel( + name='ObjectDirectory', + ), + migrations.AddField( + model_name='repositoryimages', + name='repository', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='ignis.repositorytitles'), + ), + ] diff --git a/ignis/migrations/0009_rename_repositorytitles_repositorytitle_and_more.py b/ignis/migrations/0009_rename_repositorytitles_repositorytitle_and_more.py new file mode 100644 index 00000000..81087071 --- /dev/null +++ b/ignis/migrations/0009_rename_repositorytitles_repositorytitle_and_more.py @@ -0,0 +1,20 @@ +# Generated by Django 4.0.6 on 2022-11-22 15:17 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('ignis', '0008_postimage_repositoryimages_repositorytitles_and_more'), + ] + + operations = [ + migrations.RenameModel( + old_name='RepositoryTitles', + new_name='RepositoryTitle', + ), + migrations.DeleteModel( + name='RepositoryImages', + ), + ] diff --git a/ignis/migrations/0010_alter_postimage_post.py b/ignis/migrations/0010_alter_postimage_post.py new file mode 100644 index 00000000..3fda7708 --- /dev/null +++ b/ignis/migrations/0010_alter_postimage_post.py @@ -0,0 +1,20 @@ +# Generated by Django 4.0.6 on 2022-11-22 15:30 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('blog', '0004_alter_post_post_image'), + ('ignis', '0009_rename_repositorytitles_repositorytitle_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='postimage', + name='post', + field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='blog.post'), + ), + ] diff --git a/ignis/migrations/0011_postimage_name.py b/ignis/migrations/0011_postimage_name.py new file mode 100644 index 00000000..43be1a41 --- /dev/null +++ b/ignis/migrations/0011_postimage_name.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.6 on 2022-11-22 15:34 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ignis', '0010_alter_postimage_post'), + ] + + operations = [ + migrations.AddField( + model_name='postimage', + name='name', + field=models.CharField(default=None, max_length=100, null=True), + ), + ] diff --git a/ignis/models.py b/ignis/models.py index ac6d64d2..91809204 100644 --- a/ignis/models.py +++ b/ignis/models.py @@ -1,19 +1,26 @@ from django.db import models +from blog.models import Post +from dotenv import load_dotenv +import os -# 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.ForeignKey('ObjectDirectory', on_delete=models.CASCADE) +load_dotenv() +UPLOAD_ROOT = 'images/' if os.getenv('ENVIRONMENT') == 'development' else '/home/ubuntu/database/images/' - def __str__(self): - return self.md5 +# Only For Storing Images -class ObjectDirectory(models.Model): - name = models.CharField(max_length=255, null=True) +class PostImage(models.Model): + post = models.ForeignKey(Post, default=None, on_delete=models.CASCADE, null=True) + image = models.ImageField(upload_to="{}/post_images".format(UPLOAD_ROOT)) + temp_post_id = models.CharField(max_length=12, default=None, null=True) + name = models.CharField(max_length=100, default=None, null=True) def __str__(self): return self.name + +class RepositoryTitle(models.Model): + repository = models.CharField(max_length=100) + image = models.ImageField(upload_to="{}/repository_titles".format(UPLOAD_ROOT)) + + def __str__(self): + return self.repository + diff --git a/ignis/objectstorage.py b/ignis/objectstorage.py deleted file mode 100644 index 7e437750..00000000 --- a/ignis/objectstorage.py +++ /dev/null @@ -1,36 +0,0 @@ -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) - if not Object.objects.filter(md5=md5).exists(): - 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 de4ff05b..ea0e3b66 100644 --- a/ignis/urls.py +++ b/ignis/urls.py @@ -6,7 +6,6 @@ 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('/image/<post_id>/<image_name>', views.get_image, name='get_image'), path('/cover/<str:repository>', views.cover_image, name='cover_image'), - path('/su/mvdir', views.mvdir, name='mvdir'), ] diff --git a/ignis/views.py b/ignis/views.py index 9f539d05..ba0e5f81 100644 --- a/ignis/views.py +++ b/ignis/views.py @@ -4,11 +4,11 @@ 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 +from .models import PostImage, RepositoryTitle import json import requests +from django.core.files.base import ContentFile # from .github import get_cover # Create your views here. @@ -44,36 +44,64 @@ def post_image(request, post_id): 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 get_image(request, post_id, image_name): + if 'rpi_' in post_id: + # post has random post identifier => means it's a new post and not saved yet + # get image from temp_post_id + pi = PostImage.objects.filter(temp_post_id=post_id, name=image_name) + else: + # post has id => means it's an existing post + # get image from post_id + pi = PostImage.objects.filter(post=Post.objects.get(id=post_id), name=image_name) + if not pi: + return HttpResponse('No image found!', status=404) + + # open image and return + image = pi[0].image + with open(image.path, 'rb') as f: + return HttpResponse(f.read(), content_type='image/{}'.format(image.name.split('.')[-1])) @csrf_exempt def cover_image(request, repository): - url = 'https://socialify.git.ci/luciferreeves/{}/png?font=KoHo&language=1&name=1&pattern=Solid&theme=Dark'.format(repository) - - image = requests.get(url).content - - # reduce image size to 320x160 - image = Image.open(BytesIO(image)) - image = image.resize((320, 160), Image.ANTIALIAS) - - # remove black background - image = image.convert('RGBA').getdata() - new_data = [] - for item in image: - if item[0] == 0 and item[1] == 0 and item[2] == 0: - new_data.append((255, 255, 255, 0)) - else: - new_data.append(item) - - # Convert back to png and return - output = BytesIO() - image = Image.new('RGBA', (320, 160)) - image.putdata(new_data) - image.save(output, format='GIF') - return HttpResponse(output.getvalue(), content_type='image/gif') + force_reload = request.GET.get('force_reload') + # check if the image is in RepositoryTitles + try: + if force_reload: + raise Exception('Force reload') + repository_title = RepositoryTitle.objects.get(repository=repository) + image = repository_title.image + except: + # image is not in RepositoryTitles + # get image + url = 'https://socialify.git.ci/luciferreeves/{}/png?font=KoHo&language=1&name=1&pattern=Solid&theme=Dark'.format(repository) + image = requests.get(url).content + + # reduce image size to 320x160 + image = Image.open(BytesIO(image)) + image = image.resize((320, 160), Image.ANTIALIAS) + + # remove black background + image = image.convert('RGBA').getdata() + new_data = [] + for item in image: + if item[0] == 0 and item[1] == 0 and item[2] == 0: + new_data.append((255, 255, 255, 0)) + else: + new_data.append(item) + + # Convert back to png and return + output = BytesIO() + image = Image.new('RGBA', (320, 160)) + image.putdata(new_data) + image.save(output, format='GIF') + image = output.getvalue() + + # save image to RepositoryTitles + image = ContentFile(image, name='{}.png'.format(repository)) + repository_title = RepositoryTitle(repository=repository, image=image) + repository_title.save() + + return HttpResponse(image, content_type='image/gif') def upload_image(request): @@ -82,48 +110,24 @@ def upload_image(request): 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) + if not request.POST.get('id'): + return HttpResponse('No id provided!', status=400) - # upload image to object storage + # upload image to PostImage model 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 + post_id = request.POST['id'] + if 'rpi_' in post_id: + # post has random post identifier => means it's a new post and not saved yet + # save image to temp_post_id + pi = PostImage(image=image, temp_post_id=post_id, post=None, name=image.name) + else: + # post has id => means it's an existing post + # save image to post_id + pi = PostImage(image=image, post=Post.objects.get(id=post_id), name=image.name) + pi.save() response = { - 'url': "/ignis/image/{}/{}".format(slug, image_hash) + 'url': '/ignis/image/{}/{}'.format(post_id, pi.name) } - - 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) + return HttpResponse(json.dumps(response), content_type='application/json') + return HttpResponse('Method not allowed', status=405) - 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/templates/blog_admin/edit_post.html b/templates/blog_admin/edit_post.html index b50848e3..5d113d9f 100644 --- a/templates/blog_admin/edit_post.html +++ b/templates/blog_admin/edit_post.html @@ -45,6 +45,16 @@ value="{{ blog_slug }}" /> </div> + <div class="form-group"> + <input + style="display: inline-block" + type="hidden" + class="form-control" + id="post_id" + name="post_id" + value="{{ post.id }}" + /> + </div> <br> <div class="form-group"> <label for="content">Post Image</label> @@ -245,7 +255,7 @@ const url = "/ignis/upload"; const formData = new FormData(); formData.append("image", fileInput.files[0]); - formData.append("slug", document.getElementById("slug").value); + formData.append("id", document.getElementById("post_id").value); // append csrf token in header const headers = new Headers(); headers.append("X-CSRFToken", "{{ csrf_token }}"); diff --git a/templates/blog_admin/new_post.html b/templates/blog_admin/new_post.html index 7972cc87..ce4b9485 100644 --- a/templates/blog_admin/new_post.html +++ b/templates/blog_admin/new_post.html @@ -39,6 +39,16 @@ value="{{ blog_slug }}" /> </div> + <div class="form-group"> + <input + style="display: inline-block" + type="hidden" + class="form-control" + id="random_post_identifier" + name="random_post_identifier" + value="{{ random_post_identifier }}" + /> + </div> <br> <div class="form-group"> <label for="content">Post Image</label> @@ -238,7 +248,7 @@ const url = "/ignis/upload"; const formData = new FormData(); formData.append("image", fileInput.files[0]); - formData.append("slug", document.getElementById("slug").value); + formData.append("id", document.getElementById("random_post_identifier").value); // append csrf token in header const headers = new Headers(); headers.append("X-CSRFToken", "{{ csrf_token }}"); |
