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> | ||||
|         <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 %} | ||||
|         <style> | ||||
|             {% block style %}{% endblock %} | ||||
|         </style> | ||||
|         <script> | ||||
|             document.documentElement.setAttribute('data-bs-theme', (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')); | ||||
|         </script> | ||||
| @@ -15,8 +18,8 @@ | ||||
|     <body class="min-vh-100 d-flex flex-column"> | ||||
|          | ||||
|         <div class="container-fluid"> | ||||
|             <div class="row bg-info px-2"> | ||||
|                 <div clas="col-md-12 text-white"> | ||||
|             <div class="row bg-primary pt-2"> | ||||
|                 <div clas="col-md-12 text-white "> | ||||
|                     {% if brand_logo %} | ||||
|                     <img class="mb2 me-2" width="46" height="46" src="{{ brand_logo }}"> | ||||
|                     {% else %} | ||||
| @@ -26,7 +29,7 @@ | ||||
|                     {% endif %} | ||||
|                     <span class="display-6 font-weight-bold text-white me-2">{{ brand_name }}</span> | ||||
|                     {% if subtitle %} | ||||
|                     <span class="h2">{{ subtitle }}</h2> | ||||
|                     <span class="h2 text-truncate-sm">{{ subtitle }}</h2> | ||||
|                     {% endif %} | ||||
|                 </div>         | ||||
|             </div> | ||||
| @@ -61,8 +64,25 @@ | ||||
|                 </ul> | ||||
|             </div> | ||||
|         </div> | ||||
|             <div class="row h-100"> | ||||
|                 <div class="col-lg-3"> | ||||
|             <div class="row h-100 mb-4"> | ||||
|                 <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 class="col-lg-6"> | ||||
|                     <main> | ||||
| @@ -83,6 +103,7 @@ | ||||
|                     <a class="text-white" href="{% url 'tinywiki:page' 'tw-license' %}"> © 2025</a> | ||||
|                 </span> | ||||
|         </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/bootstrap@5.3.8/dist/js/bootstrap.min.js"></script> | ||||
|         {% 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 .models import Page,Image | ||||
| from django.utils.translation import gettext_lazy as _ | ||||
|  | ||||
| class PageForm(forms.ModelForm): | ||||
|     class Meta: | ||||
| @@ -12,6 +13,11 @@ class PageForm(forms.ModelForm): | ||||
|             'content', | ||||
|         ] | ||||
|          | ||||
| class PageDeleteForm(forms.Form): | ||||
|     slug = forms.SlugField(allow_unicode=False, | ||||
|                            required=False, | ||||
|                            label=_("Slug")) | ||||
|      | ||||
| class PageAdminForm(forms.ModelForm): | ||||
|     class Meta: | ||||
|         model = Page | ||||
| @@ -23,4 +29,26 @@ class PageAdminForm(forms.ModelForm): | ||||
|             'content_type_data', | ||||
|             '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])    | ||||
|                  | ||||
|          | ||||
|     def init_default_pages(apps,schema_editor)->None: | ||||
|     def init_builtin_pages(apps,schema_editor)->None: | ||||
|         from ..models import Page,Image | ||||
|         from ..enums import WikiContentType,WikiPageStatus | ||||
|         from pathlib import Path | ||||
| @@ -85,14 +85,47 @@ class Migration(migrations.Migration): | ||||
|                                     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 | ||||
|          | ||||
|     def init_user_pages(apps,schema_edit)->None: | ||||
|         from ..models import Page,Image | ||||
|         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: | ||||
|         #TODO | ||||
|         pass | ||||
|  | ||||
|     operations = [ | ||||
|         migrations.RunPython(init_tinywiki_groups_and_permissions), | ||||
|         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", | ||||
|         "status":"published", | ||||
|         "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 | ||||
| import markdown | ||||
|  | ||||
| import bbcode as _bbcode | ||||
| from tinywiki.parser import bbcode | ||||
|  | ||||
| from .parser import parse_bbcode | ||||
| from . import settings | ||||
| import tinywiki | ||||
| @@ -121,4 +125,12 @@ class Image(models.Model): | ||||
|                                     related_name="tinywiki_image_uploads") | ||||
|     uploaded_at = models.DateTimeField(_("uploaded at"), | ||||
|                                        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 | ||||
| from . import formatters | ||||
| PARSER = bbcode.Parser(newline="\n",escape_html=True) | ||||
| PARSER = bbcode.Parser(newline="\n",escape_html=True,replace_links=False) | ||||
|  | ||||
| def _(): | ||||
|     for i in formatters.SIMPLE_FORMATTERS: | ||||
|   | ||||
| @@ -10,26 +10,36 @@ from .text_formatters import ( | ||||
|     render_wiki_image, | ||||
|     render_wiki_link, | ||||
|     render_wiki_url, | ||||
| ) | ||||
| from .simple_formatters import ( | ||||
|     SIMPLE_HEADER_FORMATTERS, | ||||
|     render_table, | ||||
|     render_table_header, | ||||
|     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 | ||||
| SIMPLE_FORMATTERS=[ | ||||
|     *SIMPLE_HEADER_FORMATTERS, | ||||
| ] | ||||
|  | ||||
| #a list of tuples containing an tuple of args and a dict of kwargs | ||||
| FORMATTERS=[ | ||||
|     (('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',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),{}), | ||||
|     (('ul',render_unordered_list),{}), | ||||
|     (('li',render_list_item),{}), | ||||
|     (('p',render_paragraph),{}), | ||||
|     (('image',render_image),{}), | ||||
|     (('wiki-image',render_wiki_image),{'standalone':True}) | ||||
|     (('p',render_paragraph),{'same_tag_closes':False}), | ||||
|     (('image',render_image),{'same_tag_closes':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>"),{}), | ||||
|     (('h2',"<h2>%(value)s</h2>"),{}), | ||||
|     (('h3',"<h3>%(value)s</h3>"),{}), | ||||
|     (('h4',"<h4>%(value)s</h4>"),{}), | ||||
|     (('h5',"<h5>%(value)s</h5>"),{}), | ||||
|     (('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}), | ||||
|     (('reg',"®"),{'standalone':True}), | ||||
|     (('trade',"™"),{'standalone':True}), | ||||
|   | ||||
| @@ -2,9 +2,10 @@ from django_project.settings import STATIC_URL | ||||
| from django.urls import reverse | ||||
| from django.template.loader import render_to_string | ||||
| from django.utils.translation import gettext as _ | ||||
|  | ||||
| from ... import settings | ||||
| from ... import models | ||||
|  | ||||
| import bbcode | ||||
|  | ||||
| def render_url(tag_name:str,value,options,parent,context): | ||||
|     try: | ||||
| @@ -12,17 +13,19 @@ def render_url(tag_name:str,value,options,parent,context): | ||||
|     except KeyError: | ||||
|         url = value | ||||
|          | ||||
|     if '://' not in url: | ||||
|     if not url.startswith('/') and '://' not in url: | ||||
|         url = "http://" + url | ||||
|          | ||||
|     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>" | ||||
|  | ||||
| def render_wiki_url(tag_name,value,options,parent,context): | ||||
|     if tag_name in options: | ||||
|         url = reverse("tinywiki:page",kwargs={'slug':options[tag_name]}) | ||||
|         slug=options['tag_name'] | ||||
|         slug=options[tag_name] | ||||
|         try: | ||||
|             page = models.Page.objects.get(slug=slug) | ||||
|         except models.Page.DoesNotExist: | ||||
| @@ -33,47 +36,49 @@ def render_wiki_url(tag_name,value,options,parent,context): | ||||
|         slug=None | ||||
|          | ||||
|     if settings.USE_BOOTSTRAP: | ||||
|         href = settings.settings.STATIC_URL+"tinywiki/icons/bootstrap-icons.svg" | ||||
|         if page: | ||||
|             if page.slug.startswith('tw-'): | ||||
|                 svg=render_to_string('tinywiki/icons/journal.svg') | ||||
|                 svg = "journal" | ||||
|             elif page.slug: | ||||
|                 svg=render_to_string('tinywiki/icons/book.svg') | ||||
|                 svg = "book" | ||||
|         else: | ||||
|             svg=render_to_string('tinywiki/icons/file-earmark-x') | ||||
|          | ||||
|         return f"<a href=\"{url}\" class=\"icon-link icon-link-hover\">{value}{svg}</a>" | ||||
|             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}\">{value}</a>" | ||||
|  | ||||
|  | ||||
| def render_wiki_link(tag_name,value,options,parent,context): | ||||
|     if tag_name in options: | ||||
|         slug = options['tag_name'] | ||||
|         slug = options[tag_name] | ||||
|         print("slug",slug) | ||||
|         try: | ||||
|             page = models.Page.objects.get(slug=slug) | ||||
|             title = page.title | ||||
|             if slug.starts_with('tw-'): | ||||
|                 svg = "tinywiki/icons/journal.svg" | ||||
|             if slug.startswith('tw-'): | ||||
|                 svg = "journal" | ||||
|             else: | ||||
|                 svg = "tinywiki/icons/book.svg" | ||||
|         except: | ||||
|                 svg = "book" | ||||
|         except models.Page.DoesNotExist: | ||||
|             page = None | ||||
|             title = _("Page not found") | ||||
|             svg_template = "tinywiki/icons/file-earmark-x.svg" | ||||
|             svg = "file-earmark-x" | ||||
|         url = reverse("tinywiki:page",kwargs={'slug':slug}) | ||||
|     else: | ||||
|         slug = None | ||||
|         title = _("Home") | ||||
|         url = reverse("tinywiki:home") | ||||
|         svg_template = "tinywiki/icons/house.svg" | ||||
|         svg = "house" | ||||
|          | ||||
|     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>" | ||||
|  | ||||
| def render_codeblock(tag_name:str,value,options,parent,context)->str: | ||||
|     if 'codeblock' in options: | ||||
|         return f"<pre><code class=\"language-{options['codeblock']}\">{value}</pre></code>" | ||||
|     return f"<pre><code>{value}</pre></code>" | ||||
|     if tag_name in options: | ||||
|         return f"<pre style=\"overflow-x:auto;\"><code class=\"language-{options[tag_name]}\">{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: | ||||
|     return f"<ol>{value}</ol>" | ||||
| @@ -112,7 +117,7 @@ def render_image(tag_name:str,value,options,parent,context): | ||||
|      | ||||
|     if 'width' in options: | ||||
|         _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};") | ||||
|         else:   | ||||
|             if _w.endswith('%'): | ||||
| @@ -129,16 +134,41 @@ def render_image(tag_name:str,value,options,parent,context): | ||||
|                     fig_classes.append(f'w-{width}') | ||||
|                 else: | ||||
|                     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: | ||||
|         pos = options['position'] | ||||
|         if settings.USE_BOOTSTRAP: | ||||
|             if pos == "left" or pos=="start": | ||||
|                 fig_classes += ["float-start","me-2"] | ||||
|                 classes += ["float-start","me-2"] | ||||
|             elif pos == "right" or pos == "end": | ||||
|                 fig_classes += ["float-end","ms-2"] | ||||
|                 classes += ["float-end","ms-2"] | ||||
|             elif pos == "center": | ||||
|                 fig_classes += ["mx-auto","d-block"] | ||||
|                 classes += ["mx-auto","d-block"] | ||||
|                  | ||||
|     if styles: | ||||
|         style=f"style=\"{"".join(styles)}\"" | ||||
| @@ -150,7 +180,7 @@ def render_image(tag_name:str,value,options,parent,context): | ||||
|     else: | ||||
|         fig_style="" | ||||
|     if settings.USE_BOOTSTRAP: | ||||
|         return f'<figure class="{" ".join(fig_classes)} {fig_style}"><img src="{options[tag_name]}" class="{' '.join(classes)}" alt="{alt}" {style}><figcaption class="figure-caption text-end">{value}</figcaption></figure>' | ||||
|         return f'<figure class="{" ".join(fig_classes)} {fig_style}"><img src="{options[tag_name]}" class="{' '.join(classes)}" alt="{alt}" {style}><figcaption class="figure-caption text-end">{ value }</figcaption></figure>' | ||||
|     else: | ||||
|         return f'<figure {fig_style}><img src="{options[tag_name]}" {style}><figcaption>{value}</figcaption></figure>' | ||||
|  | ||||
| @@ -176,7 +206,7 @@ def render_wiki_image(tag_name:str,value,options,parent,context): | ||||
|      | ||||
|     if 'width' in options: | ||||
|         _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};") | ||||
|         else:   | ||||
|             if _w.endswith('%'): | ||||
| @@ -193,7 +223,29 @@ def render_wiki_image(tag_name:str,value,options,parent,context): | ||||
|                     fig_classes.append(f'w-{width}') | ||||
|                 else: | ||||
|                     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: | ||||
|         pos = options['position'] | ||||
|         if settings.USE_BOOTSTRAP: | ||||
| @@ -215,7 +267,155 @@ def render_wiki_image(tag_name:str,value,options,parent,context): | ||||
|         fig_style="" | ||||
|          | ||||
|     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: | ||||
|         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> | ||||
|         <meta charset="utf-8"> | ||||
|         <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 scripts %}{% endblock %} | ||||
|     </head> | ||||
|   | ||||
| @@ -12,12 +12,15 @@ | ||||
| {% else %} | ||||
| <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  | ||||
|     slug <i>tw-home</i> 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 | ||||
|     Markdown read the <a href="#">Guide for Markdown used by TinyWiki</a>. Or if you want to use | ||||
|     BBCode there is a <a href="#">Guide for BBCode used by TinyWiki</a> too.{% endblocktranslate %} | ||||
|  | ||||
| <p style="text-align:justify">{% blocktranslate %}You are seeing this welcome page because there is no Welcome page | ||||
|     configured for your Wiki. To configure a welcome page {{ create_tw_home }} and put the content for your welcome-page there.{% endblocktranslate %}</p> | ||||
| <p style="text-align:justify">{% blocktranslate %}You can use BBCode or Markdown to write your pages. If you don't know | ||||
|     Markdown or BBCode there are two guides you can consult before you start editing your pages.{% endblocktranslate %} | ||||
|     <ol> | ||||
|         <li>{{ bbcode_guide }}</li> | ||||
|         <li>{{ markdown_guide }}</li> | ||||
|     </ol> | ||||
| </p> | ||||
| {% endif %} | ||||
| {% 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 %} | ||||
| {% load i18n %} | ||||
| {% load i18n static %} | ||||
|  | ||||
| {% 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 %} | ||||
| {% 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 %} | ||||
|  | ||||
| {% block extra_scripts %} | ||||
| @@ -18,9 +26,24 @@ | ||||
|     <a class="btn btn-primary" href="{% url 'tinywiki:page-edit' slug=page.slug %}">{% translate "Edit Page" %}</a> | ||||
|     {% endif %} | ||||
|     {% 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 %} | ||||
|  | ||||
| </div> | ||||
| <h1>{{ page.title }}</h1> | ||||
| {{ 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 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> | ||||
| <a class="button button-secondary" href="{% url 'tinywiki:home' %}">{% translate "Cancel" %}</a> | ||||
| {% endif %} | ||||
| {% 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 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> | ||||
| <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,77 +4,44 @@ | ||||
| {% 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 %} | ||||
|     {% 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> | ||||
|         {% 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 %} | ||||
|     </p> | ||||
|     <p> | ||||
|         {% with slug_extra|default:"" as slug_class %} | ||||
|         {{ form.slug.label_tag }}{% render_field form.slug class=slug_class %} | ||||
|          | ||||
|         {{ form.slug.label_tag }} | ||||
|         {% with slug_extra|default:"" as 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 %} | ||||
|     </p> | ||||
|     <p> | ||||
|         {% with content_type_extra|default:"" as content_type_class %} | ||||
|         {{ form.content_type_data.label_tag }}{% render_field form.content_type_data class=content_type_class %} | ||||
|         {{ form.content_type_data.label_tag }} {% render_field form.content_type_data class=content_type_class %} | ||||
|         {% endwith %} | ||||
|     </p> | ||||
|     <p> | ||||
|         {% with status_extra|default:"" as status_class %} | ||||
|         {{ form.status_data.label_tag }}{% render_field form.status_data class=status_class %} | ||||
|         {{ form.status_data.label_tag }} {% render_field form.status_data class=status_class %} | ||||
|         {% endwith %} | ||||
|     </p> | ||||
|     <p> | ||||
|         {% with content_extra|default:"" as content_class %} | ||||
|         {{ 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 %} | ||||
|     </p>     | ||||
|     {% endif %}  | ||||
|     </p> | ||||
|     <div class="text-end"> | ||||
|     {% block form_buttons %}{% endblock form_buttons %} | ||||
|     </div> | ||||
| </form> | ||||
| {% endblock content%} | ||||
|  | ||||
|   | ||||
| @@ -1,11 +1,13 @@ | ||||
| {% extends base_template %} | ||||
| {% load i18n %} | ||||
| {% load i18n static %} | ||||
|  | ||||
| {% block extra_css %} | ||||
| <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/styles/default.min.css"> | ||||
| {% endblock %} | ||||
| {% 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 %} | ||||
|  | ||||
| {% block extra_scripts %} | ||||
| @@ -17,8 +19,11 @@ | ||||
|     <a href="{% url 'tinywiki:page-edit' slug=page.slug %}">{% translate "Edit Page" %}</a> | ||||
| {% endif %} | ||||
| {% 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 %} | ||||
| <h1>{{ page.title }}</h1> | ||||
| {{ 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-create/",PageCreateView.as_view(),name="page-create"), | ||||
|     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: | ||||
|     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 | ||||
|     def get_base_template_name(cls)->str: | ||||
|         return cls.base_template_name | ||||
|   | ||||
| @@ -1,6 +1,9 @@ | ||||
| from curses.ascii import isalpha | ||||
| 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 .base import View | ||||
| from django.http import HttpRequest,HttpResponse | ||||
| @@ -18,12 +21,34 @@ class HomeView(View): | ||||
|     def get(self,request): | ||||
|         try: | ||||
|             page = Page.objects.get(slug='tw-home') | ||||
|             if (not Page.status == WikiPageStatus.PUBLISHED | ||||
|                 and not request.user.is_staff  | ||||
|                 and not request.user.has_perm('page.view-all')): | ||||
|                 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, | ||||
|                       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): | ||||
|     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.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.core.exceptions import PermissionDenied | ||||
|  | ||||
| from django.utils.translation import gettext as _ | ||||
|  | ||||
| from .. import settings | ||||
|  | ||||
| from ..models import Page | ||||
| from .base import View,FormView | ||||
| from ..forms import PageForm,PageAdminForm | ||||
| from ..forms import PageForm,PageAdminForm,PageDeleteForm | ||||
|  | ||||
| class PageView(View): | ||||
|     template_name = "tinywiki/page/view.html" | ||||
| @@ -49,13 +50,19 @@ class PageView(View): | ||||
|          | ||||
|      | ||||
|     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, | ||||
|                       self.get_template_name(), | ||||
|                       self.get_context_data(page=page)) | ||||
|          | ||||
| class PageCreateView(LoginRequiredMixin,UserPassesTestMixin,FormView): | ||||
|     template_name = "tinywiki/page/create.html" | ||||
|     bs_template_name = "tinywiki/page/bs-create.html" | ||||
|     form_class = PageForm | ||||
|      | ||||
|     def test_func(self) -> bool: | ||||
| @@ -65,14 +72,26 @@ class PageCreateView(LoginRequiredMixin,UserPassesTestMixin,FormView): | ||||
|             return self.request.user.has_perm('tinywiki.tinywiki-create') | ||||
|         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): | ||||
|         context = super().get_context_data(**kwargs) | ||||
|         context['create']=True | ||||
|         context['slug'] = self.request.GET.get('slug',None) | ||||
|         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): | ||||
| @@ -149,6 +168,7 @@ class PageCreateView(LoginRequiredMixin,UserPassesTestMixin,FormView): | ||||
|  | ||||
| class PageEditView(LoginRequiredMixin,UserPassesTestMixin,FormView): | ||||
|     template_name = "tinywiki/page/edit.html" | ||||
|     bs_template_name = "tinywiki/page/bs-edit.html" | ||||
|     form_class = PageForm | ||||
|      | ||||
|     def test_func(self) -> bool: | ||||
| @@ -156,9 +176,16 @@ class PageEditView(LoginRequiredMixin,UserPassesTestMixin,FormView): | ||||
|             if self.request.user.is_staff: | ||||
|                 return True | ||||
|         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['create']=False | ||||
|         context['page']=page | ||||
|         context.setdefault("title_extra","") | ||||
|         context.setdefault("slug_extra","") | ||||
|         context.setdefault("content_type_extra","") | ||||
| @@ -195,6 +222,7 @@ class PageEditView(LoginRequiredMixin,UserPassesTestMixin,FormView): | ||||
|         return render(self.request, | ||||
|                       self.get_template_name(), | ||||
|                       self.get_context_data( | ||||
|                           page=self.instance, | ||||
|                           slug_extra=slug_extra, | ||||
|                           title_extra=title_extra, | ||||
|                           status_extra=status_extra, | ||||
| @@ -218,7 +246,10 @@ class PageEditView(LoginRequiredMixin,UserPassesTestMixin,FormView): | ||||
|                 raise PermissionDenied() | ||||
|              | ||||
|         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): | ||||
|         instance = get_object_or_404(Page,slug=slug) | ||||
| @@ -255,7 +286,138 @@ class PageEditView(LoginRequiredMixin,UserPassesTestMixin,FormView): | ||||
|                           self.get_template_name(), | ||||
|                           self.get_context_data(slug_invalid=True)) | ||||
|              | ||||
| class PageDeleteView(View): | ||||
|     template_name = "tinywiki/page/delete" | ||||
|     def get(self,request,slug): | ||||
|         return render() | ||||
| class PageDeleteView(LoginRequiredMixin,UserPassesTestMixin,FormView): | ||||
|     template_name = "tinywiki/page/delete.html" | ||||
|     form_class = PageDeleteForm | ||||
|     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