2025.10.06-03:08:05
This commit is contained in:
91
templates/account/email.html
Normal file
91
templates/account/email.html
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
{% extends "account/base_manage_email.html" %}
|
||||||
|
{% load static allauth i18n widget_tweaks %}
|
||||||
|
{% block head_title %}
|
||||||
|
{% trans "Email Addresses" %}
|
||||||
|
{% endblock head_title %}
|
||||||
|
{% block content %}
|
||||||
|
{% element h1 %}
|
||||||
|
{% trans "Email Addresses" %}
|
||||||
|
{% endelement %}
|
||||||
|
{% if emailaddresses %}
|
||||||
|
{% element p %}
|
||||||
|
{% trans 'The following email addresses are associated with your account:' %}
|
||||||
|
{% endelement %}
|
||||||
|
{% url 'account_email' as email_url %}
|
||||||
|
{% element form form=form action=email_url method="post" tags="email,list" %}
|
||||||
|
{% slot body %}
|
||||||
|
{% csrf_token %}
|
||||||
|
{% for radio in emailaddress_radios %}
|
||||||
|
{% with emailaddress=radio.emailaddress %}
|
||||||
|
{% element field type="radio" checked=radio.checked name="email" value=emailaddress.email id=radio.id %}
|
||||||
|
{% slot label %}
|
||||||
|
{{ emailaddress.email }}
|
||||||
|
{% if emailaddress.verified %}
|
||||||
|
{% element badge tags="success,email,verified" %}
|
||||||
|
{% translate "Verified" %}
|
||||||
|
{% endelement %}
|
||||||
|
{% else %}
|
||||||
|
{% element badge tags="warning,email,unverified" %}
|
||||||
|
{% translate "Unverified" %}
|
||||||
|
{% endelement %}
|
||||||
|
{% endif %}
|
||||||
|
{% if emailaddress.primary %}
|
||||||
|
{% element badge tags="email,primary" %}
|
||||||
|
{% translate "Primary" %}
|
||||||
|
{% endelement %}
|
||||||
|
{% endif %}
|
||||||
|
{% endslot %}
|
||||||
|
{% endelement %}
|
||||||
|
{% endwith %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endslot %}
|
||||||
|
|
||||||
|
{% slot actions %}
|
||||||
|
<div class="text-end">
|
||||||
|
{% element button type="submit" class="btn btn-primary mb-3" name="action_primary" %}
|
||||||
|
{% trans 'Make Primary' %}
|
||||||
|
{% endelement %}
|
||||||
|
{% element button tags="secondary" class="btn btn-secondary mb-3" type="submit" name="action_send" %}
|
||||||
|
{% trans 'Re-send Verification' %}
|
||||||
|
{% endelement %}
|
||||||
|
{% element button class="btn btn-danger mb-3" tags="danger,delete" type="submit" name="action_remove" %}
|
||||||
|
{% trans 'Remove' %}
|
||||||
|
{% endelement %}
|
||||||
|
</div>
|
||||||
|
{% endslot %}
|
||||||
|
|
||||||
|
{% endelement %}
|
||||||
|
{% else %}
|
||||||
|
{% include "account/snippets/warn_no_email.html" %}
|
||||||
|
{% endif %}
|
||||||
|
{% if can_add_email %}
|
||||||
|
{% element h2 %}
|
||||||
|
{% trans "Add Email Address" %}
|
||||||
|
{% endelement %}
|
||||||
|
{% url 'account_email' as action_url %}
|
||||||
|
{% element form form=form method="post" action=action_url tags="email,add" %}
|
||||||
|
{% slot body %}
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="form-floating form-floating-sm mb-3">
|
||||||
|
{% render_field form.email class="form-control" placeholder="Neue Email-Adresse" %}
|
||||||
|
<label for="id_email">Neue Email-Adresse</label>
|
||||||
|
</div>
|
||||||
|
{% endslot %}
|
||||||
|
{% slot actions %}
|
||||||
|
<div class="text-end">
|
||||||
|
{% element button name="action_add" class="btn btn-success" type="submit" %}
|
||||||
|
{% trans "Add Email" %}
|
||||||
|
{% endelement %}
|
||||||
|
</div>
|
||||||
|
{% endslot %}
|
||||||
|
{% endelement %}
|
||||||
|
{% endif %}
|
||||||
|
{% endblock content %}
|
||||||
|
{% block extra_body %}
|
||||||
|
<script src="{% static 'account/js/account.js' %}"></script>
|
||||||
|
<script src="{% static 'account/js/onload.js' %}"></script>
|
||||||
|
<script data-allauth-onload="allauth.account.forms.manageEmailForm" type="application/json">{
|
||||||
|
"i18n": {"confirmDelete": "{% trans 'Do you really want to remove the selected email address?' %}"}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
{% endblock extra_body %}
|
||||||
68
templates/account/email_change.html
Normal file
68
templates/account/email_change.html
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
{% extends "account/base_manage_email.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load allauth %}
|
||||||
|
{% block head_title %}
|
||||||
|
{% trans "Email Address" %}
|
||||||
|
{% endblock head_title %}
|
||||||
|
{% block content %}
|
||||||
|
{% element h1 %}
|
||||||
|
{% trans "Email Address" %}
|
||||||
|
{% endelement %}
|
||||||
|
{% if not emailaddresses %}
|
||||||
|
{% include "account/snippets/warn_no_email.html" %}
|
||||||
|
{% endif %}
|
||||||
|
{% url 'account_email' as action_url %}
|
||||||
|
{% element form method="post" action=action_url %}
|
||||||
|
{% slot body %}
|
||||||
|
{% csrf_token %}
|
||||||
|
{% if current_emailaddress %}
|
||||||
|
{% element field id="current_email" disabled=True type="email" value=current_emailaddress.email %}
|
||||||
|
{% slot label %}
|
||||||
|
{% translate "Current email" %}:
|
||||||
|
{% endslot %}
|
||||||
|
{% endelement %}
|
||||||
|
{% endif %}
|
||||||
|
{% if new_emailaddress %}
|
||||||
|
{% element field id="new_email" value=new_emailaddress.email disabled=True type="email" %}
|
||||||
|
{% slot label %}
|
||||||
|
{% if not current_emailaddress %}
|
||||||
|
{% translate "Current email" %}:
|
||||||
|
{% else %}
|
||||||
|
{% translate "Changing to" %}:
|
||||||
|
{% endif %}
|
||||||
|
{% endslot %}
|
||||||
|
{% slot help_text %}
|
||||||
|
{% blocktranslate %}Your email address is still pending verification.{% endblocktranslate %}
|
||||||
|
{% element button class="btn btn-secondary" form="pending-email" type="submit" name="action_send" tags="minor,secondary" %}
|
||||||
|
{% trans 'Re-send Verification' %}
|
||||||
|
{% endelement %}
|
||||||
|
{% if current_emailaddress %}
|
||||||
|
{% element button class="btn btn-danger" form="pending-email" type="submit" name="action_remove" tags="danger,minor" %}
|
||||||
|
{% trans 'Cancel Change' %}
|
||||||
|
{% endelement %}
|
||||||
|
{% endif %}
|
||||||
|
{% endslot %}
|
||||||
|
{% endelement %}
|
||||||
|
{% endif %}
|
||||||
|
{% element field id=form.email.auto_id name="email" value=form.email.value errors=form.email.errors type="email" %}
|
||||||
|
{% slot label %}
|
||||||
|
{% translate "Change to" %}:
|
||||||
|
{% endslot %}
|
||||||
|
{% endelement %}
|
||||||
|
{% endslot %}
|
||||||
|
{% slot actions %}
|
||||||
|
{% element button class="btn btn-primary" name="action_add" type="submit" %}
|
||||||
|
{% trans "Change Email" %}
|
||||||
|
{% endelement %}
|
||||||
|
{% endslot %}
|
||||||
|
{% endelement %}
|
||||||
|
{% if new_emailaddress %}
|
||||||
|
<form style="display: none"
|
||||||
|
id="pending-email"
|
||||||
|
method="post"
|
||||||
|
action="{% url 'account_email' %}">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="email" value="{{ new_emailaddress.email }}">
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock content %}
|
||||||
41
templates/account/email_confirm.html
Normal file
41
templates/account/email_confirm.html
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
{% extends "account/base_entrance.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load account %}
|
||||||
|
{% load allauth %}
|
||||||
|
{% block head_title %}
|
||||||
|
{% trans "Confirm Email Address" %}
|
||||||
|
{% endblock head_title %}
|
||||||
|
{% block content %}
|
||||||
|
{% element h1 %}
|
||||||
|
{% trans "Confirm Email Address" %}
|
||||||
|
{% endelement %}
|
||||||
|
{% if confirmation %}
|
||||||
|
{% user_display confirmation.email_address.user as user_display %}
|
||||||
|
{% if can_confirm %}
|
||||||
|
{% element p %}
|
||||||
|
{% blocktrans with confirmation.email_address.email as email %}Please confirm that <a href="mailto:{{ email }}">{{ email }}</a> is an email address for user {{ user_display }}.{% endblocktrans %}
|
||||||
|
{% endelement %}
|
||||||
|
{% url 'account_confirm_email' confirmation.key as action_url %}
|
||||||
|
{% element form method="post" action=action_url %}
|
||||||
|
{% slot actions %}
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ redirect_field }}
|
||||||
|
<div class="text-end">
|
||||||
|
{% element button class="btn btn-success" type="submit" %}
|
||||||
|
{% trans 'Confirm' %}
|
||||||
|
{% endelement %}
|
||||||
|
</div>
|
||||||
|
{% endslot %}
|
||||||
|
{% endelement %}
|
||||||
|
{% else %}
|
||||||
|
{% element p %}
|
||||||
|
{% blocktrans %}Unable to confirm {{ email }} because it is already confirmed by a different account.{% endblocktrans %}
|
||||||
|
{% endelement %}
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
{% url 'account_email' as email_url %}
|
||||||
|
{% element p %}
|
||||||
|
{% blocktrans %}This email confirmation link expired or is invalid. Please <a href="{{ email_url }}">issue a new email confirmation request</a>.{% endblocktrans %}
|
||||||
|
{% endelement %}
|
||||||
|
{% endif %}
|
||||||
|
{% endblock content %}
|
||||||
49
templates/account/login.html
Normal file
49
templates/account/login.html
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load i18n widget_tweaks allauth account socialaccount %}
|
||||||
|
|
||||||
|
{% block head_title %}cmoser.eu - {% translate 'Log in' %}{% endblock head_title %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<main class="card rounded mx-auto p-4" style="width:30rem;">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="d-flex flex-column">
|
||||||
|
<div class="flex flex-col text-center mb-4">
|
||||||
|
<h1 class="h3">Melde dich bei deinem Account an</h1>
|
||||||
|
<form method="POST" class="mb-4">
|
||||||
|
{% csrf_token %}
|
||||||
|
{% if form.errors %}
|
||||||
|
{% for field, errors in form.errors.items %}
|
||||||
|
{% for error in errors %}
|
||||||
|
<div class="p-2 my-2 text-sm text-red-700 bg-red-50 rounded-md border-red-300 border">
|
||||||
|
{{ error }}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="form-floating mb-3">
|
||||||
|
{% render_field form.login class="form-control" placeholder="Emailadresse" %}
|
||||||
|
<label for="{{ form.login.id_for_label }}">Emailaddresse</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-floating mb-3">
|
||||||
|
{% render_field form.password class="form-control" %}
|
||||||
|
<label for="{{ form.password.id_for_label }}">Passwort</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{% render_field form.remember class="input-checked" %}
|
||||||
|
<label class="input-checked-label" for="{{ form.remember.id_for_label }}">{{ form.remember.label }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="text-end">
|
||||||
|
<button type="submit" class="btn btn-success">Sign in</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<h2 class="h4 text-center">Oder nutze deinen Socialaccount</h3>
|
||||||
|
{% if SOCIALACCOUNT_ENABLED %}
|
||||||
|
{% include 'socialaccount/snippets/login.html' with page_layout="entrance" %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
{% endblock content %}
|
||||||
39
templates/account/password_change.html
Normal file
39
templates/account/password_change.html
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
{% extends "account/base_manage_password.html" %}
|
||||||
|
{% load allauth i18n widget_tweaks %}
|
||||||
|
{% block head_title %}
|
||||||
|
{% trans "Change Password" %}
|
||||||
|
{% endblock head_title %}
|
||||||
|
{% block content %}
|
||||||
|
{% element h1 %}
|
||||||
|
{% trans "Change Password" %}
|
||||||
|
{% endelement %}
|
||||||
|
{% url 'account_change_password' as action_url %}
|
||||||
|
{% element form form=form method="post" action=action_url %}
|
||||||
|
{% slot body %}
|
||||||
|
{% csrf_token %}
|
||||||
|
{#{{ redirect_field }}#}
|
||||||
|
{# {% element fields form=form %}{% endelement %} #}
|
||||||
|
|
||||||
|
<div class="form-floating form-floating-sm mb-3">
|
||||||
|
{% render_field form.oldpassword placeholder="Aktuelles Passwort" class="form-control" %}
|
||||||
|
<label for="{{ id_oldpassword }}">Aktuelles Passwort</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-floating form-floating-sm mb-3">
|
||||||
|
{% render_field form.password1 class="form-control" placehoder="Neues Passwort" autocomplete="new-password" required="" %}
|
||||||
|
<label for="{{ id_password1 }}">Neues Passwort</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-floating form-flaoting-sm mb-3">
|
||||||
|
{% render_field form.password2 class="form-control" placehoder="Neues Passwort" autocomplete="new-password" required="" %}
|
||||||
|
<label for="{{ id_password2 }}">Neues Passwort</label>
|
||||||
|
</div>
|
||||||
|
{% endslot %}
|
||||||
|
{% slot actions %}
|
||||||
|
<div class="text-end">
|
||||||
|
<a class="me-2" href="{% url 'account_reset_password' %}">{% trans "Forgot Password?" %}</a>
|
||||||
|
{% element button class="btn btn-primary" type="submit" %}
|
||||||
|
{% trans "Change Password" %}
|
||||||
|
{% endelement %}
|
||||||
|
</div>
|
||||||
|
{% endslot %}
|
||||||
|
{% endelement %}
|
||||||
|
{% endblock content %}
|
||||||
38
templates/account/password_reset.html
Normal file
38
templates/account/password_reset.html
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
{% extends "account/base_entrance.html" %}
|
||||||
|
{% load i18n allauth account widget_tweaks %}
|
||||||
|
{% block head_title %}
|
||||||
|
{% trans "Password Reset" %}
|
||||||
|
{% endblock head_title %}
|
||||||
|
{% block content %}
|
||||||
|
{% element h1 %}
|
||||||
|
{% trans "Password Reset" %}
|
||||||
|
{% endelement %}
|
||||||
|
{% if user.is_authenticated %}
|
||||||
|
{% include "account/snippets/already_logged_in.html" %}
|
||||||
|
{% endif %}
|
||||||
|
{% element p %}
|
||||||
|
{% trans "Forgotten your password? Enter your email address below, and we'll send you an email allowing you to reset it." %}
|
||||||
|
{% endelement %}
|
||||||
|
{% url 'account_reset_password' as reset_url %}
|
||||||
|
{% element form form=form method="post" action=reset_url %}
|
||||||
|
{% slot body %}
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ redirect_field }}
|
||||||
|
|
||||||
|
<div class="form-floating form-floating-sm">
|
||||||
|
{% render_field form.email name="email" autocomplete="email" placeholder="E-Mail-Adresse" max-length="320" required="" class="form-control mb-3" %}
|
||||||
|
<label for="id_email">E-Mail-Adresse</label>
|
||||||
|
</div>
|
||||||
|
{% endslot %}
|
||||||
|
{% slot actions %}
|
||||||
|
<div class="text-end mb-3">
|
||||||
|
{% element button type="submit" class="btn btn-primary" %}
|
||||||
|
{% trans 'Reset My Password' %}
|
||||||
|
{% endelement %}
|
||||||
|
</div>
|
||||||
|
{% endslot %}
|
||||||
|
{% endelement %}
|
||||||
|
{% element p %}
|
||||||
|
{% blocktrans %}Please contact us if you have any trouble resetting your password.{% endblocktrans %}
|
||||||
|
{% endelement %}
|
||||||
|
{% endblock content %}
|
||||||
13
templates/allauth/elements/button.html
Normal file
13
templates/allauth/elements/button.html
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{% load allauth %}
|
||||||
|
{% comment %} djlint:off {% endcomment %}
|
||||||
|
<{% if attrs.href %}a href="{{ attrs.href }}"{% else %}button{% endif %}
|
||||||
|
{% if attrs.form %}form="{{ attrs.form }}"{% endif %}
|
||||||
|
{% if attrs.id %}id="{{ attrs.id }}"{% endif %}
|
||||||
|
{% if attrs.name %}name="{{ attrs.name }}"{% endif %}
|
||||||
|
{% if attrs.value %}value="{{ attrs.value }}"{% endif %}
|
||||||
|
{% if attrs.type %}type="{{ attrs.type }}"{% endif %}
|
||||||
|
{% if attrs.class %}class="{{ attrs.class }}"{% endif %}
|
||||||
|
>
|
||||||
|
{% slot %}
|
||||||
|
{% endslot %}
|
||||||
|
</{% if attrs.href %}a{% else %}button{% endif %}>
|
||||||
21
templates/allauth/elements/provider.html
Normal file
21
templates/allauth/elements/provider.html
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{% load static %}
|
||||||
|
{% if not attrs.name|lower == "openid" %}
|
||||||
|
<li class="list-group-item">
|
||||||
|
<a title="{{ attrs.name }}" href="{{ attrs.href }}" class="icon-link">
|
||||||
|
{% if attrs.name|lower == "steam" %}
|
||||||
|
<svg class="bi">
|
||||||
|
<use xlink:href="{% static "img/bootstrap-icons.svg" %}#steam"></use>
|
||||||
|
</svg>
|
||||||
|
{% elif attrs.name|lower == "github" %}
|
||||||
|
<svg class="bi">
|
||||||
|
<use xlink:href="{% static "img/bootstrap-icons.svg" %}#github"></use>
|
||||||
|
</svg>
|
||||||
|
{% elif attrs.name|lower == "google" %}
|
||||||
|
<svg class="bi">
|
||||||
|
<use xlink:href="{% static "img/bootstrap-icons.svg" %}#google"></use>
|
||||||
|
</svg>
|
||||||
|
{% endif %}
|
||||||
|
{{ attrs.name }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
5
templates/allauth/elements/provider_list.html
Normal file
5
templates/allauth/elements/provider_list.html
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{% load allauth %}
|
||||||
|
<ul class="list-group">
|
||||||
|
{% slot %}
|
||||||
|
{% endslot %}
|
||||||
|
</ul>
|
||||||
2
templates/allauth/layouts/base.html
Normal file
2
templates/allauth/layouts/base.html
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
@@ -7,6 +7,9 @@
|
|||||||
<title>TinyWiki</title>
|
<title>TinyWiki</title>
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous">
|
||||||
{% block extra_css %}{% endblock %}
|
{% block extra_css %}{% endblock %}
|
||||||
|
<style>
|
||||||
|
{% block style %}{% endblock %}
|
||||||
|
</style>
|
||||||
<script>
|
<script>
|
||||||
document.documentElement.setAttribute('data-bs-theme', (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'));
|
document.documentElement.setAttribute('data-bs-theme', (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'));
|
||||||
</script>
|
</script>
|
||||||
@@ -15,7 +18,7 @@
|
|||||||
<body class="min-vh-100 d-flex flex-column">
|
<body class="min-vh-100 d-flex flex-column">
|
||||||
|
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row bg-info px-2">
|
<div class="row bg-primary pt-2">
|
||||||
<div clas="col-md-12 text-white ">
|
<div clas="col-md-12 text-white ">
|
||||||
{% if brand_logo %}
|
{% if brand_logo %}
|
||||||
<img class="mb2 me-2" width="46" height="46" src="{{ brand_logo }}">
|
<img class="mb2 me-2" width="46" height="46" src="{{ brand_logo }}">
|
||||||
@@ -26,7 +29,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<span class="display-6 font-weight-bold text-white me-2">{{ brand_name }}</span>
|
<span class="display-6 font-weight-bold text-white me-2">{{ brand_name }}</span>
|
||||||
{% if subtitle %}
|
{% if subtitle %}
|
||||||
<span class="h2">{{ subtitle }}</h2>
|
<span class="h2 text-truncate-sm">{{ subtitle }}</h2>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -61,8 +64,25 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row h-100">
|
<div class="row h-100 mb-4">
|
||||||
<div class="col-lg-3">
|
<div class="col-lg-3 pt-6 px-4 d-none d-lg-block" >
|
||||||
|
<ul class="list-group mt-2">
|
||||||
|
<li class="list-group-item bg-primary text-white">
|
||||||
|
<span class="list-group-badge">TinyWiki Pages</span>
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<a href="{% url "tinywiki:page" slug="tw-bbcode" %}">BBCode Guide</a>
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<a href="{% url "tinywiki:page" slug="tw-markdown" %}">Markdown Guide</a>
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<a href="{% url "tinywiki:page" slug="tw-license" %}">TinyWiki License</a>
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<a href="{% url "tinywiki:page" slug="tw-bootstrap-license" %}">Bootstrap License</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-6">
|
<div class="col-lg-6">
|
||||||
<main>
|
<main>
|
||||||
@@ -83,6 +103,7 @@
|
|||||||
<a class="text-white" href="{% url 'tinywiki:page' 'tw-license' %}"> © 2025</a>
|
<a class="text-white" href="{% url 'tinywiki:page' 'tw-license' %}"> © 2025</a>
|
||||||
</span>
|
</span>
|
||||||
</footer>
|
</footer>
|
||||||
|
{% block extra_body %}{% endblock extra_body %}
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.min.js"></script>
|
||||||
{% block extra_scripts %}{% endblock extra_scripts %}
|
{% block extra_scripts %}{% endblock extra_scripts %}
|
||||||
|
|||||||
55
templates/socialaccount/connections.html
Normal file
55
templates/socialaccount/connections.html
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{# {% extends "socialaccount/base_manage.html" %} #}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load allauth %}
|
||||||
|
{% block head_title %}
|
||||||
|
{% trans "Account Connections" %}
|
||||||
|
{% endblock head_title %}
|
||||||
|
{% block content %}
|
||||||
|
{% element h1 %}
|
||||||
|
{% trans "Account Connections" %}
|
||||||
|
{% endelement %}
|
||||||
|
{% if form.accounts %}
|
||||||
|
{% element p %}
|
||||||
|
{% blocktrans %}You can sign in to your account using any of the following third-party accounts:{% endblocktrans %}
|
||||||
|
{% endelement %}
|
||||||
|
{% url 'socialaccount_connections' as action_url %}
|
||||||
|
{% element form form=form method="post" action=action_url %}
|
||||||
|
{% slot body %}
|
||||||
|
{% csrf_token %}
|
||||||
|
{% for acc in form.fields.account.choices %}
|
||||||
|
{% with account=acc.0.instance.get_provider_account %}
|
||||||
|
{% setvar radio_id %}
|
||||||
|
id_account_{{ account.account.pk }}
|
||||||
|
{% endsetvar %}
|
||||||
|
{% setvar tags %}
|
||||||
|
socialaccount,{{ account.account.provider }}
|
||||||
|
{% endsetvar %}
|
||||||
|
{% element field id=radio_id type="radio" name="account" value=account.account.pk %}
|
||||||
|
{% slot label %}
|
||||||
|
{{ account }}
|
||||||
|
{% element badge tags=tags %}
|
||||||
|
{{ account.get_brand.name }}
|
||||||
|
{% endelement %}
|
||||||
|
{% endslot %}
|
||||||
|
{% endelement %}
|
||||||
|
{% endwith %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endslot %}
|
||||||
|
{% slot actions %}
|
||||||
|
{% element button tags="delete,danger" class="btn btn-danger" type="submit" %}
|
||||||
|
{% trans 'Remove' %}
|
||||||
|
{% endelement %}
|
||||||
|
{% endslot %}
|
||||||
|
{% endelement %}
|
||||||
|
{% else %}
|
||||||
|
{% element p %}
|
||||||
|
{% trans 'You currently have no third-party accounts connected to this account.' %}
|
||||||
|
{% endelement %}
|
||||||
|
{% endif %}
|
||||||
|
{% element h2 %}
|
||||||
|
{% trans 'Add a Third-Party Account' %}
|
||||||
|
{% endelement %}
|
||||||
|
{% include "socialaccount/snippets/provider_list.html" with process="connect" %}
|
||||||
|
{% include "socialaccount/snippets/login_extra.html" %}
|
||||||
|
{% endblock content %}
|
||||||
18
templates/socialaccount/snippets/login.html
Normal file
18
templates/socialaccount/snippets/login.html
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{% load allauth socialaccount %}
|
||||||
|
{% get_providers as socialaccount_providers %}
|
||||||
|
{% if socialaccount_providers %}
|
||||||
|
{% element provider_list %}
|
||||||
|
{% for provider in socialaccount_providers %}
|
||||||
|
{% if not provider.name == "OpenID" %}
|
||||||
|
{% for brand in provider.get_brands %}
|
||||||
|
{% provider_login_url provider openid=brand.openid_url process=process as href %}
|
||||||
|
{% element provider name=brand.name provider_id=provider.id href=href %}
|
||||||
|
{% endelement %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% provider_login_url provider process=process scope=scope auth_params=auth_params as href %}
|
||||||
|
{% element provider name=provider.name provider_id=provider.id href=href %}
|
||||||
|
{% endelement %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endelement %}
|
||||||
|
{% endif %}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
from .models import Page,Image
|
from .models import Page,Image
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
class PageForm(forms.ModelForm):
|
class PageForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -12,6 +13,11 @@ class PageForm(forms.ModelForm):
|
|||||||
'content',
|
'content',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
class PageDeleteForm(forms.Form):
|
||||||
|
slug = forms.SlugField(allow_unicode=False,
|
||||||
|
required=False,
|
||||||
|
label=_("Slug"))
|
||||||
|
|
||||||
class PageAdminForm(forms.ModelForm):
|
class PageAdminForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Page
|
model = Page
|
||||||
@@ -24,3 +30,25 @@ class PageAdminForm(forms.ModelForm):
|
|||||||
'content',
|
'content',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ImageUploadView(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Image
|
||||||
|
fields = [
|
||||||
|
"image",
|
||||||
|
"alt",
|
||||||
|
"description",
|
||||||
|
]
|
||||||
|
|
||||||
|
class ImageEditView(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Image
|
||||||
|
fields = [
|
||||||
|
"alt",
|
||||||
|
"description",
|
||||||
|
]
|
||||||
|
|
||||||
|
class ImageDeleteView(forms.Form):
|
||||||
|
slug = forms.SlugField(allow_unicode=False,
|
||||||
|
required=False)
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ class Migration(migrations.Migration):
|
|||||||
group.permissions.add(perm_mapping[perm])
|
group.permissions.add(perm_mapping[perm])
|
||||||
|
|
||||||
|
|
||||||
def init_default_pages(apps,schema_editor)->None:
|
def init_builtin_pages(apps,schema_editor)->None:
|
||||||
from ..models import Page,Image
|
from ..models import Page,Image
|
||||||
from ..enums import WikiContentType,WikiPageStatus
|
from ..enums import WikiContentType,WikiPageStatus
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -85,14 +85,47 @@ class Migration(migrations.Migration):
|
|||||||
content=content)
|
content=content)
|
||||||
|
|
||||||
|
|
||||||
|
def init_builtin_images(apps,schema_edit):
|
||||||
|
from pathlib import Path
|
||||||
|
from ..models import Image
|
||||||
|
from django.core.files.images import ImageFile
|
||||||
|
import json
|
||||||
|
|
||||||
|
image_dir = Path(__file__).resolve().parent / "images"
|
||||||
|
images_json_file = image_dir/ "images.json"
|
||||||
|
|
||||||
|
if not images_json_file.is_file():
|
||||||
|
return
|
||||||
|
|
||||||
|
with open(images_json_file,"rt",encoding="utf-8") as ifile:
|
||||||
|
images_data = json.loads(ifile.read())
|
||||||
|
|
||||||
|
for slug,_spec in images_data.items():
|
||||||
|
spec = dict(_spec)
|
||||||
|
image_basename = spec['image']
|
||||||
|
image_filename = image_dir / spec["image"]
|
||||||
|
spec['slug'] = slug
|
||||||
|
del spec['image']
|
||||||
|
|
||||||
|
img = Image(**spec)
|
||||||
|
with open(image_filename,"rb") as ifile:
|
||||||
|
img_file = ImageFile(ifile,image_basename)
|
||||||
|
img.image.save(image_basename,img_file)
|
||||||
|
img.save()
|
||||||
|
|
||||||
|
|
||||||
|
def init_user_imges(apps,schema_edit)->None:
|
||||||
|
#TODO
|
||||||
|
pass
|
||||||
|
|
||||||
def init_user_pages(apps,schema_edit)->None:
|
def init_user_pages(apps,schema_edit)->None:
|
||||||
from ..models import Page,Image
|
|
||||||
#TODO
|
#TODO
|
||||||
|
pass
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RunPython(init_tinywiki_groups_and_permissions),
|
migrations.RunPython(init_tinywiki_groups_and_permissions),
|
||||||
migrations.RunPython(init_tinywiki_user),
|
migrations.RunPython(init_tinywiki_user),
|
||||||
migrations.RunPython(init_default_pages),
|
migrations.RunPython(init_builtin_images),
|
||||||
|
migrations.RunPython(init_builtin_pages),
|
||||||
]
|
]
|
||||||
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 5.1 MiB |
7
tinywiki/migrations/images/images.json
Normal file
7
tinywiki/migrations/images/images.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"tw-image-01": {
|
||||||
|
"alt": "Foggy Mountain-tops in the dawn",
|
||||||
|
"image": "fabian-bachli-jQAe44MEIXU-unsplash.jpg",
|
||||||
|
"description": "Foto from [url=\"https://unsplash.com/de/@fabianbaechli?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash\"]Fabian Bächli[/url] on [url=\"https://unsplash.com/de/fotos/nebelige-berggipfel-in-der-morgendammerung-mit-sanften-wolken-jQAe44MEIXU?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash\"]Unsplash[/url]"
|
||||||
|
}
|
||||||
|
}
|
||||||
582
tinywiki/migrations/pages/bbcode.bbcode
Normal file
582
tinywiki/migrations/pages/bbcode.bbcode
Normal file
@@ -0,0 +1,582 @@
|
|||||||
|
[h1]BBCode used by TinyWiki[/h1]
|
||||||
|
|
||||||
|
[p]
|
||||||
|
BBCode uses tags to lay out your page. Tags written as opened by [code][tag][/code] and closed by [code][/tag][/code]. Standalone tags close automatically.
|
||||||
|
[/p]
|
||||||
|
|
||||||
|
[h2]Headers[/h2]
|
||||||
|
[p]
|
||||||
|
Headers are created by the [i]h[/i] tags. They range from [code][h1][/code] to [code][h6][/code]. You should start your page with a [code][h1][/code]-header. You shall only define one [code][h1][/code]-header in your page. It serves as the title displayed.
|
||||||
|
[/p]
|
||||||
|
|
||||||
|
[p]
|
||||||
|
You define headers as follows:
|
||||||
|
[/p]
|
||||||
|
[codeblock=bbcode][h1]Title header[/h1]
|
||||||
|
[h2]Header 2[/h2]
|
||||||
|
[h3]Header 3[/h3]
|
||||||
|
[h4]Header 4[/h4]
|
||||||
|
[h5]Header 5[/h5]
|
||||||
|
[h6]Header 6[/h6][/codeblock]
|
||||||
|
|
||||||
|
Which render as:
|
||||||
|
[/p]
|
||||||
|
|
||||||
|
[h1]Title header[/h1]
|
||||||
|
[h2]Header 2[/h2]
|
||||||
|
[h3]Header 3[/h3]
|
||||||
|
[h4]Header 4[/h4]
|
||||||
|
[h5]Header 5[/h5]
|
||||||
|
[h6]Header 6[/h6]
|
||||||
|
[hr]
|
||||||
|
[h2]Paragraphs[/h2]
|
||||||
|
[p]Paragraphs are created by the [code][p][/code] tag.[/p]
|
||||||
|
|
||||||
|
See the following code:
|
||||||
|
[codeblock=bbcode]
|
||||||
|
[hr]
|
||||||
|
[p]This is a paragrpah.[/p]
|
||||||
|
[p]And this is another one with a [b]encapsulated bold tag[/b].[/p]
|
||||||
|
[hr][/codeblock]
|
||||||
|
|
||||||
|
This renders as:[hr]
|
||||||
|
[p]This is a paragrpah.[/p]
|
||||||
|
[p]And this is another one with a [b]encapsulated bold tag[/b].[/p]
|
||||||
|
[hr]
|
||||||
|
|
||||||
|
[h2]Typography[/h2]
|
||||||
|
[p]
|
||||||
|
You can create [b]bold[/b] text with the [code][b][/code]-tag, [i]italic[/i] text with the [code][i][/code]-tag. To create text with [strong]strong emphasis[/strong], use the [code][strong][/code]-tag. [em]Emphasized text[/em] is created using the [code][em][/code]-tag. [u]Underlining text[/u] is done with the [code][u][/code]-tag, [s]Striked through[/s] text with the [code][s][/code]-tag. And to [mark]mark text[/mark] you can use the [code][mark][/code]-tag.
|
||||||
|
[/p]
|
||||||
|
[p]
|
||||||
|
The tags described in the previous paragraph are rendered as semantic-HTML and handled by screen-readers accordingly.
|
||||||
|
[/p]
|
||||||
|
[p]
|
||||||
|
If you want to show inline code, use the [code][code][/code]-tag. Code blocks can be can be created wtih the [code][codeblock][/code]-tag. You can also specify a language for codeblocks fields. this is done by using the [code][code=language][/code] option set to the codeblock-tag ([code][codeblock=bbcode][/code] is used to render the codeblock fields in this text).
|
||||||
|
[/p]
|
||||||
|
|
||||||
|
[p]
|
||||||
|
Line breaks are created with the [code][br][/code]-tag and horizontal rulers with the [code][hr][/code]-tag.
|
||||||
|
[/p]
|
||||||
|
[p]
|
||||||
|
An example:
|
||||||
|
[codeblock=bbcode][b]This text is bold.[/b][br]
|
||||||
|
[i]This text is italic.[/i][br]
|
||||||
|
[u]This is some underlined text.[/u][br]
|
||||||
|
[s]We can also strike through some text.[/s][br]
|
||||||
|
[strong]Here some text with strong emphasis.[/strong][br]
|
||||||
|
[em]Emphasized text can also be created.[/em][br]
|
||||||
|
[mark]And finally we mark some text.[/mark][br]
|
||||||
|
[code]finally_some_code()[/code]
|
||||||
|
[hr][/codeblock]
|
||||||
|
This renders as follows:
|
||||||
|
[/p]
|
||||||
|
[p]
|
||||||
|
[b]This text is bold.[/b][br]
|
||||||
|
[i]This text is italic.[/i][br]
|
||||||
|
[u]This is some underlined text.[/u][br]
|
||||||
|
[s]We can also strike through some text.[/s][br]
|
||||||
|
[strong]Here some text with strong emphasis.[/strong][br]
|
||||||
|
[em]Emphasized text can also be created.[/em][br]
|
||||||
|
[mark]And we mark some text.[/mark][br]
|
||||||
|
[code]finally_some_code()[/code]
|
||||||
|
[hr]
|
||||||
|
[/p]
|
||||||
|
|
||||||
|
[h2]Links[/h2]
|
||||||
|
[p]
|
||||||
|
Links are created using the [code][url][/code]-tag. You can create a link by using [code][url]www.example.com[/url[/code] to create a simple link with the same link description as the URL itself or you use the [code][url=https://www.example.com]Link description[/url][/code] style if you want the link description differ from the link itself.
|
||||||
|
[/p]
|
||||||
|
[p]
|
||||||
|
To link against a wiki-page, you can use the [code][wiki-url=page_slug][/code]-code or the [code][wiki][/code]-tag. The [code][wiki-url][/code] works nearly the same, is used to link against a page with a user-defined description. Whereas the [code][wiki=slug][/code]-tag, which is self-closing, is used to link against a wiki page and display the page's title.
|
||||||
|
[/p]
|
||||||
|
[p]Here an example:[/p]
|
||||||
|
[codeblock=bbcode][url]www.google.com[/url][br]
|
||||||
|
[url=https://duckduckgo.com]Another search engine[/url][br]
|
||||||
|
[wiki-url=tw-license]The license of Tinywiki[/wiki-url][br]
|
||||||
|
[wiki=tw-bbcode][br]
|
||||||
|
And a link to the wiki-homepage: [wiki][/codeblock]
|
||||||
|
|
||||||
|
[p]
|
||||||
|
[url]www.google.com[/url][br]
|
||||||
|
[url=https://duckduckgo.com]Another search engine[/url][br]
|
||||||
|
[wiki-url=tw-license]The license of Tinywiki[/wiki-url][br]
|
||||||
|
[wiki=tw-bbcode][br]
|
||||||
|
And a link to the wiki-homepage [wiki]
|
||||||
|
[/p]
|
||||||
|
|
||||||
|
[h2]Lists[/h2]
|
||||||
|
[p]
|
||||||
|
Unordered lists can be created using the [code][list][*] Item1 [*] Item2[/list][/code] tags. Alternatively, you can use the [code][ul][li]Item1[/li][li]Item2[/li][/ul][/code] notation.
|
||||||
|
[/p]
|
||||||
|
[p]
|
||||||
|
Ordered lists are created with the [code][ol][/code] tag. The notation is the same as the [code][ul][/code] tag.
|
||||||
|
[/p]
|
||||||
|
[p]
|
||||||
|
Here an example:
|
||||||
|
[codeblock=bbcode]
|
||||||
|
[hr]
|
||||||
|
An inline list:
|
||||||
|
[list]
|
||||||
|
[*] Item1
|
||||||
|
[*] Item2
|
||||||
|
[*] Item 3
|
||||||
|
[/list]
|
||||||
|
[hr]
|
||||||
|
An unordered list:
|
||||||
|
[ul]
|
||||||
|
[li] Item 1[/li]
|
||||||
|
[li] Item 2[/li]
|
||||||
|
[li]
|
||||||
|
[ul]
|
||||||
|
[li]Embedded 1[/li]
|
||||||
|
[li]Embedded 2[/li]
|
||||||
|
[/ul]
|
||||||
|
[/li]
|
||||||
|
[/ul]
|
||||||
|
[hr]
|
||||||
|
An ordered list:
|
||||||
|
[ol]
|
||||||
|
[li] Item 1[/li]
|
||||||
|
[li] Item 2[/li]
|
||||||
|
[li]
|
||||||
|
[ol]
|
||||||
|
[li] Embedded 1[/li]
|
||||||
|
[li]Embedded 2[/li]
|
||||||
|
[/ol]
|
||||||
|
[/li]
|
||||||
|
[/ol]
|
||||||
|
[/codeblock]
|
||||||
|
[p]
|
||||||
|
A list:
|
||||||
|
[list]
|
||||||
|
[*] Item1
|
||||||
|
[*] Item2
|
||||||
|
[*] Item 3
|
||||||
|
[/list]
|
||||||
|
[hr]
|
||||||
|
An unordered list:
|
||||||
|
[ul]
|
||||||
|
[li] Item 1[/li]
|
||||||
|
[li] Item 2[/li]
|
||||||
|
[li]
|
||||||
|
[ul]
|
||||||
|
[li]Embedded 1[/li]
|
||||||
|
[li]Embedded 2[/li]
|
||||||
|
[/ul]
|
||||||
|
[/li]
|
||||||
|
[/ul]
|
||||||
|
[hr]
|
||||||
|
An ordered list:
|
||||||
|
[ol]
|
||||||
|
[li] Item 1[/li]
|
||||||
|
[li] Item 2[/li]
|
||||||
|
[li]
|
||||||
|
[ol]
|
||||||
|
[li]Embedded 1[/li]
|
||||||
|
[li]Embedded 2[/li]
|
||||||
|
[/ol]
|
||||||
|
[/li]
|
||||||
|
[/ol]
|
||||||
|
[/p]
|
||||||
|
[h2]Tables[/h2]
|
||||||
|
[p]
|
||||||
|
Tables are created with the [code]table[/code] tag. They contain rows, created with the [code]table-row[/code] or [code][tr][/code] tag. Each row contain table headers or table data. Table headers are created with [code][table-header][/code] or [code][th][/code] tag, table data with the [code][table-data][/code] or [code][td][/code] tag.
|
||||||
|
[/p]
|
||||||
|
|
||||||
|
[codeblock=bbcode]
|
||||||
|
[table]
|
||||||
|
[table-row]
|
||||||
|
[table-header]Name[/table-header]
|
||||||
|
[table-header]Email[/table-header]
|
||||||
|
[table-header]Phone number[/table-header]
|
||||||
|
[table-row]
|
||||||
|
[table-row]
|
||||||
|
[table-data]John Doe[/table-data]
|
||||||
|
[table-data]johndoe@example.com[/table-data]
|
||||||
|
[table-data]345693887[/table-data]
|
||||||
|
[/table-row]
|
||||||
|
[table-row]
|
||||||
|
[table-data]Jane Doe[/table-data]
|
||||||
|
[table-data]janedoe@example.com[/table-data]
|
||||||
|
[table-data]685528874[/table-data]
|
||||||
|
[/table-row]
|
||||||
|
[table-row]
|
||||||
|
[table-data]Patrick Smith[/table-data]
|
||||||
|
[table-data]patricksmith@example.com[/table-data]
|
||||||
|
[table-data]5786255143[/table-data]
|
||||||
|
[/table-row]
|
||||||
|
[/table]
|
||||||
|
[/codeblock]
|
||||||
|
|
||||||
|
[table]
|
||||||
|
[table-row]
|
||||||
|
[table-header]Name[/table-header]
|
||||||
|
[table-header]Email[/table-header]
|
||||||
|
[table-header]Phone number[/table-header]
|
||||||
|
[table-row]
|
||||||
|
[table-row]
|
||||||
|
[table-data]John Doe[/table-data]
|
||||||
|
[table-data]johndoe@example.com[/table-data]
|
||||||
|
[table-data]345693887[/table-data]
|
||||||
|
[/table-row]
|
||||||
|
[table-row]
|
||||||
|
[table-data]Jane Doe[/table-data]
|
||||||
|
[table-data]janedoe@example.com[/table-data]
|
||||||
|
[table-data]685528874[/table-data]
|
||||||
|
[/table-row]
|
||||||
|
[table-row]
|
||||||
|
[table-data]Patrick Smith[/table-data]
|
||||||
|
[table-data]patricksmith@example.com[/table-data]
|
||||||
|
[table-data]5786255143[/table-data]
|
||||||
|
[/table-row]
|
||||||
|
[/table]
|
||||||
|
|
||||||
|
[h3]Bootstrap extensions[/h3]
|
||||||
|
[p][b]NOTE: The following tables might not render right when not using [url=https://getbootstrap.com]Bootstrap[/url]![/b][/p]
|
||||||
|
[p]
|
||||||
|
[p]
|
||||||
|
When using [i]Bootstrap[/i] you can style your tables. You can use [code]primary, secondary, info, warning, danger, success, light[/code] and [code]dark[/code] as an argument for the [code][table][/code] tag to give the table some color.
|
||||||
|
[/p]
|
||||||
|
|
||||||
|
[codeblock]
|
||||||
|
[table=primary]
|
||||||
|
[tr]
|
||||||
|
[th]Name[/th]
|
||||||
|
[th]Email[/th]
|
||||||
|
[th]Phone number[/th]
|
||||||
|
[/tr]
|
||||||
|
[tr]
|
||||||
|
[td]John Doe[/td]
|
||||||
|
[td]johndoe@example.com[/td]
|
||||||
|
[td]345693887[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr]
|
||||||
|
[td]Jane Doe[/td]
|
||||||
|
[td]janedoe@example.com[/td]
|
||||||
|
[td]685528874[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr]
|
||||||
|
[td]Patrick Smith[/td]
|
||||||
|
[td]patricksmith@example.com[/td]
|
||||||
|
[td]5786255143[/td]
|
||||||
|
[/tr]
|
||||||
|
[/table]
|
||||||
|
[/codeblock]
|
||||||
|
|
||||||
|
[table=primary]
|
||||||
|
[tr]
|
||||||
|
[th]Name[/th]
|
||||||
|
[th]Email[/th]
|
||||||
|
[th]Phone number[/th]
|
||||||
|
[/tr]
|
||||||
|
[tr]
|
||||||
|
[td]John Doe[/td]
|
||||||
|
[td]johndoe@example.com[/td]
|
||||||
|
[td]345693887[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr]
|
||||||
|
[td]Jane Doe[/td]
|
||||||
|
[td]janedoe@example.com[/td]
|
||||||
|
[td]685528874[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr]
|
||||||
|
[td]Patrick Smith[/td]
|
||||||
|
[td]patricksmith@example.com[/td]
|
||||||
|
[td]5786255143[/td]
|
||||||
|
[/tr]
|
||||||
|
[/table]
|
||||||
|
|
||||||
|
[p]When your wiki is powered by [i]Bootstrap[/i], you can also create bordered tables. If the bordered attribute is not one of [code]0, false, n, no[/code] or [code]off[/code], a bordered table is rendered. If the attribute is one of [code]primary, secondary, info, warning, danger, success, light[/code] or [code]dark[/code], the border colors are set accordingly.
|
||||||
|
[/p]
|
||||||
|
|
||||||
|
[codeblock]
|
||||||
|
[table bordered=1]
|
||||||
|
[tr]
|
||||||
|
[th]Name[/th]
|
||||||
|
[th]Email[/th]
|
||||||
|
[th]Phone number[/th]
|
||||||
|
[/tr]
|
||||||
|
[tr]
|
||||||
|
[td]John Doe[/td]
|
||||||
|
[td]johndoe@example.com[/td]
|
||||||
|
[td]345693887[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr]
|
||||||
|
[td]Jane Doe[/td]
|
||||||
|
[td]janedoe@example.com[/td]
|
||||||
|
[td]685528874[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr]
|
||||||
|
[td]Patrick Smith[/td]
|
||||||
|
[td]patricksmith@example.com[/td]
|
||||||
|
[td]5786255143[/td]
|
||||||
|
[/tr]
|
||||||
|
[/table]
|
||||||
|
|
||||||
|
[table bordered=success]
|
||||||
|
[tr]
|
||||||
|
[th]Name[/th]
|
||||||
|
[th]Email[/th]
|
||||||
|
[th]Phone number[/th]
|
||||||
|
[/tr]
|
||||||
|
[tr]
|
||||||
|
[td]John Doe[/td]
|
||||||
|
[td]johndoe@example.com[/td]
|
||||||
|
[td]345693887[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr]
|
||||||
|
[td]Jane Doe[/td]
|
||||||
|
[td]janedoe@example.com[/td]
|
||||||
|
[td]685528874[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr]
|
||||||
|
[td]Patrick Smith[/td]
|
||||||
|
[td]patricksmith@example.com[/td]
|
||||||
|
[td]5786255143[/td]
|
||||||
|
[/tr]
|
||||||
|
[/table]
|
||||||
|
|
||||||
|
[table=info bordered=danger]
|
||||||
|
[tr]
|
||||||
|
[th]Name[/th]
|
||||||
|
[th]Email[/th]
|
||||||
|
[th]Phone number[/th]
|
||||||
|
[/tr]
|
||||||
|
[tr]
|
||||||
|
[td]John Doe[/td]
|
||||||
|
[td]johndoe@example.com[/td]
|
||||||
|
[td]345693887[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr]
|
||||||
|
[td]Jane Doe[/td]
|
||||||
|
[td]janedoe@example.com[/td]
|
||||||
|
[td]685528874[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr]
|
||||||
|
[td]Patrick Smith[/td]
|
||||||
|
[td]patricksmith@example.com[/td]
|
||||||
|
[td]5786255143[/td]
|
||||||
|
[/tr]
|
||||||
|
[/table]
|
||||||
|
[/codeblock]
|
||||||
|
|
||||||
|
[table bordered=1]
|
||||||
|
[tr]
|
||||||
|
[th]Name[/th]
|
||||||
|
[th]Email[/th]
|
||||||
|
[th]Phone number[/th]
|
||||||
|
[/tr]
|
||||||
|
[tr]
|
||||||
|
[td]John Doe[/td]
|
||||||
|
[td]johndoe@example.com[/td]
|
||||||
|
[td]345693887[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr]
|
||||||
|
[td]Jane Doe[/td]
|
||||||
|
[td]janedoe@example.com[/td]
|
||||||
|
[td]685528874[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr]
|
||||||
|
[td]Patrick Smith[/td]
|
||||||
|
[td]patricksmith@example.com[/td]
|
||||||
|
[td]5786255143[/td]
|
||||||
|
[/tr]
|
||||||
|
[/table]
|
||||||
|
|
||||||
|
[table bordered=success]
|
||||||
|
[tr]
|
||||||
|
[th]Name[/th]
|
||||||
|
[th]Email[/th]
|
||||||
|
[th]Phone number[/th]
|
||||||
|
[/tr]
|
||||||
|
[tr]
|
||||||
|
[td]John Doe[/td]
|
||||||
|
[td]johndoe@example.com[/td]
|
||||||
|
[td]345693887[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr]
|
||||||
|
[td]Jane Doe[/td]
|
||||||
|
[td]janedoe@example.com[/td]
|
||||||
|
[td]685528874[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr]
|
||||||
|
[td]Patrick Smith[/td]
|
||||||
|
[td]patricksmith@example.com[/td]
|
||||||
|
[td]5786255143[/td]
|
||||||
|
[/tr]
|
||||||
|
[/table]
|
||||||
|
|
||||||
|
[table=info bordered=danger]
|
||||||
|
[tr]
|
||||||
|
[th]Name[/th]
|
||||||
|
[th]Email[/th]
|
||||||
|
[th]Phone number[/th]
|
||||||
|
[/tr]
|
||||||
|
[tr]
|
||||||
|
[td]John Doe[/td]
|
||||||
|
[td]johndoe@example.com[/td]
|
||||||
|
[td]345693887[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr]
|
||||||
|
[td]Jane Doe[/td]
|
||||||
|
[td]janedoe@example.com[/td]
|
||||||
|
[td]685528874[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr]
|
||||||
|
[td]Patrick Smith[/td]
|
||||||
|
[td]patricksmith@example.com[/td]
|
||||||
|
[td]5786255143[/td]
|
||||||
|
[/tr]
|
||||||
|
[/table]
|
||||||
|
|
||||||
|
[p]
|
||||||
|
With [i]Bootstrap[/i] you can also style individual rows or even cells with [code]primary, secondary, info, warning, danger, success, light[/code] or [code]dark[/code] applying to the tag.
|
||||||
|
[/p]
|
||||||
|
[codeblock]
|
||||||
|
[table]
|
||||||
|
[tr]
|
||||||
|
[th]Styling[/th]
|
||||||
|
[th]Cell[/th]
|
||||||
|
[th]Cell[/th]
|
||||||
|
[/tr]
|
||||||
|
[tr=primary]
|
||||||
|
[td]primary[/td]
|
||||||
|
[td]cell[/td]
|
||||||
|
[td]cell[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr=secondary]
|
||||||
|
[td]secondary[/td]
|
||||||
|
[td]cell[/td]
|
||||||
|
[td]cell[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr=info]
|
||||||
|
[td]info[/td]
|
||||||
|
[td]cell[/td]
|
||||||
|
[td]cell[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr=success]
|
||||||
|
[td]success[/td]
|
||||||
|
[td]cell[/td]
|
||||||
|
[td]cell[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr=warning]
|
||||||
|
[td]warning[/td]
|
||||||
|
[td]cell[/td]
|
||||||
|
[td]cell[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr=danger]
|
||||||
|
[td]danger[/td]
|
||||||
|
[td]cell[/td]
|
||||||
|
[td]cell[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr=light]
|
||||||
|
[td]light[/td]
|
||||||
|
[td]cell[/td]
|
||||||
|
[td]cell[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr=dark]
|
||||||
|
[td]dark[/td]
|
||||||
|
[td]cell[/td]
|
||||||
|
[td]cell[/td]
|
||||||
|
[/tr]
|
||||||
|
[/table]
|
||||||
|
[/codeblock]
|
||||||
|
|
||||||
|
[table]
|
||||||
|
[tr]
|
||||||
|
[th]Styling[/th]
|
||||||
|
[th]Cell[/th]
|
||||||
|
[th]Cell[/th]
|
||||||
|
[/tr]
|
||||||
|
[tr=primary]
|
||||||
|
[td]primary[/td]
|
||||||
|
[td]cell[/td]
|
||||||
|
[td]cell[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr=secondary]
|
||||||
|
[td]secondary[/td]
|
||||||
|
[td]cell[/td]
|
||||||
|
[td]cell[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr=info]
|
||||||
|
[td]info[/td]
|
||||||
|
[td]cell[/td]
|
||||||
|
[td]cell[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr=success]
|
||||||
|
[td]success[/td]
|
||||||
|
[td]cell[/td]
|
||||||
|
[td]cell[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr=warning]
|
||||||
|
[td]warning[/td]
|
||||||
|
[td]cell[/td]
|
||||||
|
[td]cell[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr=danger]
|
||||||
|
[td]danger[/td]
|
||||||
|
[td]cell[/td]
|
||||||
|
[td]cell[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr=light]
|
||||||
|
[td]light[/td]
|
||||||
|
[td]cell[/td]
|
||||||
|
[td]cell[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr=dark]
|
||||||
|
[td]dark[/td]
|
||||||
|
[td]cell[/td]
|
||||||
|
[td]cell[/td]
|
||||||
|
[/tr]
|
||||||
|
[/table]
|
||||||
|
|
||||||
|
[h3]Rowspan and Colspan[/h3]
|
||||||
|
[p]
|
||||||
|
You can also create tables that span over mutliple rows or columns. Add the [code]rowspan=n[/code] or [code]colspan=n[/code] to the table header or table data ([i]n[/i] is the number of rows or columns that should be spanned).
|
||||||
|
[/p]
|
||||||
|
[codeblock]
|
||||||
|
[table bordered=1]
|
||||||
|
[tr]
|
||||||
|
[th]Header 1[/th]
|
||||||
|
[th]Header 2[/th]
|
||||||
|
[th]Header 3[/th]
|
||||||
|
[/tr]
|
||||||
|
[tr]
|
||||||
|
[td]Row1-Column1[/td]
|
||||||
|
[td]Row1-Column2[/td]
|
||||||
|
[td rowspan=2]Row1-Column3[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr]
|
||||||
|
[td]Row2-Column1[/td]
|
||||||
|
[td]Row2-Column2[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr]
|
||||||
|
[td]Row3-Column1[/td]
|
||||||
|
[td colspan=2]Row3-Column2[/td]
|
||||||
|
[/tr]
|
||||||
|
[/table]
|
||||||
|
[/codeblock]
|
||||||
|
|
||||||
|
[table bordered=1]
|
||||||
|
[tr]
|
||||||
|
[th]Header 1[/th]
|
||||||
|
[th]Header 2[/th]
|
||||||
|
[th]Header 3[/th]
|
||||||
|
[/tr]
|
||||||
|
[tr]
|
||||||
|
[td]Row1-Column1[/td]
|
||||||
|
[td]Row1-Column2[/td]
|
||||||
|
[td rowspan=2]Row1-Column3[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr]
|
||||||
|
[td]Row2-Column1[/td]
|
||||||
|
[td]Row2-Column2[/td]
|
||||||
|
[/tr]
|
||||||
|
[tr]
|
||||||
|
[td]Row3-Column1[/td]
|
||||||
|
[td colspan=2]Row3-Column2[/td]
|
||||||
|
[/tr]
|
||||||
|
[/table]
|
||||||
|
|
||||||
|
[h2]Images[/h2]
|
||||||
@@ -10,5 +10,11 @@
|
|||||||
"content_type": "bbcode",
|
"content_type": "bbcode",
|
||||||
"status":"published",
|
"status":"published",
|
||||||
"file":"bs-license.bbcode"
|
"file":"bs-license.bbcode"
|
||||||
|
},
|
||||||
|
"tw-bbcode": {
|
||||||
|
"title": "BBCode used by TinyWiki",
|
||||||
|
"content_type": "bbcode",
|
||||||
|
"status":"published",
|
||||||
|
"file":"bbcode.bbcode"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,10 @@ from django.contrib.auth import get_user_model
|
|||||||
|
|
||||||
from tinywiki.enums import WIKI_CONTENT_TYPES, WIKI_PAGE_STATUS, WikiContentType, WikiPageStatus
|
from tinywiki.enums import WIKI_CONTENT_TYPES, WIKI_PAGE_STATUS, WikiContentType, WikiPageStatus
|
||||||
import markdown
|
import markdown
|
||||||
|
|
||||||
|
import bbcode as _bbcode
|
||||||
|
from tinywiki.parser import bbcode
|
||||||
|
|
||||||
from .parser import parse_bbcode
|
from .parser import parse_bbcode
|
||||||
from . import settings
|
from . import settings
|
||||||
import tinywiki
|
import tinywiki
|
||||||
@@ -122,3 +126,11 @@ class Image(models.Model):
|
|||||||
uploaded_at = models.DateTimeField(_("uploaded at"),
|
uploaded_at = models.DateTimeField(_("uploaded at"),
|
||||||
auto_now_add=True)
|
auto_now_add=True)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def description_html(self)->str:
|
||||||
|
return _bbcode.render_html(self.description)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def description_html_safe(self)->SafeText:
|
||||||
|
return mark_safe(self.description_html)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import bbcode
|
import bbcode
|
||||||
from . import formatters
|
from . import formatters
|
||||||
PARSER = bbcode.Parser(newline="\n",escape_html=True)
|
PARSER = bbcode.Parser(newline="\n",escape_html=True,replace_links=False)
|
||||||
|
|
||||||
def _():
|
def _():
|
||||||
for i in formatters.SIMPLE_FORMATTERS:
|
for i in formatters.SIMPLE_FORMATTERS:
|
||||||
|
|||||||
@@ -10,26 +10,36 @@ from .text_formatters import (
|
|||||||
render_wiki_image,
|
render_wiki_image,
|
||||||
render_wiki_link,
|
render_wiki_link,
|
||||||
render_wiki_url,
|
render_wiki_url,
|
||||||
)
|
render_table,
|
||||||
from .simple_formatters import (
|
render_table_header,
|
||||||
SIMPLE_HEADER_FORMATTERS,
|
render_table_data,
|
||||||
|
render_table_row,
|
||||||
|
render_youtube_video,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from .simple_formatters import SIMPLE_FORMATTERS
|
||||||
# a list of tuples containig an tuple args and a dict of kwargs
|
# a list of tuples containig an tuple args and a dict of kwargs
|
||||||
SIMPLE_FORMATTERS=[
|
|
||||||
*SIMPLE_HEADER_FORMATTERS,
|
|
||||||
]
|
|
||||||
|
|
||||||
#a list of tuples containing an tuple of args and a dict of kwargs
|
#a list of tuples containing an tuple of args and a dict of kwargs
|
||||||
FORMATTERS=[
|
FORMATTERS=[
|
||||||
(('url',render_url),{'strip':True,'swallow_trailing_newline':True,'same_tag_closes':True}),
|
(('url',render_url),{'strip':True,'swallow_trailing_newline':True,'same_tag_closes':True}),
|
||||||
(('wiki-url',render_wiki_url),{'strip':True,'swallow_trailing_newline':True,'same_tag_closes':True}),
|
(('wiki-url',render_wiki_url),{'strip':True,'swallow_trailing_newline':True,'same_tag_closes':True}),
|
||||||
(('wiki',render_wiki_link),{'strip':True,'swallow_tailin_newline':True,'standalone':True}),
|
(('wiki',render_wiki_link),{'strip':True,'swallow_tailin_newline':True,'standalone':True}),
|
||||||
(('codeblock',render_codeblock),{'strip':False,'swallow_trailing_newline':False,'same_tag_closes':True}),
|
(('codeblock',render_codeblock),{'strip':True,'swallow_trailing_newline':False,'same_tag_closes':False,"render_embedded":False}),
|
||||||
(('ol',render_ordered_list),{}),
|
(('ol',render_ordered_list),{}),
|
||||||
(('ul',render_unordered_list),{}),
|
(('ul',render_unordered_list),{}),
|
||||||
(('li',render_list_item),{}),
|
(('li',render_list_item),{}),
|
||||||
(('p',render_paragraph),{}),
|
(('p',render_paragraph),{'same_tag_closes':False}),
|
||||||
(('image',render_image),{}),
|
(('image',render_image),{'same_tag_closes':True}),
|
||||||
(('wiki-image',render_wiki_image),{'standalone':True})
|
(('img',render_image),{'same_tag_closes':True}),
|
||||||
|
(('wiki-image',render_wiki_image),{'standalone':True}),
|
||||||
|
(('wimg',render_wiki_image),{'standalone':True}),
|
||||||
|
(('table',render_table),{}),
|
||||||
|
(('table-row',render_table_row),{}),
|
||||||
|
(('tr',render_table_row),{}),
|
||||||
|
(('table-header',render_table_header),{}),
|
||||||
|
(('th',render_table_header),{}),
|
||||||
|
(('table-data',render_table_data),{}),
|
||||||
|
(('td',render_table_data),{}),
|
||||||
|
(('youtube',render_youtube_video),{'same_tag_closes':True}),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
|
|
||||||
SIMPLE_HEADER_FORMATTERS = [
|
SIMPLE_FORMATTERS = [
|
||||||
(('h1',"<h1>%(value)s</h1>"),{}),
|
(('h1',"<h1>%(value)s</h1>"),{}),
|
||||||
(('h2',"<h2>%(value)s</h2>"),{}),
|
(('h2',"<h2>%(value)s</h2>"),{}),
|
||||||
(('h3',"<h3>%(value)s</h3>"),{}),
|
(('h3',"<h3>%(value)s</h3>"),{}),
|
||||||
(('h4',"<h4>%(value)s</h4>"),{}),
|
(('h4',"<h4>%(value)s</h4>"),{}),
|
||||||
(('h5',"<h5>%(value)s</h5>"),{}),
|
(('h5',"<h5>%(value)s</h5>"),{}),
|
||||||
(('h6',"<h6>%(value)s</h6>"),{}),
|
(('h6',"<h6>%(value)s</h6>"),{}),
|
||||||
|
(('mark',"<mark>%(value)s</mark>"),{}),
|
||||||
|
(("strong","<strong>%(value)s</strong>"),{}),
|
||||||
|
(("em","<em>%(value)s</em>"),{}),
|
||||||
|
(("br","<br>"),{"standalone":True}),
|
||||||
(('copy',"©"),{'standalone':True}),
|
(('copy',"©"),{'standalone':True}),
|
||||||
(('reg',"®"),{'standalone':True}),
|
(('reg',"®"),{'standalone':True}),
|
||||||
(('trade',"™"),{'standalone':True}),
|
(('trade',"™"),{'standalone':True}),
|
||||||
|
|||||||
@@ -2,9 +2,10 @@ from django_project.settings import STATIC_URL
|
|||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from ... import settings
|
from ... import settings
|
||||||
from ... import models
|
from ... import models
|
||||||
|
import bbcode
|
||||||
|
|
||||||
def render_url(tag_name:str,value,options,parent,context):
|
def render_url(tag_name:str,value,options,parent,context):
|
||||||
try:
|
try:
|
||||||
@@ -12,17 +13,19 @@ def render_url(tag_name:str,value,options,parent,context):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
url = value
|
url = value
|
||||||
|
|
||||||
if '://' not in url:
|
if not url.startswith('/') and '://' not in url:
|
||||||
url = "http://" + url
|
url = "http://" + url
|
||||||
|
|
||||||
if settings.USE_BOOTSTRAP:
|
if settings.USE_BOOTSTRAP:
|
||||||
return f"<a href=\"{url}\" class=\"icon-link icon-link-hover\" referrer-policy=\"no-referrer\" rel=\"noreferrer noopener\">{value}{render_to_string('tinywiki/icons/box-arrow-up-right.svg')}</a>"
|
if ['noicon in options']:
|
||||||
|
return f"<a href=\"{url}\" referrer-policy=\"no-referrer\" rel=\"noreferrer noopener\">{value}</a>"
|
||||||
|
return f"<a href=\"{url}\" class=\"icon-link icon-link-hover\" referrer-policy=\"no-referrer\" rel=\"noreferrer noopener\">{value}<svg class=\"bi\"><use xlink:href=\"{settings.settings.STATIC_URL + "tinywiki/icons/bootstrap-icons.svg"}#box-arrow-up-right\"></use></svg></a>"
|
||||||
return f"<a href=\"{url}\" referrer-policy=\"no-referrer\" rel=\"noreferrer noopener\">{value}</a>"
|
return f"<a href=\"{url}\" referrer-policy=\"no-referrer\" rel=\"noreferrer noopener\">{value}</a>"
|
||||||
|
|
||||||
def render_wiki_url(tag_name,value,options,parent,context):
|
def render_wiki_url(tag_name,value,options,parent,context):
|
||||||
if tag_name in options:
|
if tag_name in options:
|
||||||
url = reverse("tinywiki:page",kwargs={'slug':options[tag_name]})
|
url = reverse("tinywiki:page",kwargs={'slug':options[tag_name]})
|
||||||
slug=options['tag_name']
|
slug=options[tag_name]
|
||||||
try:
|
try:
|
||||||
page = models.Page.objects.get(slug=slug)
|
page = models.Page.objects.get(slug=slug)
|
||||||
except models.Page.DoesNotExist:
|
except models.Page.DoesNotExist:
|
||||||
@@ -33,47 +36,49 @@ def render_wiki_url(tag_name,value,options,parent,context):
|
|||||||
slug=None
|
slug=None
|
||||||
|
|
||||||
if settings.USE_BOOTSTRAP:
|
if settings.USE_BOOTSTRAP:
|
||||||
|
href = settings.settings.STATIC_URL+"tinywiki/icons/bootstrap-icons.svg"
|
||||||
if page:
|
if page:
|
||||||
if page.slug.startswith('tw-'):
|
if page.slug.startswith('tw-'):
|
||||||
svg=render_to_string('tinywiki/icons/journal.svg')
|
svg = "journal"
|
||||||
elif page.slug:
|
elif page.slug:
|
||||||
svg=render_to_string('tinywiki/icons/book.svg')
|
svg = "book"
|
||||||
else:
|
else:
|
||||||
svg=render_to_string('tinywiki/icons/file-earmark-x')
|
svg=href + "file-earmark-x"
|
||||||
|
return f"<a href=\"{url}\" class=\"icon-link icon-link-hover\">{value}<svg class=\"bi\"><use xlink:href=\"{href}#{svg}\"></use></svg></a>"
|
||||||
return f"<a href=\"{url}\" class=\"icon-link icon-link-hover\">{value}{svg}</a>"
|
|
||||||
return f"<a href=\"{url}\">{value}</a>"
|
return f"<a href=\"{url}\">{value}</a>"
|
||||||
|
|
||||||
|
|
||||||
def render_wiki_link(tag_name,value,options,parent,context):
|
def render_wiki_link(tag_name,value,options,parent,context):
|
||||||
if tag_name in options:
|
if tag_name in options:
|
||||||
slug = options['tag_name']
|
slug = options[tag_name]
|
||||||
|
print("slug",slug)
|
||||||
try:
|
try:
|
||||||
page = models.Page.objects.get(slug=slug)
|
page = models.Page.objects.get(slug=slug)
|
||||||
title = page.title
|
title = page.title
|
||||||
if slug.starts_with('tw-'):
|
if slug.startswith('tw-'):
|
||||||
svg = "tinywiki/icons/journal.svg"
|
svg = "journal"
|
||||||
else:
|
else:
|
||||||
svg = "tinywiki/icons/book.svg"
|
svg = "book"
|
||||||
except:
|
except models.Page.DoesNotExist:
|
||||||
page = None
|
page = None
|
||||||
title = _("Page not found")
|
title = _("Page not found")
|
||||||
svg_template = "tinywiki/icons/file-earmark-x.svg"
|
svg = "file-earmark-x"
|
||||||
url = reverse("tinywiki:page",kwargs={'slug':slug})
|
url = reverse("tinywiki:page",kwargs={'slug':slug})
|
||||||
else:
|
else:
|
||||||
slug = None
|
slug = None
|
||||||
title = _("Home")
|
title = _("Home")
|
||||||
url = reverse("tinywiki:home")
|
url = reverse("tinywiki:home")
|
||||||
svg_template = "tinywiki/icons/house.svg"
|
svg = "house"
|
||||||
|
|
||||||
if settings.USE_BOOTSTRAP:
|
if settings.USE_BOOTSTRAP:
|
||||||
return f"<a href=\"{url}\" class=\"icon-link icon-link-hover\">{value}{render_to_string(svg_template)}</a>"
|
href = settings.settings.STATIC_URL + "tinywiki/icons/bootstrap-icons.svg"
|
||||||
|
return f"<a href=\"{url}\" class=\"icon-link icon-link-hover\">{title}<svg class=\"bi\"><use xlink:href=\"{href}#{svg}\"></use></svg></a>"
|
||||||
return f"<a href=\"{url}\">{value}</a>"
|
return f"<a href=\"{url}\">{value}</a>"
|
||||||
|
|
||||||
def render_codeblock(tag_name:str,value,options,parent,context)->str:
|
def render_codeblock(tag_name:str,value,options,parent,context)->str:
|
||||||
if 'codeblock' in options:
|
if tag_name in options:
|
||||||
return f"<pre><code class=\"language-{options['codeblock']}\">{value}</pre></code>"
|
return f"<pre style=\"overflow-x:auto;\"><code class=\"language-{options[tag_name]}\">{value}</pre></code>"
|
||||||
return f"<pre><code>{value}</pre></code>"
|
return f"<pre style=\"overflow-x:auto;\"><code>{value}</pre></code>"
|
||||||
|
|
||||||
def render_ordered_list(tag_name:str,value,options,parent,context)->str:
|
def render_ordered_list(tag_name:str,value,options,parent,context)->str:
|
||||||
return f"<ol>{value}</ol>"
|
return f"<ol>{value}</ol>"
|
||||||
@@ -112,7 +117,7 @@ def render_image(tag_name:str,value,options,parent,context):
|
|||||||
|
|
||||||
if 'width' in options:
|
if 'width' in options:
|
||||||
_w = options['width']
|
_w = options['width']
|
||||||
if _w.endswith('px'):
|
if _w.endswith('px') or _w.endswith('em') or _w.endswith('rem'):
|
||||||
fig_styles.append(f"width:{_w};")
|
fig_styles.append(f"width:{_w};")
|
||||||
else:
|
else:
|
||||||
if _w.endswith('%'):
|
if _w.endswith('%'):
|
||||||
@@ -129,16 +134,41 @@ def render_image(tag_name:str,value,options,parent,context):
|
|||||||
fig_classes.append(f'w-{width}')
|
fig_classes.append(f'w-{width}')
|
||||||
else:
|
else:
|
||||||
fig_styles.append(f"width:{_w}%;")
|
fig_styles.append(f"width:{_w}%;")
|
||||||
|
if 'height' in options:
|
||||||
|
_h = options['width']
|
||||||
|
if _h.endswith('px') or _h.endswith('em') or _h.endswith('rem'):
|
||||||
|
fig_styles.append(f"height:{_h};")
|
||||||
|
else:
|
||||||
|
if _h.endswith('%'):
|
||||||
|
_h= _h[:-1]
|
||||||
|
if _h.isdigit():
|
||||||
|
_h=int(_w)
|
||||||
|
if _h > 100:
|
||||||
|
_h = 100
|
||||||
|
if settings.USE_BOOTSTRAP:
|
||||||
|
if 1 < int(_h) <= 25:
|
||||||
|
height = 25
|
||||||
|
else:
|
||||||
|
height = ((_h // 25) * 25)
|
||||||
|
if height > 100:
|
||||||
|
height = 100
|
||||||
|
|
||||||
|
fig_classes.append(f'h-{height}')
|
||||||
|
else:
|
||||||
|
fig_styles.append(f"height:{_h}%;")
|
||||||
|
|
||||||
if "position" in options:
|
if "position" in options:
|
||||||
pos = options['position']
|
pos = options['position']
|
||||||
if settings.USE_BOOTSTRAP:
|
if settings.USE_BOOTSTRAP:
|
||||||
if pos == "left" or pos=="start":
|
if pos == "left" or pos=="start":
|
||||||
fig_classes += ["float-start","me-2"]
|
fig_classes += ["float-start","me-2"]
|
||||||
|
classes += ["float-start","me-2"]
|
||||||
elif pos == "right" or pos == "end":
|
elif pos == "right" or pos == "end":
|
||||||
fig_classes += ["float-end","ms-2"]
|
fig_classes += ["float-end","ms-2"]
|
||||||
|
classes += ["float-end","ms-2"]
|
||||||
elif pos == "center":
|
elif pos == "center":
|
||||||
fig_classes += ["mx-auto","d-block"]
|
fig_classes += ["mx-auto","d-block"]
|
||||||
|
classes += ["mx-auto","d-block"]
|
||||||
|
|
||||||
if styles:
|
if styles:
|
||||||
style=f"style=\"{"".join(styles)}\""
|
style=f"style=\"{"".join(styles)}\""
|
||||||
@@ -176,7 +206,7 @@ def render_wiki_image(tag_name:str,value,options,parent,context):
|
|||||||
|
|
||||||
if 'width' in options:
|
if 'width' in options:
|
||||||
_w = options['width']
|
_w = options['width']
|
||||||
if _w.endswith('px'):
|
if _w.endswith('px') or _w.endswith('em') or _w.endswith('rem'):
|
||||||
fig_styles.append(f"width:{_w};")
|
fig_styles.append(f"width:{_w};")
|
||||||
else:
|
else:
|
||||||
if _w.endswith('%'):
|
if _w.endswith('%'):
|
||||||
@@ -193,6 +223,28 @@ def render_wiki_image(tag_name:str,value,options,parent,context):
|
|||||||
fig_classes.append(f'w-{width}')
|
fig_classes.append(f'w-{width}')
|
||||||
else:
|
else:
|
||||||
fig_styles.append(f"width:{_w}%;")
|
fig_styles.append(f"width:{_w}%;")
|
||||||
|
if 'height' in options:
|
||||||
|
_h = options['width']
|
||||||
|
if _h.endswith('px') or _h.endswith('em') or _h.endswith('rem'):
|
||||||
|
fig_styles.append(f"height:{_h};")
|
||||||
|
else:
|
||||||
|
if _h.endswith('%'):
|
||||||
|
_h= _h[:-1]
|
||||||
|
if _h.isdigit():
|
||||||
|
_h=int(_w)
|
||||||
|
if _h > 100:
|
||||||
|
_h = 100
|
||||||
|
if settings.USE_BOOTSTRAP:
|
||||||
|
if 1 < int(_h) <= 25:
|
||||||
|
height = 25
|
||||||
|
else:
|
||||||
|
height = ((_h // 25) * 25)
|
||||||
|
if height > 100:
|
||||||
|
height = 100
|
||||||
|
|
||||||
|
fig_classes.append(f'h-{height}')
|
||||||
|
else:
|
||||||
|
fig_styles.append(f"height:{_h}%;")
|
||||||
|
|
||||||
if "position" in options:
|
if "position" in options:
|
||||||
pos = options['position']
|
pos = options['position']
|
||||||
@@ -215,7 +267,155 @@ def render_wiki_image(tag_name:str,value,options,parent,context):
|
|||||||
fig_style=""
|
fig_style=""
|
||||||
|
|
||||||
if settings.USE_BOOTSTRAP:
|
if settings.USE_BOOTSTRAP:
|
||||||
return f'<figure class="{" ".join(fig_classes)}" {fig_style}><img src="{image.image.url}" alt="{image.alt}" class="{' '.join(classes)}" {style}><figcaption class="figure-caption text-end">{image.description}</figcaption></figure>'
|
return f'<figure class="{" ".join(fig_classes)}" {fig_style}><img src="{image.image.url}" alt="{image.alt}" class="{' '.join(classes)}" {style}><figcaption class="figure-caption text-end">{image.description_html}</figcaption></figure>'
|
||||||
else:
|
else:
|
||||||
return f'<figure {fig_style}><img src="{image.image.url}" alt="{image.alt}" {style}><figcaption>{image.description}</figcaption></figure>'
|
return f'<figure {fig_style}><img src="{image.image.url}" alt="{image.alt}" {style}><figcaption>{image.description}</figcaption></figure>'
|
||||||
|
|
||||||
|
def render_table(tag_name:str,value,options,parent,context):
|
||||||
|
if settings.USE_BOOTSTRAP:
|
||||||
|
classes=["table"]
|
||||||
|
if "bordered" in options:
|
||||||
|
if options["bordered"] not in ("0","n","no","false","off"):
|
||||||
|
classes.append("table-bordered")
|
||||||
|
if options["bordered"] in ("primary","secondary","info","warning","danger","success","light","dark"):
|
||||||
|
classes.append(f"border-{options['bordered']}")
|
||||||
|
|
||||||
|
if tag_name in options:
|
||||||
|
if options[tag_name] in ("primary","secondary","info","warning","danger","success","light","dark"):
|
||||||
|
classes.append(f"table-{options[tag_name]}")
|
||||||
|
|
||||||
|
return f"<table class=\"{" ".join(classes)}\">{value}</table>"
|
||||||
|
|
||||||
|
return f"<table>{value}</table>"
|
||||||
|
|
||||||
|
def render_table_row(tag_name:str,value,options,parent,context):
|
||||||
|
classes=[]
|
||||||
|
if settings.USE_BOOTSTRAP:
|
||||||
|
if tag_name in options:
|
||||||
|
if options[tag_name] in ("primary","secondary","info","warning","danger","success","light","dark"):
|
||||||
|
classes.append(f"table-{options[tag_name]}")
|
||||||
|
class_attr=f"class=\"{" ".join(classes)}\"" if classes else ""
|
||||||
|
return f"<tr {class_attr}>{value}</tr>"
|
||||||
|
|
||||||
|
def render_table_header(tag_name:str,value,options,parent,context):
|
||||||
|
extra_attributes=[]
|
||||||
|
classes=[]
|
||||||
|
if "colspan" in options:
|
||||||
|
extra_attributes.append(f"colspan=\"{options['colspan']}\"")
|
||||||
|
if "rowspan" in options:
|
||||||
|
extra_attributes.append(f"rowspan=\"{options['rowspan']}\"")
|
||||||
|
if settings.USE_BOOTSTRAP:
|
||||||
|
if tag_name in options:
|
||||||
|
if options[tag_name] in ("primary","secondary","info","warning","danger","success","light","dark"):
|
||||||
|
classes.append(f"table-{options[tag_name]}")
|
||||||
|
class_attr=f"class=\"{" ".join(classes)}\"" if classes else ""
|
||||||
|
return f"<th {class_attr} {" ".join(extra_attributes)}>{value}</th>"
|
||||||
|
|
||||||
|
def render_table_data(tag_name:str,value,options,parent,context):
|
||||||
|
extra_attributes=[]
|
||||||
|
classes=[]
|
||||||
|
if "colspan" in options:
|
||||||
|
extra_attributes.append(f"colspan=\"{options['colspan']}\"")
|
||||||
|
if "rowspan" in options:
|
||||||
|
extra_attributes.append(f"rowspan=\"{options['rowspan']}\"")
|
||||||
|
if settings.USE_BOOTSTRAP:
|
||||||
|
if tag_name in options:
|
||||||
|
if options[tag_name] in ("primary","secondary","info","warning","danger","success","light","dark"):
|
||||||
|
classes.append(f"table-{options[tag_name]}")
|
||||||
|
class_attr=f"class=\"{" ".join(classes)}\"" if classes else ""
|
||||||
|
return f"<td {class_attr} {" ".join(extra_attributes)}>{value}</td>"
|
||||||
|
|
||||||
|
def render_youtube_video(tag_name:str,value,options,parent,context):
|
||||||
|
if tag_name not in options:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
if 'alt' in options:
|
||||||
|
alt=options['alt']
|
||||||
|
else:
|
||||||
|
alt=""
|
||||||
|
|
||||||
|
if settings.USE_BOOTSTRAP:
|
||||||
|
styles=[]
|
||||||
|
classes=["w-100"]
|
||||||
|
div_classes=["my-1"]
|
||||||
|
div_styles=[]
|
||||||
|
else:
|
||||||
|
styles=["max-width:100%;"]
|
||||||
|
classes=[]
|
||||||
|
div_classes=[]
|
||||||
|
div_styles=[]
|
||||||
|
|
||||||
|
|
||||||
|
if 'width' in options:
|
||||||
|
_w = options['width']
|
||||||
|
if _w.endswith('px') or _w.endswith('em') or _w.endswith('rem'):
|
||||||
|
if settings.USE_BOOTSTRAP:
|
||||||
|
div_styles.append(f"width:{_w};")
|
||||||
|
styles.append(f"width:{_w};")
|
||||||
|
else:
|
||||||
|
if _w.endswith('%'):
|
||||||
|
_w = _w[:-1]
|
||||||
|
if _w.isdigit():
|
||||||
|
_w=int(_w)
|
||||||
|
if _w > 100:
|
||||||
|
_w = 100
|
||||||
|
if settings.USE_BOOTSTRAP:
|
||||||
|
if 1 < int(_w) <= 25:
|
||||||
|
width = 25
|
||||||
|
else:
|
||||||
|
width = ((_w // 25) * 25)
|
||||||
|
div_classes.append(f'w-{width}')
|
||||||
|
else:
|
||||||
|
styles.append(f"width:{_w}%;")
|
||||||
|
if 'height' in options:
|
||||||
|
_h = options['width']
|
||||||
|
if _h.endswith('px') or _h.endswith('em') or _h.endswith('rem'):
|
||||||
|
if settings.USE_BOOTSTRAP:
|
||||||
|
div_styles.append(f"height:{_h};")
|
||||||
|
else:
|
||||||
|
styles.append(f"height:{_h};")
|
||||||
|
else:
|
||||||
|
if _h.endswith('%'):
|
||||||
|
_h= _h[:-1]
|
||||||
|
if _h.isdigit():
|
||||||
|
_h=int(_w)
|
||||||
|
if _h > 100:
|
||||||
|
_h = 100
|
||||||
|
if settings.USE_BOOTSTRAP:
|
||||||
|
if 1 < int(_h) <= 25:
|
||||||
|
height = 25
|
||||||
|
else:
|
||||||
|
height = ((_h // 25) * 25)
|
||||||
|
if height > 100:
|
||||||
|
height = 100
|
||||||
|
|
||||||
|
div_classes.append(f'h-{height}')
|
||||||
|
else:
|
||||||
|
styles.append(f"height:{_h}%;")
|
||||||
|
|
||||||
|
if "position" in options:
|
||||||
|
pos = options['position']
|
||||||
|
if settings.USE_BOOTSTRAP:
|
||||||
|
if pos == "left" or pos=="start":
|
||||||
|
div_classes += ["float-start","me-2"]
|
||||||
|
#classes += ["float-start","me-2"]
|
||||||
|
elif pos == "right" or pos == "end":
|
||||||
|
div_classes += ["float-end","ms-2"]
|
||||||
|
#classes += ["float-end","ms-2"]
|
||||||
|
elif pos == "center":
|
||||||
|
div_classes += ["mx-auto","d-block"]
|
||||||
|
#classes += ["mx-auto","d-block"]
|
||||||
|
|
||||||
|
if styles:
|
||||||
|
style=f"style=\"{"".join(styles)}\""
|
||||||
|
else:
|
||||||
|
style=""
|
||||||
|
|
||||||
|
if div_styles:
|
||||||
|
div_style=f'style="{"".join(div_styles)}"'
|
||||||
|
else:
|
||||||
|
div_style=""
|
||||||
|
if settings.USE_BOOTSTRAP:
|
||||||
|
return f'<div class=""{' '.join(div_classes)}" {div_style}><iframe class={" ".join(classes)}" src="https://www.youtube.com/embed/{options[tag_name]}" allowfullscreen></iframe></div>'
|
||||||
|
else:
|
||||||
|
return f'<div {div_style}><iframe src="https://www.youtube.com/embed/{options[tag_name]}?rel=0" allowfullscreen></iframe></div>'
|
||||||
1
tinywiki/static/tinywiki/css/atom-one-dark.min.css
vendored
Normal file
1
tinywiki/static/tinywiki/css/atom-one-dark.min.css
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#abb2bf;background:#282c34}.hljs-comment,.hljs-quote{color:#5c6370;font-style:italic}.hljs-doctag,.hljs-formula,.hljs-keyword{color:#c678dd}.hljs-deletion,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-subst{color:#e06c75}.hljs-literal{color:#56b6c2}.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#98c379}.hljs-attr,.hljs-number,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-pseudo,.hljs-template-variable,.hljs-type,.hljs-variable{color:#d19a66}.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-symbol,.hljs-title{color:#61aeee}.hljs-built_in,.hljs-class .hljs-title,.hljs-title.class_{color:#e6c07b}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline}
|
||||||
1
tinywiki/static/tinywiki/css/atom-one-light.min.css
vendored
Normal file
1
tinywiki/static/tinywiki/css/atom-one-light.min.css
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#383a42;background:#fafafa}.hljs-comment,.hljs-quote{color:#a0a1a7;font-style:italic}.hljs-doctag,.hljs-formula,.hljs-keyword{color:#a626a4}.hljs-deletion,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-subst{color:#e45649}.hljs-literal{color:#0184bb}.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#50a14f}.hljs-attr,.hljs-number,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-pseudo,.hljs-template-variable,.hljs-type,.hljs-variable{color:#986801}.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-symbol,.hljs-title{color:#4078f2}.hljs-built_in,.hljs-class .hljs-title,.hljs-title.class_{color:#c18401}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline}
|
||||||
38
tinywiki/static/tinywiki/js/bbcode.js
Normal file
38
tinywiki/static/tinywiki/js/bbcode.js
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
Language: bbcode
|
||||||
|
Author: Paul Reid <paul@reid-family.org>
|
||||||
|
Description: highlightjs language definition for bbcode files
|
||||||
|
Category: config
|
||||||
|
*/
|
||||||
|
|
||||||
|
hljs.registerLanguage('bbcode', function (hljs) {
|
||||||
|
return {
|
||||||
|
case_insensitive: true,
|
||||||
|
contains: [
|
||||||
|
{
|
||||||
|
className: 'name',
|
||||||
|
begin: /\[[^=\s\]]*/
|
||||||
|
},
|
||||||
|
{
|
||||||
|
className: 'name',
|
||||||
|
begin: ']'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
className: 'attribute',
|
||||||
|
begin: /(?<==)[^\]\s]*/
|
||||||
|
},
|
||||||
|
{
|
||||||
|
className: 'attr',
|
||||||
|
begin: /(?<=\[[^\]]* )[^\s=\]]*/
|
||||||
|
},
|
||||||
|
{
|
||||||
|
className: 'string',
|
||||||
|
begin: /[=;:8]'?\-?[\)\(3SPDO>@$|/]/
|
||||||
|
},
|
||||||
|
{
|
||||||
|
className: 'string',
|
||||||
|
begin: /:[\w]*:/
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
});
|
||||||
1244
tinywiki/static/tinywiki/js/highlight.min.js
vendored
Normal file
1244
tinywiki/static/tinywiki/js/highlight.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
tinywiki/static/tinywiki/js/htmx.min.js
vendored
Normal file
1
tinywiki/static/tinywiki/js/htmx.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -4,7 +4,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>TinyWiki</title>
|
<title>{% block head_title %}TinyWiki{% endblock %}</title>
|
||||||
{% block extra_css %}{% endblock %}
|
{% block extra_css %}{% endblock %}
|
||||||
{% block scripts %}{% endblock %}
|
{% block scripts %}{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
@@ -12,12 +12,15 @@
|
|||||||
{% else %}
|
{% else %}
|
||||||
<h1>Welcome to TinyWiki</h1>
|
<h1>Welcome to TinyWiki</h1>
|
||||||
|
|
||||||
<p>{% blocktranslate %}You are seeing this welcome page because there is no Welcome page
|
|
||||||
configured for your Wiki. To configure a welcome page create a new page with the
|
<p style="text-align:justify">{% blocktranslate %}You are seeing this welcome page because there is no Welcome page
|
||||||
slug <i>tw-home</i> and put the content for your welcome-page there.{% endblocktranslate %}</p>
|
configured for your Wiki. To configure a welcome page {{ create_tw_home }} and put the content for your welcome-page there.{% endblocktranslate %}</p>
|
||||||
<p>{% blocktranslate %}You can use Markdown or BBCode to write your pages. If you don't know
|
<p style="text-align:justify">{% blocktranslate %}You can use BBCode or Markdown to write your pages. If you don't know
|
||||||
Markdown read the <a href="#">Guide for Markdown used by TinyWiki</a>. Or if you want to use
|
Markdown or BBCode there are two guides you can consult before you start editing your pages.{% endblocktranslate %}
|
||||||
BBCode there is a <a href="#">Guide for BBCode used by TinyWiki</a> too.{% endblocktranslate %}
|
<ol>
|
||||||
|
<li>{{ bbcode_guide }}</li>
|
||||||
|
<li>{{ markdown_guide }}</li>
|
||||||
|
</ol>
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-book" viewBox="0 0 16 16">
|
|
||||||
<path d="M1 2.828c.885-.37 2.154-.769 3.388-.893 1.33-.134 2.458.063 3.112.752v9.746c-.935-.53-2.12-.603-3.213-.493-1.18.12-2.37.461-3.287.811zm7.5-.141c.654-.689 1.782-.886 3.112-.752 1.234.124 2.503.523 3.388.893v9.923c-.918-.35-2.107-.692-3.287-.81-1.094-.111-2.278-.039-3.213.492zM8 1.783C7.015.936 5.587.81 4.287.94c-1.514.153-3.042.672-3.994 1.105A.5.5 0 0 0 0 2.5v11a.5.5 0 0 0 .707.455c.882-.4 2.303-.881 3.68-1.02 1.409-.142 2.59.087 3.223.877a.5.5 0 0 0 .78 0c.633-.79 1.814-1.019 3.222-.877 1.378.139 2.8.62 3.681 1.02A.5.5 0 0 0 16 13.5v-11a.5.5 0 0 0-.293-.455c-.952-.433-2.48-.952-3.994-1.105C10.413.809 8.985.936 8 1.783"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 772 B |
@@ -1,4 +0,0 @@
|
|||||||
<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-box-arrow-up-right\" viewBox=\"0 0 16 16\">
|
|
||||||
<path fill-rule=\"evenodd\" d=\"M8.636 3.5a.5.5 0 0 0-.5-.5H1.5A1.5 1.5 0 0 0 0 4.5v10A1.5 1.5 0 0 0 1.5 16h10a1.5 1.5 0 0 0 1.5-1.5V7.864a.5.5 0 0 0-1 0V14.5a.5.5 0 0 1-.5.5h-10a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5h6.636a.5.5 0 0 0 .5-.5\"/>
|
|
||||||
<path fill-rule=\"evenodd\" d=\"M16 .5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0 0 1h3.793L6.146 9.146a.5.5 0 1 0 .708.708L15 1.707V5.5a.5.5 0 0 0 1 0z\"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 552 B |
@@ -1,4 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-file-earmark-x" viewBox="0 0 16 16">
|
|
||||||
<path d="M6.854 7.146a.5.5 0 1 0-.708.708L7.293 9l-1.147 1.146a.5.5 0 0 0 .708.708L8 9.707l1.146 1.147a.5.5 0 0 0 .708-.708L8.707 9l1.147-1.146a.5.5 0 0 0-.708-.708L8 8.293z"/>
|
|
||||||
<path d="M14 14V4.5L9.5 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2M9.5 3A1.5 1.5 0 0 0 11 4.5h2V14a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h5.5z"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 485 B |
@@ -1,3 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-house" viewBox="0 0 16 16">
|
|
||||||
<path d="M8.707 1.5a1 1 0 0 0-1.414 0L.646 8.146a.5.5 0 0 0 .708.708L2 8.207V13.5A1.5 1.5 0 0 0 3.5 15h9a1.5 1.5 0 0 0 1.5-1.5V8.207l.646.647a.5.5 0 0 0 .708-.708L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293zM13 7.207V13.5a.5.5 0 0 1-.5.5h-9a.5.5 0 0 1-.5-.5V7.207l5-5z"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 415 B |
@@ -1,4 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-journal" viewBox="0 0 16 16">
|
|
||||||
<path d="M3 0h10a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2v-1h1v1a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v1H1V2a2 2 0 0 1 2-2"/>
|
|
||||||
<path d="M1 5v -.5a.5.5 0 0 1 1 0V5h.5a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1zm0 3v-.5a.5.5 0 0 1 1 0V8h.5a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1zm0 3v-.5a.5.5 0 0 1 1 0v.5h.5a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1z"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 498 B |
0
tinywiki/templates/tinywiki/image/hx-upload.html
Normal file
0
tinywiki/templates/tinywiki/image/hx-upload.html
Normal file
0
tinywiki/templates/tinywiki/image/upload.html
Normal file
0
tinywiki/templates/tinywiki/image/upload.html
Normal file
35
tinywiki/templates/tinywiki/page/bs-create.html
Normal file
35
tinywiki/templates/tinywiki/page/bs-create.html
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{% extends "tinywiki/page/bs-model-base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block head_title %}{{ site_title }} - {% translate "Create a new Wiki page" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block title %}{% translate "Create a new Wiki page" %}{% endblock title%}
|
||||||
|
|
||||||
|
{% block scripts %}
|
||||||
|
<script>
|
||||||
|
function save_and_continue() {
|
||||||
|
form = document.getElementById("wiki-page-form");
|
||||||
|
form.action = form.action + "?save=1";
|
||||||
|
form.submit();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
{% endblock scripts %}
|
||||||
|
|
||||||
|
{% block form_buttons %}
|
||||||
|
<button class="btn btn-primary" type="button" onclick="save_and_continue();">{% translate "Create page and continue" %}<button>
|
||||||
|
<button class="btn btn-success">{% translate "Create Page" %}</button>
|
||||||
|
<a class="btn btn-secondary" href="{% url 'tinywiki:home' %}">{% translate "Cancel" %}</a>
|
||||||
|
{% endblock form_buttons %}
|
||||||
|
|
||||||
|
{% block extended_extra_scripts %}
|
||||||
|
<script>
|
||||||
|
document.addEventListener('keydown',function(e) {
|
||||||
|
if (e.ctrlKey && e.key == "s") {
|
||||||
|
e.preventDefault();
|
||||||
|
var form = document.getElementById('wiki-page-form');
|
||||||
|
form.action = form.action + "?save=1";
|
||||||
|
form.submit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock extended_extra_scripts%}
|
||||||
45
tinywiki/templates/tinywiki/page/bs-edit.html
Normal file
45
tinywiki/templates/tinywiki/page/bs-edit.html
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
{% extends "tinywiki/page/bs-model-base.html" %}
|
||||||
|
{% load i18n static %}
|
||||||
|
|
||||||
|
{% block head_title %}{{ site_title }} - {% translate "Edit Wiki Page" %}{% endblock head_title %}
|
||||||
|
|
||||||
|
{% block title %}{% translate "Edit Wiki Page" %}{% endblock title%}
|
||||||
|
|
||||||
|
{% block scripts %}
|
||||||
|
<script src="{% static "tinywiki/js/htmx.min.js" %}"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block form_buttons %}
|
||||||
|
<span id="popover-btn-save-and-continue">
|
||||||
|
<button id="btn-save-and-continue"
|
||||||
|
type="button"
|
||||||
|
class="btn btn-primary"
|
||||||
|
id="btn-save-and-continue"
|
||||||
|
data-bs-toggle="popopver"
|
||||||
|
data-bs-placement="top"
|
||||||
|
hx-target="#popover-btn-save-and-continue"
|
||||||
|
hx-post="{% url "tinywiki:hx-page-edit" pk=page.pk %}"
|
||||||
|
>
|
||||||
|
{% translate "Save and continue" %}
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
<button type="submit" class="btn btn-success">{% translate "Save and show" %}</button>
|
||||||
|
<a class="btn btn-secondary" href="{% url "tinywiki:page" form.instance.slug %}">{% translate "Cancel" %}</a>
|
||||||
|
{% endblock form_buttons %}
|
||||||
|
|
||||||
|
{% block extended_extra_scripts%}
|
||||||
|
<script>
|
||||||
|
document.addEventListener('keydown',function(e) {
|
||||||
|
if (e.ctrlKey && e.key == "s") {
|
||||||
|
e.preventDefault();
|
||||||
|
btn = document.getElementById("btn-save-and-continue")
|
||||||
|
btn.click()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
btn_save_popover = new bootstrap.Popover(document.getElementById("btn-save-and-continue"),trigger="manual");
|
||||||
|
btn_save_popover.show();
|
||||||
|
setTimeout(()=>{btn_save_popover.hide();},3000);
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
66
tinywiki/templates/tinywiki/page/bs-model-base.html
Normal file
66
tinywiki/templates/tinywiki/page/bs-model-base.html
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
{% extends base_template %}
|
||||||
|
{% load i18n widget_tweaks %}
|
||||||
|
|
||||||
|
{% block style %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>{% block title %}{% endblock title%}</h1>
|
||||||
|
<form method="POST"
|
||||||
|
class="mb-4"
|
||||||
|
id="wiki-page-form"
|
||||||
|
action="{% if create %}{% url "tinywiki:page-create" %}{% else %}{% url "tinywiki:page-edit" slug=form.instance.slug|default:"1" %}{% endif %}">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="form-floating form-floating-lg mb-3">
|
||||||
|
{% render_field form.title class="form-control form-control-lg" %}
|
||||||
|
{{ form.title.label_tag }}
|
||||||
|
</div>
|
||||||
|
<div class="form-floating form-floating-sm mb-3">
|
||||||
|
{% if create and slug %}
|
||||||
|
{% render_field form.slug class="form-control form-control-sm" value=slug placeholder="slug" %}
|
||||||
|
{% else %}
|
||||||
|
{% render_field form.slug class="form-control form-control-sm" palceholder="slug" %}
|
||||||
|
{% endif %}
|
||||||
|
{{ form.slug.label_tag }}
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<div class="form-floating form-floating-sm mb-3">
|
||||||
|
{% render_field form.content_type_data class="form-select form-select-sm" %}
|
||||||
|
{{ form.content_type_data.label_tag }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<div class="form-floating form-floating-sm mb-3">
|
||||||
|
{% render_field form.status_data class="form-select form-select-sm" %}
|
||||||
|
{{ form.status_data.label_tag }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-floating mb-3">
|
||||||
|
{% render_field form.content class="form-control h-100" style="font-family:monospace;" rows=25 %}
|
||||||
|
{{ form.content.label_tag }}
|
||||||
|
</div>
|
||||||
|
<div class="text-end">
|
||||||
|
{% block form_buttons %}{% endblock form_buttons %}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% endblock content%}
|
||||||
|
|
||||||
|
{% block extra_scripts %}
|
||||||
|
<script>
|
||||||
|
document.getElementById("id_content").addEventListener('keydown',function(e) {
|
||||||
|
if (e.key == "Tab") {
|
||||||
|
e.preventDefault();
|
||||||
|
var start = this.selectionStart;
|
||||||
|
var end = this.selectionEnd;
|
||||||
|
|
||||||
|
console.log(this.value);
|
||||||
|
// set textarea value to: text before caret + 4 spaces + text after caret
|
||||||
|
this.value = this.value.substring(0,start) + " " + this.value.substring(end);
|
||||||
|
this.selectionStart = this.selectionEnd = start + 4;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% block extended_extra_scripts %}{% endblock %}
|
||||||
|
{% endblock extra_scripts %}
|
||||||
@@ -1,11 +1,19 @@
|
|||||||
{% extends base_template %}
|
{% extends base_template %}
|
||||||
{% load i18n %}
|
{% load i18n static %}
|
||||||
|
|
||||||
{% block extra_css %}
|
{% block extra_css %}
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/styles/default.min.css">
|
<link rel="stylesheet" id="hljs-theme" href="{% static 'tinywiki/css/atom-one-light.min.css' %}">
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/highlight.min.js"></script>
|
<script>
|
||||||
|
const theme=(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
||||||
|
if (theme==="dark") {
|
||||||
|
let hljs_theme = document.getElementById('hljs-theme');
|
||||||
|
hljs_theme.setAttribute('href',href="{% static 'tinywiki/css/atom-one-dark.min.css' %}");
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script src="{% static 'tinywiki/js/highlight.min.js' %}"></script>
|
||||||
|
<script src="{% static 'tinywiki/js/bbcode.js' %}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block extra_scripts %}
|
{% block extra_scripts %}
|
||||||
@@ -18,9 +26,24 @@
|
|||||||
<a class="btn btn-primary" href="{% url 'tinywiki:page-edit' slug=page.slug %}">{% translate "Edit Page" %}</a>
|
<a class="btn btn-primary" href="{% url 'tinywiki:page-edit' slug=page.slug %}">{% translate "Edit Page" %}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if user_can_delete_wiki_page %}
|
{% if user_can_delete_wiki_page %}
|
||||||
<button class="btn btn-danger" type="button">Delete Page</button>
|
<button class="btn btn-danger"
|
||||||
|
hx-get="{% url 'tinywiki:hx-page-delete' pk=page.pk %}"
|
||||||
|
hx-target="#modal-here"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
hx-trigger="clicked"
|
||||||
|
data-bs-toggle="modal"
|
||||||
|
data-bs-target="#modal-here"
|
||||||
|
>{% translate "Delete Page" %}</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<h1>{{ page.title }}</h1>
|
|
||||||
{{ page.html_content }}
|
{{ page.html_content }}
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|
||||||
|
{% block extra_body %}
|
||||||
|
<div id="modal-here" class="modal modal-blur fade" style="display:none;" aria-hidden="false" tab-index="-1">
|
||||||
|
<div class="modal-dialog modal-md modal-dialog-centered" role="document">
|
||||||
|
<div class="modal-content"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock extra_body%}
|
||||||
@@ -4,13 +4,6 @@
|
|||||||
{% block title %}{% translate "Create a new Wiki page" %}{% endblock title%}
|
{% block title %}{% translate "Create a new Wiki page" %}{% endblock title%}
|
||||||
|
|
||||||
{% block form_buttons %}
|
{% block form_buttons %}
|
||||||
{% if use_bootstrap %}
|
|
||||||
<div class="text-end">
|
|
||||||
<button class="btn btn-primary">{% translate "Create Page" %}</button>
|
|
||||||
<a class="btn btn-secondary" href="{% url 'tinywiki:home' %}">{% translate "Cancel" %}</a>
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
<button type="submit">{% translate "Create Page" %}</button>
|
<button type="submit">{% translate "Create Page" %}</button>
|
||||||
<a class="button button-secondary" href="{% url 'tinywiki:home' %}">{% translate "Cancel" %}</a>
|
<a class="button button-secondary" href="{% url 'tinywiki:home' %}">{% translate "Cancel" %}</a>
|
||||||
{% endif %}
|
|
||||||
{% endblock form_buttons %}
|
{% endblock form_buttons %}
|
||||||
12
tinywiki/templates/tinywiki/page/delete.html
Normal file
12
tinywiki/templates/tinywiki/page/delete.html
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{% extends base_template %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<form method="POST">
|
||||||
|
{% csrf_token %}
|
||||||
|
<p>Type <b>{{ page.slug }}</b> in the field to delete the page.</p>
|
||||||
|
{{ form.as_p }}
|
||||||
|
<button class="button button-danger" type="submit">{% translate "Delete Page" %}</button>
|
||||||
|
<a class="button button-secondary" href="{% url 'tinywiki:page' page.slug %}">Cancel</a>
|
||||||
|
</form>
|
||||||
|
{% endblock content %}
|
||||||
@@ -4,13 +4,6 @@
|
|||||||
{% block title %}{% translate "Edit Wiki Page" %}{% endblock title%}
|
{% block title %}{% translate "Edit Wiki Page" %}{% endblock title%}
|
||||||
|
|
||||||
{% block form_buttons %}
|
{% block form_buttons %}
|
||||||
{% if use_bootstrap %}
|
|
||||||
<div class="text-end">
|
|
||||||
<button type="submit" class="btn btn-primary">{% translate "Save Changes" %}</button>
|
|
||||||
<a class="btn btn-secondary" href="{% url "tinywiki:page" form.instance.slug %}">{% translate "Cancel" %}</a>
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
<button type="submit">{% translate "Save Changes" %}</button>
|
<button type="submit">{% translate "Save Changes" %}</button>
|
||||||
<a class="button button-secondary" href="{% url "tinywiki:page" form.instance.slug %}">{% translate "Cancel" %}</a>
|
<a class="button button-secondary" href="{% url "tinywiki:page" form.instance.slug %}">{% translate "Cancel" %}</a>
|
||||||
{% endif %}
|
|
||||||
{% endblock form_buttons %}
|
{% endblock form_buttons %}
|
||||||
27
tinywiki/templates/tinywiki/page/hx-bs-delete.html
Normal file
27
tinywiki/templates/tinywiki/page/hx-bs-delete.html
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{% load i18n widget_tweaks %}
|
||||||
|
<div class="modal-dialog modal-md modal-dialog-centered">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h1 class="modal-title fs-5" id="exampleModalLabel">{% translate "Delete Wiki-Page" %}</h1>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form method="POST">
|
||||||
|
{% csrf_token %}
|
||||||
|
<p>
|
||||||
|
{% with page.slug as page_slug %}
|
||||||
|
{% blocktranslate %}Type <b>{{ page_slug }}</b> in the field below to delete the Wiki-Page.{% endblocktranslate %}
|
||||||
|
{% endwith %}
|
||||||
|
<div class="form-floating form-floating-sm">
|
||||||
|
{% render_field form.slug class="form-control form-cotrol-sm" placeholder=page.slug %}
|
||||||
|
<label for="{{ form.slug.auto_id }}">{{ form.slug }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="text-end">
|
||||||
|
<button class="btn btn-danger" type="submit">{% translate "Delete the page" %}</button>
|
||||||
|
<button clasS="btn btn-secondary" type="button" data-bs-dismiss="modal">{% translate "Cancel" %}</button>
|
||||||
|
</div>
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
26
tinywiki/templates/tinywiki/page/hx-bs-edit.html
Normal file
26
tinywiki/templates/tinywiki/page/hx-bs-edit.html
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
<span id="popover-btn-save-and-continue">
|
||||||
|
<button type="button"
|
||||||
|
class="btn btn-primary"
|
||||||
|
id="btn-save-and-continue"
|
||||||
|
data-bs-toggle="popopver"
|
||||||
|
data-bs-placement="top"
|
||||||
|
hx-target="#popover-btn-save-and-continue"
|
||||||
|
hx-post="{% url "tinywiki:hx-page-edit" pk=page.pk %}"
|
||||||
|
{% if save_success %}
|
||||||
|
data-bs-title="Successfully saved"
|
||||||
|
data-bs-content="Your Wiki page has been successfully saved!"
|
||||||
|
{% else %}
|
||||||
|
data-bs-title="Saving failed"
|
||||||
|
data-bs-content="Your Wiki page could not be saved!"
|
||||||
|
{% endif %}
|
||||||
|
>
|
||||||
|
Save and continue
|
||||||
|
</button>
|
||||||
|
<script>
|
||||||
|
btn_save_popover = new bootstrap.Popover(document.getElementById("btn-save-and-continue"),trigger="manual");
|
||||||
|
btn_save_popover.show();
|
||||||
|
setTimeout(() => {btn_save_popover.hide();},3000)
|
||||||
|
</script>
|
||||||
|
</span>
|
||||||
|
|
||||||
@@ -4,58 +4,24 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>{% block title %}{% endblock title%}</h1>
|
<h1>{% block title %}{% endblock title%}</h1>
|
||||||
<form method="POST"
|
<form method="POST"
|
||||||
|
class="mb-4"
|
||||||
id="wiki-page-form"
|
id="wiki-page-form"
|
||||||
action="{% if create %}{% url "tinywiki:page-create" %}{% else %}{% url "tinywiki:page-edit" slug=form.instance.slug|default:"1" %}{% endif %}">
|
action="{% if create %}{% url "tinywiki:page-create" %}{% else %}{% url "tinywiki:page-edit" slug=form.instance.slug|default:"1" %}{% endif %}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{% if use_bootstrap %}
|
|
||||||
<div class="form-floating mb-2">
|
|
||||||
{% with "form-control form-control-lg "|add:title_extra as title_class %}
|
|
||||||
{% render_field form.title class=title_class placeholder="{{ form.title.label }}" %}
|
|
||||||
{% endwith %}
|
|
||||||
<label for="{{ form.title.auto_id }}">{{ form.title.label }}</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-floating mb-2">
|
|
||||||
{% with "form-control form-control-sm "|add:slug_extra as slug_class%}
|
|
||||||
{% render_field form.slug class=slug_class placeholder="{{ form.slug.label }}" %}
|
|
||||||
{% endwith %}
|
|
||||||
<label for="{{ form.slug.auto_id }}">{{ form.slug.label }}</label>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6 mb-2">
|
|
||||||
<div class="form-floating mb-2">
|
|
||||||
{% with "form-select form-select-sm "|add:content_type_extra as content_type_class %}
|
|
||||||
{% render_field form.content_type_data class=content_type_class %}
|
|
||||||
{% endwith %}
|
|
||||||
<label for="{{ for.content_type_data.auto_id }}">{{ form.content_type_data.label }}</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="form-floating mb-2">
|
|
||||||
{% with "form-select form-select-sm "|add:status_extra as status_class %}
|
|
||||||
{% render_field form.status_data class=status_class %}
|
|
||||||
{% endwith %}
|
|
||||||
<label for="{{ for.status_data.auto_id }}">{{ form.status_data.label }}</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-floating mb-3">
|
|
||||||
{% with "form-control h-100 "|add:content_extra as content_class %}
|
|
||||||
{% render_field form.content class=content_class rows="25"%}
|
|
||||||
{% endwith %}
|
|
||||||
<label for="{{ form.content.auto_id }}">{{ form.content.label }}</label>
|
|
||||||
</div>
|
|
||||||
<div class="text-end">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
<p>
|
<p>
|
||||||
{% with title_extra|default:"" as title_class %}
|
{% with title_extra|default:"" as title_class %}
|
||||||
{{ form.title.label_tag }}{% render_field form.title class=Title_class %}
|
{{ form.title.label_tag }} {% render_field form.title class="{{ title_class }}" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
|
||||||
|
{{ form.slug.label_tag }}
|
||||||
{% with slug_extra|default:"" as slug_class%}
|
{% with slug_extra|default:"" as slug_class%}
|
||||||
{{ form.slug.label_tag }}{% render_field form.slug class=slug_class %}
|
{% if create and slug %}
|
||||||
|
{% render_field form.slug class=slug_class value=slug placeholder="slug" %}
|
||||||
|
{% else %}
|
||||||
|
{% render_field form.slug class=slug_class palceholder="slug" %}
|
||||||
|
{% endif %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
@@ -73,8 +39,9 @@
|
|||||||
{{ form.content.label_tag }} {% render_field form.content class=content_class cols=80 rows=30 %}
|
{{ form.content.label_tag }} {% render_field form.content class=content_class cols=80 rows=30 %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
<div class="text-end">
|
||||||
{% block form_buttons %}{% endblock form_buttons %}
|
{% block form_buttons %}{% endblock form_buttons %}
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{% endblock content%}
|
{% endblock content%}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
{% extends base_template %}
|
{% extends base_template %}
|
||||||
{% load i18n %}
|
{% load i18n static %}
|
||||||
|
|
||||||
{% block extra_css %}
|
{% block extra_css %}
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/styles/default.min.css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/styles/default.min.css">
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/highlight.min.js"></script>
|
<script src="{% static 'tinywiki/js/htmx.min.js' %}"></script>
|
||||||
|
<script src="{% static 'tinywiki/js/highlight.min.js' %}"></script>
|
||||||
|
<script src="{% static 'tinywiki/js/bbcode.js' %}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block extra_scripts %}
|
{% block extra_scripts %}
|
||||||
@@ -17,8 +19,11 @@
|
|||||||
<a href="{% url 'tinywiki:page-edit' slug=page.slug %}">{% translate "Edit Page" %}</a>
|
<a href="{% url 'tinywiki:page-edit' slug=page.slug %}">{% translate "Edit Page" %}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if user_can_delete_wiki_page %}
|
{% if user_can_delete_wiki_page %}
|
||||||
<a href="#">Delete Page</a>
|
<a href="{% url 'tinywiki:page-delete' slug=page.slug %}">{% translate "Delete Page" %}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<h1>{{ page.title }}</h1>
|
<h1>{{ page.title }}</h1>
|
||||||
{{ page.html_content }}
|
{{ page.html_content }}
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|
||||||
|
{% block extra_body %}
|
||||||
|
{% block %}
|
||||||
@@ -10,4 +10,7 @@ urlpatterns = [
|
|||||||
path("page/<slug:slug>/",PageView.as_view(),name="page"),
|
path("page/<slug:slug>/",PageView.as_view(),name="page"),
|
||||||
path("page-create/",PageCreateView.as_view(),name="page-create"),
|
path("page-create/",PageCreateView.as_view(),name="page-create"),
|
||||||
path("page/<slug:slug>/edit/",PageEditView.as_view(),name='page-edit'),
|
path("page/<slug:slug>/edit/",PageEditView.as_view(),name='page-edit'),
|
||||||
|
path("page/<slug:slug>/dekete/",PageDeleteView.as_view(),name='page-delete'),
|
||||||
|
path("__hx__/page/<int:pk>/edit/",HxPageEditView.as_view(),name='hx-page-edit'),
|
||||||
|
path("__hx__/page/<int:pk>/delete/",HxPageDeleteView.as_view(),name='hx-page-delete'),
|
||||||
]
|
]
|
||||||
@@ -6,6 +6,57 @@ from typing import Any
|
|||||||
class Base:
|
class Base:
|
||||||
base_template_name = settings.TINYWIKI_BASE_TEMPLATE
|
base_template_name = settings.TINYWIKI_BASE_TEMPLATE
|
||||||
|
|
||||||
|
@property
|
||||||
|
def user_can_edit_pages(self)->bool:
|
||||||
|
if (self.request.user.is_staff
|
||||||
|
or self.request.user.has_perm('page.tiynwiki-edit')
|
||||||
|
or self.request.user.has_perm('page.tinywiki-edit-all')):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def user_can_edit_all_pages(self)->bool:
|
||||||
|
if (self.request.user.is_staff
|
||||||
|
or self.request.user.has_perm('page.tinywiki-edit-all')):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def user_can_edit_system_pages(self)->bool:
|
||||||
|
if (self.request.user.is_staff
|
||||||
|
or self.request.user.has_perm('page.tiynwiki-edit-system')):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def user_can_create_pages(self)->bool:
|
||||||
|
if (self.request.user.is_staff
|
||||||
|
or self.request.user.has_perm('page.tiynwiki-create')):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def user_can_create_system_pages(self)->bool:
|
||||||
|
if (self.request.user.is_staff
|
||||||
|
or self.request.user.has_perm('page.tiynwiki-create-system')):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def page_is_creatable(self,slug:str)->bool:
|
||||||
|
if not slug:
|
||||||
|
return False
|
||||||
|
if slug.startswith('tw-'):
|
||||||
|
return self.user_can_create_system_pages
|
||||||
|
return self.user_can_create_pages
|
||||||
|
|
||||||
|
def page_is_editable(self,page)->bool:
|
||||||
|
if page.slug.startswith('tw-'):
|
||||||
|
return self.user_can_edit_system_pages
|
||||||
|
elif (self.user_can_edit_all_pages
|
||||||
|
or (self.request.user.pk == page.author.pk and self.user_can_edit_pages)):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_base_template_name(cls)->str:
|
def get_base_template_name(cls)->str:
|
||||||
return cls.base_template_name
|
return cls.base_template_name
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
from curses.ascii import isalpha
|
from curses.ascii import isalpha
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
|
from django_project.settings import STATIC_URL
|
||||||
from tinywiki import settings
|
from tinywiki import settings
|
||||||
from .base import View
|
from .base import View
|
||||||
from django.http import HttpRequest,HttpResponse
|
from django.http import HttpRequest,HttpResponse
|
||||||
@@ -18,12 +21,34 @@ class HomeView(View):
|
|||||||
def get(self,request):
|
def get(self,request):
|
||||||
try:
|
try:
|
||||||
page = Page.objects.get(slug='tw-home')
|
page = Page.objects.get(slug='tw-home')
|
||||||
except Page.DoesNotExist:
|
if (not Page.status == WikiPageStatus.PUBLISHED
|
||||||
|
and not request.user.is_staff
|
||||||
|
and not request.user.has_perm('page.view-all')):
|
||||||
page = None
|
page = None
|
||||||
|
|
||||||
|
except Page.DoesNotExist:
|
||||||
|
page = None
|
||||||
|
if self.user_can_create_system_pages:
|
||||||
|
if settings.USE_BOOTSTRAP:
|
||||||
|
create_tw_home = f"<a class=\"icon-link icon-link-hover\" href={reverse("tinywiki:page",kwargs={'slug':'tw-home'})}>{_('create a new page with the slug <i>tw-home</i>')}<svg class=\"bi\"><use xlink:href=\"{settings.settings.STATIC_URL + 'tinywiki/icons/bootstrap-icons.svg' }#house-add\" ></use></svg></a>"
|
||||||
|
else:
|
||||||
|
create_tw_home = f"<a href={reverse("tinywiki:page",kwargs={'slug':'tw-home'})}>{_('create a new page with the slug <i>tw-home</i>')}</a>"
|
||||||
|
else:
|
||||||
|
create_tw_home = "create a new page with the slug <i>tw-home</i>"
|
||||||
|
|
||||||
|
if settings.USE_BOOTSTRAP:
|
||||||
|
markdown_guide = f"<a class=\"icon-link icon-link-hover\" href={reverse('tinywiki:page',kwargs={'slug':'tw-markdown'})}>{_('Guide for markdown used by TinyWiki')}<svg class=\"bi\"><use xlink:href=\"{settings.settings.STATIC_URL + 'tinywiki/icons/bootstrap-icons.svg' }#journal\"></use></svg></a>"
|
||||||
|
bbcode_guide = f"<a class=\"icon-link icon-link-hover\" href={reverse('tinywiki:page',kwargs={'slug':'tw-bbcode'})}>{_('Guide for BBCode used by TinyWiki')}<svg class=\"bi\"><use xlink:href=\"{settings.settings.STATIC_URL + 'tinywiki/icons/bootstrap-icons.svg' }#journal\"></use></svg></a>"
|
||||||
|
else:
|
||||||
|
markdown_guide = f"<a class=\"icon-link icon-link-hover\" href={reverse('tinywiki:page',kwargs={'slug':'tw-markdown'})}>{_('Guide for markdown used by TinyWiki')}</a>"
|
||||||
|
bbcode_guide = f"<a class=\"icon-link icon-link-hover\" href={reverse('tinywiki:page',kwargs={'slug':'tw-bbcode'})}>{_('Guide for BBCode used by TinyWiki')}</a>"
|
||||||
return render(request,
|
return render(request,
|
||||||
self.get_template_name(),
|
self.get_template_name(),
|
||||||
self.get_context_data(page=page))
|
self.get_context_data(page=page,
|
||||||
|
user_can_create_system_pages=self.user_can_create_system_pages,
|
||||||
|
create_tw_home=mark_safe(create_tw_home),
|
||||||
|
markdown_guide=mark_safe(markdown_guide),
|
||||||
|
bbcode_guide=mark_safe(bbcode_guide)))
|
||||||
|
|
||||||
class TocView(View):
|
class TocView(View):
|
||||||
template_name = "tinywiki/home/wiki-content.html"
|
template_name = "tinywiki/home/wiki-content.html"
|
||||||
|
|||||||
19
tinywiki/views/image.py
Normal file
19
tinywiki/views/image.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
from .base import FormView
|
||||||
|
|
||||||
|
class ImageUplodadView(FormView):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class ImageEditView(FormView):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class ImageDeleteFiew(FormView):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class HxImageUplaodView(FormView):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class HxImageEditView(FormView):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class HxImageDeleteView(FormView):
|
||||||
|
pass
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
from django.shortcuts import render,get_object_or_404,redirect
|
from django.shortcuts import render,get_object_or_404,redirect
|
||||||
from django.urls import reverse, reverse_lazy
|
from django.urls import reverse, reverse_lazy
|
||||||
from django.http import HttpRequest,HttpResponse
|
from django.http import HttpRequest,HttpResponse,Http404
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin,UserPassesTestMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin,UserPassesTestMixin
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
|
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from .. import settings
|
from .. import settings
|
||||||
|
|
||||||
from ..models import Page
|
from ..models import Page
|
||||||
from .base import View,FormView
|
from .base import View,FormView
|
||||||
from ..forms import PageForm,PageAdminForm
|
from ..forms import PageForm,PageAdminForm,PageDeleteForm
|
||||||
|
|
||||||
class PageView(View):
|
class PageView(View):
|
||||||
template_name = "tinywiki/page/view.html"
|
template_name = "tinywiki/page/view.html"
|
||||||
@@ -49,13 +50,19 @@ class PageView(View):
|
|||||||
|
|
||||||
|
|
||||||
def get(self,request:HttpRequest,slug:str)->HttpResponse:
|
def get(self,request:HttpRequest,slug:str)->HttpResponse:
|
||||||
page = get_object_or_404(Page,slug=slug)
|
try:
|
||||||
|
page = Page.objects.get(slug=slug)
|
||||||
|
except Page.DoesNotExist:
|
||||||
|
if self.page_is_creatable(slug):
|
||||||
|
return redirect(reverse('tinywiki:page-create') + f"?slug={slug}")
|
||||||
|
raise Http404()
|
||||||
return render(request,
|
return render(request,
|
||||||
self.get_template_name(),
|
self.get_template_name(),
|
||||||
self.get_context_data(page=page))
|
self.get_context_data(page=page))
|
||||||
|
|
||||||
class PageCreateView(LoginRequiredMixin,UserPassesTestMixin,FormView):
|
class PageCreateView(LoginRequiredMixin,UserPassesTestMixin,FormView):
|
||||||
template_name = "tinywiki/page/create.html"
|
template_name = "tinywiki/page/create.html"
|
||||||
|
bs_template_name = "tinywiki/page/bs-create.html"
|
||||||
form_class = PageForm
|
form_class = PageForm
|
||||||
|
|
||||||
def test_func(self) -> bool:
|
def test_func(self) -> bool:
|
||||||
@@ -65,14 +72,26 @@ class PageCreateView(LoginRequiredMixin,UserPassesTestMixin,FormView):
|
|||||||
return self.request.user.has_perm('tinywiki.tinywiki-create')
|
return self.request.user.has_perm('tinywiki.tinywiki-create')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def get_template_name(self) -> str:
|
||||||
|
if settings.USE_BOOTSTRAP:
|
||||||
|
return self.bs_template_name
|
||||||
|
return self.template_name
|
||||||
|
|
||||||
|
def get(self,request):
|
||||||
|
return render(request,
|
||||||
|
self.get_template_name(),
|
||||||
|
self.get_context_data())
|
||||||
|
|
||||||
def get_context_data(self,**kwargs):
|
def get_context_data(self,**kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['create']=True
|
context['create']=True
|
||||||
|
context['slug'] = self.request.GET.get('slug',None)
|
||||||
context.setdefault("title_extra","")
|
context.setdefault("title_extra","")
|
||||||
context.setdefault("slug_extra","")
|
context.setdefault("slug_extra","")
|
||||||
context.setdefault("content_type_extra","")
|
context.setdefault("content_type_extra","")
|
||||||
context.setdefault("status_extra","")
|
context.setdefault("status_extra","")
|
||||||
context.setdefault("content_extra","")
|
context.setdefault("content_extra","")
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def form_invalid(self, form):
|
def form_invalid(self, form):
|
||||||
@@ -149,6 +168,7 @@ class PageCreateView(LoginRequiredMixin,UserPassesTestMixin,FormView):
|
|||||||
|
|
||||||
class PageEditView(LoginRequiredMixin,UserPassesTestMixin,FormView):
|
class PageEditView(LoginRequiredMixin,UserPassesTestMixin,FormView):
|
||||||
template_name = "tinywiki/page/edit.html"
|
template_name = "tinywiki/page/edit.html"
|
||||||
|
bs_template_name = "tinywiki/page/bs-edit.html"
|
||||||
form_class = PageForm
|
form_class = PageForm
|
||||||
|
|
||||||
def test_func(self) -> bool:
|
def test_func(self) -> bool:
|
||||||
@@ -156,9 +176,16 @@ class PageEditView(LoginRequiredMixin,UserPassesTestMixin,FormView):
|
|||||||
if self.request.user.is_staff:
|
if self.request.user.is_staff:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
def get_context_data(self,**kwargs):
|
|
||||||
|
def get_template_name(self) -> str:
|
||||||
|
if settings.USE_BOOTSTRAP:
|
||||||
|
return self.bs_template_name
|
||||||
|
return self.template_name
|
||||||
|
|
||||||
|
def get_context_data(self,page,**kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['create']=False
|
context['create']=False
|
||||||
|
context['page']=page
|
||||||
context.setdefault("title_extra","")
|
context.setdefault("title_extra","")
|
||||||
context.setdefault("slug_extra","")
|
context.setdefault("slug_extra","")
|
||||||
context.setdefault("content_type_extra","")
|
context.setdefault("content_type_extra","")
|
||||||
@@ -195,6 +222,7 @@ class PageEditView(LoginRequiredMixin,UserPassesTestMixin,FormView):
|
|||||||
return render(self.request,
|
return render(self.request,
|
||||||
self.get_template_name(),
|
self.get_template_name(),
|
||||||
self.get_context_data(
|
self.get_context_data(
|
||||||
|
page=self.instance,
|
||||||
slug_extra=slug_extra,
|
slug_extra=slug_extra,
|
||||||
title_extra=title_extra,
|
title_extra=title_extra,
|
||||||
status_extra=status_extra,
|
status_extra=status_extra,
|
||||||
@@ -218,7 +246,10 @@ class PageEditView(LoginRequiredMixin,UserPassesTestMixin,FormView):
|
|||||||
raise PermissionDenied()
|
raise PermissionDenied()
|
||||||
|
|
||||||
self.instance = instance
|
self.instance = instance
|
||||||
return super().get(request)
|
|
||||||
|
return render(request,
|
||||||
|
self.get_template_name(),
|
||||||
|
self.get_context_data(page=instance))
|
||||||
|
|
||||||
def post(self,request,slug:str):
|
def post(self,request,slug:str):
|
||||||
instance = get_object_or_404(Page,slug=slug)
|
instance = get_object_or_404(Page,slug=slug)
|
||||||
@@ -255,7 +286,138 @@ class PageEditView(LoginRequiredMixin,UserPassesTestMixin,FormView):
|
|||||||
self.get_template_name(),
|
self.get_template_name(),
|
||||||
self.get_context_data(slug_invalid=True))
|
self.get_context_data(slug_invalid=True))
|
||||||
|
|
||||||
class PageDeleteView(View):
|
class PageDeleteView(LoginRequiredMixin,UserPassesTestMixin,FormView):
|
||||||
template_name = "tinywiki/page/delete"
|
template_name = "tinywiki/page/delete.html"
|
||||||
def get(self,request,slug):
|
form_class = PageDeleteForm
|
||||||
return render()
|
def test_func(self) -> bool:
|
||||||
|
if self.request.user.is_staff:
|
||||||
|
return True
|
||||||
|
if self.request.user.has_perm('tinyiwki-delete-all') or self.request.user.has_perm('tinywiki-delete'):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get(self,request:HttpRequest,slug:str)->HttpResponse:
|
||||||
|
instance = get_object_or_404(Page,slug=slug)
|
||||||
|
if not request.user.is_staff and not instance.author == request.user:
|
||||||
|
raise PermissionDenied()
|
||||||
|
|
||||||
|
return render(request,
|
||||||
|
self.template_name,
|
||||||
|
self.get_context_data(form=self.get_form_class()(),
|
||||||
|
page=instance))
|
||||||
|
|
||||||
|
def form_invalid(self,form:PageDeleteForm)->HttpResponse:
|
||||||
|
return redirect(reverse('tinywiki:page',kwargs={'slug':self.instance.slug}))
|
||||||
|
|
||||||
|
def form_valid(self,form:PageDeleteForm)->HttpResponse:
|
||||||
|
if self.instance.slug == form.cleaned_data['slug']:
|
||||||
|
try:
|
||||||
|
self.instance.delete()
|
||||||
|
return redirect(reverse("tinywiki:home"))
|
||||||
|
except: pass
|
||||||
|
return redirect(reverse('tinywiki:page',kwargs={'slug':self.instance.slug}))
|
||||||
|
|
||||||
|
|
||||||
|
def post(self,request:HttpRequest,slug:str)->HttpResponse:
|
||||||
|
self.instance = get_object_or_404(Page,slug=slug)
|
||||||
|
if not request.user.is_staff and not instance.author == request.user:
|
||||||
|
raise PermissionDenied()
|
||||||
|
return super().post(request)
|
||||||
|
|
||||||
|
|
||||||
|
class HxPageEditView(LoginRequiredMixin,UserPassesTestMixin,FormView):
|
||||||
|
template_name = "tinywiki/page/hx-edit.html"
|
||||||
|
bs_template_name = "tinywiki/page/hx-bs-edit.html"
|
||||||
|
form_class = PageForm
|
||||||
|
|
||||||
|
def test_func(self) -> bool:
|
||||||
|
if self.request.user.is_authenticated:
|
||||||
|
if self.request.user.is_staff:
|
||||||
|
return True
|
||||||
|
if self.request.user.has_perm('tinywiki-edit-page') or self.request.user.has_perm('tinywiki-edit-all-pages'):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_template_name(self) -> str:
|
||||||
|
if settings.USE_BOOTSTRAP:
|
||||||
|
return self.bs_template_name
|
||||||
|
return self.template_name
|
||||||
|
|
||||||
|
def get_context_data(self,**kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context['create']=False
|
||||||
|
context['page']=self.instance
|
||||||
|
context.setdefault("title_extra","")
|
||||||
|
context.setdefault("slug_extra","")
|
||||||
|
context.setdefault("content_type_extra","")
|
||||||
|
context.setdefault("status_extra","")
|
||||||
|
context.setdefault("content_extra","")
|
||||||
|
return context
|
||||||
|
|
||||||
|
def form_invalid(self, form):
|
||||||
|
if 'slug' in form.errors:
|
||||||
|
slug_extra = "is-invalid"
|
||||||
|
else:
|
||||||
|
slug_extra = "is-valid"
|
||||||
|
|
||||||
|
if 'title' in form.errors:
|
||||||
|
title_extra = 'is-invalid'
|
||||||
|
else:
|
||||||
|
title_extra = 'is-valid'
|
||||||
|
|
||||||
|
if 'status_data' in form.errors:
|
||||||
|
status_extra = 'is-invalid'
|
||||||
|
else:
|
||||||
|
status_extra = 'is-valid'
|
||||||
|
|
||||||
|
if 'content_type_data' in form.errors:
|
||||||
|
content_type_extra = 'is-invalid'
|
||||||
|
else:
|
||||||
|
content_type_extra = 'is-valid'
|
||||||
|
|
||||||
|
if 'content' in form.errors:
|
||||||
|
content_extra = 'is-invalid'
|
||||||
|
else:
|
||||||
|
content_extra = 'is-valid'
|
||||||
|
|
||||||
|
return render(self.request,
|
||||||
|
self.get_template_name(),
|
||||||
|
self.get_context_data(
|
||||||
|
page=self.instance,
|
||||||
|
))
|
||||||
|
|
||||||
|
def post(self,request,pk:int):
|
||||||
|
instance = get_object_or_404(Page,pk=pk)
|
||||||
|
user = request.user
|
||||||
|
if (instance.slug.startswith('tw-') and not user.is_staff):
|
||||||
|
raise PermissionDenied(_("Only staff users are allowed to edit TinyWiki system pages!"))
|
||||||
|
|
||||||
|
if user.pk != instance.author.pk:
|
||||||
|
if not user.is_staff and not user.has_perm("page.tinywiki-edit-all"):
|
||||||
|
raise PermissionDenied()
|
||||||
|
else:
|
||||||
|
if not user.has_perm('page.tinywiki-edit-all') or not user.has_perm('page.tinywiki-edit'):
|
||||||
|
raise PermissionDenied()
|
||||||
|
|
||||||
|
self.instance = instance
|
||||||
|
|
||||||
|
return super().post(request)
|
||||||
|
|
||||||
|
def get_form(self):
|
||||||
|
return self.get_form_class()(instance=self.instance,**self.get_form_kwargs())
|
||||||
|
|
||||||
|
def form_valid(self,form):
|
||||||
|
user = self.request.user
|
||||||
|
instance = form.save(commit=False)
|
||||||
|
instance.created_by = user
|
||||||
|
instance.last_edited_by = user
|
||||||
|
try:
|
||||||
|
form.save(commit=True)
|
||||||
|
return render(self.request,self.get_template_name(),self.get_context_data(save_success=True))
|
||||||
|
except:
|
||||||
|
return render(self.request,
|
||||||
|
self.get_template_name(),
|
||||||
|
self.get_context_data(save_success=False))
|
||||||
|
|
||||||
|
class HxPageDeleteView(View):
|
||||||
|
pass
|
||||||
Reference in New Issue
Block a user