aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBobby <[email protected]>2024-06-13 19:31:04 +0000
committerBobby <[email protected]>2024-06-13 19:31:04 +0000
commitc27b2930170dbc69d5b2c302bff2eba6b97a5525 (patch)
tree2c29f50b15926c41de662791182091fab1a2d2dc
parent77275c2c688aa1f337659d98255582627450d43f (diff)
downloadthatcomputerscientist-c27b2930170dbc69d5b2c302bff2eba6b97a5525.tar.xz
thatcomputerscientist-c27b2930170dbc69d5b2c302bff2eba6b97a5525.zip
Ability to Reset Passwords and Better Email Templates
-rw-r--r--blog/urls.py64
-rw-r--r--blog/views.py879
-rw-r--r--static/css/login-area.css131
-rw-r--r--templates/blog/partials/sidebar.html1
-rw-r--r--templates/blog/resetpass.html24
-rw-r--r--templates/blog/resetpass_input.html23
-rw-r--r--users/accountFunctions.py10
-rw-r--r--users/forms.py262
-rw-r--r--users/mail_send.py34
-rw-r--r--users/templates/email_change_verification_email.html37
-rw-r--r--users/templates/reset_password_email.html25
-rw-r--r--users/templates/verification_email.html36
-rw-r--r--users/urls.py37
-rw-r--r--users/views.py299
14 files changed, 1290 insertions, 572 deletions
diff --git a/blog/urls.py b/blog/urls.py
index 57e5d08d..5f4ebf88 100644
--- a/blog/urls.py
+++ b/blog/urls.py
@@ -3,30 +3,44 @@ from django.urls import path
from . import views
from .feed import RSSFeed
-app_name = 'blog'
+app_name = "blog"
urlpatterns = [
- path('', views.home, name='home'),
- path('account', views.account, name='account'),
- path('register', views.register, name='register'),
- path('search', views.search, name='search'),
- path('weblog', views.articles, name='articles'),
- path('weblog/<str:slug>', views.post, name='post'),
- path('weblog/<str:slug>/comment', views.comment, name='comment'),
- path('weblog/<str:slug>/anon_comment', views.anon_comment, name='anon_comment'),
- path('weblog/<str:slug>/edit_comment', views.edit_comment, name='edit_comment'),
- path('weblog/<str:slug>/anon_edit_comment', views.anon_edit_comment, name='anon_edit_comment'),
- path('weblog/<str:slug>/delete_comment/<int:comment_id>', views.delete_comment, name='delete_comment'),
- path('weblog/<str:slug>/anon_delete_comment/<int:comment_id>', views.anon_delete_comment, name='anon_delete_comment'),
- path('archives', views.archives, name='archives'),
- path('archives/<str:date>', views.articles, name='archive_posts'),
- path('categories', views.categories, name='categories'),
- path('categories/<str:cg>', views.articles, name='category_posts'),
- path('tags', views.tags, name='tags'),
- path('tags/<str:tag_slug>', views.tag_posts, name='tag_posts'),
- path('~<str:username>', views.user_activity, name='user_activity'),
- path('policy', views.policy, name='policy'),
- path('socialify', views.socialify, name='socialify'),
- path('rss/', RSSFeed(), name='rss_feed'),
- path('anidata', views.anidata, name='anidata'),
- path('anilist', views.anilist, name='anilist'),
+ path("", views.home, name="home"),
+ path("account", views.account, name="account"),
+ path("register", views.register, name="register"),
+ path("forgotpassword", views.forgotpassword, name="forgotpassword"),
+ path('forgotpassword/reset?uid=<str:uid>&token=<str:token>', views.resetpassword, name='resetpassword'),
+ path("search", views.search, name="search"),
+ path("weblog", views.articles, name="articles"),
+ path("weblog/<str:slug>", views.post, name="post"),
+ path("weblog/<str:slug>/comment", views.comment, name="comment"),
+ path("weblog/<str:slug>/anon_comment", views.anon_comment, name="anon_comment"),
+ path("weblog/<str:slug>/edit_comment", views.edit_comment, name="edit_comment"),
+ path(
+ "weblog/<str:slug>/anon_edit_comment",
+ views.anon_edit_comment,
+ name="anon_edit_comment",
+ ),
+ path(
+ "weblog/<str:slug>/delete_comment/<int:comment_id>",
+ views.delete_comment,
+ name="delete_comment",
+ ),
+ path(
+ "weblog/<str:slug>/anon_delete_comment/<int:comment_id>",
+ views.anon_delete_comment,
+ name="anon_delete_comment",
+ ),
+ path("archives", views.archives, name="archives"),
+ path("archives/<str:date>", views.articles, name="archive_posts"),
+ path("categories", views.categories, name="categories"),
+ path("categories/<str:cg>", views.articles, name="category_posts"),
+ path("tags", views.tags, name="tags"),
+ path("tags/<str:tag_slug>", views.tag_posts, name="tag_posts"),
+ path("~<str:username>", views.user_activity, name="user_activity"),
+ path("policy", views.policy, name="policy"),
+ path("socialify", views.socialify, name="socialify"),
+ path("rss/", RSSFeed(), name="rss_feed"),
+ path("anidata", views.anidata, name="anidata"),
+ path("anilist", views.anilist, name="anilist"),
]
diff --git a/blog/views.py b/blog/views.py
index ec4d9376..ac086ad1 100644
--- a/blog/views.py
+++ b/blog/views.py
@@ -23,36 +23,51 @@ from haystack.query import SearchQuerySet
from user_agents import parse
from announcements.models import Announcement
-from users.forms import RegisterForm, UpdateUserDetailsForm
+from users.accountFunctions import verify_token
+from users.forms import RegisterForm, ResetPasswordForm, UpdateUserDetailsForm, ForgotPasswordForm
from users.models import UserProfile
from users.tokens import CaptchaTokenGenerator
-from .context_processors import (add_excerpt, add_num_comments, avatar_list,
- check_spam, comment_processor,
- highlight_code_blocks, recent_posts)
+from .context_processors import (
+ add_excerpt,
+ add_num_comments,
+ avatar_list,
+ check_spam,
+ comment_processor,
+ highlight_code_blocks,
+ recent_posts,
+)
from .models import AnonymousCommentUser, Category, Comment, Post, Tag
from .recommender import next_read
load_dotenv()
+
def atoi(text):
return int(text) if text.isdigit() else text
+
def natural_keys(text):
- '''
+ """
alist.sort(key=natural_keys) sorts in human order
http://nedbatchelder.com/blog/200712/human_sorting.html
(See Toothy's implementation in the comments)
- '''
- return [ atoi(c) for c in re.split(r'(\d+)', text) ]
+ """
+ return [atoi(c) for c in re.split(r"(\d+)", text)]
# Create your views here.
+
def home(request):
- announcements = Announcement.objects.filter(is_public=True).order_by('-created_at')
+ announcements = Announcement.objects.filter(is_public=True).order_by("-created_at")
announcements = announcements if len(announcements) > 0 else None
- return render(request, 'blog/home.html', {'title': 'Home', 'posts': recent_posts(), 'announcements': announcements})
+ return render(
+ request,
+ "blog/home.html",
+ {"title": "Home", "posts": recent_posts(), "announcements": announcements},
+ )
+
def tags(request):
tags = Tag.objects.all()
@@ -63,33 +78,47 @@ def tags(request):
tag.pxs = min(tag.pxs, 36)
tags = sorted(tags, key=lambda x: x.count, reverse=True)
tags = [tag for tag in tags if tag.count > 0]
- return render(request, 'blog/tags.html', {'title': 'Tags', 'tags': tags})
+ return render(request, "blog/tags.html", {"title": "Tags", "tags": tags})
+
def tag_posts(request, tag_slug):
try:
tag = Tag.objects.get(slug=tag_slug)
except Tag.DoesNotExist:
tag = {
- 'name': tag_slug,
- 'slug': tag_slug,
- 'count': 0,
+ "name": tag_slug,
+ "slug": tag_slug,
+ "count": 0,
}
- return render(request, 'blog/tagged.html', {'title': 'Posts Tagged With: ' + tag_slug, 'posts': None, 'tag': tag})
- posts = Post.objects.filter(tags__name__in=[tag.name], is_public=True).order_by('views')
+ return render(
+ request,
+ "blog/tagged.html",
+ {"title": "Posts Tagged With: " + tag_slug, "posts": None, "tag": tag},
+ )
+ posts = Post.objects.filter(tags__name__in=[tag.name], is_public=True).order_by(
+ "views"
+ )
for post in posts:
post.excerpt = add_excerpt(post)
post.num_comments = add_num_comments(post)
- return render(request, 'blog/tagged.html', {'title': 'Posts Tagged With: ' + tag.name, 'posts': posts, 'tag': tag})
+ return render(
+ request,
+ "blog/tagged.html",
+ {"title": "Posts Tagged With: " + tag.name, "posts": posts, "tag": tag},
+ )
+
def account(request):
user = request.user
avatarlist = avatar_list()
for key in avatarlist:
- avatarlist[key] = [re.sub(r'\.gif$', '', string) for string in avatarlist[key]]
+ avatarlist[key] = [re.sub(r"\.gif$", "", string) for string in avatarlist[key]]
avatarlist[key].sort(key=natural_keys)
avatarlist = {k: avatarlist[k] for k in sorted(avatarlist)}
- blinkies = [re.sub(r'\.gif$', '', string) for string in os.listdir('static/images/blinkies')]
+ blinkies = [
+ re.sub(r"\.gif$", "", string) for string in os.listdir("static/images/blinkies")
+ ]
blinkies.sort(key=natural_keys)
if user.is_authenticated:
@@ -99,97 +128,209 @@ def account(request):
# Set a random avatar
avatar_dir = choice(list(avatarlist.keys()))
avatar_file = choice(avatarlist[avatar_dir])
- user_profile.avatar_url = avatar_dir + '/' + avatar_file
+ user_profile.avatar_url = avatar_dir + "/" + avatar_file
user_profile.save()
except UserProfile.DoesNotExist:
# Create a new user profile and set a random avatar
user_profile = UserProfile.objects.create(user=user)
avatar_dir = choice(list(avatarlist.keys()))
avatar_file = choice(avatarlist[avatar_dir])
- user_profile.avatar_url = avatar_dir + '/' + avatar_file
+ user_profile.avatar_url = avatar_dir + "/" + avatar_file
user_profile.save()
- if request.GET.get('tab') == 'details':
- update_form = UpdateUserDetailsForm(user=user, initial={'first_name': user.first_name, 'last_name': user.last_name, 'bio': user_profile.bio, 'is_public': user_profile.is_public, 'email_public': user_profile.email_public, 'location': user_profile.location})
+ if request.GET.get("tab") == "details":
+ update_form = UpdateUserDetailsForm(
+ user=user,
+ initial={
+ "first_name": user.first_name,
+ "last_name": user.last_name,
+ "bio": user_profile.bio,
+ "is_public": user_profile.is_public,
+ "email_public": user_profile.email_public,
+ "location": user_profile.location,
+ },
+ )
else:
update_form = None
- return render(request, 'blog/account.html', {'title': 'Account', 'user_profile': user_profile, 'avatarlist': avatarlist, 'update_form': update_form, 'blinkies': blinkies})
+ return render(
+ request,
+ "blog/account.html",
+ {
+ "title": "Account",
+ "user_profile": user_profile,
+ "avatarlist": avatarlist,
+ "update_form": update_form,
+ "blinkies": blinkies,
+ },
+ )
else:
# Redirect to login page
- return redirect('blog:home')
+ return redirect("blog:home")
+
def register(request):
user = request.user
if user.is_authenticated:
- return redirect('blog:account')
+ return redirect("blog:account")
else:
- random_string = ''.join([choice(ascii_letters + digits) for n in range(6)])
+ random_string = "".join([choice(ascii_letters + digits) for n in range(6)])
captcha = CaptchaTokenGenerator().encrypt(random_string)
- if request.method == 'POST':
- expected_captcha = CaptchaTokenGenerator().decrypt(request.POST.get('expected_captcha'))
+ if request.method == "POST":
+ expected_captcha = CaptchaTokenGenerator().decrypt(
+ request.POST.get("expected_captcha")
+ )
form = RegisterForm(request.POST, expected_captcha=expected_captcha)
if form.is_valid():
form.save(request=request)
- messages.success(request, 'Account was created! Please check your email to verify your account.', extra_tags='accountCreated')
- return redirect('blog:register')
+ messages.success(
+ request,
+ "Account was created! Please check your email to verify your account.",
+ extra_tags="accountCreated",
+ )
+ return redirect("blog:register")
else:
- return render(request, 'blog/register.html', {'title': 'Register', 'form': form, 'captcha': captcha})
+ return render(
+ request,
+ "blog/register.html",
+ {"title": "Register", "form": form, "captcha": captcha},
+ )
else:
form = RegisterForm(expected_captcha=random_string)
- return render(request, 'blog/register.html', {'title': 'Register', 'form': form, 'captcha': captcha})
+ return render(
+ request,
+ "blog/register.html",
+ {"title": "Register", "form": form, "captcha": captcha},
+ )
+
+
+def forgotpassword(request):
+ user = request.user
+ if user.is_authenticated:
+ return redirect("blog:account")
+ else:
+ if request.method == "POST":
+ form = ForgotPasswordForm(request.POST)
+ if form.is_valid():
+ form.save(request)
+ messages.success(
+ request,
+ "An email has been sent to you with instructions on how to reset your password.",
+ extra_tags="passwordReset",
+ )
+ return redirect("blog:forgotpassword")
+ else:
+ return render(
+ request,
+ "blog/resetpass.html",
+ {"title": "Forgot Password", "form": form},
+ )
+ else:
+ form = ForgotPasswordForm()
+ return render(
+ request,
+ "blog/resetpass.html",
+ {"title": "Forgot Password", "form": form},
+ )
+
+def resetpassword(request, uid, token):
+ user = request.user
+ if user.is_authenticated:
+ return redirect("blog:account")
+ else:
+ if request.method == "POST":
+ form = ResetPasswordForm(request.POST)
+ if form.is_valid():
+ token_object = verify_token('resetpassword', uid, token)
+ if token_object is not None and token_object.verified:
+ user = User.objects.get(pk=token_object.user_id)
+ form.save(user)
+ messages.success(
+ request,
+ "Your password has been reset. You can now login with your new password.",
+ extra_tags="passwordReset",
+ )
+ token_object.delete()
+ return redirect("blog:resetpassword", uid=uid, token=token)
+ else:
+ messages.error(
+ request,
+ "Invalid or expired reset password link. Please try again.",
+ extra_tags="passwordReset",
+ )
+ return redirect("blog:forgotpassword")
+ else:
+ return render(
+ request,
+ "blog/resetpass_input.html",
+ {"title": "Reset Password", "form": form},
+ )
+ else:
+ form = ResetPasswordForm()
+ return render(
+ request,
+ "blog/resetpass_input.html",
+ {"title": "Reset Password", "form": form},
+ )
+
def post(request, slug):
try:
post = Post.objects.get(slug=slug)
# Get the number of views for this post
- x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
+ x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR")
if x_forwarded_for:
- ip = x_forwarded_for.split(',')[0]
+ ip = x_forwarded_for.split(",")[0]
else:
- ip = request.META.get('REMOTE_ADDR')
- user_agent_string = request.META.get('HTTP_USER_AGENT', '')
+ ip = request.META.get("REMOTE_ADDR")
+ user_agent_string = request.META.get("HTTP_USER_AGENT", "")
user_agent = parse(user_agent_string)
- user_identifier = f'{ip}_{user_agent.browser.family}_{user_agent.browser.version_string}_{user_agent.os.family}_{user_agent.os.version_string}'
- cache_key = f'post_view_count_{slug}_{user_identifier}'
+ user_identifier = f"{ip}_{user_agent.browser.family}_{user_agent.browser.version_string}_{user_agent.os.family}_{user_agent.os.version_string}"
+ cache_key = f"post_view_count_{slug}_{user_identifier}"
view_count = cache.get(cache_key, 0)
if view_count == 0:
post.views += 1
post.save()
- cache.set(cache_key, 1, 60 * 60 * 24 * 7) # 1 week
+ cache.set(cache_key, 1, 60 * 60 * 24 * 7) # 1 week
# code stored in .ql-syntax class
- soup = BeautifulSoup(post.body, 'html.parser')
- code_blocks = soup.find_all('pre')
+ soup = BeautifulSoup(post.body, "html.parser")
+ code_blocks = soup.find_all("pre")
for code_block in code_blocks:
- data_language = code_block.get('data-language')
- if data_language == 'true':
+ data_language = code_block.get("data-language")
+ if data_language == "true":
data_language = None
- code_block.replace_with(BeautifulSoup(highlight_code_blocks(code_block, language=data_language), 'html.parser'))
+ code_block.replace_with(
+ BeautifulSoup(
+ highlight_code_blocks(code_block, language=data_language),
+ "html.parser",
+ )
+ )
# float: right every other image
- images = soup.find_all('img')
+ images = soup.find_all("img")
for i in range(len(images)):
if i % 2 != 0:
- images[i]['style'] = 'float: right; margin-right: 0px; margin-left: 11px;'
+ images[i][
+ "style"
+ ] = "float: right; margin-right: 0px; margin-left: 11px;"
# remove all paragraphs which are: "<p class="ql-align-justify"><br></p>"
- for p in soup.find_all('p', class_='ql-align-justify'):
- if p.find('br') is not None:
+ for p in soup.find_all("p", class_="ql-align-justify"):
+ if p.find("br") is not None:
p.decompose()
# separate the body in two parts -> the first paragraph and the rest
- first_paragraph = soup.find('p')
+ first_paragraph = soup.find("p")
if first_paragraph is not None:
first_paragraph = str(first_paragraph)
- first_paragraph = first_paragraph.replace('<p>', '<p class="subhead">')
- soup.find('p').decompose()
+ first_paragraph = first_paragraph.replace("<p>", '<p class="subhead">')
+ soup.find("p").decompose()
post.first_paragraph = first_paragraph
post.body = str(soup)
- post.views = '{:,}'.format(post.views)
-
+ post.views = "{:,}".format(post.views)
tags = post.tags.all()
comments = Comment.objects.filter(post=post)
@@ -206,189 +347,302 @@ def post(request, slug):
if post.is_public:
# modify request.meta description (only text) and image
- request.meta['description'] = BeautifulSoup(first_paragraph, 'html.parser').get_text()
- request.meta['image'] = 'https://shi.foo/ignis/post_image/720/' + str(post.id) + '.gif'
+ request.meta["description"] = BeautifulSoup(
+ first_paragraph, "html.parser"
+ ).get_text()
+ request.meta["image"] = (
+ "https://shi.foo/ignis/post_image/720/" + str(post.id) + ".gif"
+ )
read_next = next_read(post)
- return render(request, 'blog/post.html', {'title': post.title, 'post': post, 'tags': tags, 'comments': comments, 'view_count': view_count, 'read_next': read_next})
+ return render(
+ request,
+ "blog/post.html",
+ {
+ "title": post.title,
+ "post": post,
+ "tags": tags,
+ "comments": comments,
+ "view_count": view_count,
+ "read_next": read_next,
+ },
+ )
else:
- if request.user.is_authenticated and request.user.is_superuser or request.user.is_staff:
- return render(request, 'blog/post.html', {'title': post.title, 'post': post, 'tags': tags, 'comments': comments, 'view_count': view_count})
+ if (
+ request.user.is_authenticated
+ and request.user.is_superuser
+ or request.user.is_staff
+ ):
+ return render(
+ request,
+ "blog/post.html",
+ {
+ "title": post.title,
+ "post": post,
+ "tags": tags,
+ "comments": comments,
+ "view_count": view_count,
+ },
+ )
else:
raise Http404
except Post.DoesNotExist:
raise Http404
+
def comment(request, slug):
- if request.method == 'POST':
+ if request.method == "POST":
if request.user.is_authenticated:
try:
- print(request.POST.get('comment'))
- r_spam = check_spam(comment=request.POST.get('comment'), post=Post.objects.get(slug=slug))
- if r_spam != 'N':
- messages.error(request, r_spam, extra_tags='spam')
- return redirect(reverse('blog:post', kwargs={'slug': slug}) + '#new-comment')
-
+ print(request.POST.get("comment"))
+ r_spam = check_spam(
+ comment=request.POST.get("comment"),
+ post=Post.objects.get(slug=slug),
+ )
+ if r_spam != "N":
+ messages.error(request, r_spam, extra_tags="spam")
+ return redirect(
+ reverse("blog:post", kwargs={"slug": slug}) + "#new-comment"
+ )
+
# then we continue
post = Post.objects.get(slug=slug)
if post.is_public:
- comment = Comment.objects.create(user=request.user, post=post, body=request.POST.get('comment'))
- return redirect(reverse('blog:post', kwargs={'slug': slug}) + '#comment-' + str(comment.id))
+ comment = Comment.objects.create(
+ user=request.user, post=post, body=request.POST.get("comment")
+ )
+ return redirect(
+ reverse("blog:post", kwargs={"slug": slug})
+ + "#comment-"
+ + str(comment.id)
+ )
else:
- if request.user.is_authenticated and request.user.is_superuser or request.user.is_staff:
- Comment.objects.create(user=request.user, post=post, body=request.POST.get('comment'))
- return redirect(reverse('blog:post', kwargs={'slug': slug}) + '#comment-' + str(comment.id))
+ if (
+ request.user.is_authenticated
+ and request.user.is_superuser
+ or request.user.is_staff
+ ):
+ Comment.objects.create(
+ user=request.user,
+ post=post,
+ body=request.POST.get("comment"),
+ )
+ return redirect(
+ reverse("blog:post", kwargs={"slug": slug})
+ + "#comment-"
+ + str(comment.id)
+ )
else:
- return HttpResponse('Post not found!', status=404)
+ return HttpResponse("Post not found!", status=404)
except Post.DoesNotExist:
- return HttpResponse('Post not found!', status=404)
+ return HttpResponse("Post not found!", status=404)
else:
- return redirect('blog:home')
+ return redirect("blog:home")
else:
- return redirect('blog:home')
-
+ return redirect("blog:home")
+
+
def anon_comment(request, slug):
- if request.method == 'POST':
+ if request.method == "POST":
if request.user.is_authenticated:
# not allowed this is anonymous comment form
- return redirect(reverse('blog:post', kwargs={'slug': slug}))
+ return redirect(reverse("blog:post", kwargs={"slug": slug}))
else:
- anonymous_name = request.POST.get('anonymous-name')
- anonymous_email = request.POST.get('anonymous-email')
- anonymous_token, at = request.POST.get('anonymous-token'), request.POST.get('anonymous-token')
- new_anonymous_token = request.POST.get('new-anonymous-token')
- anonymous_comment = request.POST.get('anonymous-comment')
- res_spam = check_spam(comment=anonymous_comment, post=Post.objects.get(slug=slug))
- if res_spam != 'N':
- messages.error(request, res_spam, extra_tags='spam')
- return redirect(reverse('blog:post', kwargs={'slug': slug}) + '#new-comment')
+ anonymous_name = request.POST.get("anonymous-name")
+ anonymous_email = request.POST.get("anonymous-email")
+ anonymous_token, at = request.POST.get("anonymous-token"), request.POST.get(
+ "anonymous-token"
+ )
+ new_anonymous_token = request.POST.get("new-anonymous-token")
+ anonymous_comment = request.POST.get("anonymous-comment")
+ res_spam = check_spam(
+ comment=anonymous_comment, post=Post.objects.get(slug=slug)
+ )
+ if res_spam != "N":
+ messages.error(request, res_spam, extra_tags="spam")
+ return redirect(
+ reverse("blog:post", kwargs={"slug": slug}) + "#new-comment"
+ )
# now continue with the comment
if not anonymous_name:
- messages.error(request, 'Please enter a name!')
- return redirect(reverse('blog:post', kwargs={'slug': slug}))
+ messages.error(request, "Please enter a name!")
+ return redirect(reverse("blog:post", kwargs={"slug": slug}))
if not anonymous_comment:
- messages.error(request, 'Please enter a comment!')
- return redirect(reverse('blog:post', kwargs={'slug': slug}))
+ messages.error(request, "Please enter a comment!")
+ return redirect(reverse("blog:post", kwargs={"slug": slug}))
if not anonymous_email:
- anonymous_email = ''.join(random.choice(string.ascii_lowercase) for i in range(10)) + '@anonymous.shi.foo'
+ anonymous_email = (
+ "".join(random.choice(string.ascii_lowercase) for i in range(10))
+ + "@anonymous.shi.foo"
+ )
if not anonymous_token:
- anonymous_token = ''.join(random.choice(string.ascii_lowercase) for i in range(10))
+ anonymous_token = "".join(
+ random.choice(string.ascii_lowercase) for i in range(10)
+ )
at = anonymous_token
# generate a random avatar for the anonymous user
avatarlist = avatar_list()
for key in avatarlist:
- avatarlist[key] = [re.sub(r'\.gif$', '', string) for string in avatarlist[key]]
+ avatarlist[key] = [
+ re.sub(r"\.gif$", "", string) for string in avatarlist[key]
+ ]
avatarlist[key].sort(key=natural_keys)
avatarlist = {k: avatarlist[k] for k in sorted(avatarlist)}
avatar_dir = choice(list(avatarlist.keys()))
avatar_file = choice(avatarlist[avatar_dir])
- anonymous_avatar = avatar_dir + '/' + avatar_file
- anonymous_token = hashlib.sha256(anonymous_token.encode('utf-8')).hexdigest()
+ anonymous_avatar = avatar_dir + "/" + avatar_file
+ anonymous_token = hashlib.sha256(
+ anonymous_token.encode("utf-8")
+ ).hexdigest()
try:
- anonymous_user = AnonymousCommentUser.objects.get(email=anonymous_email, token=anonymous_token)
+ anonymous_user = AnonymousCommentUser.objects.get(
+ email=anonymous_email, token=anonymous_token
+ )
except AnonymousCommentUser.DoesNotExist:
- anonymous_user = AnonymousCommentUser.objects.create(email=anonymous_email, token=anonymous_token,
- avatar=anonymous_avatar)
+ anonymous_user = AnonymousCommentUser.objects.create(
+ email=anonymous_email,
+ token=anonymous_token,
+ avatar=anonymous_avatar,
+ )
if new_anonymous_token:
at = new_anonymous_token
- new_anonymous_token = hashlib.sha256(new_anonymous_token.encode('utf-8')).hexdigest()
+ new_anonymous_token = hashlib.sha256(
+ new_anonymous_token.encode("utf-8")
+ ).hexdigest()
anonymous_user.token = new_anonymous_token
anonymous_user.save()
-
+
# update the anonymous user's name if it has changed
if anonymous_user.name != anonymous_name:
anonymous_user.name = anonymous_name
anonymous_user.save()
-
- comment = Comment.objects.create(anonymous_user=anonymous_user, post=Post.objects.get(slug=slug), body=anonymous_comment)
+
+ comment = Comment.objects.create(
+ anonymous_user=anonymous_user,
+ post=Post.objects.get(slug=slug),
+ body=anonymous_comment,
+ )
# redirect to the post with the comment but set the anonymous user cookie
- response = redirect(reverse('blog:post', kwargs={'slug': slug}) + '#comment-' + str(comment.id))
- response.set_cookie('anonymous_name', anonymous_user.name, max_age=60*60*24*365)
- response.set_cookie('anonymous_email', anonymous_user.email, max_age=60*60*24*365)
- response.set_cookie('anonymous_token', at, max_age=60*60*24*365)
+ response = redirect(
+ reverse("blog:post", kwargs={"slug": slug})
+ + "#comment-"
+ + str(comment.id)
+ )
+ response.set_cookie(
+ "anonymous_name", anonymous_user.name, max_age=60 * 60 * 24 * 365
+ )
+ response.set_cookie(
+ "anonymous_email", anonymous_user.email, max_age=60 * 60 * 24 * 365
+ )
+ response.set_cookie("anonymous_token", at, max_age=60 * 60 * 24 * 365)
return response
else:
- return redirect('blog:home')
+ return redirect("blog:home")
+
def edit_comment(request, slug):
- if request.method == 'POST':
+ if request.method == "POST":
if request.user.is_authenticated:
try:
- comment = Comment.objects.get(id=request.POST.get('comment_id'))
+ comment = Comment.objects.get(id=request.POST.get("comment_id"))
# check for spam first
- user_ip = request.META.get('HTTP_X_FORWARDED_FOR')
+ user_ip = request.META.get("HTTP_X_FORWARDED_FOR")
if user_ip:
- user_ip = user_ip.split(',')[0]
+ user_ip = user_ip.split(",")[0]
else:
- user_ip = request.META.get('REMOTE_ADDR')
- user_agent_string = request.META.get('HTTP_USER_AGENT', '')
+ user_ip = request.META.get("REMOTE_ADDR")
+ user_agent_string = request.META.get("HTTP_USER_AGENT", "")
user_agent = parse(user_agent_string)
# if check_spam(user_ip=user_ip, user_agent=user_agent, comment=request.POST.get('body'), author=request.user.username
- res_spam = check_spam(comment=request.POST.get('body'), post=comment.post)
- if res_spam != 'N':
- messages.error(request, request.POST.get('body'), extra_tags='spam')
- return redirect(reverse('blog:post', kwargs={'slug': slug}) + '#comment-' + str(comment.id))
+ res_spam = check_spam(
+ comment=request.POST.get("body"), post=comment.post
+ )
+ if res_spam != "N":
+ messages.error(request, request.POST.get("body"), extra_tags="spam")
+ return redirect(
+ reverse("blog:post", kwargs={"slug": slug})
+ + "#comment-"
+ + str(comment.id)
+ )
if comment.user == request.user:
- comment.body = request.POST.get('body')
+ comment.body = request.POST.get("body")
comment.edited = True
comment.edited_at = datetime.now()
comment.save()
- return redirect(reverse('blog:post', kwargs={'slug': slug}) + '#comment-' + str(comment.id))
+ return redirect(
+ reverse("blog:post", kwargs={"slug": slug})
+ + "#comment-"
+ + str(comment.id)
+ )
else:
- return HttpResponse('Unauthorized!', status=401)
+ return HttpResponse("Unauthorized!", status=401)
except Comment.DoesNotExist:
- return HttpResponse('Comment not found!', status=404)
+ return HttpResponse("Comment not found!", status=404)
else:
- return redirect('blog:home')
+ return redirect("blog:home")
else:
- return redirect('blog:home')
-
+ return redirect("blog:home")
+
+
def anon_edit_comment(request, slug):
- if request.method == 'POST':
+ if request.method == "POST":
if request.user.is_authenticated:
# not allowed this is anonymous comment form
- return redirect(reverse('blog:post', kwargs={'slug': slug}))
+ return redirect(reverse("blog:post", kwargs={"slug": slug}))
else:
- anonymous_token = request.COOKIES.get('anonymous_token')
+ anonymous_token = request.COOKIES.get("anonymous_token")
if not anonymous_token:
- return HttpResponse('Unauthorized!', status=401)
+ return HttpResponse("Unauthorized!", status=401)
try:
- anonymous_token = hashlib.sha256(anonymous_token.encode('utf-8')).hexdigest()
- comment = Comment.objects.get(id=request.POST.get('comment_id'))
+ anonymous_token = hashlib.sha256(
+ anonymous_token.encode("utf-8")
+ ).hexdigest()
+ comment = Comment.objects.get(id=request.POST.get("comment_id"))
# check for spam first
- user_ip = request.META.get('HTTP_X_FORWARDED_FOR')
+ user_ip = request.META.get("HTTP_X_FORWARDED_FOR")
if user_ip:
- user_ip = user_ip.split(',')[0]
+ user_ip = user_ip.split(",")[0]
else:
- user_ip = request.META.get('REMOTE_ADDR')
- user_agent_string = request.META.get('HTTP_USER_AGENT', '')
+ user_ip = request.META.get("REMOTE_ADDR")
+ user_agent_string = request.META.get("HTTP_USER_AGENT", "")
user_agent = parse(user_agent_string)
- res_spam = check_spam(comment=request.POST.get('body'), post=comment.post)
- if res_spam != 'N':
- # if check_spam(user_ip=user_ip, user_agent=user_agent, comment=request.POST.get('body'), author=comment.anonymous_user.name):
- messages.error(request, request.POST.get('body'), extra_tags='spam')
- return redirect(reverse('blog:post', kwargs={'slug': slug}) + '#comment-' + str(comment.id))
+ res_spam = check_spam(
+ comment=request.POST.get("body"), post=comment.post
+ )
+ if res_spam != "N":
+ # if check_spam(user_ip=user_ip, user_agent=user_agent, comment=request.POST.get('body'), author=comment.anonymous_user.name):
+ messages.error(request, request.POST.get("body"), extra_tags="spam")
+ return redirect(
+ reverse("blog:post", kwargs={"slug": slug})
+ + "#comment-"
+ + str(comment.id)
+ )
if comment.anonymous_user.token == anonymous_token:
- comment.body = request.POST.get('body')
+ comment.body = request.POST.get("body")
comment.edited = True
comment.edited_at = datetime.now()
comment.save()
- return redirect(reverse('blog:post', kwargs={'slug': slug}) + '#comment-' + str(comment.id))
+ return redirect(
+ reverse("blog:post", kwargs={"slug": slug})
+ + "#comment-"
+ + str(comment.id)
+ )
else:
- return HttpResponse('Unauthorized!', status=401)
+ return HttpResponse("Unauthorized!", status=401)
except Comment.DoesNotExist:
- return HttpResponse('Comment not found!', status=404)
+ return HttpResponse("Comment not found!", status=404)
else:
- return redirect('blog:home')
+ return redirect("blog:home")
+
def delete_comment(request, slug, comment_id):
if request.user.is_authenticated:
@@ -396,92 +650,142 @@ def delete_comment(request, slug, comment_id):
comment = Comment.objects.get(id=comment_id)
if comment.user == request.user:
comment.delete()
- return redirect(reverse('blog:post', kwargs={'slug': slug}) + '#comments')
+ return redirect(
+ reverse("blog:post", kwargs={"slug": slug}) + "#comments"
+ )
else:
- return HttpResponse('Unauthorized!', status=401)
+ return HttpResponse("Unauthorized!", status=401)
except Comment.DoesNotExist:
- return HttpResponse('Comment not found!', status=404)
+ return HttpResponse("Comment not found!", status=404)
else:
- return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
+ return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
+
def anon_delete_comment(request, slug, comment_id):
if request.user.is_authenticated:
# not allowed this is anonymous comment form
- return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
+ return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
else:
- anonymous_token = request.COOKIES.get('anonymous_token')
+ anonymous_token = request.COOKIES.get("anonymous_token")
if not anonymous_token:
- return HttpResponse('Unauthorized!', status=401)
- anonymous_token = hashlib.sha256(anonymous_token.encode('utf-8')).hexdigest()
+ return HttpResponse("Unauthorized!", status=401)
+ anonymous_token = hashlib.sha256(anonymous_token.encode("utf-8")).hexdigest()
try:
- comment = Comment.objects.get(id=comment_id, anonymous_user__token=anonymous_token)
+ comment = Comment.objects.get(
+ id=comment_id, anonymous_user__token=anonymous_token
+ )
comment.delete()
- return redirect(reverse('blog:post', kwargs={'slug': slug}) + '#comments')
+ return redirect(reverse("blog:post", kwargs={"slug": slug}) + "#comments")
except Comment.DoesNotExist:
- return HttpResponse('Comment not found!', status=404)
+ return HttpResponse("Comment not found!", status=404)
+
def search(request):
- query = request.GET.get('q')
- search_in = request.GET.getlist('search_in') if request.GET.get('search_in') else ['posts']
- sort_by = request.GET.get('sort_by') if request.GET.get('sort_by') else 'relevance'
- order = request.GET.get('order') if request.GET.get('order') else 'ascending'
- date_range = request.GET.get('date_range') if request.GET.get('date_range') else 'any'
+ query = request.GET.get("q")
+ search_in = (
+ request.GET.getlist("search_in") if request.GET.get("search_in") else ["posts"]
+ )
+ sort_by = request.GET.get("sort_by") if request.GET.get("sort_by") else "relevance"
+ order = request.GET.get("order") if request.GET.get("order") else "ascending"
+ date_range = (
+ request.GET.get("date_range") if request.GET.get("date_range") else "any"
+ )
search_model_map = {
- 'posts': Post,
- 'users': User,
- 'comments': Comment,
+ "posts": Post,
+ "users": User,
+ "comments": Comment,
}
now = timezone.now()
if query:
search_results = SearchQuerySet().filter(content=query)
if search_in:
- search_results = search_results.models(*[search_model_map[model] for model in search_in])
+ search_results = search_results.models(
+ *[search_model_map[model] for model in search_in]
+ )
# search_results = [result.object for result in search_results]
- posts = [result.object for result in search_results if isinstance(result.object, Post)]
- users = [result.object for result in search_results if isinstance(result.object, User)]
- comments = [result.object for result in search_results if isinstance(result.object, Comment)]
+ posts = [
+ result.object
+ for result in search_results
+ if isinstance(result.object, Post)
+ ]
+ users = [
+ result.object
+ for result in search_results
+ if isinstance(result.object, User)
+ ]
+ comments = [
+ result.object
+ for result in search_results
+ if isinstance(result.object, Comment)
+ ]
# match-case sort_by
- if sort_by == 'relevance' and order == 'ascending':
+ if sort_by == "relevance" and order == "ascending":
posts = sorted(posts, key=lambda post: post.views)
users = sorted(users, key=lambda user: user.username)
comments = sorted(comments, key=lambda comment: comment.id)
- elif sort_by == 'relevance' and order == 'descending':
+ elif sort_by == "relevance" and order == "descending":
posts = sorted(posts, key=lambda post: post.views, reverse=True)
users = sorted(users, key=lambda user: user.username, reverse=True)
comments = sorted(comments, key=lambda comment: comment.id, reverse=True)
- elif sort_by == 'date' and order == 'ascending':
+ elif sort_by == "date" and order == "ascending":
posts = sorted(posts, key=lambda post: post.date)
users = sorted(users, key=lambda user: user.date_joined)
comments = sorted(comments, key=lambda comment: comment.created_at)
- elif sort_by == 'date' and order == 'descending':
+ elif sort_by == "date" and order == "descending":
posts = sorted(posts, key=lambda post: post.date, reverse=True)
users = sorted(users, key=lambda user: user.date_joined, reverse=True)
- comments = sorted(comments, key=lambda comment: comment.created_at, reverse=True)
+ comments = sorted(
+ comments, key=lambda comment: comment.created_at, reverse=True
+ )
# filter by date_range
- if date_range == 'past_day':
+ if date_range == "past_day":
posts = [post for post in posts if post.date >= now - timedelta(days=1)]
- users = [user for user in users if user.date_joined >= now - timedelta(days=1)]
- comments = [comment for comment in comments if comment.created_at >= now - timedelta(days=1)]
- elif date_range == 'past_week':
+ users = [
+ user for user in users if user.date_joined >= now - timedelta(days=1)
+ ]
+ comments = [
+ comment
+ for comment in comments
+ if comment.created_at >= now - timedelta(days=1)
+ ]
+ elif date_range == "past_week":
posts = [post for post in posts if post.date >= now - timedelta(days=7)]
- users = [user for user in users if user.date_joined >= now - timedelta(days=7)]
- comments = [comment for comment in comments if comment.created_at >= now - timedelta(days=7)]
- elif date_range == 'past_month':
+ users = [
+ user for user in users if user.date_joined >= now - timedelta(days=7)
+ ]
+ comments = [
+ comment
+ for comment in comments
+ if comment.created_at >= now - timedelta(days=7)
+ ]
+ elif date_range == "past_month":
posts = [post for post in posts if post.date >= now - timedelta(days=30)]
- users = [user for user in users if user.date_joined >= now - timedelta(days=30)]
- comments = [comment for comment in comments if comment.created_at >= now - timedelta(days=30)]
- elif date_range == 'past_year':
+ users = [
+ user for user in users if user.date_joined >= now - timedelta(days=30)
+ ]
+ comments = [
+ comment
+ for comment in comments
+ if comment.created_at >= now - timedelta(days=30)
+ ]
+ elif date_range == "past_year":
posts = [post for post in posts if post.date >= now - timedelta(days=365)]
- users = [user for user in users if user.date_joined >= now - timedelta(days=365)]
- comments = [comment for comment in comments if comment.created_at >= now - timedelta(days=365)]
- elif date_range == 'any':
+ users = [
+ user for user in users if user.date_joined >= now - timedelta(days=365)
+ ]
+ comments = [
+ comment
+ for comment in comments
+ if comment.created_at >= now - timedelta(days=365)
+ ]
+ elif date_range == "any":
# no need to filter
pass
-
+
search_results = len(posts) + len(users) + len(comments)
else:
search_results = 0
@@ -494,15 +798,31 @@ def search(request):
for comment in comments:
comment.body = comment_processor(comment.body)
- return render(request, 'blog/search.html', {'title': f"Search results for '{query}'", 'query': query, 'search_results': search_results, 'search_in': search_in, 'sort_by': sort_by, 'order': order, 'date_range': date_range, 'posts': posts, 'users': users, 'comments': comments})
+ return render(
+ request,
+ "blog/search.html",
+ {
+ "title": f"Search results for '{query}'",
+ "query": query,
+ "search_results": search_results,
+ "search_in": search_in,
+ "sort_by": sort_by,
+ "order": order,
+ "date_range": date_range,
+ "posts": posts,
+ "users": users,
+ "comments": comments,
+ },
+ )
+
def articles(request, date=None, cg=None):
- type = 'articles'
- page = request.GET.get('page') if request.GET.get('page') else 1
- order_by = request.GET.get('order_by') if request.GET.get('order_by') else 'date'
- direction = request.GET.get('direction') if request.GET.get('direction') else 'desc'
+ type = "articles"
+ page = request.GET.get("page") if request.GET.get("page") else 1
+ order_by = request.GET.get("order_by") if request.GET.get("order_by") else "date"
+ direction = request.GET.get("direction") if request.GET.get("direction") else "desc"
categories = Category.objects.all()
- category = request.GET.get('category')
+ category = request.GET.get("category")
try:
page = int(page)
except:
@@ -511,28 +831,35 @@ def articles(request, date=None, cg=None):
posts = Post.objects.filter(is_public=True)
if date:
- date_month = date.split('_')[0] # month name like 'Decemeber'
- date_year = date.split('_')[1] # year like '2019'
- date_m = datetime.strptime(date_month, '%B').month # convert month name to month number
- posts = Post.objects.filter(is_public=True, date__month=date_m, date__year=date_year)
- type = 'articles-archive'
- date = date_month + ' ' + date_year
-
+ date_month = date.split("_")[0] # month name like 'Decemeber'
+ date_year = date.split("_")[1] # year like '2019'
+ date_m = datetime.strptime(
+ date_month, "%B"
+ ).month # convert month name to month number
+ posts = Post.objects.filter(
+ is_public=True, date__month=date_m, date__year=date_year
+ )
+ type = "articles-archive"
+ date = date_month + " " + date_year
+
if cg:
cg = str.lower(cg)
- if category and cg != category and category != 'all':
- return redirect(reverse('blog:categories') + '/{}'.format(category))
+ if category and cg != category and category != "all":
+ return redirect(reverse("blog:categories") + "/{}".format(category))
category = cg
posts = Post.objects.filter(is_public=True, category__slug=cg)
- type = 'articles-category'
-
-
- posts = posts.order_by('-' + order_by) if direction == 'desc' else Post.objects.filter(is_public=True).order_by(order_by)
- if category and category != 'all':
+ type = "articles-category"
+
+ posts = (
+ posts.order_by("-" + order_by)
+ if direction == "desc"
+ else Post.objects.filter(is_public=True).order_by(order_by)
+ )
+ if category and category != "all":
posts = posts.filter(category__slug=category)
category_name = Category.objects.get(slug=category).name
else:
- category = 'all'
+ category = "all"
posts = Paginator(posts, 10)
num_pages = posts.num_pages
try:
@@ -543,99 +870,159 @@ def articles(request, date=None, cg=None):
for post in posts:
post.excerpt = add_excerpt(post)
post.num_comments = add_num_comments(post)
- return render(request, 'blog/articles.html', {'title': 'Articles', 'posts': posts, 'num_pages': num_pages, 'page': page, 'order_by': order_by, 'direction': direction, 'categories': categories, 'category': category, 'category_name': category_name if category != 'all' else '', 'type': type, 'date': date if date else '', 'cg': cg if cg else ''})
+ return render(
+ request,
+ "blog/articles.html",
+ {
+ "title": "Articles",
+ "posts": posts,
+ "num_pages": num_pages,
+ "page": page,
+ "order_by": order_by,
+ "direction": direction,
+ "categories": categories,
+ "category": category,
+ "category_name": category_name if category != "all" else "",
+ "type": type,
+ "date": date if date else "",
+ "cg": cg if cg else "",
+ },
+ )
+
def user_activity(request, username):
try:
user = User.objects.get(username__iexact=username)
user_profile = UserProfile.objects.get(user=user)
if user_profile.is_public or user == request.user:
- recent_comments = Comment.objects.filter(user=user).order_by('-created_at')[:5]
+ recent_comments = Comment.objects.filter(user=user).order_by("-created_at")[
+ :5
+ ]
else:
recent_comments = []
if user_profile.email_public:
user_email = user.email
else:
- user_email = ''
+ user_email = ""
for comment in recent_comments:
comment.body = comment_processor(comment.body)
- return render(request, 'blog/activity.html', {'title': 'User Activity', 'activity_user': user, 'activity_user_profile': user_profile, 'activity_recent_comments': recent_comments, 'activity_user_email': user_email})
+ return render(
+ request,
+ "blog/activity.html",
+ {
+ "title": "User Activity",
+ "activity_user": user,
+ "activity_user_profile": user_profile,
+ "activity_recent_comments": recent_comments,
+ "activity_user_email": user_email,
+ },
+ )
except User.DoesNotExist:
# return default 404 page
raise Http404
+
def archives(request):
- archives = Post.objects.filter(is_public=True).dates('date', 'month', order='DESC')
- return render(request, 'blog/archives.html', {'title': 'Archives', 'archives': archives})
+ archives = Post.objects.filter(is_public=True).dates("date", "month", order="DESC")
+ return render(
+ request, "blog/archives.html", {"title": "Archives", "archives": archives}
+ )
+
def categories(request):
categories = Category.objects.all()
- return render(request, 'blog/categories.html', {'title': 'Categories', 'categories': categories})
+ return render(
+ request,
+ "blog/categories.html",
+ {"title": "Categories", "categories": categories},
+ )
+
def policy(request):
- return render(request, 'blog/site_policy.html', {'title': 'Site Policy'})
+ return render(request, "blog/site_policy.html", {"title": "Site Policy"})
+
def socialify(request):
- url = request.GET.get('url') if request.GET.get('url') else None
+ url = request.GET.get("url") if request.GET.get("url") else None
if url:
# convert Github URL to repo owner/name
- if 'github.com' in url:
- url = url.split('github.com/')[1]
- url = url.split('/')
- url = url[0] + '/' + url[1]
+ if "github.com" in url:
+ url = url.split("github.com/")[1]
+ url = url.split("/")
+ url = url[0] + "/" + url[1]
socialify_options = {
- 'theme': 'Dark' if not request.GET.get('theme') else request.GET.get('theme'),
- 'font': 'Inter' if not request.GET.get('font') else request.GET.get('font'),
- 'description': 0 if not request.GET.get('description') else request.GET.get('description'),
- 'forks': 0 if not request.GET.get('forks') else request.GET.get('forks'),
- 'issues': 0 if not request.GET.get('issues') else request.GET.get('issues'),
- 'language_1': 0 if not request.GET.get('language_1') else request.GET.get('language_1'),
- 'language_2': 0 if not request.GET.get('language_2') else request.GET.get('language_2'),
- 'name': 0 if not request.GET.get('name') else request.GET.get('name'),
- 'owner': 1 if not request.GET.get('owner') else request.GET.get('owner'),
- 'stargazers': 0 if not request.GET.get('stargazers') else request.GET.get('stargazers'),
- 'pulls': 0 if not request.GET.get('pulls') else request.GET.get('pulls'),
- 'pattern': 'Plus' if not request.GET.get('pattern') else request.GET.get('pattern'),
+ "theme": "Dark" if not request.GET.get("theme") else request.GET.get("theme"),
+ "font": "Inter" if not request.GET.get("font") else request.GET.get("font"),
+ "description": (
+ 0 if not request.GET.get("description") else request.GET.get("description")
+ ),
+ "forks": 0 if not request.GET.get("forks") else request.GET.get("forks"),
+ "issues": 0 if not request.GET.get("issues") else request.GET.get("issues"),
+ "language_1": (
+ 0 if not request.GET.get("language_1") else request.GET.get("language_1")
+ ),
+ "language_2": (
+ 0 if not request.GET.get("language_2") else request.GET.get("language_2")
+ ),
+ "name": 0 if not request.GET.get("name") else request.GET.get("name"),
+ "owner": 1 if not request.GET.get("owner") else request.GET.get("owner"),
+ "stargazers": (
+ 0 if not request.GET.get("stargazers") else request.GET.get("stargazers")
+ ),
+ "pulls": 0 if not request.GET.get("pulls") else request.GET.get("pulls"),
+ "pattern": (
+ "Plus" if not request.GET.get("pattern") else request.GET.get("pattern")
+ ),
}
for key, value in socialify_options.items():
- if value == 'on':
+ if value == "on":
socialify_options[key] = 1
- elif value == 'off':
+ elif value == "off":
socialify_options[key] = 0
- return render(request, 'blog/socialify.html', {'title': 'Socialify', 'options': socialify_options, 'url': url})
+ return render(
+ request,
+ "blog/socialify.html",
+ {"title": "Socialify", "options": socialify_options, "url": url},
+ )
+
def anilist(request):
- return render(request, 'blog/anilist.html', {'title': 'My Anime List'})
+ return render(request, "blog/anilist.html", {"title": "My Anime List"})
+
@xframe_options_sameorigin
def anidata(request):
- malURL = 'https://myanimelist.net/animelist/crvs'
+ malURL = "https://myanimelist.net/animelist/crvs"
MAL = requests.get(malURL)
MALContent = MAL.content
MALStatus = MAL.status_code
-
+
if MALStatus != 200:
MALContent = '<html><head><link rel="stylesheet" href="/static/css/styles.css"><style>img { width: 75%; display: block; margin: -20px auto 20px auto; } h1 { text-align: center; } p { text-align: center; } body {background: transparent !important;} </style></head><body><img src="/static/images/site/sad-failure.gif" alt="Sad Failure"><h1>MyAnimeList does not seem to respond at the moment.</h1><p>Maybe, we go <a href="https://myanimelist.net/animelist/crvs" target="_blank">knock on their door</a> instead?</p></body></html>'
else:
- MALContent = MALContent.decode('utf-8')
- MALParsed = BeautifulSoup(MALContent, 'html.parser')
+ MALContent = MALContent.decode("utf-8")
+ MALParsed = BeautifulSoup(MALContent, "html.parser")
# remove script tags
- for tag in MALParsed(['script', 'meta', 'noscript']):
+ for tag in MALParsed(["script", "meta", "noscript"]):
tag.extract()
# add myanimelist.net to relative links
- for link in MALParsed.find_all('a'):
- if link.get('href') and link.get('href')[0] == '/':
- link['href'] = 'https://myanimelist.net' + link['href']
+ for link in MALParsed.find_all("a"):
+ if link.get("href") and link.get("href")[0] == "/":
+ link["href"] = "https://myanimelist.net" + link["href"]
# make all links open in new tab
- link['target'] = '_blank'
+ link["target"] = "_blank"
MALContent = MALParsed.prettify()
- return render(request, 'blog/anidata.html', {'title': 'My Anime List', 'MALContent': MALContent})
+ return render(
+ request,
+ "blog/anidata.html",
+ {"title": "My Anime List", "MALContent": MALContent},
+ )
diff --git a/static/css/login-area.css b/static/css/login-area.css
index ceae4de3..a707919c 100644
--- a/static/css/login-area.css
+++ b/static/css/login-area.css
@@ -1,101 +1,116 @@
#login-area {
- width: 250px;
- height: 350px;
- background: url('../images/backgrounds/login-area.png') no-repeat;
- background-size: 250px 350px;
- margin: auto;
- padding: 0px;
- border: 0px;
- position: relative;
+ width: 250px;
+ height: 350px;
+ background: url("../images/backgrounds/login-area.png") no-repeat;
+ background-size: 250px 350px;
+ margin: auto;
+ padding: 0px;
+ border: 0px;
+ position: relative;
}
#login-form {
- display: block;
- padding-top: 151px;
- padding-left: 20px;
- padding-right: 20px;
+ display: block;
+ padding-top: 151px;
+ padding-left: 20px;
+ padding-right: 20px;
}
-#login-form input[type=text], #login-form input[type=password] {
- display: block;
- margin: 0px auto 18px auto;
- width: 180px;
- font-size: 12px;
- padding: 4px 8px;
- background: transparent;
- color: #fff;
- border-radius: 4px;
+#login-form input[type="text"],
+#login-form input[type="password"] {
+ display: block;
+ margin: 0px auto 18px auto;
+ width: 180px;
+ font-size: 12px;
+ padding: 4px 8px;
+ background: transparent;
+ color: #fff;
+ border-radius: 4px;
}
/* Reset auto fill */
#login-form input:-webkit-autofill,
-#login-form input:-webkit-autofill:hover,
-#login-form input:-webkit-autofill:focus,
-#login-form input:-webkit-autofill:active{
- transition: background-color 1s ease-in 2000s;
+#login-form input:-webkit-autofill:hover,
+#login-form input:-webkit-autofill:focus,
+#login-form input:-webkit-autofill:active {
+ transition: background-color 1s ease-in 2000s;
}
#login-form input::placeholder {
- color: #dcdcdc;
+ color: #dcdcdc;
}
-#login-form input[type=submit] {
- width: 131px;
- height: 26px;
- cursor: pointer;
- position: absolute;
- bottom: 55px;
- left: 61px;
- border-radius: 2px;
- background: transparent;
+#login-form input[type="submit"] {
+ width: 131px;
+ height: 26px;
+ cursor: pointer;
+ position: absolute;
+ bottom: 55px;
+ left: 61px;
+ border-radius: 2px;
+ background: transparent;
}
#login-area > #register-now-button {
- display: block;
- width: 60px;
- height: 12px;
- cursor: pointer;
- border-radius: 2px;
- position: absolute;
- bottom: 30px;
- right: 40px;
- background: transparent;
+ display: block;
+ width: 60px;
+ height: 12px;
+ cursor: pointer;
+ border-radius: 2px;
+ position: absolute;
+ bottom: 30px;
+ right: 40px;
+ background: transparent;
+}
+
+#login-area > #forgot-password-button {
+ display: block;
+ width: 80px;
+ height: 12px;
+ cursor: pointer;
+ border-radius: 2px;
+ position: absolute;
+ bottom: 106px;
+ right: 26px;
+ background: transparent;
}
#login-error {
- position: relative;
+ position: relative;
}
#login-error > .RFEERR {
- background: url('../images/backgrounds/login-messages/RFEERR.png') no-repeat;
+ background: url("../images/backgrounds/login-messages/RFEERR.png") no-repeat;
}
#login-error > .IUOPERR {
- background: url('../images/backgrounds/login-messages/IUOPERR.png') no-repeat;
+ background: url("../images/backgrounds/login-messages/IUOPERR.png") no-repeat;
}
#login-error > .ENVERR {
- background: url('../images/backgrounds/login-messages/ENVERR.png') no-repeat;
+ background: url("../images/backgrounds/login-messages/ENVERR.png") no-repeat;
}
#login-error > .VESENDERR {
- background: url('../images/backgrounds/login-messages/VESENDERR.png') no-repeat;
+ background: url("../images/backgrounds/login-messages/VESENDERR.png")
+ no-repeat;
}
#login-error > .VESENT {
- background: url('../images/backgrounds/login-messages/VESENT.png') no-repeat;
+ background: url("../images/backgrounds/login-messages/VESENT.png") no-repeat;
}
#login-error > .VESUCCESS {
- background: url('../images/backgrounds/login-messages/VESUCCESS.png') no-repeat;
+ background: url("../images/backgrounds/login-messages/VESUCCESS.png")
+ no-repeat;
}
#login-error > .messageBox {
- position: absolute;
- background-size: 250px 166px;
- width: 250px;
- height: 166px;
- top: -100px;
- left: -140px;
- z-index: 2;
+ position: absolute;
+ background-size: 250px 166px;
+ width: 250px;
+ height: 166px;
+ top: -100px;
+ left: -140px;
+ z-index: 2;
}
diff --git a/templates/blog/partials/sidebar.html b/templates/blog/partials/sidebar.html
index d638f3b4..6f50ed56 100644
--- a/templates/blog/partials/sidebar.html
+++ b/templates/blog/partials/sidebar.html
@@ -34,6 +34,7 @@
<input type="submit" value="">
</form>
<a href="{% url 'blog:register' %}" id="register-now-button"></a>
+ <a href="{% url 'blog:forgotpassword' %}" id="forgot-password-button"></a>
</div>
{% endif %}
diff --git a/templates/blog/resetpass.html b/templates/blog/resetpass.html
new file mode 100644
index 00000000..7dbdab05
--- /dev/null
+++ b/templates/blog/resetpass.html
@@ -0,0 +1,24 @@
+{% extends 'blog/partials/base.html' %} {% block content %}
+<p>
+ Forgot your password? No problem! Just enter your email address below, (the
+ email you registered with) and I will send you a link to reset your password.
+</p>
+<hr />
+<form method="post">
+ <input
+ type="hidden"
+ name="csrfmiddlewaretoken"
+ value="{{ csrf_token }}"
+ style="display: none"
+ />
+ <table id="resetpass_form">
+ {{ form.as_table }}
+ </table>
+ <br />
+ <input type="submit" value="Reset password" class="button button-special" />
+</form>
+<br /><br />
+<p><i>Anonymous users cannot reset their password.</i></p>
+{% for message in messages %} {% if 'passwordReset' in message.tags %}
+<p><small class="success">{{ message.message }}</small></p>
+{% endif %} {% endfor %} {% endblock content %}
diff --git a/templates/blog/resetpass_input.html b/templates/blog/resetpass_input.html
new file mode 100644
index 00000000..7adc54d6
--- /dev/null
+++ b/templates/blog/resetpass_input.html
@@ -0,0 +1,23 @@
+{% extends 'blog/partials/base.html' %} {% block content %}
+<p>
+ Enter a new password for your account. Your password must be at least 8
+ characters long.
+</p>
+<hr />
+<form method="post">
+ <input
+ type="hidden"
+ name="csrfmiddlewaretoken"
+ value="{{ csrf_token }}"
+ style="display: none"
+ />
+ <table id="resetpass_form">
+ {{ form.as_table }}
+ </table>
+ <br />
+ <input type="submit" value="Reset password" class="button button-special" />
+</form>
+<br /><br />
+{% for message in messages %} {% if 'passwordReset' in message.tags %}
+<p><small class="success">{{ message.message }}</small></p>
+{% endif %} {% endfor %} {% endblock content %}
diff --git a/users/accountFunctions.py b/users/accountFunctions.py
index f5d77d72..38bc7099 100644
--- a/users/accountFunctions.py
+++ b/users/accountFunctions.py
@@ -28,12 +28,18 @@ def store_token(token_type, user, email=None):
token_store.save()
return uid, token
-def verify_token(token_type, uid, token):
+def verify_token(token_type, uid, token, hold_verification=False):
try:
token_store = TokenStore.objects.get(token_type=token_type, uid=uid, token=token)
if token_store.expires > timezone.now() and not token_store.verified and token_store.token_type == token_type and token_store.uid == uid and token_store.token == token:
+
+ if hold_verification:
+ return token_store
token_store.verified = True
- UserProfile.objects.filter(user=token_store.user).update(email_verified=True)
+
+ if token_type == "verifyemail":
+ UserProfile.objects.filter(user=token_store.user).update(email_verified=True)
+
token_store.save()
return token_store
diff --git a/users/forms.py b/users/forms.py
index e618cec8..016ad6bb 100644
--- a/users/forms.py
+++ b/users/forms.py
@@ -16,109 +16,221 @@ from .mail_send import send_email
class RegisterForm(forms.Form):
- username = forms.CharField(label='Username', max_length=30, min_length=4)
- email = forms.EmailField(label='Email')
- password1 = forms.CharField(label='Password', widget=forms.PasswordInput, min_length=8)
- password2 = forms.CharField(label='Password (again)', widget=forms.PasswordInput, min_length=8)
- captcha = forms.CharField(label='Captcha', max_length=6)
+ username = forms.CharField(label="Username", max_length=30, min_length=4)
+ email = forms.EmailField(label="Email")
+ password1 = forms.CharField(
+ label="Password", widget=forms.PasswordInput, min_length=8
+ )
+ password2 = forms.CharField(
+ label="Password (again)", widget=forms.PasswordInput, min_length=8
+ )
+ captcha = forms.CharField(label="Captcha", max_length=6)
expected_captcha = None
protected_usernames = [
- 'admin',
- 'administrator',
- 'root',
- 'thatcomputerscientist',
- 'skippy',
- 'system',
- 'test',
- 'user',
- 'webmaster',
- 'www',
- 'postmaster',
- 'hostmaster',
- 'info',
- 'support',
- 'anonymous',
- 'guest',
- 'nobody',
- 'someone',
- 'moderator',
- 'moderators',
- 'mods',
- 'crvs'
+ "admin",
+ "administrator",
+ "root",
+ "thatcomputerscientist",
+ "skippy",
+ "system",
+ "test",
+ "user",
+ "webmaster",
+ "www",
+ "postmaster",
+ "hostmaster",
+ "info",
+ "support",
+ "anonymous",
+ "guest",
+ "nobody",
+ "someone",
+ "moderator",
+ "moderators",
+ "mods",
+ "crvs",
]
- allowed_chars = string.ascii_letters + string.digits
+ allowed_chars = string.ascii_letters + string.digits
def __init__(self, *args, **kwargs):
- if 'expected_captcha' in kwargs:
- self.expected_captcha = kwargs.pop('expected_captcha')
+ if "expected_captcha" in kwargs:
+ self.expected_captcha = kwargs.pop("expected_captcha")
super().__init__(*args, **kwargs)
def clean(self):
cleaned_data = super().clean()
- password1 = cleaned_data.get('password1')
- password2 = cleaned_data.get('password2')
- captcha = cleaned_data.get('captcha')
+ password1 = cleaned_data.get("password1")
+ password2 = cleaned_data.get("password2")
+ captcha = cleaned_data.get("captcha")
if password1 and password2:
if password1 != password2:
- raise forms.ValidationError('Passwords do not match.')
+ raise forms.ValidationError("Passwords do not match.")
if len(password1) < 8:
- raise forms.ValidationError('Password must be at least 8 characters long.')
+ raise forms.ValidationError("Password must be at least 8 characters long.")
if str.lower(captcha) != str.lower(self.expected_captcha):
- raise forms.ValidationError('Captcha does not match.')
- if User.objects.filter(username=cleaned_data.get('username')).exists():
- raise forms.ValidationError('Username not available. Please choose another.')
- if cleaned_data.get('username').lower() in self.protected_usernames:
- raise forms.ValidationError('Username not available. Please choose another.')
- for char in cleaned_data.get('username'):
+ raise forms.ValidationError("Captcha does not match.")
+ if User.objects.filter(username=cleaned_data.get("username")).exists():
+ raise forms.ValidationError(
+ "Username not available. Please choose another."
+ )
+ if cleaned_data.get("username").lower() in self.protected_usernames:
+ raise forms.ValidationError(
+ "Username not available. Please choose another."
+ )
+ for char in cleaned_data.get("username"):
if char not in self.allowed_chars:
- raise forms.ValidationError('Username contains invalid characters. Only A-Z, a-z, and 0-9 are allowed.')
- if User.objects.filter(email=cleaned_data.get('email')).exists():
- raise forms.ValidationError('Email already exists. Please login if this account is yours.')
+ raise forms.ValidationError(
+ "Username contains invalid characters. Only A-Z, a-z, and 0-9 are allowed."
+ )
+ if User.objects.filter(email=cleaned_data.get("email")).exists():
+ raise forms.ValidationError(
+ "Email already exists. Please login if this account is yours."
+ )
return cleaned_data
def save(self, request):
user = User.objects.create_user(
- username=self.cleaned_data.get('username').lower(),
- email=self.cleaned_data.get('email').lower(),
- password=self.cleaned_data.get('password1'),
+ username=self.cleaned_data.get("username").lower(),
+ email=self.cleaned_data.get("email").lower(),
+ password=self.cleaned_data.get("password1"),
)
user.save()
user_profile = UserProfile.objects.create(user=user)
avatar_dir = choice(list(avatar_list().keys()))
avatar_file = choice(avatar_list()[avatar_dir])
- user_profile.avatar_url = avatar_dir + '/' + avatar_file.replace('.gif', '')
+ user_profile.avatar_url = avatar_dir + "/" + avatar_file.replace(".gif", "")
user_profile.save()
- uid, token = store_token(token_type='verifyemail', user=user, email=user.email)
+ uid, token = store_token(token_type="verifyemail", user=user, email=user.email)
# Send verification email
- subject = 'Verify your email address'
- message = render_to_string('verification_email.html', {
- 'user': user.username if user.first_name is None else user.first_name,
- 'site_name': 'Shifoo',
- 'uid': uid,
- 'token': token,
- 'protocol': 'https://' if request.is_secure() else 'http://',
- 'domain': request.get_host(),
- })
- message = strip_tags(message)
+ subject = "Verify your email address"
+ message = render_to_string(
+ "verification_email.html",
+ {
+ "user": user.username if user.first_name is None else user.first_name,
+ "site_name": "Shifoo",
+ "uid": uid,
+ "token": token,
+ "protocol": "https://" if request.is_secure() else "http://",
+ "domain": request.get_host(),
+ },
+ )
+ # message = strip_tags(message)
# send_mail(subject, message, 'Shifoo <' + settings.EMAIL_HOST_USER + '>', [user.email], fail_silently=False)
- if (send_email(sender='[email protected]', sender_name='Shifoo', recipient=user.email, subject=subject, body_html=message, body_text=message)):
+ if send_email(
+ sender="[email protected]",
+ sender_name="Shifoo",
+ recipient=user.email,
+ subject=subject,
+ body_html=message,
+ body_text=message,
+ ):
return user
else:
return user
-class UpdateUserDetailsForm(forms.Form):
- first_name = forms.CharField(label='First name', max_length=30, required=False, widget=forms.TextInput(attrs={'placeholder': 'First name'}))
- last_name = forms.CharField(label='Last name', max_length=30, required=False, widget=forms.TextInput(attrs={'placeholder': 'Last name'}))
- location = forms.CharField(label='Location', max_length=30, required=False, widget=forms.TextInput(attrs={'placeholder': 'Location'}))
- bio = forms.CharField(label='Bio', max_length=500, required=False, widget=forms.Textarea(attrs={'placeholder': 'Bio'}))
- is_public = forms.ChoiceField(label='Activity Visibility', choices=((True, 'Public'), (False, 'Private')), widget=forms.RadioSelect)
- email_public = forms.ChoiceField(label='Email Visibility', choices=((True, 'Public'), (False, 'Private')), widget=forms.RadioSelect)
+class ForgotPasswordForm(forms.Form):
+ email = forms.EmailField(label="Email", required=True)
+
+ def clean(self):
+ cleaned_data = super().clean()
+ return cleaned_data
+
+ def save(self, request):
+ email = self.cleaned_data.get("email")
+ user = User.objects.get(email=email)
+ uid, token = store_token(
+ token_type="resetpassword", user=user, email=user.email
+ )
+ subject = "Reset your password"
+ message = render_to_string(
+ "reset_password_email.html",
+ {
+ "user": user.username if user.first_name is None else user.first_name,
+ "site_name": "Shifoo",
+ "uid": uid,
+ "token": token,
+ "protocol": "https://" if request.is_secure() else "http://",
+ "domain": request.get_host(),
+ },
+ )
+ # message = strip_tags(message)
+ if send_email(
+ sender="[email protected]",
+ sender_name="Shifoo",
+ recipient=user.email,
+ subject=subject,
+ body_html=message,
+ body_text=message,
+ ):
+ return user
+ else:
+ raise forms.ValidationError("Failed to send email.")
+
+class ResetPasswordForm(forms.Form):
+ password1 = forms.CharField(
+ label="New Password", widget=forms.PasswordInput, min_length=8
+ )
+ password2 = forms.CharField(
+ label="New Password (again)", widget=forms.PasswordInput, min_length=8
+ )
+
+ def clean(self):
+ cleaned_data = super().clean()
+ password1 = cleaned_data.get("password1")
+ password2 = cleaned_data.get("password2")
+ if password1 and password2:
+ if password1 != password2:
+ raise forms.ValidationError("Passwords do not match.")
+ if len(password1) < 8:
+ raise forms.ValidationError("Password must be at least 8 characters long.")
+ return cleaned_data
+
+ def save(self, user):
+ user.set_password(self.cleaned_data.get("password1"))
+ user.save()
+ return user
+
+class UpdateUserDetailsForm(forms.Form):
+ first_name = forms.CharField(
+ label="First name",
+ max_length=30,
+ required=False,
+ widget=forms.TextInput(attrs={"placeholder": "First name"}),
+ )
+ last_name = forms.CharField(
+ label="Last name",
+ max_length=30,
+ required=False,
+ widget=forms.TextInput(attrs={"placeholder": "Last name"}),
+ )
+ location = forms.CharField(
+ label="Location",
+ max_length=30,
+ required=False,
+ widget=forms.TextInput(attrs={"placeholder": "Location"}),
+ )
+ bio = forms.CharField(
+ label="Bio",
+ max_length=500,
+ required=False,
+ widget=forms.Textarea(attrs={"placeholder": "Bio"}),
+ )
+ is_public = forms.ChoiceField(
+ label="Activity Visibility",
+ choices=((True, "Public"), (False, "Private")),
+ widget=forms.RadioSelect,
+ )
+ email_public = forms.ChoiceField(
+ label="Email Visibility",
+ choices=((True, "Public"), (False, "Private")),
+ widget=forms.RadioSelect,
+ )
def __init__(self, *args, **kwargs):
- self.user = kwargs.pop('user')
+ self.user = kwargs.pop("user")
super().__init__(*args, **kwargs)
def clean(self):
@@ -126,15 +238,15 @@ class UpdateUserDetailsForm(forms.Form):
return cleaned_data
def save(self):
- self.user.first_name = self.cleaned_data.get('first_name')
- self.user.last_name = self.cleaned_data.get('last_name')
+ self.user.first_name = self.cleaned_data.get("first_name")
+ self.user.last_name = self.cleaned_data.get("last_name")
self.user.save()
user_profile = UserProfile.objects.get(user=self.user)
- user_profile.location = self.cleaned_data.get('location')
- user_profile.bio = self.cleaned_data.get('bio')
- user_profile.is_public = self.cleaned_data.get('is_public')
- user_profile.email_public = self.cleaned_data.get('email_public')
+ user_profile.location = self.cleaned_data.get("location")
+ user_profile.bio = self.cleaned_data.get("bio")
+ user_profile.is_public = self.cleaned_data.get("is_public")
+ user_profile.email_public = self.cleaned_data.get("email_public")
user_profile.save()
- return (self.user, user_profile) \ No newline at end of file
+ return (self.user, user_profile)
diff --git a/users/mail_send.py b/users/mail_send.py
index be167eb1..df837a32 100644
--- a/users/mail_send.py
+++ b/users/mail_send.py
@@ -13,51 +13,55 @@ def send_email(sender, sender_name, recipient, subject, body_html, body_text):
# this is the approved sender email
SENDER = sender
SENDERNAME = sender_name
-
+
# Replace [email protected] with a "To" address. If your account
# is still in the sandbox, this address must be verified.
RECIPIENT = recipient
-
+
# Replace the USERNAME_SMTP value with your Email Delivery SMTP username.
USERNAME_SMTP = settings.USERNAME_SMTP
-
+
# Put the PASSWORD value from your Email Delivery SMTP password into the following file.
PASSWORD_SMTP = settings.PASSWORD_SMTP
-
+
# If you're using Email Delivery in a different region, replace the HOST value with an appropriate SMTP endpoint.
# Use port 25 or 587 to connect to the SMTP endpoint.
HOST = settings.EMAIL_HOST
PORT = settings.EMAIL_PORT
-
+
# The subject line of the email.
SUBJECT = subject
-
+
# The email body for recipients with non-HTML email clients.
BODY_TEXT = body_text
-
+
# The HTML body of the email.
BODY_HTML = body_html
# create message container
msg = EmailMessage()
- msg['Subject'] = SUBJECT
- msg['From'] = email.utils.formataddr((SENDERNAME, SENDER))
- msg['To'] = RECIPIENT
+ msg["Subject"] = SUBJECT
+ msg["From"] = email.utils.formataddr((SENDERNAME, SENDER))
+ msg["To"] = RECIPIENT
# make the message multi-part alternative, making the content the first part
- msg.add_alternative(BODY_TEXT, subtype='text')
+ msg.add_alternative(BODY_TEXT, subtype="text")
# this adds the additional part to the message
# According to RFC 2046, the last part of a multipart message, in this case
# the HTML message, is best and preferred.
- msg.add_alternative(BODY_HTML, subtype='html')
+ msg.add_alternative(BODY_HTML, subtype="html")
# Try to send the message.
- try:
+ try:
server = smtplib.SMTP(HOST, PORT)
server.ehlo()
# most python runtimes default to a set of trusted public CAs that will include the CA used by OCI Email Delivery.
# However, on platforms lacking that default (or with an outdated set of CAs), customers may need to provide a capath that includes our public CA.
- server.starttls(context=ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH, cafile=None, capath=None))
+ server.starttls(
+ context=ssl.create_default_context(
+ purpose=ssl.Purpose.SERVER_AUTH, cafile=None, capath=None
+ )
+ )
# smtplib docs recommend calling ehlo() before & after starttls()
server.ehlo()
server.login(USERNAME_SMTP, PASSWORD_SMTP)
@@ -68,4 +72,4 @@ def send_email(sender, sender_name, recipient, subject, body_html, body_text):
except Exception as e:
return e
else:
- return True \ No newline at end of file
+ return True
diff --git a/users/templates/email_change_verification_email.html b/users/templates/email_change_verification_email.html
index 3db0a9d6..f6f9b127 100644
--- a/users/templates/email_change_verification_email.html
+++ b/users/templates/email_change_verification_email.html
@@ -1,12 +1,27 @@
-{% autoescape off %}
-Hi {{ user }},
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>Change Email</title>
+ </head>
+ <body>
+ <h3>Change Your Current Email</h3>
+ <p>Hi {{ user }},</p>
+ <p>
+ We received a request to change your email address on {{ site_name }}. To
+ verify and change your email address, please click the link below.
+ </p>
+ <a
+ href="{{ protocol }}{{ domain }}{% url 'users:changeemail' 'changeemail' uid token %}"
+ >Change Email</a
+ >
+ <p>If the above link does not work, copy and paste the URL below into your browser:</p>
+ <a href="{{ protocol }}{{ domain }}{% url 'users:changeemail' 'changeemail' uid token %}">{{ protocol }}{{ domain }}{% url 'users:changeemail' 'changeemail' uid token %}</a>
-We received a request to change you email address on {{ site_name }}. To verify and change your email address, please click the link below.
-{{ protocol }}{{ domain }}{% url 'users:changeemail' 'changeemail' uid token %}
-
-Please ignore this email if you did not make this request.
-
-Thanks,
-{{ site_name }} Team
-
-{% endautoescape %} \ No newline at end of file
+ <p>Please ignore this email if you did not make this request.</p>
+ <p>Thanks,</p>
+ <p>Bobby from {{ site_name }}</p>
+ </body>
+</html>
diff --git a/users/templates/reset_password_email.html b/users/templates/reset_password_email.html
new file mode 100644
index 00000000..f9651a13
--- /dev/null
+++ b/users/templates/reset_password_email.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>Reset Password</title>
+ </head>
+ <body>
+ <h3>Reset Your Current Password</h3>
+ <p>Hi {{ user }},</p>
+ <p>
+ We received a request to reset your password on {{ site_name }}. To reset
+ your password, please click the link below.
+ </p>
+ <a href="{{ protocol }}{{ domain }}{% url 'blog:resetpassword' uid token %}"
+ >Reset Password</a
+ >
+ <p>If the above link does not work, copy and paste the URL below into your browser:</p>
+ <a href="{{ protocol }}{{ domain }}{% url 'blog:resetpassword' uid token %}">{{ protocol }}{{ domain }}{% url 'blog:resetpassword' uid token %}</a>
+ <p>Please ignore this email if you did not make this request.</p>
+ <p>Thanks,</p>
+ <p>Bobby from {{ site_name }}</p>
+ </body>
+</html>
diff --git a/users/templates/verification_email.html b/users/templates/verification_email.html
index cd85ab38..06e91a79 100644
--- a/users/templates/verification_email.html
+++ b/users/templates/verification_email.html
@@ -1,10 +1,26 @@
-{% autoescape off %}
-Hi {{ user }},
-
-Thanks for registering an account on {{ site_name }}. To verify your email address, please click the link below.
-{{ protocol }}{{ domain }}{% url 'users:changeemail' 'verifyemail' uid token %}
-
-Thanks,
-{{ site_name }} Team
-{% endautoescape %}
-
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>Verify Email</title>
+ </head>
+ <body>
+ <h1>Verify Your New Account</h1>
+ <p>Hi {{ user }},</p>
+ <p>
+ Thanks for registering an account on {{ site_name }}. To verify your email
+ address, please click the link below.
+ </p>
+ <a
+ href="{{ protocol }}{{ domain }}{% url 'users:changeemail' 'verifyemail' uid token %}"
+ >Verify Email</a
+ >
+ <p>If the above link does not work, copy and paste the URL below into your browser:</p>
+ <a href="{{ protocol }}{{ domain }}{% url 'users:changeemail' 'verifyemail' uid token %}">{{ protocol }}{{ domain }}{% url 'users:changeemail' 'verifyemail' uid token %}</a>
+ <p>Please ignore this email if you did not make this request.</p>
+ <p>Thanks,</p>
+ <p>Bobby from {{ site_name }}</p>
+ </body>
+</html>
diff --git a/users/urls.py b/users/urls.py
index 98dff57c..b7081e42 100644
--- a/users/urls.py
+++ b/users/urls.py
@@ -3,22 +3,29 @@ from django.urls import path
from . import views
-app_name = 'users'
+app_name = "users"
urlpatterns = [
- path('/login', views.login_user, name='login'),
- path('/logout', views.logout_user, name='logout'),
- path('/update', views.update_user, name='update'),
- path('/changepassword', views.change_password, name='changepassword'),
- path('/sendchangeuseremail', views.send_change_user_email, name='sendchangeuseremail'),
- path('/sendverificationemail', views.send_verification_email, name='sendverificationemail'),
- path('/updateavatar', views.update_avatar, name='updateavatar'),
- path('/updateblinkies', views.update_blinkie, name='updateblinkie'),
- path('/delete', views.delete_user, name='delete'),
- path('/<mode>/<uid>/<token>', views.verify_email, name='verifyemail'),
- path('/<mode>/<uid>/<token>', views.verify_email, name='changeemail'),
+ path("/login", views.login_user, name="login"),
+ path("/logout", views.logout_user, name="logout"),
+ path("/update", views.update_user, name="update"),
+ path("/changepassword", views.change_password, name="changepassword"),
+ path(
+ "/sendchangeuseremail", views.send_change_user_email, name="sendchangeuseremail"
+ ),
+ path(
+ "/sendverificationemail",
+ views.send_verification_email,
+ name="sendverificationemail",
+ ),
+ path("/updateavatar", views.update_avatar, name="updateavatar"),
+ path("/updateblinkies", views.update_blinkie, name="updateblinkie"),
+ path("/delete", views.delete_user, name="delete"),
+ path("/<mode>/<uid>/<token>", views.verify_email, name="verifyemail"),
+ path("/<mode>/<uid>/<token>", views.verify_email, name="changeemail"),
+ path("/resetpassword/<uid>/<token>", views.reset_password, name="resetpassword"),
]
# Configure Admin Site
-admin.site.site_header = 'Shifoo Administation'
-admin.site.site_title = 'Shifoo'
-admin.site.index_title = 'Administration Area'
+admin.site.site_header = "Shifoo Administation"
+admin.site.site_title = "Shifoo"
+admin.site.index_title = "Administration Area"
diff --git a/users/views.py b/users/views.py
index 7c166911..5dba135b 100644
--- a/users/views.py
+++ b/users/views.py
@@ -1,6 +1,5 @@
from django.contrib import messages
-from django.contrib.auth import (authenticate, login, logout,
- update_session_auth_hash)
+from django.contrib.auth import authenticate, login, logout, update_session_auth_hash
from django.contrib.auth.models import User
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import redirect, reverse
@@ -16,14 +15,14 @@ from .models import UserProfile
# Create your views here.
def login_user(request):
# pass
- next = request.POST.get('next', 'blog:home')
- username = request.POST['username']
- password = request.POST['password']
- if username == '' or password == '' or username is None or password is None:
+ next = request.POST.get("next", "blog:home")
+ username = request.POST["username"]
+ password = request.POST["password"]
+ if username == "" or password == "" or username is None or password is None:
# required fields are empty
- messages.error(request, 'RFEERR', extra_tags='loginError')
- return HttpResponseRedirect(next + '?username=' + username)
- else:
+ messages.error(request, "RFEERR", extra_tags="loginError")
+ return HttpResponseRedirect(next + "?username=" + username)
+ else:
# check if email is verified
user = authenticate(request, username=username, password=password)
if user is not None:
@@ -37,195 +36,265 @@ def login_user(request):
return HttpResponseRedirect(next)
else:
# email not verified
- messages.error(request, 'ENVERR', extra_tags='loginError')
- return HttpResponseRedirect(next + '?username=' + username)
+ messages.error(request, "ENVERR", extra_tags="loginError")
+ return HttpResponseRedirect(next + "?username=" + username)
else:
# invalid credentials
- messages.error(request, 'IUOPERR', extra_tags='loginError')
- return HttpResponseRedirect(next + '?username=' + username)
+ messages.error(request, "IUOPERR", extra_tags="loginError")
+ return HttpResponseRedirect(next + "?username=" + username)
+
def logout_user(request):
logout(request)
- return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
+ return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
+
def update_user(request):
user = request.user
if user is not None:
- if request.method == 'POST':
+ if request.method == "POST":
form = UpdateUserDetailsForm(request.POST, user=user)
if form.is_valid():
form.save()
- messages.success(request, 'Profile was successfully updated!')
- return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
+ messages.success(request, "Profile was successfully updated!")
+ return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
else:
- messages.error(request, 'Unable to update profile! Please try again later.')
- return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
+ messages.error(
+ request, "Unable to update profile! Please try again later."
+ )
+ return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
else:
- return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
+ return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
else:
- messages.error(request, 'You must be logged in to update your profile!')
- return redirect('blog:home')
+ messages.error(request, "You must be logged in to update your profile!")
+ return redirect("blog:home")
+
def delete_user(request):
user = request.user
if user is not None:
- if request.method == 'POST':
- password = request.POST['password']
+ if request.method == "POST":
+ password = request.POST["password"]
if user.check_password(password):
# delete user, all comments, user profile details, and all posts
user.delete()
- messages.success(request, 'Your account was successfully deleted!')
- return redirect('blog:home')
+ messages.success(request, "Your account was successfully deleted!")
+ return redirect("blog:home")
else:
- messages.error(request, 'Incorrect password!')
- return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
+ messages.error(request, "Incorrect password!")
+ return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
else:
- messages.error(request, 'Unable to delete account! Please try again later.')
- return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
+ messages.error(request, "Unable to delete account! Please try again later.")
+ return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
else:
- messages.error(request, 'You must be logged in to delete your account!')
- return redirect('blog:home')
+ messages.error(request, "You must be logged in to delete your account!")
+ return redirect("blog:home")
+
def update_avatar(request):
user = request.user
if user is not None:
- if request.method == 'POST':
+ if request.method == "POST":
user_profile = UserProfile.objects.get(user=user)
- user_profile.avatar_url = request.POST['avatar']
+ user_profile.avatar_url = request.POST["avatar"]
user_profile.save()
- messages.success(request, 'Avatar was successfully updated!')
- return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
+ messages.success(request, "Avatar was successfully updated!")
+ return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
else:
- messages.error(request, 'Unable to update avatar! Please try again later.')
- return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
+ messages.error(request, "Unable to update avatar! Please try again later.")
+ return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
else:
- messages.error(request, 'You must be logged in to update your avatar!')
- return redirect('blog:home')
+ messages.error(request, "You must be logged in to update your avatar!")
+ return redirect("blog:home")
+
def update_blinkie(request):
- user = request.user
+ user = request.user
if user is not None:
- if request.method == 'POST':
+ if request.method == "POST":
user_profile = UserProfile.objects.get(user=user)
- user_profile.blinkie_url = request.POST['blinkie']
+ user_profile.blinkie_url = request.POST["blinkie"]
user_profile.save()
- messages.success(request, 'Blinkie was successfully updated!')
- return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
+ messages.success(request, "Blinkie was successfully updated!")
+ return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
else:
- messages.error(request, 'Unable to update blinkie! Please try again later.')
- return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
+ messages.error(request, "Unable to update blinkie! Please try again later.")
+ return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
else:
- messages.error(request, 'You must be logged in to update your blinkie!')
- return redirect('blog:home')
-
+ messages.error(request, "You must be logged in to update your blinkie!")
+ return redirect("blog:home")
+
+
def change_password(request):
username = request.user
- old_password = request.POST['oldPassword']
- new_password = request.POST['newPassword']
- confirm_password = request.POST['confirmPassword']
+ old_password = request.POST["oldPassword"]
+ new_password = request.POST["newPassword"]
+ confirm_password = request.POST["confirmPassword"]
if username is not None:
user = User.objects.get(username=username)
if user.check_password(old_password):
if new_password == confirm_password:
if len(new_password) < 8:
- messages.error(request, 'The new password must be at least 8 characters long!')
- return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
+ messages.error(
+ request, "The new password must be at least 8 characters long!"
+ )
+ return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
user.set_password(new_password)
user.save()
update_session_auth_hash(request, user)
- messages.success(request, 'Password was successfully changed!')
- return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
+ messages.success(request, "Password was successfully changed!")
+ return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
else:
- messages.error(request, 'The new password and confirmation password do not match!')
- return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
+ messages.error(
+ request, "The new password and confirmation password do not match!"
+ )
+ return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
else:
- messages.error(request, 'Old password is incorrect!')
- return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
+ messages.error(request, "Old password is incorrect!")
+ return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
else:
- messages.error(request, 'Unable to change password! Please try again later.')
- return redirect('blog:home')
+ messages.error(request, "Unable to change password! Please try again later.")
+ return redirect("blog:home")
+
def send_change_user_email(request):
user = request.user
- new_email = request.POST['email']
+ new_email = request.POST["email"]
if user is not None:
# Check if the new and the old email are the same
if user.email == new_email:
- messages.error(request, 'New email is the same as the old one!')
- return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
+ messages.error(request, "New email is the same as the old one!")
+ return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
# check if email is already in use
if User.objects.filter(email=new_email).exists():
- messages.error(request, 'Email is already in use!')
+ messages.error(request, "Email is already in use!")
# Redirect to referrer
- return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
+ return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
# Send verification email
- subject = 'Verify your email address'
- uid, token = store_token(token_type='changeemail', user=user, email=new_email)
-
- message = render_to_string('email_change_verification_email.html', {
- 'user': user.username if user.first_name is None else user.first_name,
- 'site_name': 'Shifoo',
- 'uid': uid,
- 'token': token,
- 'protocol': request.scheme + '://',
- 'domain': request.get_host(),
- })
- message = strip_tags(message)
+ subject = "Change your email address"
+ uid, token = store_token(token_type="changeemail", user=user, email=new_email)
+
+ message = render_to_string(
+ "email_change_verification_email.html",
+ {
+ "user": user.username if user.first_name is None else user.first_name,
+ "site_name": "Shifoo",
+ "uid": uid,
+ "token": token,
+ "protocol": request.scheme + "://",
+ "domain": request.get_host(),
+ },
+ )
+ # message = strip_tags(message)
# send_mail(subject, message, 'That Computer Scientist <' + settings.EMAIL_HOST_USER + '>', [new_email])
- if (send_email(sender='[email protected]', sender_name='Shifoo', recipient=new_email, subject=subject, body_html=message, body_text=message)):
- messages.success(request, 'Verification email was sent! Please check your email.')
- return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
+ if send_email(
+ sender="[email protected]",
+ sender_name="Shifoo",
+ recipient=new_email,
+ subject=subject,
+ body_html=message,
+ body_text=message,
+ ):
+ messages.success(
+ request, "Verification email was sent! Please check your email."
+ )
+ return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
else:
- messages.error(request, 'Unable to change email! Please try again later.')
- return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
-
+ messages.error(request, "Unable to change email! Please try again later.")
+ return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
+
else:
- messages.error(request, 'Unable to change email! Please try again later.')
- return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
-
+ messages.error(request, "Unable to change email! Please try again later.")
+ return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
+
+
def send_verification_email(request):
# this is a post only view
- if request.method == 'POST':
- username = request.POST.get('username')
- subject = 'Verify your email address'
+ if request.method == "POST":
+ username = request.POST.get("username")
+ subject = "Verify your email address"
user = User.objects.get(username=username)
- uid, token = store_token(token_type='verifyemail', user=user, email=user.email)
-
- message = render_to_string('verification_email.html', {
- 'user': user.username if user.first_name is None else user.first_name,
- 'site_name': 'Shifoo',
- 'uid': uid,
- 'token': token,
- 'protocol': 'https://' if request.is_secure() else 'http://',
- 'domain': request.get_host(),
- })
- message = strip_tags(message)
- if (send_email(sender='[email protected]', sender_name='Shifoo', recipient=user.email, subject=subject, body_html=message, body_text=message)):
- messages.success(request, 'VESENT', extra_tags='loginError')
- return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
+ uid, token = store_token(token_type="verifyemail", user=user, email=user.email)
+
+ message = render_to_string(
+ "verification_email.html",
+ {
+ "user": user.username if user.first_name is None else user.first_name,
+ "site_name": "Shifoo",
+ "uid": uid,
+ "token": token,
+ "protocol": "https://" if request.is_secure() else "http://",
+ "domain": request.get_host(),
+ },
+ )
+ # message = strip_tags(message)
+ if send_email(
+ sender="[email protected]",
+ sender_name="Shifoo",
+ recipient=user.email,
+ subject=subject,
+ body_html=message,
+ body_text=message,
+ ):
+ messages.success(request, "VESENT", extra_tags="loginError")
+ return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
else:
- messages.error(request, 'VESENDERR', extra_tags='loginError')
- return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
+ messages.error(request, "VESENDERR", extra_tags="loginError")
+ return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
else:
- messages.error(request, 'VESENDERR', extra_tags='loginError')
- return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
+ messages.error(request, "VESENDERR", extra_tags="loginError")
+ return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
+
def verify_email(request, mode, uid, token):
token_object = verify_token(mode, uid, token)
- redirect_to = reverse('blog:account') + '?tab=email' if mode == 'changeemail' else 'blog:home'
- success_message = 'Email was successfully changed!' if mode == 'changeemail' else 'VESUCCESS'
- error_message = 'Unable to verify email! Please try again later.'
+ redirect_to = (
+ reverse("blog:account") + "?tab=email" if mode == "changeemail" else "blog:home"
+ )
+ success_message = (
+ "Email was successfully changed!" if mode == "changeemail" else "VESUCCESS"
+ )
+ error_message = "Unable to verify email! Please try again later."
if token_object is not None and token_object.verified:
user = User.objects.get(pk=token_object.user_id)
user.email = token_object.email
user.save()
token_object.delete()
- messages.success(request, success_message, extra_tags='loginError' if mode == 'verifyemail' else '')
+ messages.success(
+ request,
+ success_message,
+ extra_tags="loginError" if mode == "verifyemail" else "",
+ )
return redirect(redirect_to)
else:
messages.error(request, error_message)
return redirect(redirect_to)
- \ No newline at end of file
+
+
+def reset_password(request, uid, token):
+ mode = "resetpassword"
+ token_object = verify_token(mode, uid, token)
+
+ # Token is not verified yet, but confirmed that it belongs to the user
+ # Now we send a form for the user to reset their password
+ if token_object is not None and token_object.verified:
+ print(token_object.user_id)
+ # redirect to forgotpassword/reset?uid=uid&token=token
+ return HttpResponseRedirect(
+ reverse("blog:resetpassword")
+ + "?uid="
+ + token_object.user_id
+ + "&token="
+ + token
+ )
+ else:
+ # Token is invalid
+ messages.error(
+ request,
+ "Unable to reset password! Please try again later.",
+ extra_tags="passwordReset",
+ )
+ return redirect("blog:forgotpassword")