diff --git a/manage.py b/manage.py
old mode 100755
new mode 100644
diff --git a/tblog/migrations/0002_tbpost_edited_at.py b/tblog/migrations/0002_tbpost_edited_at.py
new file mode 100644
index 0000000..8d05f8a
--- /dev/null
+++ b/tblog/migrations/0002_tbpost_edited_at.py
@@ -0,0 +1,18 @@
+# Generated by Django 5.1.4 on 2025-01-04 20:04
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('tblog', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='tbpost',
+ name='edited_at',
+ field=models.IntegerField(null=True),
+ ),
+ ]
diff --git a/tblog/models.py b/tblog/models.py
index 4f781aa..cf00bc7 100644
--- a/tblog/models.py
+++ b/tblog/models.py
@@ -20,6 +20,7 @@ class tBPost(models.Model):
title = models.TextField(max_length=1_000)
content = models.TextField(max_length=500_000)
timestamp = models.IntegerField()
+ edited_at = models.IntegerField(null=True)
text_format = models.CharField(max_length=10) # plain, mono, markdown, html ... (more to come?)
class Meta:
diff --git a/tblog/static/css/blog.css b/tblog/static/css/blog.css
new file mode 100644
index 0000000..ca990af
--- /dev/null
+++ b/tblog/static/css/blog.css
@@ -0,0 +1,16 @@
+#real-blog-container[data-blog-format="mono"] pre,
+#real-blog-container[data-blog-format="plain"] pre {
+ white-space: pre-wrap;
+ overflow-x: scroll;
+}
+
+#real-blog-container[data-blog-format="html"],
+#real-blog-container[data-blog-format="markdown"] {
+ width: 100%;
+ overflow-x: scroll;
+}
+
+#blog-container {
+ max-width: 1000px;
+ width: calc(90vw - 40px);
+}
diff --git a/tblog/templates/blog.html b/tblog/templates/blog.html
index c432051..d1783a7 100644
--- a/tblog/templates/blog.html
+++ b/tblog/templates/blog.html
@@ -8,8 +8,18 @@
{% block body %}
{{ blog.title }}
-
+
+
+
+ {% if blog.edited_at %}
+ - Edited
+
+ {% endif %}
+
+
{% if username == blog.u_by.username %}
+ Edit post
+
{% endif %}
- Loading...
+ Loading...
{% endif %}
+
+
diff --git a/tblog/urls.py b/tblog/urls.py
index f5e39b6..444967c 100644
--- a/tblog/urls.py
+++ b/tblog/urls.py
@@ -1,7 +1,7 @@
from django.contrib import admin
from django.urls import include, path
-from .views import auth, index, user, view_blog, write
+from .views import auth, edit_blog, index, user, view_blog, write
urlpatterns = [
path("", index),
@@ -9,6 +9,7 @@ urlpatterns = [
path("create/", write),
path("blog//", user),
path("blog///", view_blog),
+ path("blog///edit/", edit_blog),
path("static/", include("tblog.views.static")),
path("django-admin/", admin.site.urls)
]
diff --git a/tblog/views/__init__.py b/tblog/views/__init__.py
index 684d3be..326ef4b 100644
--- a/tblog/views/__init__.py
+++ b/tblog/views/__init__.py
@@ -1 +1,2 @@
-from .templates import auth, index, user, view_blog, write # noqa: F401
+from .templates import (auth, edit_blog, index, user, view_blog, # noqa: F401
+ write)
diff --git a/tblog/views/static.py b/tblog/views/static.py
index c5c4be2..291cfb9 100644
--- a/tblog/views/static.py
+++ b/tblog/views/static.py
@@ -22,6 +22,7 @@ file_associations = {
urlpatterns = [path(i, cache_control(**{"max-age": 60 * 60 * 24 * 30})(get_static_serve(i, file_associations[i.split(".")[-1]]))) for i in [
"css/ace.css",
+ "css/blog.css",
"css/write.css",
"js/write.js"
]]
diff --git a/tblog/views/templates.py b/tblog/views/templates.py
index b0d42cf..77a31ed 100644
--- a/tblog/views/templates.py
+++ b/tblog/views/templates.py
@@ -38,51 +38,51 @@ def index(request: WSGIRequest) -> HttpResponse:
def write(request: WSGIRequest) -> HttpResponse:
username = get_username(request)
- if username:
- repopulate = {}
- error = None
-
- if request.method == "POST":
- url = (request.POST.get("url") or "").strip().replace(" ", "-").lower()
- title = (request.POST.get("title") or "").strip()
- content = (request.POST.get("raw") or "").strip()
- fmt = request.POST.get("format")
-
- repopulate = {
- "url": url,
- "title": title,
- "content": content,
- "format": fmt
- }
-
- if not (url and title and content) or fmt not in ["plain", "mono", "markdown", "html"] or len(url) > 250 or len(title) > 1_000 or len(content) > 500_000:
- error = "Invalid input(s)"
-
- else:
- try:
- tBPost.objects.create(
- u_by=get_user_object(username, i_promise_this_user_exists=True),
- url=request.POST.get("url"),
- title=request.POST.get("title"),
- content=request.POST.get("raw"),
- timestamp=math.floor(time.time()),
- text_format=request.POST.get("format")
- )
- except IntegrityError:
- error = f"Url '{url}' already in use"
- else:
- return HttpResponseRedirect(f"/blog/{username}/{escape_url(url)}/")
-
+ if not username:
return render_template(
- request, "write.html",
- title="Writing",
- username=username,
- error=error,
- repopulate=repopulate
+ request, "noauth/index.html"
)
+ repopulate = {}
+ error = None
+
+ if request.method == "POST":
+ url = (request.POST.get("url") or "").strip().replace(" ", "-").lower()
+ title = (request.POST.get("title") or "").strip()
+ content = (request.POST.get("raw") or "").strip()
+ fmt = request.POST.get("format")
+
+ repopulate = {
+ "url": url,
+ "title": title,
+ "content": content,
+ "format": fmt
+ }
+
+ if not (url and title and content) or fmt not in ["plain", "mono", "markdown", "html"] or len(url) > 250 or len(title) > 1_000 or len(content) > 500_000:
+ error = "Invalid input(s)"
+
+ else:
+ try:
+ tBPost.objects.create(
+ u_by=get_user_object(username, i_promise_this_user_exists=True),
+ url=url,
+ title=title,
+ content=content,
+ timestamp=math.floor(time.time()),
+ text_format=fmt
+ )
+ except IntegrityError:
+ error = f"Url '{url}' already in use"
+ else:
+ return HttpResponseRedirect(f"/blog/{username}/{escape_url(url)}/")
+
return render_template(
- request, "noauth/index.html"
+ request, "write.html",
+ title="Writing",
+ username=username,
+ error=error,
+ repopulate=repopulate
)
def view_blog(request: WSGIRequest, username: str, url: str) -> HttpResponse:
@@ -109,6 +109,70 @@ def view_blog(request: WSGIRequest, username: str, url: str) -> HttpResponse:
username=self_username
)
+def edit_blog(request: WSGIRequest, username: str, url: str) -> HttpResponse:
+ try:
+ blog = tBPost.objects.get(
+ u_by=get_user_object(username),
+ url=url
+ )
+ except tBPost.DoesNotExist:
+ return render_template(
+ request, "404.html"
+ )
+
+ self_username = get_username(request)
+
+ if username != self_username:
+ return render_template(
+ request, "404.html"
+ )
+
+ error = None
+ repopulate = {
+ "url": blog.url,
+ "title": blog.title,
+ "content": blog.content,
+ "format": blog.text_format
+ }
+
+ if request.method == "POST":
+ url = (request.POST.get("url") or "").strip().replace(" ", "-").lower() or blog.url
+ title = (request.POST.get("title") or "").strip() or blog.title
+ content = (request.POST.get("raw") or "").strip() or blog.content
+ fmt = request.POST.get("format") or blog.text_format
+
+ repopulate = {
+ "url": url,
+ "title": title,
+ "content": content,
+ "format": fmt
+ }
+
+ if not (url and title and content) or fmt not in ["plain", "mono", "markdown", "html"] or len(url) > 250 or len(title) > 1_000 or len(content) > 500_000:
+ error = "Invalid input(s)"
+
+ else:
+ try:
+ blog.url = url
+ blog.title = title
+ blog.content = content
+ blog.edited_at = math.floor(time.time())
+ blog.text_format = fmt
+ blog.save()
+ except IntegrityError:
+ error = f"Url '{url}' already in use"
+ else:
+ return HttpResponseRedirect(f"/blog/{username}/{escape_url(url)}/")
+
+ return render_template(
+ request, "write.html",
+ title="Editing",
+ editing=True,
+ username=username,
+ error=error,
+ repopulate=repopulate
+ )
+
def user(request: WSGIRequest, username: str) -> HttpResponse:
user = get_user_object(username)
@@ -117,7 +181,7 @@ def user(request: WSGIRequest, username: str) -> HttpResponse:
request, "user.html",
username=user.username,
self_username=get_username(request),
- posts=user.posts.all() # type: ignore
+ posts=user.posts.order_by("-id") # type: ignore
)
return render_template(