From 2fed369027f322c2600a5f73ac064424f4de3935 Mon Sep 17 00:00:00 2001 From: m5ka Date: Wed, 27 Mar 2024 15:43:24 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=93=9D=20allow=20editing=20of=20p?= =?UTF-8?q?osts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- moku/config/urls.py | 3 +- moku/static/css/moku.css | 9 ++- moku/templates/moku/feed.jinja | 73 -------------------- moku/templates/moku/post/edit.jinja | 8 +++ moku/templates/moku/post/index.jinja | 26 +++++++ moku/templates/moku/snippets/post.jinja | 8 ++- moku/templates/moku/snippets/post_form.jinja | 55 +++++++++++++++ moku/views/post.py | 63 ++++++++++++++--- moku/views/user.py | 2 +- 9 files changed, 159 insertions(+), 88 deletions(-) delete mode 100644 moku/templates/moku/feed.jinja create mode 100644 moku/templates/moku/post/edit.jinja create mode 100644 moku/templates/moku/post/index.jinja create mode 100644 moku/templates/moku/snippets/post_form.jinja diff --git a/moku/config/urls.py b/moku/config/urls.py index 6ed874c..f6e568b 100644 --- a/moku/config/urls.py +++ b/moku/config/urls.py @@ -5,7 +5,7 @@ from django.urls import include, path from moku.views.auth import LoginView, LogoutView from moku.views.blog import IndexBlogView -from moku.views.post import FeedView +from moku.views.post import EditPostview, FeedView from moku.views.recipe import ( DeleteRecipeView, DeleteStepView, @@ -34,6 +34,7 @@ urlpatterns = [ path("blog", IndexBlogView.as_view(), name="blog.index"), path("privacy", PrivacyView.as_view(), name="privacy"), path("terms", TermsView.as_view(), name="terms"), + path("edit/", EditPostview.as_view(), name="post.edit"), path("user/", ProfileView.as_view(), name="profile"), path("user//json", UserJSONView.as_view(), name="json"), path("recipes", IndexRecipeView.as_view(), name="recipe.index"), diff --git a/moku/static/css/moku.css b/moku/static/css/moku.css index 8fa7852..80e1245 100644 --- a/moku/static/css/moku.css +++ b/moku/static/css/moku.css @@ -62,6 +62,10 @@ body { color: var(--charcoal); } +a.subtle { + font-weight: normal; +} + a.subtle:hover { color: var(--orange); } @@ -295,6 +299,7 @@ form .emoji-picker ul li input[type=radio]:checked + label { form button[type=submit]:not(.logout) { padding: .4rem .6rem; font-size: 1.5rem; + font-family: var(--font-family); background: var(--tangerine); border: 1px solid var(--orange); outline: none; @@ -430,14 +435,14 @@ header nav ul { margin-block-start: 1.2rem; } -.block ul, +.block ul:not(.errors):not(.emoji-picker-group), .block ol { padding-inline-start: 3.2rem; display: grid; row-gap: .4rem; } -.block ul { +.block ul:not(.errors):not(.emoji-picker-group) { list-style: disc; } diff --git a/moku/templates/moku/feed.jinja b/moku/templates/moku/feed.jinja deleted file mode 100644 index bafb50c..0000000 --- a/moku/templates/moku/feed.jinja +++ /dev/null @@ -1,73 +0,0 @@ -{% extends "moku/base.jinja" %} - -{% block content %} -
- -
- {% if not posts %} -

{% trans %}no posts yet...{% endtrans %} 🥱

- {% endif %} - {% for post in posts %} - {% include "moku/snippets/post.jinja" %} - {% endfor %} -
-
-{% endblock content %} \ No newline at end of file diff --git a/moku/templates/moku/post/edit.jinja b/moku/templates/moku/post/edit.jinja new file mode 100644 index 0000000..9dc348f --- /dev/null +++ b/moku/templates/moku/post/edit.jinja @@ -0,0 +1,8 @@ +{% extends "moku/base.jinja" %} + +{% block content %} +
+

{% trans time_ago=post.created_at|naturaltime %}editing post from {{ time_ago }}{% endtrans %}

+ {% include "moku/snippets/post_form.jinja" %} +
+{% endblock content %} \ No newline at end of file diff --git a/moku/templates/moku/post/index.jinja b/moku/templates/moku/post/index.jinja new file mode 100644 index 0000000..4ebc612 --- /dev/null +++ b/moku/templates/moku/post/index.jinja @@ -0,0 +1,26 @@ +{% extends "moku/base.jinja" %} + +{% block content %} +
+ +
+ {% if not posts %} +

{% trans %}no posts yet...{% endtrans %} 🥱

+ {% endif %} + {% for post in posts %} + {% include "moku/snippets/post.jinja" %} + {% endfor %} +
+
+{% endblock content %} \ No newline at end of file diff --git a/moku/templates/moku/snippets/post.jinja b/moku/templates/moku/snippets/post.jinja index 7e3042e..da69edd 100644 --- a/moku/templates/moku/snippets/post.jinja +++ b/moku/templates/moku/snippets/post.jinja @@ -9,7 +9,13 @@
{{ post.emoji }}

{{ post.text|safe }}

- + {% if post.recipe %}
{% trans %}recipe{% endtrans %} diff --git a/moku/templates/moku/snippets/post_form.jinja b/moku/templates/moku/snippets/post_form.jinja new file mode 100644 index 0000000..95dd7ce --- /dev/null +++ b/moku/templates/moku/snippets/post_form.jinja @@ -0,0 +1,55 @@ +
+ {% include "moku/snippets/form_errors.jinja" %} + {% csrf_token %} +
+ {% set current_emoji = form.emoji.value() or emoji[0][1][0] %} + {% for emoji_category in emoji %} + + {{ emoji_category[0] }} +
    + {% for emoji_choice in emoji_category[1] %} + {% set emoji_label = emoji_choice|unemoji %} +
  • + + +
  • + {% endfor %} +
+
+ {% endfor %} +
+
+ + + {{ form.food.help_text }} +
+
+ + + {{ form.verb.help_text }} +
+
+ + {{ form.recipe }} + {{ form.recipe.help_text }} +
+
+ +
{{ form.image }}
+ {{ form.image.help_text }} +
+
+ +
+ \ No newline at end of file diff --git a/moku/views/post.py b/moku/views/post.py index cb65465..4c1dee3 100644 --- a/moku/views/post.py +++ b/moku/views/post.py @@ -1,6 +1,8 @@ from django.contrib import messages +from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin from django.core.exceptions import PermissionDenied -from django.shortcuts import redirect +from django.shortcuts import get_object_or_404, redirect +from django.utils.functional import cached_property from django.utils.translation import gettext as _ from moku.constants import EMOJI_CATEGORIES, Verbs @@ -11,10 +13,57 @@ from moku.models.recipe import Recipe from moku.views.base import FormView +def _get_verbs(username): + return ( + (verb[0], verb[1] % {"user": f"@{username}", "food": "..."}) + for verb in Verbs.CHOICES + ) + + +class EditPostview(LoginRequiredMixin, UserPassesTestMixin, FormView): + """Allows users to edit their previous posts.""" + + template_name = "moku/post/edit.jinja" + form_class = PostForm + + def form_valid(self, form): + if ( + form.instance.recipe + and form.instance.recipe.created_by.id != self.request.user.id + ): + messages.error( + self.request, _("you can't add someone else's recipe to your post!") + ) + return self.form_invalid(form) + if "image" in form.changed_data and form.instance.image.name: + form.instance.image = process_post_image(form.instance.image) + form.save() + messages.success(self.request, _("your post was updated!")) + return redirect("feed") + + def get_context_data(self, **kwargs): + return { + **super().get_context_data(**kwargs), + "post": self.post_object, + "verbs": _get_verbs(self.request.user.username), + "emoji": EMOJI_CATEGORIES, + } + + def get_form(self): + return self.form_class(instance=self.post_object, **self.get_form_kwargs()) + + @cached_property + def post_object(self): + return get_object_or_404(Post, uuid=self.kwargs.get("uuid")) + + def test_func(self): + return self.post_object.created_by.id == self.request.user.id + + class FeedView(FormView): """Allows users to see recent posts and create a new post.""" - template_name = "moku/feed.jinja" + template_name = "moku/post/index.jinja" form_class = PostForm def form_valid(self, form): @@ -29,7 +78,7 @@ class FeedView(FormView): ) return redirect("feed") form.instance.created_by = self.request.user - if "image" in form.changed_data and form.instance.image is not None: + if "image" in form.changed_data and form.instance.image.name: form.instance.image = process_post_image(form.instance.image) form.save() messages.success(self.request, _("your post was made!")) @@ -48,13 +97,7 @@ class FeedView(FormView): return { **context, "emoji": EMOJI_CATEGORIES, - "verbs": ( - ( - verb[0], - verb[1] % {"user": f"@{self.request.user.username}", "food": "..."}, - ) - for verb in Verbs.CHOICES - ), + "verbs": _get_verbs(self.request.user.username), } def get_form(self, form_class=None): diff --git a/moku/views/user.py b/moku/views/user.py index 557c29b..00ae139 100644 --- a/moku/views/user.py +++ b/moku/views/user.py @@ -27,7 +27,7 @@ class EditProfileView(LoginRequiredMixin, FormView): page_title = gettext_lazy("edit profile") def form_valid(self, form): - if "avatar" in form.changed_data and form.instance.avatar is not None: + if "avatar" in form.changed_data and form.instance.avatar.name: form.instance.avatar = process_avatar_image(form.instance.avatar) form.save() messages.success(self.request, _("profile updated successfully!"))