Add markdown pages for customization by respos
This commit is contained in:
parent
ccd8ee9cc9
commit
d56d0b44f3
25 changed files with 221 additions and 60 deletions
|
@ -25,6 +25,7 @@ INSTALLED_APPS = [
|
|||
"django.contrib.sessions",
|
||||
"django.contrib.messages",
|
||||
"django.contrib.staticfiles",
|
||||
"markdownx",
|
||||
"haystack",
|
||||
"mainsite",
|
||||
"inventory",
|
||||
|
@ -80,6 +81,22 @@ AUTH_PASSWORD_VALIDATORS = [
|
|||
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"},
|
||||
]
|
||||
|
||||
# Use markdown extensions
|
||||
MARKDOWNX_MARKDOWN_EXTENSIONS = [
|
||||
"markdown.extensions.extra",
|
||||
"markdown.extensions.sane_lists",
|
||||
"iconfonts",
|
||||
"mainsite.markdown",
|
||||
"inventory.markdown",
|
||||
]
|
||||
|
||||
MARKDOWNX_MARKDOWN_EXTENSION_CONFIGS = {
|
||||
"iconfonts": {
|
||||
"prefix": "fa-",
|
||||
"base": "fa",
|
||||
}
|
||||
}
|
||||
|
||||
# Update the search database on save
|
||||
HAYSTACK_SIGNAL_PROCESSOR = "haystack.signals.RealtimeSignalProcessor"
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ from django.conf.urls.static import static
|
|||
|
||||
urlpatterns = [
|
||||
path("admin/", admin.site.urls),
|
||||
path("markdownx/", include("markdownx.urls")),
|
||||
path("inventory/", include("inventory.urls")),
|
||||
path("auth/", include("gestiojeux_auth.urls")),
|
||||
path("", include("mainsite.urls")),
|
||||
|
|
52
inventory/markdown.py
Normal file
52
inventory/markdown.py
Normal file
|
@ -0,0 +1,52 @@
|
|||
import markdown
|
||||
from django.template.loader import get_template
|
||||
from .models import Category, Tag, Game
|
||||
|
||||
|
||||
class InventoryLinkProcessor(markdown.inlinepatterns.InlineProcessor):
|
||||
model = None
|
||||
pattern = None
|
||||
context_object_name = "object"
|
||||
template_name = None
|
||||
|
||||
def __init__(self, md):
|
||||
super().__init__(self.pattern, md)
|
||||
|
||||
def handleMatch(self, m, data):
|
||||
template = get_template(self.template_name)
|
||||
object_instance = self.model.objects.get(slug=m.group(1))
|
||||
html = template.render({self.context_object_name: object_instance})
|
||||
placeholder = self.md.htmlStash.store(html)
|
||||
return placeholder, m.start(0), m.end(0)
|
||||
|
||||
|
||||
class CategoryLinkProcessor(InventoryLinkProcessor):
|
||||
model = Category
|
||||
pattern = r"\[\[category:([\w-]+)\]\]"
|
||||
context_object_name = "category"
|
||||
template_name = "inventory/partials/category_item.html"
|
||||
|
||||
|
||||
class TagLinkProcessor(InventoryLinkProcessor):
|
||||
model = Tag
|
||||
pattern = r"\[\[tag:([\w-]+)\]\]"
|
||||
context_object_name = "tag"
|
||||
template_name = "inventory/partials/tag_item.html"
|
||||
|
||||
|
||||
class GameLinkProcessor(InventoryLinkProcessor):
|
||||
model = Game
|
||||
pattern = r"\[\[game:([\w-]+)\]\]"
|
||||
context_object_name = "game"
|
||||
template_name = "inventory/partials/game_item.html"
|
||||
|
||||
|
||||
class InventoryMarkdownExtension(markdown.extensions.Extension):
|
||||
def extendMarkdown(self, md):
|
||||
md.inlinePatterns.register(CategoryLinkProcessor(md), "category_link", 75)
|
||||
md.inlinePatterns.register(TagLinkProcessor(md), "tag_link", 75)
|
||||
md.inlinePatterns.register(GameLinkProcessor(md), "game_link", 75)
|
||||
|
||||
|
||||
def makeExtension(**kwargs):
|
||||
return InventoryMarkdownExtension(**kwargs)
|
|
@ -7,7 +7,7 @@
|
|||
Il y a {{ game_list|length }} jeu{{ game_list|pluralize:"x" }} dans cette catégorie :
|
||||
<ul>
|
||||
{% for game in game_list %}
|
||||
{% include "./partials/game_item.html" %}
|
||||
<li>{% include "./partials/game_item.html" %}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endwith %}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
Il y a {{ category_list|length }} catégorie{{ category_list|pluralize }} de jeux :
|
||||
<ul>
|
||||
{% for category in category_list %}
|
||||
{% include "./partials/category_item.html" %}
|
||||
<li>{% include "./partials/category_item.html" %}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
Il y a {{ game_list|length }} jeu{{ game_list|pluralize:"x" }} en salle jeux :
|
||||
<ul>
|
||||
{% for game in game_list %}
|
||||
{% include "./partials/game_item.html" %}
|
||||
<li>{% include "./partials/game_item.html" %}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
<li>
|
||||
<a href="{{ category.get_absolute_url }}"><i class="fa fa-bookmark" aria-hidden="true"></i> <span class="title">{{ category.name }}</span></a>
|
||||
</li>
|
||||
<a class="inventory_item category" href="{{ category.get_absolute_url }}">
|
||||
<i class="fa fa-bookmark" aria-hidden="true"></i> <span class="title">{{ category.name }}</span>
|
||||
</a>
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
<li>
|
||||
<a href="{{ game.get_absolute_url }}">
|
||||
<a class="inventory_item game" href="{{ game.get_absolute_url }}">
|
||||
<span class="title">{{ game.title }}</span>
|
||||
<div class="details">
|
||||
<p><i class="fa fa-fw fa-users" aria-hidden="true"></i> {{ game.get_player_range }}</p>
|
||||
<p><i class="fa fa-fw fa-clock-o" aria-hidden="true"></i> {{ game.duration }}
|
||||
<p><i class="fa fa-fw fa-bookmark"></i> {{ game.category }}</p>
|
||||
<span class="details">
|
||||
<span><i class="fa fa-fw fa-users" aria-hidden="true"></i> {{ game.get_player_range }}</span>
|
||||
{% if game.duration %}
|
||||
<span><i class="fa fa-fw fa-clock-o" aria-hidden="true"></i> {{ game.duration }}</span>
|
||||
{% endif %}
|
||||
<span><i class="fa fa-fw fa-bookmark"></i> {{ game.category }}</span>
|
||||
{% for tag in game.tags.all %}
|
||||
<p><i class="fa fa-fw fa-tag" aria-hidden="true"></i> {{ tag }}</p>
|
||||
<span><i class="fa fa-fw fa-tag" aria-hidden="true"></i> {{ tag }}</span>
|
||||
{% endfor %}
|
||||
{% if game.game_designer %}
|
||||
<p><i class="fa fa-fw fa-wrench" aria-hidden="true"></i> {{ game.game_designer }}</p>
|
||||
<span><i class="fa fa-fw fa-wrench" aria-hidden="true"></i> {{ game.game_designer }}</span>
|
||||
{% endif %}
|
||||
{% if game.illustrator %}
|
||||
<p><i class="fa fa-fw fa-paint-brush" aria-hidden="true"></i> {{ game.illustrator }}</p>
|
||||
<span><i class="fa fa-fw fa-paint-brush" aria-hidden="true"></i> {{ game.illustrator }}</span>
|
||||
{% endif %}
|
||||
{% if game.editor %}
|
||||
<p><i class="fa fa-fw fa-cogs" aria-hidden="true"></i> {{ game.editor }}</p>
|
||||
<span><i class="fa fa-fw fa-cogs" aria-hidden="true"></i> {{ game.editor }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</span>
|
||||
</a>
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
<li>
|
||||
<a href="{{ tag.get_absolute_url }}"><i class="fa fa-tag" aria-hidden="true"></i> <span class="title">{{ tag.name }}</span></a>
|
||||
</li>
|
||||
<a class="inventory_item tag" href="{{ tag.get_absolute_url }}">
|
||||
<i class="fa fa-tag" aria-hidden="true"></i> <span class="title">{{ tag.name }}</span>
|
||||
</a>
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
<ul>
|
||||
{% for result in page_obj.object_list %}
|
||||
<li>
|
||||
{% if result.model_name == "game" %}
|
||||
{% include "./partials/game_item.html" with game=result.object %}
|
||||
{% elif result.model_name == "category" %}
|
||||
|
@ -19,6 +20,7 @@
|
|||
{% elif result.model_name == "tag" %}
|
||||
{% include "./partials/tag_item.html" with tag=result.object %}
|
||||
{% endif %}
|
||||
</li>
|
||||
{% empty %}
|
||||
<li>Aucun résultat trouvé</li>
|
||||
{% endfor %}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
Il y a {{ game_list|length }} jeu{{ game_list|pluralize:"x" }} marqué{{ game_list|pluralize }} avec ce tag :
|
||||
<ul>
|
||||
{% for game in game_list %}
|
||||
{% include "./partials/game_item.html" %}
|
||||
<li>{% include "./partials/game_item.html" %}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endwith %}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
Il y a {{ tag_list|length }} tag{{ tag_list|pluralize }} dans la ludothèque :
|
||||
<ul>
|
||||
{% for tag in tag_list %}
|
||||
{% include "./partials/tag_item.html" %}
|
||||
<li>{% include "./partials/tag_item.html" %}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
default_app_config = "mainsite.apps.MainsiteConfig"
|
|
@ -1,3 +1,5 @@
|
|||
from django.contrib import admin
|
||||
from .models import MarkdownPage
|
||||
from markdownx.admin import MarkdownxModelAdmin
|
||||
|
||||
# Register your models here.
|
||||
admin.site.register(MarkdownPage, MarkdownxModelAdmin)
|
||||
|
|
|
@ -2,4 +2,5 @@ from django.apps import AppConfig
|
|||
|
||||
|
||||
class MainsiteConfig(AppConfig):
|
||||
name = 'mainsite'
|
||||
name = "mainsite"
|
||||
verbose_name = "Site internet"
|
||||
|
|
28
mainsite/markdown.py
Normal file
28
mainsite/markdown.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
import markdown
|
||||
import re
|
||||
|
||||
|
||||
class NbspPreprocessor(markdown.preprocessors.Preprocessor):
|
||||
"""Replace regular spaces with non-breaking spaces within a text around relevant
|
||||
symbols"""
|
||||
|
||||
NBSP_BEFORE = [":", "!", "?", "»", ":", ";", "—", "€"]
|
||||
NBSP_AFTER = ["«", "—"]
|
||||
|
||||
def run(self, lines):
|
||||
text = "\n".join(lines)
|
||||
re_before = re.compile("(?: *\n *| +)([{}])".format("".join(self.NBSP_BEFORE)))
|
||||
re_after = re.compile("([{}])(?: +| *\n *)".format("".join(self.NBSP_AFTER)))
|
||||
|
||||
text = re_before.sub(r" \1", text)
|
||||
text = re_after.sub(r"\1 ", text)
|
||||
return text.split("\n")
|
||||
|
||||
|
||||
class MainSiteMarkdownExtension(markdown.extensions.Extension):
|
||||
def extendMarkdown(self, md):
|
||||
md.preprocessors.register(NbspPreprocessor(md), "nbsp", 10)
|
||||
|
||||
|
||||
def makeExtension(**kwargs):
|
||||
return MainSiteMarkdownExtension(**kwargs)
|
28
mainsite/migrations/0001_initial.py
Normal file
28
mainsite/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
# Generated by Django 3.1.2 on 2020-12-13 14:19
|
||||
|
||||
from django.db import migrations, models
|
||||
import markdownx.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='MarkdownPage',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('slug', models.SlugField(blank=True, help_text="Identifiant de la page qui se voit dans l'URL. Ne doit pas collisionner avec une page existante. Laisser vide pour la page d'accueil, requis sinon.", unique=True, verbose_name='Adresse de la page')),
|
||||
('content', markdownx.models.MarkdownxField(verbose_name='Contenu')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'page Markdown',
|
||||
'verbose_name_plural': 'pages Markdown',
|
||||
'ordering': ['slug'],
|
||||
},
|
||||
),
|
||||
]
|
|
@ -1,3 +1,27 @@
|
|||
from django.db import models
|
||||
from django.urls import reverse
|
||||
from markdownx.models import MarkdownxField
|
||||
|
||||
# Create your models here.
|
||||
|
||||
class MarkdownPage(models.Model):
|
||||
slug = models.SlugField(
|
||||
blank=True,
|
||||
unique=True,
|
||||
verbose_name="Adresse de la page",
|
||||
help_text="Identifiant de la page qui se voit dans l'URL. Ne doit pas collisionner avec une page existante. Laisser vide pour la page d'accueil, requis sinon.",
|
||||
)
|
||||
content = MarkdownxField(verbose_name="Contenu")
|
||||
|
||||
class Meta:
|
||||
verbose_name = "page Markdown"
|
||||
verbose_name_plural = "pages Markdown"
|
||||
ordering = ["slug"]
|
||||
|
||||
def __str__(self):
|
||||
return self.slug or "Page d'accueil"
|
||||
|
||||
def get_absolute_url(self):
|
||||
if self.slug:
|
||||
return reverse("mainsite:md_page", args=[self.slug])
|
||||
else:
|
||||
return reverse("mainsite:home")
|
||||
|
|
|
@ -291,7 +291,7 @@ ul {
|
|||
list-style-type: none;
|
||||
}
|
||||
|
||||
li>a {
|
||||
a.inventory_item {
|
||||
display: block;
|
||||
padding: 15px;
|
||||
margin: 10px 5px;
|
||||
|
@ -309,10 +309,6 @@ li>a {
|
|||
flex-wrap: wrap;
|
||||
margin: 5px;
|
||||
gap: 5px 20px;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
|
|
|
@ -440,29 +440,27 @@ ul {
|
|||
padding: 0;
|
||||
list-style-type: none; }
|
||||
|
||||
li > a {
|
||||
a.inventory_item {
|
||||
display: block;
|
||||
padding: 15px;
|
||||
margin: 10px 5px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid transparent;
|
||||
text-decoration: none; }
|
||||
li > a .title {
|
||||
a.inventory_item .title {
|
||||
text-decoration: underline #c9dbe0; }
|
||||
li > a .details {
|
||||
a.inventory_item .details {
|
||||
font-size: 0.7em;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 5px;
|
||||
gap: 5px 20px; }
|
||||
li > a .details p {
|
||||
margin: 0; }
|
||||
li > a:hover {
|
||||
a.inventory_item:hover {
|
||||
border-color: #51808c;
|
||||
background-color: white; }
|
||||
li > a:hover .title {
|
||||
a.inventory_item:hover .title {
|
||||
text-decoration-color: currentColor; }
|
||||
li > a:focus {
|
||||
a.inventory_item:focus {
|
||||
border-color: #51808c;
|
||||
background-color: white;
|
||||
box-shadow: 0 0 1.5px 1px #6bb8c4; }
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block "content" %}
|
||||
<h1>Bienvenue en salle Jeux</h1>
|
||||
Site d'inventaire et de gestion du club Jeux de l'ENS.
|
||||
{% endblock %}
|
5
mainsite/templates/mainsite/markdown_page.html
Normal file
5
mainsite/templates/mainsite/markdown_page.html
Normal file
|
@ -0,0 +1,5 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block "content" %}
|
||||
{{ markdown_body }}
|
||||
{% endblock %}
|
|
@ -1,8 +1,9 @@
|
|||
from django.urls import path
|
||||
from .views import HomepageView
|
||||
from .views import MarkdownPageView
|
||||
|
||||
app_name = "mainsite"
|
||||
|
||||
urlpatterns = [
|
||||
path("", HomepageView.as_view(), name="home"),
|
||||
path("", MarkdownPageView.as_view(), {"slug": ""}, name="home"),
|
||||
path("<slug:slug>/", MarkdownPageView.as_view(), name="md_page"),
|
||||
]
|
||||
|
|
|
@ -1,5 +1,14 @@
|
|||
from django.views.generic import TemplateView
|
||||
from django.views.generic import DetailView
|
||||
from django.utils.safestring import mark_safe
|
||||
from markdownx.utils import markdownify
|
||||
from .models import MarkdownPage
|
||||
|
||||
|
||||
class HomepageView(TemplateView):
|
||||
template_name = "mainsite/home.html"
|
||||
class MarkdownPageView(DetailView):
|
||||
model = MarkdownPage
|
||||
template_name = "mainsite/markdown_page.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context["markdown_body"] = mark_safe(markdownify(self.object.content))
|
||||
return context
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
Django
|
||||
django-autoslug
|
||||
django-markdownx
|
||||
markdown-iconfonts
|
||||
Pillow
|
||||
django-cas-ng
|
||||
django-haystack
|
||||
|
|
Loading…
Reference in a new issue