add upload image's cropping, and updating. create userpage

This commit is contained in:
Tan, Kian-ting 2022-11-24 00:24:57 +08:00
parent 0216bf49cb
commit cb71ba8911
14 changed files with 328 additions and 20 deletions

View file

@ -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)

View file

@ -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

View file

@ -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/<username>', views.user_timeline),
]
if settings.DEBUG:

View file

@ -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')

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View file

@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="512"
height="512"
viewBox="0 0 512 512"
version="1.1"
id="svg5"
inkscape:version="1.2.1 (1:1.2.1+202210291244+9c6d41e410)"
sodipodi:docname="default_logo.svg"
inkscape:export-filename="media/default_logo.png"
inkscape:export-xdpi="114.81297"
inkscape:export-ydpi="114.81297"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:document-units="px"
showgrid="false"
inkscape:zoom="0.921875"
inkscape:cx="390.50847"
inkscape:cy="293.42373"
inkscape:window-width="1920"
inkscape:window-height="1027"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs2">
<linearGradient
inkscape:collect="always"
id="linearGradient352">
<stop
style="stop-color:#1b9200;stop-opacity:1;"
offset="0"
id="stop348" />
<stop
style="stop-color:#007afe;stop-opacity:1;"
offset="1"
id="stop350" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient352"
id="linearGradient354"
x1="0.14300847"
y1="133.74536"
x2="512.77649"
y2="325.92068"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(3.188427,3.8227623)" />
</defs>
<g
inkscape:label="圖層 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:url(#linearGradient354);fill-opacity:1;stroke-width:9.8"
id="rect286"
width="512"
height="512"
x="0"
y="0" />
<g
id="g1268"
transform="translate(0.14876459,1.2657939)">
<g
id="g1215"
transform="matrix(1.1490599,0,0,1.1490599,-37.374838,1.0563917)"
style="stroke-width:0.870277">
<circle
style="fill:#f9f9f9;stroke-width:8.52872"
id="path997"
cx="256"
cy="137.12709"
r="90.975639" />
<path
id="path1211"
style="fill:#f9f9f9;stroke-width:8.52872"
d="M 422.30759,442.08789 H 89.692413 c 0,0 74.458447,-218.16893 166.307587,-218.16893 91.84915,0 166.30759,218.16893 166.30759,218.16893 z"
sodipodi:nodetypes="ccsc" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

9
static/generic.css Normal file
View file

@ -0,0 +1,9 @@
div.avatar-preview{
}
#avatar-img{
width: 200px;
height: 200px;
object-fit: cover;
}

View file

@ -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({

21
static/user_config.js Normal file
View file

@ -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);
}
}
}

View file

@ -1,8 +1,13 @@
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
{% block meta%}
{% endblock %}
<meta charset="utf-8">
{% block title %}<title>Local Library</title>{% endblock %}
{% block title %}<title>Local Library</title>
<link type="text/css" rel="stylesheet" href="{% static 'generic.css' %}"></style>
{% endblock %}
</head>
<body>

View file

@ -1,6 +1,9 @@
{% extends "base_generic.html" %}
{% load static %}
{% load tz %}
{% block meta %}
<meta id="_token" content="{{ csrf_token }}">
{% endblock %}
{% get_current_timezone as TIME_ZONE %}
{% block headbar %}

View file

@ -5,13 +5,13 @@
<form method="post">
{% csrf_token %}
{{ form.as_p }}
Leave it blank: <input id ="{{ honeypot_name }}" type="text" name="{{ honeypot_name }}"><br>
<span id="{{ honeypot_name }}-some-desc">Leave it blank: </span><input id ="{{ honeypot_name }}" type="text" name="{{ honeypot_name }}"><br>
<button type="submit">Sign up</button>
</form>
<script type="text/javascript">
$ = (x) => document.getElementById(x);
$("{{ honeypot_name }}-some-desc").style.display = 'none';
$("{{ honeypot_name }}").style.display = 'none';
</script>
{% endblock %}

View file

@ -1,10 +1,19 @@
{% extends "base_generic.html" %}
{% load static %}
{% block content %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<p>{{ form.shown_name.label}}: {{ form.shown_name}}</p>
<p> Avatar Preview: <div class="avatar-preview"><img id="avatar-img" src="{{avatar.url}}");></div></p>
<p>{{ form.avatar.label}}: {{ form.avatar}}</p>
<p>{{ form.desc.label}}: {{ form.desc}}</p>
<p>{{ form.email.label}}: {{ form.email}}</p>
<button type="submit">Refresh</button>
</form>
<script type="text/javascript" src="{% static 'user_config.js'%}">
</script>
{% endblock %}

View file

@ -0,0 +1,62 @@
{% extends "base_generic.html" %}
{% load static %}
{% load tz %}
{% block meta %}
<meta id="_token" content="{{ csrf_token }}">
{% endblock %}
{% get_current_timezone as TIME_ZONE %}
{% block headbar %}
{{ request.user.shown_name }} (<a href="/user/{{ request.user.username}}">My timeline</a>) - <a href="/account/config">Configs</a> - <a href="/account/logout">Log out</a>
{% endblock %}
{% block content %}
<form method="POST" id="posting-form">
{% csrf_token %}
<div class="posting-form-group">
<label>Post</label>
<textarea id="post_text" name="post_text" placeholder="What do you want to post?"
maxlength="500" style="resize: none;" oninput="auto_expand(this)"></textarea>
</div>
{% csrf_token %}
<label for="privilege">Privileges:</label>
<select name="privilege" id="privil_choosing">
<option value="public" selected>Public Timeline</option>
<option value="unpublic">Not in Public Timeline</option>
<option value="private">Private</option>
</select>
<button id="submit_post" type="button" class="btn">Post!</button>
</form>
<div id="intro">{{user_shown_name}}<br>{{username}}</div>
<div id="public_timeline">
<div id="new_post_notifier" value=""></div>
<div id="latest_time" style="display: block;">{{latest_received_time|date:"Y-m-d H:i:s.u"}}+0000</div>
{% for post in public_timeline_list %}
<div id="post-{{post.id}}" class="post"><a href="/user/{{post.poster}}">{{public_post.poster.shown_name}}</a>
at <a href="/post/{{post.id}}" class="post-time">{{public_post.post_time|date:"Y-m-d H:i:s.u"}}+0000</a><br/>
{{public_post.text|linebreaksbr}}<br/>
<span id="reply-{{post.id}}" value="{{post.id}}" class="reply">↩️</span>
- <span id="repost-{{post.id}}" value="{{post.id}}" class="repost">🔁</span>
- <span id="fav-{{post.id}}" value="{{post.id}}" class="fav"></span>
</div>
{% endfor %}
<div id="previous_post_loader">More posts</div>
<div id="oldest_time" style="display: block;">{{oldest_received_time|date:"Y-m-d H:i:s.u"}}+0000</div>
</div>
<pre> % static 'timeline.js' 要修改
</pre>
<script type="text/javascript" src="{% static 'timeline.js' %}">
</script>
{% endblock %}