Rudimentary but fully-functional entry permalink pages :3

This commit is contained in:
Danielle McLean 2017-10-25 12:31:08 +11:00
parent d267df337f
commit 2b6691f8a5
Signed by: 00dani
GPG key ID: 5A5D2D1AFF12EEC5
13 changed files with 133 additions and 73 deletions

View file

@ -20,6 +20,7 @@ django-activeurl = "*"
django-otp = "*" django-otp = "*"
qrcode = "*" qrcode = "*"
django-otp-agents = "*" django-otp-agents = "*"
python-slugify = "*"
[dev-packages] [dev-packages]

80
Pipfile.lock generated
View file

@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "1d582e5b51466de2e917f117b537193fd1826f51d3a9a218f83d392d68d0c461" "sha256": "a6c119918001f41c9b233d8cf9606c9acd10c6dcc13ed248f88511b198d9e929"
}, },
"host-environment-markers": { "host-environment-markers": {
"implementation_name": "cpython", "implementation_name": "cpython",
@ -207,39 +207,39 @@
}, },
"psycopg2": { "psycopg2": {
"hashes": [ "hashes": [
"sha256:3ab693b907b2c7a34c1dca198cfc454516cfd75440fd913bb372da6f70d6db55", "sha256:594aa9a095de16614f703d759e10c018bdffeafce2921b8e80a0e8a0ebbc12e5",
"sha256:3c99c88216e0fc62113a1177aae9db18a533274370e99a4537b433b87b319360", "sha256:1cf5d84290c771eeecb734abe2c6c3120e9837eb12f99474141a862b9061ac51",
"sha256:df70e0a387b0145a7a80de67d43d84e1c5a24a33bbfaedb96271876d573e5fb6", "sha256:0344b181e1aea37a58c218ccb0f0f771295de9aa25a625ed076e6996c6530f9e",
"sha256:8d2003d23d8fc59f0f0a2e73c13baa41581006b8227a9d82bbdc1aa233285ba4", "sha256:25250867a4cd1510fb755ef9cb38da3065def999d8e92c44e49a39b9b76bc893",
"sha256:face605eea5826fa36600c04447b61674992bbd5eb177f66f86f61a04436dbd9", "sha256:317612d5d0ca4a9f7e42afb2add69b10be360784d21ce4ecfbca19f1f5eadf43",
"sha256:ebee4f59803eda1ed4035649469a713818dbf5b6de2e12396edd9fa228727c20", "sha256:9d6266348b15b4a48623bf4d3e50445d8e581da413644f365805b321703d0fac",
"sha256:4d7c872d9c85745964cf8efabba3fb1a6010b345c18d0e2b620509f0444aa729", "sha256:ddca39cc55877653b5fcf59976d073e3d58c7c406ef54ae8e61ddf8782867182",
"sha256:99a298b9030f8fd36f885c5d0661e4d5a059136a541bb6c4d7d1100e38da3cd1", "sha256:988d2ec7560d42ef0ac34b3b97aad14c4f068792f00e1524fa1d3749fe4e4b64",
"sha256:b7646f7bdb42ba3cf7ef9f500f6514b5e413d25c5b3093d70e6ba52df417a83c", "sha256:7a9c6c62e6e05df5406e9b5235c31c376a22620ef26715a663cee57083b3c2ea",
"sha256:39369e40bc3e062da5da93ce5f1e7f9bed95e9a60cb6f003d316f2a834722e2d", "sha256:7a75565181e75ba0b9fb174b58172bf6ea9b4331631cfe7bafff03f3641f5d73",
"sha256:c939b238cbc18e786909d20277c9f051241696ebb03ca9e3490094f526ce69a7", "sha256:94e4128ba1ea56f02522fffac65520091a9de3f5c00da31539e085e13db4771b",
"sha256:9a0a74a6f20d82c389095c88d4d281e66eb3ffbc09adf543dcec4bec22436569", "sha256:92179bd68c2efe72924a99b6745a9172471931fc296f9bfdf9645b75eebd6344",
"sha256:16482d050db68503abbb693795d75e9fca287a00f662376759825404533ca87c", "sha256:b9358e203168fef7bfe9f430afaed3a2a624717a1d19c7afa7dfcbd76e3cd95c",
"sha256:f74e50341f45fb9bcd3598c11b5774c3e4349ee38cf15c342cc7075c73ef1f95", "sha256:009e0bc09a57dbef4b601cb8b46a2abad51f5274c8be4bba276ff2884cd4cc53",
"sha256:e80a1ae04218e854a5d71085f9498c8c979033ca855cedeed3afc5d976c348ce", "sha256:d3ac07240e2304181ffdb13c099840b5eb555efc7be9344503c0c03aa681de79",
"sha256:f07791ee5793621bfaaa844f731529cd72321280f94e8dc3bd4ef524d20f58f2", "sha256:40fa5630cd7d237cd93c4d4b64b9e5ed9273d1cfce55241c7f9066f5db70629d",
"sha256:0bd0b1cf81eb6d74a77199570d5ce097fb3c6b8e57acc1edd328cef5c552f98a", "sha256:6c2f1a76a9ebd9ecf7825b9e20860139ca502c2bf1beabf6accf6c9e66a7e0c3",
"sha256:4db0de7d6236acbf7d020926489b6c4b98e845ba98df11057f22d54866e26b20", "sha256:37f54452c7787dbdc0a634ca9773362b91709917f0b365ed14b831f03cbd34ba",
"sha256:604c49bf196c654c417ade1dc765bd23fe9bc3392d9a8c7184a7077142d621b3", "sha256:8f5942a4daf1ffac42109dc4a72f786af4baa4fa702ede1d7c57b4b696c2e7d6",
"sha256:6194e81d8839b636118f5275c53be3c70eb3c3abcf836de675c34b20c06025ea", "sha256:bf708455cd1e9fa96c05126e89a0c59b200d086c7df7bbafc7d9be769e4149a3",
"sha256:e26aca5eb272869fdf8e55111f36026517c1c0799eb7ef1ddf67d4412affe1ef", "sha256:82c40ea3ac1555e0462803380609fbe8b26f52620f3d4f8eb480cfd8ceed8a14",
"sha256:35d1fec112337c2607c8ca17dee3c2f8455e07dd69d140ff8e86ef1240dace3d", "sha256:207ba4f9125a0a4200691e82d5eee7ea1485708eabe99a07fc7f08696fae62f4",
"sha256:891a70235c202158780fc0ab98d7ca2a7ed1625c26725b15119d47b2d852a5c6", "sha256:0cd4c848f0e9d805d531e44973c8f48962e20eb7fc0edac3db4f9dbf9ed5ab82",
"sha256:4cca242df228364b4de6241c54553301856bc253d7f21e15009b11c812eb7b5c", "sha256:57baf63aeb2965ca4b52613ce78e968b6d2bde700c97f6a7e8c6c236b51ab83e",
"sha256:3fbba0dd7f3ac458f355dcfc4d7d9cd082c19748e453bcd81bf9d8bc14260c01", "sha256:2954557393cfc9a5c11a5199c7a78cd9c0c793a047552d27b1636da50d013916",
"sha256:04a5b2805c620ddecdff33e015631cc8d7ea8dd01e1fcc930bfe002fdc26b9e0", "sha256:7c31dade89634807196a6b20ced831fbd5bec8a21c4e458ea950c9102c3aa96f",
"sha256:4fbe2c29f6d4c8d9eac5fc3c42c42036e7cc58e225036130b7713afa72489aac", "sha256:1286dd16d0e46d59fa54582725986704a7a3f3d9aca6c5902a7eceb10c60cb7e",
"sha256:471e3140e1cb241ecb53602cdc98fe305b82c854cfa53c3e343d642683dc96c7", "sha256:697ff63bc5451e0b0db48ad205151123d25683b3754198be7ab5fcb44334e519",
"sha256:279dbf220c94c9f73aefac719ea3b9550ca791389bc9184c15e835516bc8428d", "sha256:fc993c9331d91766d54757bbc70231e29d5ceb2d1ac08b1570feaa0c38ab9582",
"sha256:a4d7134058e8869d785c0be386cd702fe2a4be14b678d7571a51045e1bbad862", "sha256:9d64fed2681552ed642e9c0cc831a9e95ab91de72b47d0cb68b5bf506ba88647",
"sha256:9b7b16e26448b43cf167f785d8b5345007731ebf153a510e12dae826800caa65" "sha256:5c3213be557d0468f9df8fe2487eaf2990d9799202c5ff5cb8d394d09fad9b2a"
], ],
"version": "==2.7.3.1" "version": "==2.7.3.2"
}, },
"python-memcached": { "python-memcached": {
"hashes": [ "hashes": [
@ -247,6 +247,13 @@
], ],
"version": "==1.58" "version": "==1.58"
}, },
"python-slugify": {
"hashes": [
"sha256:c3733135d3b184196fdb8844f6a74bbfb9cf6720d1dcce3254bdc434353f938f",
"sha256:57a385df7a1c6dbd15f7666eaff0ff29d3f60363b228b1197c5308ed3ba5f824"
],
"version": "==1.2.4"
},
"pytz": { "pytz": {
"hashes": [ "hashes": [
"sha256:c883c2d6670042c7bc1688645cac73dd2b03193d1f7a6847b6154e96890be06d", "sha256:c883c2d6670042c7bc1688645cac73dd2b03193d1f7a6847b6154e96890be06d",
@ -293,6 +300,13 @@
"sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9" "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9"
], ],
"version": "==1.11.0" "version": "==1.11.0"
},
"unidecode": {
"hashes": [
"sha256:61f807220eda0203a774a09f84b4304a3f93b5944110cc132af29ddb81366883",
"sha256:280a6ab88e1f2eb5af79edff450021a0d3f0448952847cd79677e55e58bad051"
],
"version": "==0.4.21"
} }
}, },
"develop": {} "develop": {}

View file

@ -1,13 +1,22 @@
class Note: class Entry:
fields = ()
@classmethod
def has(cls, field):
return field in cls.fields
class Note(Entry):
id = 'note' id = 'note'
icon = 'fa fa-paper-plane' icon = 'fa fa-paper-plane'
plural = 'notes' plural = 'notes'
class Article: class Article(Entry):
id = 'article' id = 'article'
icon = 'fa fa-file-text' icon = 'fa fa-file-text'
plural = 'articles' plural = 'articles'
fields = ('slug', 'name')
all = (Note, Article) all = (Note, Article)

View file

@ -1,5 +1,7 @@
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.db import models from django.db import models
from django.urls import reverse
from slugify import slugify
from . import kinds from . import kinds
ENTRY_KINDS = [(k.id, k.__name__) for k in kinds.all] ENTRY_KINDS = [(k.id, k.__name__) for k in kinds.all]
@ -28,6 +30,20 @@ class Entry(models.Model):
content=self.content content=self.content
) )
@property
def url(self):
kind = kinds.from_id[self.kind]
route = 'entries:{kind}_entry'.format(kind=kind.plural)
args = [self.id]
if kind.has('slug'):
route += '_slug'
args.append(self.slug)
return reverse(route, args=args)
@property
def slug(self):
return slugify(self.name)
class Meta: class Meta:
verbose_name_plural = 'entries' verbose_name_plural = 'entries'
ordering = ['-published'] ordering = ['-published']

View file

@ -1,24 +1,4 @@
{% load humanize %}<article class="card h-entry"> {% extends 'lemoncurry/layout.html' %}
<div class="card-body"> {% block main %}
{% if entry.name %}<h4 class="card-title p-name">{{ entry.name }}</h4>{% endif %} {% include 'entries/h-entry.html' %}
<div class="e-content{% if not entry.name %} p-name{% endif %}">{{ entry.content }}</div> {% endblock %}
</div>
<div class="card-footer">
<a class="p-author h-card" href="{{ entry.author.url }}">
<img class="u-photo" src="{{ entry.author.avatar.url }}" />
{{ entry.author.first_name }} {{ entry.author.last_name }}
</a>
<a class="u-url" href="{{ entry.url }}">
<time class="dt-published" datetime="{{ entry.published.isoformat }}">
<i class="fa fa-calendar"></i>
{{ entry.published | naturaltime }}
</time>
</a>
{% if entry.updated != entry.published %}
<time class="dt-updated" datetime="{{ entry.updated.isoformat }}">
<i class="fa fa-pencil"></i>
{{ entry.updated | naturaltime }}
</time>
{% endif %}
</div>
</article>

View file

@ -0,0 +1,24 @@
{% load humanize %}<article class="card h-entry">
<div class="card-body">
{% if entry.name %}<h4 class="card-title p-name">{{ entry.name }}</h4>{% endif %}
<div class="e-content{% if not entry.name %} p-name{% endif %}">{{ entry.content }}</div>
</div>
<div class="card-footer">
<a class="p-author h-card" href="{{ entry.author.url }}">
<img class="u-photo" src="{{ entry.author.avatar.url }}" />
{{ entry.author.first_name }} {{ entry.author.last_name }}
</a>
<a class="u-url" href="{{ entry.url }}">
<time class="dt-published" datetime="{{ entry.published.isoformat }}">
<i class="fa fa-calendar"></i>
{{ entry.published | naturaltime }}
</time>
</a>
{% if entry.updated != entry.published %}
<time class="dt-updated" datetime="{{ entry.updated.isoformat }}">
<i class="fa fa-pencil"></i>
{{ entry.updated | naturaltime }}
</time>
{% endif %}
</div>
</article>

View file

@ -4,7 +4,7 @@
<ol class="list-unstyled"> <ol class="list-unstyled">
{% for entry in entries %} {% for entry in entries %}
<li> <li>
{% include 'entries/entry.html' %} {% include 'entries/h-entry.html' %}
</li> </li>
{% endfor %} {% endfor %}
</ol> </ol>

View file

@ -6,7 +6,19 @@ app_name = 'entries'
urlpatterns = [] urlpatterns = []
for k in kinds.all: for k in kinds.all:
index = k.plural + '_index' index = k.plural + '_index'
urlpatterns.extend(( urlpatterns.append(
url(k.plural, views.index, name=index, kwargs={'kind': k}), url(r'^{k}$'.format(k=k.plural), views.index, name=index, kwargs={'kind': k})
)) )
breadcrumbs.add(app_name + ':' + index, label=k.plural, parent='home:index') breadcrumbs.add(app_name + ':' + index, label=k.plural, parent='home:index')
entry = k.plural + '_entry'
pattern = r'^{k}/(?P<id>\d+)'.format(k=k.plural)
urlpatterns.append(
url(pattern + '$', views.entry, name=entry)
)
breadcrumbs.add(app_name + ':' + entry, parent=app_name + ':' + index)
if k.has('slug'):
urlpatterns.append(
url(pattern + r'/(?P<slug>.+)$', views.entry, name=entry + '_slug')
)
breadcrumbs.add(app_name + ':' + entry + '_slug', parent=app_name + ':' + index)

View file

@ -8,3 +8,11 @@ def index(request, kind):
'entries': entries, 'entries': entries,
'title': kind.plural 'title': kind.plural
}) })
def entry(request, id, slug=None):
entry = Entry.objects.get(pk=id)
return render(request, 'entries/entry.html', {
'entry': entry,
'title': entry.name or entry.content
})

View file

@ -1,7 +1,7 @@
breadcrumbs = {} breadcrumbs = {}
def add(route, label, parent=None): def add(route, label=None, parent=None):
breadcrumbs[route] = {'label': label, 'route': route, 'parent': parent} breadcrumbs[route] = {'label': label, 'route': route, 'parent': parent}

View file

@ -35,7 +35,7 @@
</div> </div>
</nav> </nav>
{% if request.resolver_match.view_name %} {% if request.resolver_match.view_name %}
{% nav_crumbs request.resolver_match.view_name %} {% nav_crumbs request.resolver_match.view_name title %}
{% endif %} {% endif %}
</header> </header>

View file

@ -2,13 +2,9 @@
<nav class="breadcrumbs" aria-label="breadcrumb" role="navigation"> <nav class="breadcrumbs" aria-label="breadcrumb" role="navigation">
<ol class="breadcrumb"> <ol class="breadcrumb">
{% for crumb in crumbs %} {% for crumb in crumbs %}
<li class="breadcrumb-item"> <li class="breadcrumb-item"><a href="{% url crumb.route %}">{{ crumb.label }}</a></li>
<a href="{% url crumb.route %}">{{ crumb.label }}</a>
</li>
{% endfor %} {% endfor %}
<li class="breadcrumb-item active" aria-current="page"> <li class="breadcrumb-item active" aria-current="page">{% firstof current.label title %}</li>
{{ current.label }}
</li>
</ol> </ol>
</nav> </nav>
{% endif %} {% endif %}

View file

@ -68,7 +68,7 @@ def nav_right(request):
@register.inclusion_tag('lemoncurry/tags/breadcrumbs.html') @register.inclusion_tag('lemoncurry/tags/breadcrumbs.html')
def nav_crumbs(route): def nav_crumbs(route, title):
crumbs = breadcrumbs.find(route) crumbs = breadcrumbs.find(route)
current = crumbs.pop() current = crumbs.pop()
return {'crumbs': crumbs, 'current': current} return {'crumbs': crumbs, 'current': current, 'title': title}