Add markdown pages for customization by respos

This commit is contained in:
Guillaume Bertholon 2020-12-18 19:30:09 +01:00
parent ccd8ee9cc9
commit d56d0b44f3
25 changed files with 221 additions and 60 deletions

View file

@ -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"

View file

@ -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
View 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)

View file

@ -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 %}

View file

@ -6,7 +6,7 @@
Il y a {{ category_list|length }} catégorie{{ category_list|pluralize }} de jeux&nbsp;:
<ul>
{% for category in category_list %}
{% include "./partials/category_item.html" %}
<li>{% include "./partials/category_item.html" %}</li>
{% endfor %}
</ul>
{% endblock %}

View file

@ -6,7 +6,7 @@
Il y a {{ game_list|length }} jeu{{ game_list|pluralize:"x" }} en salle jeux&nbsp;:
<ul>
{% for game in game_list %}
{% include "./partials/game_item.html" %}
<li>{% include "./partials/game_item.html" %}</li>
{% endfor %}
</ul>
{% endblock %}

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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 %}

View file

@ -7,7 +7,7 @@
Il y a {{ game_list|length }} jeu{{ game_list|pluralize:"x" }} marqué{{ game_list|pluralize }} avec ce tag&nbsp;:
<ul>
{% for game in game_list %}
{% include "./partials/game_item.html" %}
<li>{% include "./partials/game_item.html" %}</li>
{% endfor %}
</ul>
{% endwith %}

View file

@ -6,7 +6,7 @@
Il y a {{ tag_list|length }} tag{{ tag_list|pluralize }} dans la ludothèque&nbsp;:
<ul>
{% for tag in tag_list %}
{% include "./partials/tag_item.html" %}
<li>{% include "./partials/tag_item.html" %}</li>
{% endfor %}
</ul>
{% endblock %}

View file

@ -0,0 +1 @@
default_app_config = "mainsite.apps.MainsiteConfig"

View file

@ -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)

View file

@ -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
View 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"&nbsp;\1", text)
text = re_after.sub(r"\1&nbsp;", 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)

View 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'],
},
),
]

View file

@ -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")

View file

@ -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 {

View file

@ -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; }

View file

@ -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 %}

View file

@ -0,0 +1,5 @@
{% extends "base.html" %}
{% block "content" %}
{{ markdown_body }}
{% endblock %}

View file

@ -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"),
]

View file

@ -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

View file

@ -1,5 +1,7 @@
Django
django-autoslug
django-markdownx
markdown-iconfonts
Pillow
django-cas-ng
django-haystack