diff --git a/moku/config/urls.py b/moku/config/urls.py index 74d08ac..6ed874c 100644 --- a/moku/config/urls.py +++ b/moku/config/urls.py @@ -4,6 +4,7 @@ from django.contrib import admin 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.recipe import ( DeleteRecipeView, @@ -13,7 +14,7 @@ from moku.views.recipe import ( NewRecipeView, ShowRecipeView, ) -from moku.views.static import ChangelogView, PrivacyView, TermsView +from moku.views.static import PrivacyView, TermsView from moku.views.user import ( EditProfileView, EditSettingsView, @@ -30,7 +31,7 @@ urlpatterns = [ path("signup", SignupView.as_view(), name="signup"), path("profile", EditProfileView.as_view(), name="profile.edit"), path("settings", EditSettingsView.as_view(), name="settings"), - path("changelog", ChangelogView.as_view(), name="changelog"), + path("blog", IndexBlogView.as_view(), name="blog.index"), path("privacy", PrivacyView.as_view(), name="privacy"), path("terms", TermsView.as_view(), name="terms"), path("user/", ProfileView.as_view(), name="profile"), diff --git a/moku/locale/lio/LC_MESSAGES/django.po b/moku/locale/lio/LC_MESSAGES/django.po index 163e1ff..863197e 100644 --- a/moku/locale/lio/LC_MESSAGES/django.po +++ b/moku/locale/lio/LC_MESSAGES/django.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-03-26 18:45+0000\n" +"POT-Creation-Date: 2024-03-27 12:13+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: m5ka \n" "Language-Team: LANGUAGE \n" @@ -15,11 +15,11 @@ msgstr "" msgid "english" msgstr "lami english" -#: moku/config/settings.py:144 +#: moku/config/settings.py:143 msgid "lami lioa" msgstr "lami lioa" -#: moku/config/settings.py:145 +#: moku/config/settings.py:143 msgid "toki pona" msgstr "lami tokipona" @@ -154,6 +154,22 @@ msgstr "noli" msgid "about me" msgstr "oisa nai" +#: moku/models/blog.py:9 +msgid "title" +msgstr "" + +#: moku/models/blog.py:11 +msgid "the title of the blog post." +msgstr "" + +#: moku/models/blog.py:14 +msgid "content" +msgstr "" + +#: moku/models/blog.py:15 +msgid "the content of the blog post. markdown is allowed here." +msgstr "" + #: moku/models/post.py:37 moku/models/recipe.py:24 msgid "unique id" msgstr "oioa mini" @@ -286,23 +302,24 @@ msgstr "haoa" msgid "log in" msgstr "mimi" -#: moku/templates/moku/base.jinja:37 moku/views/user.py:86 +#: moku/templates/moku/base.jinja:37 moku/views/user.py:93 msgid "sign up" msgstr "holo hono" -#: moku/templates/moku/base.jinja:55 +#: moku/templates/moku/base.jinja:55 moku/templates/moku/blog.jinja:5 +#: moku/views/blog.py:9 msgid "blog" msgstr "pali oiki" -#: moku/templates/moku/base.jinja:56 moku/views/user.py:38 +#: moku/templates/moku/base.jinja:56 moku/views/user.py:45 msgid "settings" msgstr "oiki laia" -#: moku/templates/moku/base.jinja:57 moku/views/static.py:24 +#: moku/templates/moku/base.jinja:57 moku/views/static.py:17 msgid "terms of use" msgstr "pali koko" -#: moku/templates/moku/base.jinja:58 moku/views/static.py:17 +#: moku/templates/moku/base.jinja:58 moku/views/static.py:10 msgid "privacy policy" msgstr "pali pamo" @@ -310,6 +327,10 @@ msgstr "pali pamo" msgid "donate" msgstr "hana kipi" +#: moku/templates/moku/blog.jinja:7 +msgid "no blog posts yet..." +msgstr "ka koma iapa..." + #: moku/templates/moku/feed.jinja:52 msgid "post!" msgstr "si iapa!" @@ -429,18 +450,19 @@ msgstr "ko oioa haino ia iapa ia koka ia '-' ia '_' ia '.'." #: moku/validators.py:30 #, python-format msgid "Username must be between %(min_length)d and %(max_length)d characters." -msgstr "ko homa nao na kino %(min_length)d. ko oioi nao na kino %(max_length)d." +msgstr "" +"ko homa nao na kino %(min_length)d. ko oioi nao na kino %(max_length)d." #: moku/views/auth.py:28 #, python-format msgid "welcome back, %(username)s!" msgstr "ka maio li kai %(username)s sa mai!" -#: moku/views/post.py:33 +#: moku/views/post.py:28 msgid "you can't add someone else's recipe to your post!" msgstr "ko lisa sai io iapa ia haiki na haino oiki mo kai!" -#: moku/views/post.py:40 +#: moku/views/post.py:35 msgid "your post was made!" msgstr "ka haika ni iapa sa hali!" @@ -480,30 +502,29 @@ msgstr "ko poli sai io iaoa li haiki hina ni sia sioa sa laka." msgid "step added successfully!" msgstr "ka iaio ni sia haiki sa hali!" -#: moku/views/static.py:10 -msgid "changelog" -msgstr "pali oiki" - -#: moku/views/user.py:20 +#: moku/views/user.py:27 msgid "edit profile" msgstr "oiki na pali haino" -#: moku/views/user.py:26 +#: moku/views/user.py:33 msgid "profile updated successfully!" msgstr "ka sioa ni pali haino sa hali!" -#: moku/views/user.py:46 +#: moku/views/user.py:53 msgid "uh oh. i think something went a little bit oopsie." msgstr "haia. ko kaki lo mia mo mia sa laka nai." -#: moku/views/user.py:49 +#: moku/views/user.py:56 msgid "settings updated!" msgstr "ka sioa ni maoa kai sa hali!" -#: moku/views/user.py:94 +#: moku/views/user.py:101 msgid "sorry! someone else got to that username first." msgstr "ko kami ni oioa hina mo haino oiki sa laka." -#: moku/views/user.py:98 +#: moku/views/user.py:105 msgid "that's it! just log in, and you're ready to go." msgstr "poi! si iapa ni oioa ni oioa pamo li io oima ni lia kai ha." + +#~ msgid "changelog" +#~ msgstr "pali oiki" diff --git a/moku/locale/tok/LC_MESSAGES/django.po b/moku/locale/tok/LC_MESSAGES/django.po index 904ebf2..f6651f9 100644 --- a/moku/locale/tok/LC_MESSAGES/django.po +++ b/moku/locale/tok/LC_MESSAGES/django.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: 0.1.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-03-26 18:45+0000\n" +"POT-Creation-Date: 2024-03-27 12:13+0000\n" "PO-Revision-Date: 2024-03-26 18:28+0000\n" "Last-Translator: m5ka \n" "Language-Team: LANGUAGE \n" @@ -15,11 +15,11 @@ msgstr "" msgid "english" msgstr "toki inli" -#: moku/config/settings.py:144 +#: moku/config/settings.py:143 msgid "lami lioa" msgstr "toki liwa" -#: moku/config/settings.py:145 +#: moku/config/settings.py:143 msgid "toki pona" msgstr "toki pona" @@ -154,6 +154,22 @@ msgstr "ma" msgid "about me" msgstr "mi sama ni" +#: moku/models/blog.py:9 +msgid "title" +msgstr "" + +#: moku/models/blog.py:11 +msgid "the title of the blog post." +msgstr "" + +#: moku/models/blog.py:14 +msgid "content" +msgstr "" + +#: moku/models/blog.py:15 +msgid "the content of the blog post. markdown is allowed here." +msgstr "" + #: moku/models/post.py:37 moku/models/recipe.py:24 msgid "unique id" msgstr "nimi wan" @@ -290,23 +306,24 @@ msgstr "weka" msgid "log in" msgstr "insa" -#: moku/templates/moku/base.jinja:37 moku/views/user.py:86 +#: moku/templates/moku/base.jinja:37 moku/views/user.py:93 msgid "sign up" msgstr "pali e lipu jan" -#: moku/templates/moku/base.jinja:55 +#: moku/templates/moku/base.jinja:55 moku/templates/moku/blog.jinja:5 +#: moku/views/blog.py:9 msgid "blog" msgstr "lipu ante" -#: moku/templates/moku/base.jinja:56 moku/views/user.py:38 +#: moku/templates/moku/base.jinja:56 moku/views/user.py:45 msgid "settings" msgstr "ante linluwi" -#: moku/templates/moku/base.jinja:57 moku/views/static.py:24 +#: moku/templates/moku/base.jinja:57 moku/views/static.py:17 msgid "terms of use" msgstr "lipu kepeken" -#: moku/templates/moku/base.jinja:58 moku/views/static.py:17 +#: moku/templates/moku/base.jinja:58 moku/views/static.py:10 msgid "privacy policy" msgstr "lipu len" @@ -314,6 +331,10 @@ msgstr "lipu len" msgid "donate" msgstr "pana mani" +#: moku/templates/moku/blog.jinja:7 +msgid "no blog posts yet..." +msgstr "sitelen li lon ala..." + #: moku/templates/moku/feed.jinja:52 msgid "post!" msgstr "o pali!" @@ -446,11 +467,11 @@ msgstr "" msgid "welcome back, %(username)s!" msgstr "jan %(username)s o toki!" -#: moku/views/post.py:33 +#: moku/views/post.py:28 msgid "you can't add someone else's recipe to your post!" msgstr "sina ken ala pana e sitelen kepeken nasin pali pi jan ante!" -#: moku/views/post.py:40 +#: moku/views/post.py:35 msgid "your post was made!" msgstr "sitelen sina li lon!" @@ -490,30 +511,29 @@ msgstr "nasin pali ni li ken ala kama suli kin." msgid "step added successfully!" msgstr "kipisi pi nasin pali li lon!" -#: moku/views/static.py:10 -msgid "changelog" -msgstr "lipu ante" - -#: moku/views/user.py:20 +#: moku/views/user.py:27 msgid "edit profile" msgstr "ante pi lipu jan" -#: moku/views/user.py:26 +#: moku/views/user.py:33 msgid "profile updated successfully!" msgstr "lipu jan li ante!" -#: moku/views/user.py:46 +#: moku/views/user.py:53 msgid "uh oh. i think something went a little bit oopsie." msgstr "a! ike li kama." -#: moku/views/user.py:49 +#: moku/views/user.py:56 msgid "settings updated!" msgstr "wile sina li ante!" -#: moku/views/user.py:94 +#: moku/views/user.py:101 msgid "sorry! someone else got to that username first." msgstr "jan ante li jo e nimi ni." -#: moku/views/user.py:98 +#: moku/views/user.py:105 msgid "that's it! just log in, and you're ready to go." msgstr "pona a! o sitelen e nimi sin ni la sina ken lon insa." + +#~ msgid "changelog" +#~ msgstr "lipu ante" diff --git a/moku/markdown.py b/moku/markdown.py new file mode 100644 index 0000000..a1b3ebc --- /dev/null +++ b/moku/markdown.py @@ -0,0 +1,52 @@ +from django.apps import apps +from django.conf import settings +from mistune import HTMLRenderer, Markdown +from mistune.plugins.formatting import strikethrough, subscript, superscript +from mistune.plugins.url import url + +USERNAME_PATTERN = ( + r"@[a-z0-9-_.]{" + + str(settings.USERNAME_MIN_LENGTH) + + r"," + + str(settings.USERNAME_MAX_LENGTH) + + r"}" +) + + +def _parse_username_link(inline, m, state): + User = apps.get_model("moku", "User") + + text = m.group(0) + pos = m.end() + if state.in_link: + inline.process_text(text, state) + return pos + + try: + user = User.objects.get(username=text[1:]) + except User.DoesNotExist: + inline.process_text(text, state) + return pos + + state.append_token( + { + "type": "link", + "children": [{"type": "text", "raw": f"@{user.username}"}], + "attrs": {"url": user.get_absolute_url()}, + } + ) + return pos + + +def _username(md): + md.inline.register("username", USERNAME_PATTERN, _parse_username_link) + + +full_markdown = Markdown( + renderer=HTMLRenderer(), + plugins=[strikethrough, subscript, superscript, url, _username], +) + +basic_markdown = Markdown( + renderer=HTMLRenderer(), plugins=[strikethrough, subscript, superscript, url] +) diff --git a/moku/migrations/0008_blogpost.py b/moku/migrations/0008_blogpost.py new file mode 100644 index 0000000..03bc2e4 --- /dev/null +++ b/moku/migrations/0008_blogpost.py @@ -0,0 +1,26 @@ +# Generated by Django 5.0.3 on 2024-03-27 11:35 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('moku', '0007_add_lami_lioa_to_languages'), + ] + + operations = [ + migrations.CreateModel( + name='BlogPost', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(help_text='the title of the blog post.', max_length=128, verbose_name='title')), + ('text', models.TextField(help_text='the content of the blog post. markdown is allowed here.', verbose_name='content')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('created_by', models.ForeignKey(db_column='created_by_user_id', on_delete=django.db.models.deletion.PROTECT, related_name='+', to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/moku/models/__init__.py b/moku/models/__init__.py index e27b18e..63c8160 100644 --- a/moku/models/__init__.py +++ b/moku/models/__init__.py @@ -1,5 +1,6 @@ +from moku.models.blog import BlogPost from moku.models.post import Post from moku.models.recipe import Recipe, RecipeStep from moku.models.user import User, UserSettings -__all__ = ["Post", "Recipe", "RecipeStep", "User", "UserSettings"] +__all__ = ["BlogPost", "Post", "Recipe", "RecipeStep", "User", "UserSettings"] diff --git a/moku/models/blog.py b/moku/models/blog.py new file mode 100644 index 0000000..6090cce --- /dev/null +++ b/moku/models/blog.py @@ -0,0 +1,30 @@ +from django.db import models +from django.utils.translation import gettext_lazy as _ + +from moku.markdown import full_markdown + + +class BlogPost(models.Model): + title = models.CharField( + verbose_name=_("title"), + max_length=128, + help_text=_("the title of the blog post."), + ) + text = models.TextField( + verbose_name=_("content"), + help_text=_("the content of the blog post. markdown is allowed here."), + ) + created_by = models.ForeignKey( + "User", + related_name="+", + db_column="created_by_user_id", + on_delete=models.PROTECT, + ) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + def __str__(self): + return self.title + + def as_html(self): + return full_markdown(self.text) diff --git a/moku/static/css/moku.css b/moku/static/css/moku.css index 132c7f2..7639eff 100644 --- a/moku/static/css/moku.css +++ b/moku/static/css/moku.css @@ -308,6 +308,32 @@ header nav ul { row-gap: .8rem; } +.blog { + display: grid; + row-gap: 2.4rem; +} + +.blog > h2 { + font-size: 2.2rem; + font-weight: bold; +} + +.blog-post { + display: grid; + row-gap: .4rem; +} + +.blog-post > h3 { + font-size: 2rem; + font-weight: bold; +} + +.blog-post .post-metadata { + font-size: 1.4rem; + color: var(--charcoal); + margin-block-end: 1.2rem; +} + .grid-content { display: grid; row-gap: 1.2rem; diff --git a/moku/templates/moku/base.jinja b/moku/templates/moku/base.jinja index 48936b0..72b09e7 100644 --- a/moku/templates/moku/base.jinja +++ b/moku/templates/moku/base.jinja @@ -52,7 +52,7 @@