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 @@
{% 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 %}
+{{ 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 %}
+
+{{user_shown_name}}
{{username}}
+
+
+
{{latest_received_time|date:"Y-m-d H:i:s.u"}}+0000
+
+ {% for post in public_timeline_list %}
+
+
+ {% endfor %}
+
More posts
+
{{oldest_received_time|date:"Y-m-d H:i:s.u"}}+0000
+
+
+
+ % static 'timeline.js' 要修改
+
+
+{% endblock %}
\ No newline at end of file