Rudimentary but fully-functional entry permalink pages :3
This commit is contained in:
parent
d267df337f
commit
2b6691f8a5
13 changed files with 133 additions and 73 deletions
1
Pipfile
1
Pipfile
|
@ -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
80
Pipfile.lock
generated
|
@ -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": {}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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']
|
||||||
|
|
|
@ -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>
|
|
||||||
|
|
24
entries/templates/entries/h-entry.html
Normal file
24
entries/templates/entries/h-entry.html
Normal 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>
|
|
@ -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>
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
})
|
||||||
|
|
|
@ -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}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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 %}
|
||||||
|
|
|
@ -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}
|
||||||
|
|
Loading…
Reference in a new issue