diff --git a/khaikang/models.py b/khaikang/models.py index b5c6482..e6208db 100644 --- a/khaikang/models.py +++ b/khaikang/models.py @@ -6,6 +6,8 @@ import hashlib + + class User(AbstractUser): """customized user field.""" username = models.CharField(max_length=30, unique=True) # max to 30 char @@ -19,7 +21,7 @@ class User(AbstractUser): def upload_path(instance, orig_filename): avatar_filename = hashlib.sha256(orig_filename.encode('utf-8')).hexdigest()[:10] - return f"img/profile/user_{instance.id}/{avatar_filename}" + return f"img/profile/user_{instance.id}/{orig_filename}" avatar = models.ImageField(upload_to=upload_path) diff --git a/khaikang/settings.py b/khaikang/settings.py index 91059c6..9314a6c 100644 --- a/khaikang/settings.py +++ b/khaikang/settings.py @@ -119,7 +119,7 @@ USE_TZ = True STATIC_URL = 'static/' STATICFILES_DIRS= [os.path.join(BASE_DIR, 'static')] # test only -# STATIC_ROOT = '/home/kiantin1/public_html/khaikang/static/' deploy only +STATIC_ROOT = '/home/kiantin1/public_html/khaikang/static/' # Default primary key field type # https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field diff --git a/khaikang/urls.py b/khaikang/urls.py index c6af8b1..9077ca8 100644 --- a/khaikang/urls.py +++ b/khaikang/urls.py @@ -19,6 +19,7 @@ from django.urls import include from . import views from . import settings from django.conf.urls.static import static +from django.contrib.staticfiles.urls import staticfiles_urlpatterns urlpatterns = [ path('admin/', admin.site.urls), @@ -30,6 +31,7 @@ urlpatterns = [ path('api/get_recent_posts_counter', views.api_get_recent_posts_counter), path('api/get_latest_posts', views.api_get_latest_posts), path('api/get_previous_posts', views.api_get_previous_posts), + path('user/', views.user_timeline), ] if settings.DEBUG: diff --git a/khaikang/views.py b/khaikang/views.py index 2d284de..794be06 100644 --- a/khaikang/views.py +++ b/khaikang/views.py @@ -1,7 +1,7 @@ import json from django.http import HttpResponse from django.template import loader -from .models import User, Post +from .models import User, Post, Following from django.utils import timezone from django.contrib.auth.forms import UserCreationForm from django.shortcuts import redirect @@ -13,7 +13,10 @@ from django.db.models import Q import string import random from django import forms - +from django.utils.safestring import mark_safe +from string import Template +from django.forms import ImageField +import os def api_get_previous_posts(request): if request.method == 'POST': @@ -94,6 +97,7 @@ def signup(request): def save(self, commit=True): user = super(KhaikangUserCreationForm, self).save(commit=False) user.shown_name = self.cleaned_data["username"] + user.avatar = "static/default_avatar.png" if commit: user.save() return user @@ -114,7 +118,6 @@ def signup(request): if form.is_valid(): form.fields['shown_name'] = form.fields['username'] - print(form.fields['shown_name']) form.save() return redirect('/account/login') #redirect to login @@ -126,33 +129,117 @@ def signup(request): template = loader.get_template('signup.html') return HttpResponse(template.render({'form': form, 'honeypot_name': honeypot_name}, request)) + + + def user_config(request): + from PIL import Image + def erase_exif(image_url): + original_image = Image.open(image_url) + + image_data = list(original_image.getdata()) + image_without_exif = Image.new(original_image.mode, original_image.size) + image_without_exif.putdata(image_data) + + image_without_exif.save(image_url) + + """crop image to square""" + def crop_image(image_path): + img = Image.open(image_path) + width, height = img.size + left = 0 + top = 0 + right = img.width + bottom = img.height + if width > height: + left = (width - height) / 2 + right = (width + height) / 2 + elif width < height: + top = (height - width) / 2 + bottom = (width + height) / 2 + else: + pass + + img = img.crop((left, top, right, bottom)) + img.save(image_path) + + + + class UserConfigForm(forms.ModelForm): class Meta: + model = User - fields = ('shown_name', 'avatar', 'desc', 'email') + fields = ('shown_name', 'avatar', 'desc', 'email') - - current_user = User.objects.get(id=request.user.id) + + + if request.user == AnonymousUser(): + return redirect('/account/login') #redirect to login + + if request.method == "POST": + current_user = User.objects.get(id=request.user.id) + old_image_path = os.path.abspath(f"./media/{current_user.avatar.name}") form = UserConfigForm(request.POST,request.FILES, instance=current_user) - - + if form.is_valid(): + print(old_image_path) + + if os.path.exists(old_image_path) and (old_image_path != os.path.abspath("./media/static/default_avatar.png")): + a = os.remove(old_image_path) + print(a) + form.save() + image_path = os.path.abspath(f"./media/{current_user.avatar.name}") + erase_exif(image_path) + crop_image(image_path) + + + # + #current_user_avatar = request.user.avatar + #print(current_user_avatar) + #erase_exif(current_user_avatar) + else: pass + current_user = User.objects.get(id=request.user.id) form = UserConfigForm(initial={'shown_name': request.user.shown_name, 'desc' : request.user.desc, 'url' : request.user.url, 'email' : request.user.email}) template = loader.get_template('user_config.html') - return HttpResponse(template.render({'form': form}, request)) + return HttpResponse(template.render({'form': form, 'avatar': current_user.avatar}, request)) + +def user_timeline(request, username): + + viewed_user = User.objects.get(username=username) + if Following.objects.filter(follower = request.user.id).filter(followee=viewed_user.id) or request.user.id == viewed_user.id: + viewed_timeline_list = Post.objects.filter(poster = viewed_user.id).order_by('-id')[:20] + else: + viewed_timeline_list = Post.objects.filter(poster = viewed_user.id).filter(privilage = 'public').order_by('-id')[:20] + + latest_received_time = timezone.now() + if len(viewed_timeline_list) > 0: + oldest_received_time = viewed_timeline_list[-1].post_time + else: + oldest_received_time = datetime.strptime("1970/01/01 00:00", "%Y/%m/%d %H:%M") + + template = loader.get_template('user_timeline.html') + + context = { + 'username' : username, + 'user_shown_name' : viewed_user.shown_name, + 'latest_received_time' : latest_received_time, + 'oldest_received_time' : oldest_received_time, + 'public_timeline_list': viewed_timeline_list, + } + return HttpResponse(template.render(context, request)) def home(request): @@ -162,7 +249,11 @@ def home(request): public_timeline_list = Post.objects.filter(privilage = 'public').order_by('-id')[:20] latest_received_time = timezone.now() - oldest_received_time = public_timeline_list[19].post_time + + if len(public_timeline_list) > 0: + oldest_received_time = public_timeline_list[-1].post_time + else: + oldest_received_time = datetime.strptime("1970/01/01 00:00", "%Y/%m/%d %H:%M") template = loader.get_template('index.html') diff --git a/media/static/default_avatar.png b/media/static/default_avatar.png new file mode 100644 index 0000000..25d9f2f Binary files /dev/null and b/media/static/default_avatar.png differ diff --git a/media/static/default_avatar.svg b/media/static/default_avatar.svg new file mode 100644 index 0000000..3a3b3b4 --- /dev/null +++ b/media/static/default_avatar.svg @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/generic.css b/static/generic.css new file mode 100644 index 0000000..4ea3221 --- /dev/null +++ b/static/generic.css @@ -0,0 +1,9 @@ +div.avatar-preview{ + +} + +#avatar-img{ + width: 200px; + height: 200px; + object-fit: cover; +} \ No newline at end of file diff --git a/static/timeline.js b/static/timeline.js index af197b2..3a2acb4 100644 --- a/static/timeline.js +++ b/static/timeline.js @@ -17,8 +17,13 @@ x => timezoneChangingOne(x) + $name = (x) => document.getElementsByName(x); $ = (x) => document.getElementById(x); + +csrf_token = $("_token").content; + + var httpRequest; $("submit_post").addEventListener('click', make_req); @@ -27,12 +32,14 @@ async function make_req() { post_text = $('post_text').value; post_privilage = $('privil_choosing').value; + + if (post_text != ""){ await fetch('/api/post', { method: 'POST', headers: { -"X-CSRFToken": "{{ csrf_token }}", +"X-CSRFToken": csrf_token, 'Content-Type': 'application/json', }, body: JSON.stringify({ @@ -71,7 +78,7 @@ latest_time_string = $("latest_time").innerHTML; await fetch('/api/get_recent_posts_counter', { method: 'POST', headers: { -"X-CSRFToken": "{{ csrf_token }}", +"X-CSRFToken": csrf_token, 'Content-Type': 'application/json', }, body: JSON.stringify({ @@ -105,7 +112,7 @@ oldest_time_string = $("oldest_time").innerHTML; await fetch('/api/get_previous_posts', { method: 'POST', headers: { -"X-CSRFToken": "{{ csrf_token }}", +"X-CSRFToken": csrf_token, 'Content-Type': 'application/json', }, body: JSON.stringify({ @@ -126,7 +133,7 @@ latest_time_string = $("latest_time").innerHTML; await fetch('/api/get_latest_posts', { method: 'POST', headers: { -"X-CSRFToken": "{{ csrf_token }}", +"X-CSRFToken": csrf_token, 'Content-Type': 'application/json', }, body: JSON.stringify({ diff --git a/static/user_config.js b/static/user_config.js new file mode 100644 index 0000000..3ad95d3 --- /dev/null +++ b/static/user_config.js @@ -0,0 +1,21 @@ +$name = (x) => document.getElementsByName(x); +$ = (x) => document.getElementById(x); + +avatarSelector = $name('avatar')[0]; + +avatarSelector.addEventListener('change', updateAvatarPreview); + + +function updateAvatarPreview(event) { + tmpFilename = avatarSelector.files[0]; + if (tmpFilename) { + + fileSizeMaxBound = 4 * (1024 ** 2); // in MB + if (tmpFilename.size > fileSizeMaxBound){ + alert(`The file size is ${tmpFilename.size / (1024 ** 2)} MB, exceeding ${fileSizeMaxBound / (1024 ** 2)}MB`); + avatarSelector.value = ""; + }else{ + $('avatar-img').src = URL.createObjectURL(tmpFilename); + } + } +} \ No newline at end of file diff --git a/templates/base_generic.html b/templates/base_generic.html index 42fb8c4..3e233fa 100644 --- a/templates/base_generic.html +++ b/templates/base_generic.html @@ -1,8 +1,13 @@ +{% load static %} + {% block meta%} + {% endblock %} - {% block title %}Local Library{% endblock %} + {% block title %}Local Library + + {% endblock %} diff --git a/templates/index.html b/templates/index.html index 0497381..9488faf 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,6 +1,9 @@ {% extends "base_generic.html" %} {% load static %} {% load tz %} +{% block meta %} + +{% endblock %} {% get_current_timezone as TIME_ZONE %} {% block headbar %} diff --git a/templates/signup.html b/templates/signup.html index 7c0caad..9b85edc 100644 --- a/templates/signup.html +++ b/templates/signup.html @@ -5,13 +5,13 @@
{% csrf_token %} {{ form.as_p }} - Leave it blank:
+ Leave it blank:
{% endblock %} \ No newline at end of file diff --git a/templates/user_config.html b/templates/user_config.html index cccd6ab..f7d72d4 100644 --- a/templates/user_config.html +++ b/templates/user_config.html @@ -1,10 +1,19 @@ {% extends "base_generic.html" %} +{% load static %} + {% block content %}
{% csrf_token %} -{{ form.as_p }} +

{{ form.shown_name.label}}: {{ form.shown_name}}

+

Avatar Preview:

+

{{ form.avatar.label}}: {{ form.avatar}}

+

{{ form.desc.label}}: {{ form.desc}}

+

{{ form.email.label}}: {{ form.email}}

+ + {% endblock %} \ No newline at end of file diff --git a/templates/user_timeline.html b/templates/user_timeline.html new file mode 100644 index 0000000..9c0d11c --- /dev/null +++ b/templates/user_timeline.html @@ -0,0 +1,62 @@ +{% extends "base_generic.html" %} +{% load static %} +{% load tz %} +{% block meta %} + +{% endblock %} + +{% get_current_timezone as TIME_ZONE %} +{% block headbar %} +{{ request.user.shown_name }} (My timeline) - Configs - Log out +{% endblock %} + +{% block content %} +
+ {% csrf_token %} +
+ + + +
+ + {% csrf_token %} + + + + + + +
+
{{user_shown_name}}
{{username}}
+
+
+
{{latest_received_time|date:"Y-m-d H:i:s.u"}}+0000
+ + {% for post in public_timeline_list %} + +
{{public_post.poster.shown_name}} + at {{public_post.post_time|date:"Y-m-d H:i:s.u"}}+0000
+ {{public_post.text|linebreaksbr}}
+ ↩️ + - 🔁 + - +
+ {% endfor %} + +
{{oldest_received_time|date:"Y-m-d H:i:s.u"}}+0000
+ +
+ +
 % static 'timeline.js' 要修改
+    
+ +{% endblock %} \ No newline at end of file