mirror of
				https://github.com/ae-utbm/sith.git
				synced 2025-11-04 02:53:06 +00:00 
			
		
		
		
	
							
								
								
									
										1
									
								
								core/static/webpack/htmx-index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								core/static/webpack/htmx-index.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					window.htmx = require("htmx.org");
 | 
				
			||||||
@@ -5,7 +5,6 @@
 | 
				
			|||||||
      <title>{% block title %}{% trans %}Welcome!{% endtrans %}{% endblock %} - Association des Étudiants UTBM</title>
 | 
					      <title>{% block title %}{% trans %}Welcome!{% endtrans %}{% endblock %} - Association des Étudiants UTBM</title>
 | 
				
			||||||
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
					      <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
				
			||||||
      <link rel="shortcut icon" href="{{ static('core/img/favicon.ico') }}">
 | 
					      <link rel="shortcut icon" href="{{ static('core/img/favicon.ico') }}">
 | 
				
			||||||
      <link rel="stylesheet" href="{{ static('user/user_stats.scss') }}">
 | 
					 | 
				
			||||||
      <link rel="stylesheet" href="{{ static('core/base.css') }}">
 | 
					      <link rel="stylesheet" href="{{ static('core/base.css') }}">
 | 
				
			||||||
      <link rel="stylesheet" href="{{ static('core/style.scss') }}">
 | 
					      <link rel="stylesheet" href="{{ static('core/style.scss') }}">
 | 
				
			||||||
      <link rel="stylesheet" href="{{ static('core/markdown.scss') }}">
 | 
					      <link rel="stylesheet" href="{{ static('core/markdown.scss') }}">
 | 
				
			||||||
@@ -14,7 +13,7 @@
 | 
				
			|||||||
      <link rel="stylesheet" href="{{ static('core/pagination.scss') }}">
 | 
					      <link rel="stylesheet" href="{{ static('core/pagination.scss') }}">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      {% block jquery_css %}
 | 
					      {% block jquery_css %}
 | 
				
			||||||
                {# Thile file is quite heavy (around 250kb), so declaring it in a block allows easy removal #}
 | 
					        {# Thile file is quite heavy (around 250kb), so declaring it in a block allows easy removal #}
 | 
				
			||||||
        <link rel="stylesheet" href="{{ static('webpack/jquery-index.css') }}">
 | 
					        <link rel="stylesheet" href="{{ static('webpack/jquery-index.css') }}">
 | 
				
			||||||
      {% endblock %}
 | 
					      {% endblock %}
 | 
				
			||||||
      <link rel="preload" as="style" href="{{ static('webpack/fontawesome-index.css') }}" onload="this.onload=null;this.rel='stylesheet'">
 | 
					      <link rel="preload" as="style" href="{{ static('webpack/fontawesome-index.css') }}" onload="this.onload=null;this.rel='stylesheet'">
 | 
				
			||||||
@@ -23,6 +22,7 @@
 | 
				
			|||||||
      <script src="{{ url('javascript-catalog') }}"></script>
 | 
					      <script src="{{ url('javascript-catalog') }}"></script>
 | 
				
			||||||
      <script src={{ static("webpack/core/components/include-index.ts") }}></script>
 | 
					      <script src={{ static("webpack/core/components/include-index.ts") }}></script>
 | 
				
			||||||
      <script src="{{ static('webpack/alpine-index.js') }}" defer></script>
 | 
					      <script src="{{ static('webpack/alpine-index.js') }}" defer></script>
 | 
				
			||||||
 | 
					      <script src="{{ static('webpack/htmx-index.js') }}" defer></script>
 | 
				
			||||||
            <!-- Jquery declared here to be accessible in every django widgets -->
 | 
					            <!-- Jquery declared here to be accessible in every django widgets -->
 | 
				
			||||||
      <script src="{{ static('webpack/jquery-index.js') }}"></script>
 | 
					      <script src="{{ static('webpack/jquery-index.js') }}"></script>
 | 
				
			||||||
            <!-- Put here to always have access to those functions on django widgets -->
 | 
					            <!-- Put here to always have access to those functions on django widgets -->
 | 
				
			||||||
@@ -37,148 +37,13 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  <body>
 | 
					  <body>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <!-- The token is always passed here to be accessible from the dom -->
 | 
					    <!-- The token is always passed here to be accessible from the dom -->
 | 
				
			||||||
        <!-- See this workaround https://docs.djangoproject.com/en/2.0/ref/csrf/#acquiring-the-token-if-csrf-use-sessions-is-true -->
 | 
					    <!-- See this workaround https://docs.djangoproject.com/en/2.0/ref/csrf/#acquiring-the-token-if-csrf-use-sessions-is-true -->
 | 
				
			||||||
    {% csrf_token %}
 | 
					    {% csrf_token %}
 | 
				
			||||||
        <!-- BEGIN HEADER -->
 | 
					
 | 
				
			||||||
    {% block header %}
 | 
					    {% block header %}
 | 
				
			||||||
      {% if not popup %}
 | 
					      {% if not popup %}
 | 
				
			||||||
        <header class="header">
 | 
					        {% include "core/base/header.jinja" %}
 | 
				
			||||||
          <div class="header-logo">
 | 
					 | 
				
			||||||
            <a class="header-logo-picture" href="{{ url('core:index') }}" style="background-image: url('{{ static('core/img/logo_no_text.png') }}')">
 | 
					 | 
				
			||||||
               
 | 
					 | 
				
			||||||
            </a>
 | 
					 | 
				
			||||||
            <a class="header-logo-text" href="{{ url('core:index') }}">
 | 
					 | 
				
			||||||
              <span>Association des Étudiants</span>
 | 
					 | 
				
			||||||
              <span>de l'Université de Technologie de Belfort-Montbéliard</span>
 | 
					 | 
				
			||||||
            </a>
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
          {% if not user.is_authenticated %}
 | 
					 | 
				
			||||||
            <div class="header-disconnected">
 | 
					 | 
				
			||||||
              <a class="button" href="{{ url('core:login') }}">{% trans %}Login{% endtrans %}</a>
 | 
					 | 
				
			||||||
              <a class="button" href="{{ url('core:register') }}">{% trans %}Register{% endtrans %}</a>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
          {% else %}
 | 
					 | 
				
			||||||
            <div class="header-connected">
 | 
					 | 
				
			||||||
              <div class="left">
 | 
					 | 
				
			||||||
                <form class="search" action="{{ url('core:search') }}" method="GET" id="header_search">
 | 
					 | 
				
			||||||
                  <input class="header-input" type="text" placeholder="{% trans %}Search{% endtrans %}" name="query" id="search" />
 | 
					 | 
				
			||||||
                  <input type="submit" value="{% trans %}Search{% endtrans %}" style="display: none;" />
 | 
					 | 
				
			||||||
                </form>
 | 
					 | 
				
			||||||
                <ul class="bars">
 | 
					 | 
				
			||||||
                  {% cache 100 "counters_activity" %}
 | 
					 | 
				
			||||||
                      {# The sith has no periodic tasks manager
 | 
					 | 
				
			||||||
                         and using cron jobs would be way too overkill here.
 | 
					 | 
				
			||||||
                         Thus the barmen timeout is handled in the only place that
 | 
					 | 
				
			||||||
                         is loaded on every page : the header bar.
 | 
					 | 
				
			||||||
                         However, let's be clear : this has nothing to do here.
 | 
					 | 
				
			||||||
                         It's' merely a contrived workaround that should
 | 
					 | 
				
			||||||
                         replaced by a proper task manager as soon as possible. #}
 | 
					 | 
				
			||||||
                    {% set _ = Counter.objects.filter(type="BAR").handle_timeout() %}
 | 
					 | 
				
			||||||
                  {% endcache %}
 | 
					 | 
				
			||||||
                  {% for bar in Counter.objects.annotate_has_barman(user).annotate_is_open().filter(type="BAR") %}
 | 
					 | 
				
			||||||
                    <li>
 | 
					 | 
				
			||||||
                      {# If the user is a barman, we redirect him directly to the barman page
 | 
					 | 
				
			||||||
                      else we redirect him to the activity page #}
 | 
					 | 
				
			||||||
                      {% if bar.has_annotated_barman %}
 | 
					 | 
				
			||||||
                        <a href="{{ url('counter:details', counter_id=bar.id) }}">
 | 
					 | 
				
			||||||
                      {% else %}
 | 
					 | 
				
			||||||
                        <a href="{{ url('counter:activity', counter_id=bar.id) }}">
 | 
					 | 
				
			||||||
                      {% endif %}
 | 
					 | 
				
			||||||
                      {% if bar.is_open %}
 | 
					 | 
				
			||||||
                        <i class="fa fa-check" style="color: #2ecc71"></i>
 | 
					 | 
				
			||||||
                      {% else %}
 | 
					 | 
				
			||||||
                        <i class="fa fa-times" style="color: #eb2f06"></i>
 | 
					 | 
				
			||||||
                      {% endif %}
 | 
					 | 
				
			||||||
                      <span>{{ bar }}</span>
 | 
					 | 
				
			||||||
                    </a>
 | 
					 | 
				
			||||||
                    </li>
 | 
					 | 
				
			||||||
                  {% endfor %}
 | 
					 | 
				
			||||||
                </ul>
 | 
					 | 
				
			||||||
              </div>
 | 
					 | 
				
			||||||
              <div class="right">
 | 
					 | 
				
			||||||
                <div class="user">
 | 
					 | 
				
			||||||
                  <div class="options">
 | 
					 | 
				
			||||||
                    <div class="username">
 | 
					 | 
				
			||||||
                      <a href="{{ url('core:user_profile', user_id=user.id) }}">{{ user.get_display_name() }}</a>
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                    <div class="links">
 | 
					 | 
				
			||||||
                      <a href="{{ url('core:user_tools') }}">{% trans %}Tools{% endtrans %}</a>
 | 
					 | 
				
			||||||
                      <a href="{{ url('core:logout') }}">{% trans %}Logout{% endtrans %}</a>
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                  </div>
 | 
					 | 
				
			||||||
                  <a
 | 
					 | 
				
			||||||
                    href="{{ url('core:user_profile', user_id=user.id) }}"
 | 
					 | 
				
			||||||
                    {% if user.profile_pict %}
 | 
					 | 
				
			||||||
                      style="background-image: url('{{ user.profile_pict.get_download_url() }}')"
 | 
					 | 
				
			||||||
                    {% else %}
 | 
					 | 
				
			||||||
                      style="background-image: url('{{ static('core/img/unknown.jpg') }}')"
 | 
					 | 
				
			||||||
                    {% endif %}
 | 
					 | 
				
			||||||
                  ></a>
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
                <div class="notification">
 | 
					 | 
				
			||||||
                  <a href="#" onclick="displayNotif()">
 | 
					 | 
				
			||||||
                    <i class="fa-regular fa-bell"></i>
 | 
					 | 
				
			||||||
                    {% set notification_count = user.notifications.filter(viewed=False).count() %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    {% if notification_count > 0 %}
 | 
					 | 
				
			||||||
                      <span>
 | 
					 | 
				
			||||||
                        {% if notification_count < 100 %}
 | 
					 | 
				
			||||||
                          {{ notification_count }}
 | 
					 | 
				
			||||||
                        {% else %}
 | 
					 | 
				
			||||||
                           
 | 
					 | 
				
			||||||
                        {% endif %}
 | 
					 | 
				
			||||||
                      </span>
 | 
					 | 
				
			||||||
                    {% endif %}
 | 
					 | 
				
			||||||
                  </a>
 | 
					 | 
				
			||||||
                  <div id="header_notif">
 | 
					 | 
				
			||||||
                    <ul>
 | 
					 | 
				
			||||||
                      {% if user.notifications.filter(viewed=False).count() > 0 %}
 | 
					 | 
				
			||||||
                        {% for n in user.notifications.filter(viewed=False).order_by('-date') %}
 | 
					 | 
				
			||||||
                          <li>
 | 
					 | 
				
			||||||
                            <a href="{{ url("core:notification", notif_id=n.id) }}">
 | 
					 | 
				
			||||||
                              <div class="datetime">
 | 
					 | 
				
			||||||
                                <span class="header_notif_date">
 | 
					 | 
				
			||||||
                                  {{ n.date|localtime|date(DATE_FORMAT) }}
 | 
					 | 
				
			||||||
                                </span>
 | 
					 | 
				
			||||||
                                <span class="header_notif_time">
 | 
					 | 
				
			||||||
                                  {{ n.date|localtime|time(DATETIME_FORMAT) }}
 | 
					 | 
				
			||||||
                                </span>
 | 
					 | 
				
			||||||
                              </div>
 | 
					 | 
				
			||||||
                              <div class="reason">
 | 
					 | 
				
			||||||
                                {{ n }}
 | 
					 | 
				
			||||||
                              </div>
 | 
					 | 
				
			||||||
                            </a>
 | 
					 | 
				
			||||||
                          </li>
 | 
					 | 
				
			||||||
                        {% endfor %}
 | 
					 | 
				
			||||||
                      {% else %}
 | 
					 | 
				
			||||||
                        <li class="empty-notification">{% trans %}You do not have any unread notification{% endtrans %}</li>
 | 
					 | 
				
			||||||
                      {% endif %}
 | 
					 | 
				
			||||||
                    </ul>
 | 
					 | 
				
			||||||
                    <div class="options">
 | 
					 | 
				
			||||||
                      <a href="{{ url('core:notification_list') }}">
 | 
					 | 
				
			||||||
                        {% trans %}View more{% endtrans %}
 | 
					 | 
				
			||||||
                      </a>
 | 
					 | 
				
			||||||
                      <a href="{{ url('core:notification_list') }}?see_all">
 | 
					 | 
				
			||||||
                        {% trans %}Mark all as read{% endtrans %}
 | 
					 | 
				
			||||||
                      </a>
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                  </div>
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
              </div>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
          {% endif %}
 | 
					 | 
				
			||||||
          <div class="header-lang">
 | 
					 | 
				
			||||||
            {% for language in LANGUAGES %}
 | 
					 | 
				
			||||||
              <form action="{{ url('set_language') }}" method="post">
 | 
					 | 
				
			||||||
                {% csrf_token %}
 | 
					 | 
				
			||||||
                <input name="next" value="{{ request.path }}" type="hidden" />
 | 
					 | 
				
			||||||
                <input name="language" value="{{ language[0] }}" type="hidden" />
 | 
					 | 
				
			||||||
                <input type="submit" value="{% if language[0] == 'en' %}🇬🇧{% else %}🇫🇷{% endif %}" />
 | 
					 | 
				
			||||||
              </form>
 | 
					 | 
				
			||||||
            {% endfor %}
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
        </header>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        {% block info_boxes %}
 | 
					        {% block info_boxes %}
 | 
				
			||||||
          <div id="info_boxes">
 | 
					          <div id="info_boxes">
 | 
				
			||||||
@@ -201,58 +66,10 @@
 | 
				
			|||||||
      {% endif %}
 | 
					      {% endif %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    {% endblock %}
 | 
					    {% endblock %}
 | 
				
			||||||
        <!-- END HEADER -->
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    {% block nav %}
 | 
					    {% block nav %}
 | 
				
			||||||
      {% if not popup %}
 | 
					      {% if not popup %}
 | 
				
			||||||
        <nav class="navbar">
 | 
					        {% include "core/base/navbar.jinja" %}
 | 
				
			||||||
          <button class="expand-button" onclick="showMenu()"><i class="fa fa-bars"></i></button>
 | 
					 | 
				
			||||||
          <div id="navbar-content" class="content" style="display: none;">
 | 
					 | 
				
			||||||
            <a class="link" href="{{ url('core:index') }}">{% trans %}Main{% endtrans %}</a>
 | 
					 | 
				
			||||||
            <div class="menu">
 | 
					 | 
				
			||||||
              <span class="head">{% trans %}Associations & Clubs{% endtrans %}</span>
 | 
					 | 
				
			||||||
              <ul class="content">
 | 
					 | 
				
			||||||
                <li><a href="{{ url('core:page', page_name='ae') }}">{% trans %}AE{% endtrans %}</a></li>
 | 
					 | 
				
			||||||
                <li><a href="{{ url('core:page', page_name='clubs') }}">{% trans %}AE's clubs{% endtrans %}</a></li>
 | 
					 | 
				
			||||||
                <li><a href="{{ url('core:page', page_name='utbm-associations') }}">{% trans %}Others UTBM's Associations{% endtrans %}</a></li>
 | 
					 | 
				
			||||||
              </ul>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
            <div class="menu">
 | 
					 | 
				
			||||||
              <span class="head">{% trans %}Events{% endtrans %}</span>
 | 
					 | 
				
			||||||
              <ul class="content">
 | 
					 | 
				
			||||||
                <li><a href="{{ url('election:list') }}">{% trans %}Elections{% endtrans %}</a></li>
 | 
					 | 
				
			||||||
                <li><a href="{{ url('core:page', page_name='ga') }}">{% trans %}Big event{% endtrans %}</a></li>
 | 
					 | 
				
			||||||
              </ul>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
            <a class="link" href="{{ url('forum:main') }}">{% trans %}Forum{% endtrans %}</a>
 | 
					 | 
				
			||||||
            <a class="link" href="{{ url('sas:main') }}">{% trans %}Gallery{% endtrans %}</a>
 | 
					 | 
				
			||||||
            <a class="link" href="{{ url('eboutic:main') }}">{% trans %}Eboutic{% endtrans %}</a>
 | 
					 | 
				
			||||||
            <div class="menu">
 | 
					 | 
				
			||||||
              <span class="head">{% trans %}Services{% endtrans %}</span>
 | 
					 | 
				
			||||||
              <ul class="content">
 | 
					 | 
				
			||||||
                <li><a href="{{ url('matmat:search_clear') }}">{% trans %}Matmatronch{% endtrans %}</a></li>
 | 
					 | 
				
			||||||
                <li><a href="/launderette">{% trans %}Launderette{% endtrans %}</a></li>
 | 
					 | 
				
			||||||
                <li><a href="{{ url('core:file_list') }}">{% trans %}Files{% endtrans %}</a></li>
 | 
					 | 
				
			||||||
                <li><a href="{{ url('pedagogy:guide') }}">{% trans %}Pedagogy{% endtrans %}</a></li>
 | 
					 | 
				
			||||||
              </ul>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
            <div class="menu">
 | 
					 | 
				
			||||||
              <span class="head">{% trans %}My Benefits{% endtrans %}</span>
 | 
					 | 
				
			||||||
              <ul class="content">
 | 
					 | 
				
			||||||
                <li><a href="{{ url('core:page', page_name='partenaires')}}">{% trans %}Sponsors{% endtrans %}</a></li>
 | 
					 | 
				
			||||||
                <li><a href="{{ url('core:page', page_name='avantages') }}">{% trans %}Subscriber benefits{% endtrans %}</a></li>
 | 
					 | 
				
			||||||
              </ul>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
            <div class="menu">
 | 
					 | 
				
			||||||
              <span class="head">{% trans %}Help{% endtrans %}</span>
 | 
					 | 
				
			||||||
              <ul class="content">
 | 
					 | 
				
			||||||
                <li><a href="{{ url('core:page', page_name='FAQ') }}">{% trans %}FAQ{% endtrans %}</a></li>
 | 
					 | 
				
			||||||
                <li><a href="{{ url('core:page', 'contacts') }}">{% trans %}Contacts{% endtrans %}</a></li>
 | 
					 | 
				
			||||||
                <li><a href="{{ url('core:page', page_name='Index') }}">{% trans %}Wiki{% endtrans %}</a></li>
 | 
					 | 
				
			||||||
              </ul>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
        </nav>
 | 
					 | 
				
			||||||
      {% endif %}
 | 
					      {% endif %}
 | 
				
			||||||
    {% endblock %}
 | 
					    {% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -265,19 +82,16 @@
 | 
				
			|||||||
      </ul>
 | 
					      </ul>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <div id="content">
 | 
					      <div id="content">
 | 
				
			||||||
        {% if list_of_tabs %}
 | 
					        {% block tabs %}
 | 
				
			||||||
          <div class="tool_bar">
 | 
					          {% include "core/base/tabs.jinja" %}
 | 
				
			||||||
            <div class="tools">
 | 
					        {% endblock %}
 | 
				
			||||||
              {% for t in list_of_tabs -%}
 | 
					
 | 
				
			||||||
                <a href="{{ t.url }}" {%- if current_tab==t.slug %} class="selected_tab" {%- endif -%}>{{ t.name }}</a>
 | 
					        {% block errors%}
 | 
				
			||||||
              {%- endfor %}
 | 
					          {% if error %}
 | 
				
			||||||
            </div>
 | 
					            {{ error }}
 | 
				
			||||||
          </div>
 | 
					          {% endif %}
 | 
				
			||||||
        {% endif %}
 | 
					        {% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        {% if error %}
 | 
					 | 
				
			||||||
          {{ error }}
 | 
					 | 
				
			||||||
        {% endif %}
 | 
					 | 
				
			||||||
        {% block content %}
 | 
					        {% block content %}
 | 
				
			||||||
        {% endblock %}
 | 
					        {% endblock %}
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										136
									
								
								core/templates/core/base/header.jinja
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								core/templates/core/base/header.jinja
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,136 @@
 | 
				
			|||||||
 | 
					<header class="header">
 | 
				
			||||||
 | 
					  <div class="header-logo">
 | 
				
			||||||
 | 
					    <a class="header-logo-picture" href="{{ url('core:index') }}" style="background-image: url('{{ static('core/img/logo_no_text.png') }}')">
 | 
				
			||||||
 | 
					       
 | 
				
			||||||
 | 
					    </a>
 | 
				
			||||||
 | 
					    <a class="header-logo-text" href="{{ url('core:index') }}">
 | 
				
			||||||
 | 
					      <span>Association des Étudiants</span>
 | 
				
			||||||
 | 
					      <span>de l'Université de Technologie de Belfort-Montbéliard</span>
 | 
				
			||||||
 | 
					    </a>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					  {% if not user.is_authenticated %}
 | 
				
			||||||
 | 
					    <div class="header-disconnected">
 | 
				
			||||||
 | 
					      <a class="button" href="{{ url('core:login') }}">{% trans %}Login{% endtrans %}</a>
 | 
				
			||||||
 | 
					      <a class="button" href="{{ url('core:register') }}">{% trans %}Register{% endtrans %}</a>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  {% else %}
 | 
				
			||||||
 | 
					    <div class="header-connected">
 | 
				
			||||||
 | 
					      <div class="left">
 | 
				
			||||||
 | 
					        <form class="search" action="{{ url('core:search') }}" method="GET" id="header_search">
 | 
				
			||||||
 | 
					          <input class="header-input" type="text" placeholder="{% trans %}Search{% endtrans %}" name="query" id="search" />
 | 
				
			||||||
 | 
					          <input type="submit" value="{% trans %}Search{% endtrans %}" style="display: none;" />
 | 
				
			||||||
 | 
					        </form>
 | 
				
			||||||
 | 
					        <ul class="bars">
 | 
				
			||||||
 | 
					          {% cache 100 "counters_activity" %}
 | 
				
			||||||
 | 
					                      {# The sith has no periodic tasks manager
 | 
				
			||||||
 | 
					                         and using cron jobs would be way too overkill here.
 | 
				
			||||||
 | 
					                         Thus the barmen timeout is handled in the only place that
 | 
				
			||||||
 | 
					                         is loaded on every page : the header bar.
 | 
				
			||||||
 | 
					                         However, let's be clear : this has nothing to do here.
 | 
				
			||||||
 | 
					                         It's' merely a contrived workaround that should
 | 
				
			||||||
 | 
					                         replaced by a proper task manager as soon as possible. #}
 | 
				
			||||||
 | 
					            {% set _ = Counter.objects.filter(type="BAR").handle_timeout() %}
 | 
				
			||||||
 | 
					          {% endcache %}
 | 
				
			||||||
 | 
					          {% for bar in Counter.objects.annotate_has_barman(user).annotate_is_open().filter(type="BAR") %}
 | 
				
			||||||
 | 
					            <li>
 | 
				
			||||||
 | 
					                      {# If the user is a barman, we redirect him directly to the barman page
 | 
				
			||||||
 | 
					                      else we redirect him to the activity page #}
 | 
				
			||||||
 | 
					              {% if bar.has_annotated_barman %}
 | 
				
			||||||
 | 
					                <a href="{{ url('counter:details', counter_id=bar.id) }}">
 | 
				
			||||||
 | 
					              {% else %}
 | 
				
			||||||
 | 
					                <a href="{{ url('counter:activity', counter_id=bar.id) }}">
 | 
				
			||||||
 | 
					              {% endif %}
 | 
				
			||||||
 | 
					              {% if bar.is_open %}
 | 
				
			||||||
 | 
					                <i class="fa fa-check" style="color: #2ecc71"></i>
 | 
				
			||||||
 | 
					              {% else %}
 | 
				
			||||||
 | 
					                <i class="fa fa-times" style="color: #eb2f06"></i>
 | 
				
			||||||
 | 
					              {% endif %}
 | 
				
			||||||
 | 
					              <span>{{ bar }}</span>
 | 
				
			||||||
 | 
					            </a>
 | 
				
			||||||
 | 
					            </li>
 | 
				
			||||||
 | 
					          {% endfor %}
 | 
				
			||||||
 | 
					        </ul>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div class="right">
 | 
				
			||||||
 | 
					        <div class="user">
 | 
				
			||||||
 | 
					          <div class="options">
 | 
				
			||||||
 | 
					            <div class="username">
 | 
				
			||||||
 | 
					              <a href="{{ url('core:user_profile', user_id=user.id) }}">{{ user.get_display_name() }}</a>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="links">
 | 
				
			||||||
 | 
					              <a href="{{ url('core:user_tools') }}">{% trans %}Tools{% endtrans %}</a>
 | 
				
			||||||
 | 
					              <a href="{{ url('core:logout') }}">{% trans %}Logout{% endtrans %}</a>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <a
 | 
				
			||||||
 | 
					            href="{{ url('core:user_profile', user_id=user.id) }}"
 | 
				
			||||||
 | 
					            {% if user.profile_pict %}
 | 
				
			||||||
 | 
					              style="background-image: url('{{ user.profile_pict.get_download_url() }}')"
 | 
				
			||||||
 | 
					            {% else %}
 | 
				
			||||||
 | 
					              style="background-image: url('{{ static('core/img/unknown.jpg') }}')"
 | 
				
			||||||
 | 
					            {% endif %}
 | 
				
			||||||
 | 
					          ></a>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="notification">
 | 
				
			||||||
 | 
					          <a href="#" onclick="displayNotif()">
 | 
				
			||||||
 | 
					            <i class="fa-regular fa-bell"></i>
 | 
				
			||||||
 | 
					            {% set notification_count = user.notifications.filter(viewed=False).count() %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            {% if notification_count > 0 %}
 | 
				
			||||||
 | 
					              <span>
 | 
				
			||||||
 | 
					                {% if notification_count < 100 %}
 | 
				
			||||||
 | 
					                  {{ notification_count }}
 | 
				
			||||||
 | 
					                {% else %}
 | 
				
			||||||
 | 
					                   
 | 
				
			||||||
 | 
					                {% endif %}
 | 
				
			||||||
 | 
					              </span>
 | 
				
			||||||
 | 
					            {% endif %}
 | 
				
			||||||
 | 
					          </a>
 | 
				
			||||||
 | 
					          <div id="header_notif">
 | 
				
			||||||
 | 
					            <ul>
 | 
				
			||||||
 | 
					              {% if user.notifications.filter(viewed=False).count() > 0 %}
 | 
				
			||||||
 | 
					                {% for n in user.notifications.filter(viewed=False).order_by('-date') %}
 | 
				
			||||||
 | 
					                  <li>
 | 
				
			||||||
 | 
					                    <a href="{{ url("core:notification", notif_id=n.id) }}">
 | 
				
			||||||
 | 
					                      <div class="datetime">
 | 
				
			||||||
 | 
					                        <span class="header_notif_date">
 | 
				
			||||||
 | 
					                          {{ n.date|localtime|date(DATE_FORMAT) }}
 | 
				
			||||||
 | 
					                        </span>
 | 
				
			||||||
 | 
					                        <span class="header_notif_time">
 | 
				
			||||||
 | 
					                          {{ n.date|localtime|time(DATETIME_FORMAT) }}
 | 
				
			||||||
 | 
					                        </span>
 | 
				
			||||||
 | 
					                      </div>
 | 
				
			||||||
 | 
					                      <div class="reason">
 | 
				
			||||||
 | 
					                        {{ n }}
 | 
				
			||||||
 | 
					                      </div>
 | 
				
			||||||
 | 
					                    </a>
 | 
				
			||||||
 | 
					                  </li>
 | 
				
			||||||
 | 
					                {% endfor %}
 | 
				
			||||||
 | 
					              {% else %}
 | 
				
			||||||
 | 
					                <li class="empty-notification">{% trans %}You do not have any unread notification{% endtrans %}</li>
 | 
				
			||||||
 | 
					              {% endif %}
 | 
				
			||||||
 | 
					            </ul>
 | 
				
			||||||
 | 
					            <div class="options">
 | 
				
			||||||
 | 
					              <a href="{{ url('core:notification_list') }}">
 | 
				
			||||||
 | 
					                {% trans %}View more{% endtrans %}
 | 
				
			||||||
 | 
					              </a>
 | 
				
			||||||
 | 
					              <a href="{{ url('core:notification_list') }}?see_all">
 | 
				
			||||||
 | 
					                {% trans %}Mark all as read{% endtrans %}
 | 
				
			||||||
 | 
					              </a>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  {% endif %}
 | 
				
			||||||
 | 
					  <div class="header-lang">
 | 
				
			||||||
 | 
					    {% for language in LANGUAGES %}
 | 
				
			||||||
 | 
					      <form action="{{ url('set_language') }}" method="post">
 | 
				
			||||||
 | 
					        {% csrf_token %}
 | 
				
			||||||
 | 
					        <input name="next" value="{{ request.path }}" type="hidden" />
 | 
				
			||||||
 | 
					        <input name="language" value="{{ language[0] }}" type="hidden" />
 | 
				
			||||||
 | 
					        <input type="submit" value="{% if language[0] == 'en' %}🇬🇧{% else %}🇫🇷{% endif %}" />
 | 
				
			||||||
 | 
					      </form>
 | 
				
			||||||
 | 
					    {% endfor %}
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</header>
 | 
				
			||||||
							
								
								
									
										48
									
								
								core/templates/core/base/navbar.jinja
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								core/templates/core/base/navbar.jinja
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
				
			|||||||
 | 
					<nav class="navbar">
 | 
				
			||||||
 | 
					  <button class="expand-button" onclick="showMenu()"><i class="fa fa-bars"></i></button>
 | 
				
			||||||
 | 
					  <div id="navbar-content" class="content" style="display: none;">
 | 
				
			||||||
 | 
					    <a class="link" href="{{ url('core:index') }}">{% trans %}Main{% endtrans %}</a>
 | 
				
			||||||
 | 
					    <div class="menu">
 | 
				
			||||||
 | 
					      <span class="head">{% trans %}Associations & Clubs{% endtrans %}</span>
 | 
				
			||||||
 | 
					      <ul class="content">
 | 
				
			||||||
 | 
					        <li><a href="{{ url('core:page', page_name='ae') }}">{% trans %}AE{% endtrans %}</a></li>
 | 
				
			||||||
 | 
					        <li><a href="{{ url('core:page', page_name='clubs') }}">{% trans %}AE's clubs{% endtrans %}</a></li>
 | 
				
			||||||
 | 
					        <li><a href="{{ url('core:page', page_name='utbm-associations') }}">{% trans %}Others UTBM's Associations{% endtrans %}</a></li>
 | 
				
			||||||
 | 
					      </ul>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div class="menu">
 | 
				
			||||||
 | 
					      <span class="head">{% trans %}Events{% endtrans %}</span>
 | 
				
			||||||
 | 
					      <ul class="content">
 | 
				
			||||||
 | 
					        <li><a href="{{ url('election:list') }}">{% trans %}Elections{% endtrans %}</a></li>
 | 
				
			||||||
 | 
					        <li><a href="{{ url('core:page', page_name='ga') }}">{% trans %}Big event{% endtrans %}</a></li>
 | 
				
			||||||
 | 
					      </ul>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <a class="link" href="{{ url('forum:main') }}">{% trans %}Forum{% endtrans %}</a>
 | 
				
			||||||
 | 
					    <a class="link" href="{{ url('sas:main') }}">{% trans %}Gallery{% endtrans %}</a>
 | 
				
			||||||
 | 
					    <a class="link" href="{{ url('eboutic:main') }}">{% trans %}Eboutic{% endtrans %}</a>
 | 
				
			||||||
 | 
					    <div class="menu">
 | 
				
			||||||
 | 
					      <span class="head">{% trans %}Services{% endtrans %}</span>
 | 
				
			||||||
 | 
					      <ul class="content">
 | 
				
			||||||
 | 
					        <li><a href="{{ url('matmat:search_clear') }}">{% trans %}Matmatronch{% endtrans %}</a></li>
 | 
				
			||||||
 | 
					        <li><a href="/launderette">{% trans %}Launderette{% endtrans %}</a></li>
 | 
				
			||||||
 | 
					        <li><a href="{{ url('core:file_list') }}">{% trans %}Files{% endtrans %}</a></li>
 | 
				
			||||||
 | 
					        <li><a href="{{ url('pedagogy:guide') }}">{% trans %}Pedagogy{% endtrans %}</a></li>
 | 
				
			||||||
 | 
					      </ul>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div class="menu">
 | 
				
			||||||
 | 
					      <span class="head">{% trans %}My Benefits{% endtrans %}</span>
 | 
				
			||||||
 | 
					      <ul class="content">
 | 
				
			||||||
 | 
					        <li><a href="{{ url('core:page', page_name='partenaires')}}">{% trans %}Sponsors{% endtrans %}</a></li>
 | 
				
			||||||
 | 
					        <li><a href="{{ url('core:page', page_name='avantages') }}">{% trans %}Subscriber benefits{% endtrans %}</a></li>
 | 
				
			||||||
 | 
					      </ul>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div class="menu">
 | 
				
			||||||
 | 
					      <span class="head">{% trans %}Help{% endtrans %}</span>
 | 
				
			||||||
 | 
					      <ul class="content">
 | 
				
			||||||
 | 
					        <li><a href="{{ url('core:page', page_name='FAQ') }}">{% trans %}FAQ{% endtrans %}</a></li>
 | 
				
			||||||
 | 
					        <li><a href="{{ url('core:page', 'contacts') }}">{% trans %}Contacts{% endtrans %}</a></li>
 | 
				
			||||||
 | 
					        <li><a href="{{ url('core:page', page_name='Index') }}">{% trans %}Wiki{% endtrans %}</a></li>
 | 
				
			||||||
 | 
					      </ul>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</nav>
 | 
				
			||||||
							
								
								
									
										9
									
								
								core/templates/core/base/tabs.jinja
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								core/templates/core/base/tabs.jinja
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					{% if list_of_tabs %}
 | 
				
			||||||
 | 
					  <div class="tool_bar">
 | 
				
			||||||
 | 
					    <div class="tools">
 | 
				
			||||||
 | 
					      {% for t in list_of_tabs -%}
 | 
				
			||||||
 | 
					        <a href="{{ t.url }}" {%- if current_tab==t.slug %} class="selected_tab" {%- endif -%}>{{ t.name }}</a>
 | 
				
			||||||
 | 
					      {%- endfor %}
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					{% endif %}
 | 
				
			||||||
							
								
								
									
										20
									
								
								core/templates/core/base_fragment.jinja
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								core/templates/core/base_fragment.jinja
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					{% block additional_css %}{% endblock %}
 | 
				
			||||||
 | 
					{% block additional_js %}{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div id="fragment-content">
 | 
				
			||||||
 | 
					  {% block tabs %}
 | 
				
			||||||
 | 
					    {% include "core/base/tabs.jinja" %}
 | 
				
			||||||
 | 
					  {% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  {% block errors %}
 | 
				
			||||||
 | 
					    {% if error %}
 | 
				
			||||||
 | 
					      {{ error }}
 | 
				
			||||||
 | 
					    {% endif %}
 | 
				
			||||||
 | 
					  {% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  {% block content %}
 | 
				
			||||||
 | 
					  {% endblock %}
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block script %}
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
@@ -1,4 +1,8 @@
 | 
				
			|||||||
{% extends "core/base.jinja" %}
 | 
					{% if is_fragment %}
 | 
				
			||||||
 | 
					  {% extends "core/base_fragment.jinja" %}
 | 
				
			||||||
 | 
					{% else %}
 | 
				
			||||||
 | 
					  {% extends "core/base.jinja" %}
 | 
				
			||||||
 | 
					{% endif %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% block title %}
 | 
					{% block title %}
 | 
				
			||||||
  {% if file %}
 | 
					  {% if file %}
 | 
				
			||||||
@@ -21,7 +25,7 @@
 | 
				
			|||||||
  {% endif %}
 | 
					  {% endif %}
 | 
				
			||||||
{% endmacro %}
 | 
					{% endmacro %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% block content %}
 | 
					{% block tabs %}
 | 
				
			||||||
  {{ print_file_name(file) }}
 | 
					  {{ print_file_name(file) }}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <div class="tool_bar">
 | 
					  <div class="tool_bar">
 | 
				
			||||||
@@ -44,6 +48,9 @@
 | 
				
			|||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
  <hr>
 | 
					  <hr>
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block content %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  {% if file %}
 | 
					  {% if file %}
 | 
				
			||||||
    {% block file %}
 | 
					    {% block file %}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,15 +4,49 @@
 | 
				
			|||||||
  {% trans %}Delete confirmation{% endtrans %}
 | 
					  {% trans %}Delete confirmation{% endtrans %}
 | 
				
			||||||
{% endblock %}
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% if is_fragment %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  {# Don't display tabs and errors #}
 | 
				
			||||||
 | 
					  {% block tabs %}
 | 
				
			||||||
 | 
					  {% endblock %}
 | 
				
			||||||
 | 
					  {% block errors %}
 | 
				
			||||||
 | 
					  {% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% endif %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% block file %}
 | 
					{% block file %}
 | 
				
			||||||
  <h2>{% trans %}Delete confirmation{% endtrans %}</h2>
 | 
					  <h2>{% trans %}Delete confirmation{% endtrans %}</h2>
 | 
				
			||||||
  <form action="" method="post">{% csrf_token %}
 | 
					
 | 
				
			||||||
 | 
					  {% if next %}
 | 
				
			||||||
 | 
					    {% set action = current + "?next=" + next %}
 | 
				
			||||||
 | 
					  {% else %}
 | 
				
			||||||
 | 
					    {% set action = current %}
 | 
				
			||||||
 | 
					  {% endif %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <form action="{{ action }}" method="post">
 | 
				
			||||||
 | 
					    {% csrf_token %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <p>{% trans obj=object %}Are you sure you want to delete "{{ obj }}"?{% endtrans %}</p>
 | 
					    <p>{% trans obj=object %}Are you sure you want to delete "{{ obj }}"?{% endtrans %}</p>
 | 
				
			||||||
    <input type="submit" value="{% trans %}Confirm{% endtrans %}" />
 | 
					    <button
 | 
				
			||||||
  </form>
 | 
					      {% if is_fragment %}
 | 
				
			||||||
  <form method="GET" action="javascript:history.back();">
 | 
					        hx-post="{{ action }}"
 | 
				
			||||||
    <input type="submit" name="cancel" value="{% trans %}Cancel{% endtrans %}" />
 | 
					        hx-target="#content"
 | 
				
			||||||
 | 
					        hx-swap="outerHtml"
 | 
				
			||||||
 | 
					      {% endif %}
 | 
				
			||||||
 | 
					    >{% trans %}Confirm{% endtrans %}</button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <button
 | 
				
			||||||
 | 
					      {% if is_fragment %}
 | 
				
			||||||
 | 
					        hx-get="{{ previous }}"
 | 
				
			||||||
 | 
					        hx-target="#content"
 | 
				
			||||||
 | 
					        hx-swap="outerHtml"
 | 
				
			||||||
 | 
					      {% else %}
 | 
				
			||||||
 | 
					        action="window.history.back()"
 | 
				
			||||||
 | 
					      {% endif %}
 | 
				
			||||||
 | 
					    >{% trans %}Cancel{% endtrans %}</button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  </form>
 | 
					  </form>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% endblock %}
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,16 @@
 | 
				
			|||||||
{% extends "core/base.jinja" %}
 | 
					{% if is_fragment %}
 | 
				
			||||||
 | 
					  {% extends "core/base_fragment.jinja" %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  {# Don't display tabs and errors #}
 | 
				
			||||||
 | 
					  {% block tabs %}
 | 
				
			||||||
 | 
					  {% endblock %}
 | 
				
			||||||
 | 
					  {% block errors %}
 | 
				
			||||||
 | 
					  {% endblock %}
 | 
				
			||||||
 | 
					{% else %}
 | 
				
			||||||
 | 
					  {% extends "core/base.jinja" %}
 | 
				
			||||||
 | 
					{% endif %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% from "core/macros.jinja" import paginate_htmx %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% block title %}
 | 
					{% block title %}
 | 
				
			||||||
  {% trans %}File moderation{% endtrans %}
 | 
					  {% trans %}File moderation{% endtrans %}
 | 
				
			||||||
@@ -7,8 +19,11 @@
 | 
				
			|||||||
{% block content %}
 | 
					{% block content %}
 | 
				
			||||||
  <h3>{% trans %}File moderation{% endtrans %}</h3>
 | 
					  <h3>{% trans %}File moderation{% endtrans %}</h3>
 | 
				
			||||||
  <div>
 | 
					  <div>
 | 
				
			||||||
    {% for f in files %}
 | 
					    {% for f in object_list %}
 | 
				
			||||||
      <div style="margin: 2px; padding: 2px; border: solid 1px red; text-align: center">
 | 
					      <div
 | 
				
			||||||
 | 
					        id="file-{{ loop.index }}"
 | 
				
			||||||
 | 
					        style="margin: 2px; padding: 2px; border: solid 1px red; text-align: center"
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
        {% if f.is_folder %}
 | 
					        {% if f.is_folder %}
 | 
				
			||||||
          <strong>Folder</strong>
 | 
					          <strong>Folder</strong>
 | 
				
			||||||
        {% else %}
 | 
					        {% else %}
 | 
				
			||||||
@@ -20,9 +35,19 @@
 | 
				
			|||||||
          {% trans %}Owner: {% endtrans %}{{ f.owner.get_display_name() }}<br/>
 | 
					          {% trans %}Owner: {% endtrans %}{{ f.owner.get_display_name() }}<br/>
 | 
				
			||||||
          {% trans %}Date: {% endtrans %}{{ f.date|date(DATE_FORMAT) }} {{ f.date|time(TIME_FORMAT) }}<br/>
 | 
					          {% trans %}Date: {% endtrans %}{{ f.date|date(DATE_FORMAT) }} {{ f.date|time(TIME_FORMAT) }}<br/>
 | 
				
			||||||
        </p>
 | 
					        </p>
 | 
				
			||||||
        <p><a href="{{ url('core:file_moderate', file_id=f.id) }}">{% trans %}Moderate{% endtrans %}</a> -
 | 
					        <p><button
 | 
				
			||||||
          <a href="{{ url('core:file_delete', file_id=f.id) }}?next={{ url('core:file_moderation') }}">{% trans %}Delete{% endtrans %}</a></p>
 | 
					          hx-get="{{ url('core:file_moderate', file_id=f.id) }}"
 | 
				
			||||||
 | 
					          hx-target="#content"
 | 
				
			||||||
 | 
					          hx-swap="outerHtml"
 | 
				
			||||||
 | 
					        >{% trans %}Moderate{% endtrans %}</button> -
 | 
				
			||||||
 | 
					          {% set current_page = url('core:file_moderation') + "?page=" + page_obj.number | string %}
 | 
				
			||||||
 | 
					          <button
 | 
				
			||||||
 | 
					            hx-get="{{ url('core:file_delete', file_id=f.id) }}?next={{ current_page | urlencode }}&previous={{ current_page | urlencode }}"
 | 
				
			||||||
 | 
					            hx-target="#file-{{ loop.index }}"
 | 
				
			||||||
 | 
					            hx-swap="outerHtml"
 | 
				
			||||||
 | 
					          >{% trans %}Delete{% endtrans %}</button></p>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    {% endfor %}
 | 
					    {% endfor %}
 | 
				
			||||||
 | 
					    {{ paginate_htmx(page_obj, paginator) }}
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
{% endblock %}
 | 
					{% endblock %}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -166,9 +166,37 @@
 | 
				
			|||||||
        current_page (django.core.paginator.Page): the current page object
 | 
					        current_page (django.core.paginator.Page): the current page object
 | 
				
			||||||
        paginator (django.core.paginator.Paginator): the paginator object
 | 
					        paginator (django.core.paginator.Paginator): the paginator object
 | 
				
			||||||
    #}
 | 
					    #}
 | 
				
			||||||
 | 
					  {{  paginate_server_side(current_page, paginator, False) }}
 | 
				
			||||||
 | 
					{% endmacro %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% macro paginate_htmx(current_page, paginator) %}
 | 
				
			||||||
 | 
					    {# Add pagination buttons for pages without Alpine but supporting framgents.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This must be coupled with a view that handles pagination
 | 
				
			||||||
 | 
					    with the Django Paginator object and supports framgents.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The relpaced fragment will be #content so make sure you are calling this macro inside your content block.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Parameters:
 | 
				
			||||||
 | 
					        current_page (django.core.paginator.Page): the current page object
 | 
				
			||||||
 | 
					        paginator (django.core.paginator.Paginator): the paginator object
 | 
				
			||||||
 | 
					    #}
 | 
				
			||||||
 | 
					  {{  paginate_server_side(current_page, paginator, True) }}
 | 
				
			||||||
 | 
					{% endmacro %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% macro paginate_server_side(current_page, paginator, use_htmx) %}
 | 
				
			||||||
  <nav class="pagination">
 | 
					  <nav class="pagination">
 | 
				
			||||||
    {% if current_page.has_previous() %}
 | 
					    {% if current_page.has_previous() %}
 | 
				
			||||||
      <a href="?page={{ current_page.previous_page_number() }}">
 | 
					      <a
 | 
				
			||||||
 | 
					        {% if use_htmx -%}
 | 
				
			||||||
 | 
					          hx-get="?page={{ current_page.previous_page_number() }}"
 | 
				
			||||||
 | 
					          hx-swap="innerHTML"
 | 
				
			||||||
 | 
					          hx-target="#content"
 | 
				
			||||||
 | 
					          hx-push-url="true"
 | 
				
			||||||
 | 
					        {%- else -%}
 | 
				
			||||||
 | 
					          href="?page={{ current_page.previous_page_number() }}"
 | 
				
			||||||
 | 
					        {%- endif -%}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
        <button>
 | 
					        <button>
 | 
				
			||||||
          <i class="fa fa-caret-left"></i>
 | 
					          <i class="fa fa-caret-left"></i>
 | 
				
			||||||
        </button>
 | 
					        </button>
 | 
				
			||||||
@@ -182,16 +210,33 @@
 | 
				
			|||||||
      {% elif i == paginator.ELLIPSIS %}
 | 
					      {% elif i == paginator.ELLIPSIS %}
 | 
				
			||||||
        <strong>{{ paginator.ELLIPSIS }}</strong>
 | 
					        <strong>{{ paginator.ELLIPSIS }}</strong>
 | 
				
			||||||
      {% else %}
 | 
					      {% else %}
 | 
				
			||||||
        <a href="?page={{ i }}">
 | 
					        <a
 | 
				
			||||||
 | 
					          {% if use_htmx -%}
 | 
				
			||||||
 | 
					            hx-get="?page={{ i }}"
 | 
				
			||||||
 | 
					            hx-swap="innerHTML"
 | 
				
			||||||
 | 
					            hx-target="#content"
 | 
				
			||||||
 | 
					            hx-push-url="true"
 | 
				
			||||||
 | 
					          {%- else -%}
 | 
				
			||||||
 | 
					            href="?page={{ i }}"
 | 
				
			||||||
 | 
					          {%- endif -%}
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
          <button>{{ i }}</button>
 | 
					          <button>{{ i }}</button>
 | 
				
			||||||
        </a>
 | 
					        </a>
 | 
				
			||||||
      {% endif %}
 | 
					      {% endif %}
 | 
				
			||||||
    {% endfor %}
 | 
					    {% endfor %}
 | 
				
			||||||
    {% if current_page.has_next() %}
 | 
					    {% if current_page.has_next() %}
 | 
				
			||||||
      <a href="?page={{ current_page.next_page_number() }}">
 | 
					      <a
 | 
				
			||||||
        <button>
 | 
					        {% if use_htmx -%}
 | 
				
			||||||
          <i class="fa fa-caret-right"></i>
 | 
					          hx-get="?page={{ current_page.next_page_number() }}"
 | 
				
			||||||
        </button>
 | 
					          hx-swap="innerHTML"
 | 
				
			||||||
 | 
					          hx-target="#content"
 | 
				
			||||||
 | 
					          hx-push-url="true"
 | 
				
			||||||
 | 
					        {%- else -%}
 | 
				
			||||||
 | 
					          href="?page={{ current_page.next_page_number() }}"
 | 
				
			||||||
 | 
					        {%- endif -%}
 | 
				
			||||||
 | 
					      ><button>
 | 
				
			||||||
 | 
					        <i class="fa fa-caret-right"></i>
 | 
				
			||||||
 | 
					      </button>
 | 
				
			||||||
      </a>
 | 
					      </a>
 | 
				
			||||||
    {% else %}
 | 
					    {% else %}
 | 
				
			||||||
      <button disabled="disabled"><i class="fa fa-caret-right"></i></button>
 | 
					      <button disabled="disabled"><i class="fa fa-caret-right"></i></button>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -77,7 +77,7 @@
 | 
				
			|||||||
      {% set default_picture = this_picture.get_download_url()|tojson %}
 | 
					      {% set default_picture = this_picture.get_download_url()|tojson %}
 | 
				
			||||||
      {% set delete_url = (
 | 
					      {% set delete_url = (
 | 
				
			||||||
        url('core:file_delete', file_id=this_picture.id, popup='')
 | 
					        url('core:file_delete', file_id=this_picture.id, popup='')
 | 
				
			||||||
        +"?next=" + profile.get_absolute_url()
 | 
					        + "?next=" + url('core:user_edit', user_id=profile.id)
 | 
				
			||||||
      )|tojson %}
 | 
					      )|tojson %}
 | 
				
			||||||
    {%- else -%}
 | 
					    {%- else -%}
 | 
				
			||||||
      {% set default_picture = static('core/img/unknown.jpg')|tojson %}
 | 
					      {% set default_picture = static('core/img/unknown.jpg')|tojson %}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,9 +21,11 @@ import pytest
 | 
				
			|||||||
from django.core import mail
 | 
					from django.core import mail
 | 
				
			||||||
from django.core.cache import cache
 | 
					from django.core.cache import cache
 | 
				
			||||||
from django.core.mail import EmailMessage
 | 
					from django.core.mail import EmailMessage
 | 
				
			||||||
from django.test import Client, TestCase
 | 
					from django.test import Client, RequestFactory, TestCase
 | 
				
			||||||
from django.urls import reverse
 | 
					from django.urls import reverse
 | 
				
			||||||
from django.utils.timezone import now
 | 
					from django.utils.timezone import now
 | 
				
			||||||
 | 
					from django.views.generic import View
 | 
				
			||||||
 | 
					from django.views.generic.base import ContextMixin
 | 
				
			||||||
from model_bakery import baker
 | 
					from model_bakery import baker
 | 
				
			||||||
from pytest_django.asserts import assertInHTML, assertRedirects
 | 
					from pytest_django.asserts import assertInHTML, assertRedirects
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -32,6 +34,7 @@ from club.models import Membership
 | 
				
			|||||||
from core.markdown import markdown
 | 
					from core.markdown import markdown
 | 
				
			||||||
from core.models import AnonymousUser, Group, Page, User
 | 
					from core.models import AnonymousUser, Group, Page, User
 | 
				
			||||||
from core.utils import get_semester_code, get_start_of_semester
 | 
					from core.utils import get_semester_code, get_start_of_semester
 | 
				
			||||||
 | 
					from core.views import AllowFragment
 | 
				
			||||||
from sith import settings
 | 
					from sith import settings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -538,3 +541,18 @@ class TestDateUtils(TestCase):
 | 
				
			|||||||
            # forward time to the middle of the next semester
 | 
					            # forward time to the middle of the next semester
 | 
				
			||||||
            frozen_time.move_to(mid_autumn)
 | 
					            frozen_time.move_to(mid_autumn)
 | 
				
			||||||
            assert get_start_of_semester() == autumn_2023
 | 
					            assert get_start_of_semester() == autumn_2023
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_allow_fragment_mixin():
 | 
				
			||||||
 | 
					    class TestAllowFragmentView(AllowFragment, ContextMixin, View):
 | 
				
			||||||
 | 
					        def get(self, *args, **kwargs):
 | 
				
			||||||
 | 
					            context = self.get_context_data(**kwargs)
 | 
				
			||||||
 | 
					            return context["is_fragment"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    request = RequestFactory().get("/test")
 | 
				
			||||||
 | 
					    base_headers = request.headers
 | 
				
			||||||
 | 
					    assert not TestAllowFragmentView.as_view()(request)
 | 
				
			||||||
 | 
					    request.headers = {"HX-Request": False, **base_headers}
 | 
				
			||||||
 | 
					    assert not TestAllowFragmentView.as_view()(request)
 | 
				
			||||||
 | 
					    request.headers = {"HX-Request": True, **base_headers}
 | 
				
			||||||
 | 
					    assert TestAllowFragmentView.as_view()(request)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -142,6 +142,30 @@ class TestFileHandling(TestCase):
 | 
				
			|||||||
        assert "ls</a>" in str(response.content)
 | 
					        assert "ls</a>" in str(response.content)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@pytest.mark.django_db
 | 
				
			||||||
 | 
					class TestFileModerationView:
 | 
				
			||||||
 | 
					    """Test access to file moderation view"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @pytest.mark.parametrize(
 | 
				
			||||||
 | 
					        ("user_factory", "status_code"),
 | 
				
			||||||
 | 
					        [
 | 
				
			||||||
 | 
					            (lambda: None, 403),  # Anonymous user
 | 
				
			||||||
 | 
					            (lambda: baker.make(User, is_superuser=True), 200),
 | 
				
			||||||
 | 
					            (lambda: baker.make(User), 403),
 | 
				
			||||||
 | 
					            (lambda: subscriber_user.make(), 403),
 | 
				
			||||||
 | 
					            (lambda: old_subscriber_user.make(), 403),
 | 
				
			||||||
 | 
					            (lambda: board_user.make(), 403),
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    def test_view_access(
 | 
				
			||||||
 | 
					        self, client: Client, user_factory: Callable[[], User | None], status_code: int
 | 
				
			||||||
 | 
					    ):
 | 
				
			||||||
 | 
					        user = user_factory()
 | 
				
			||||||
 | 
					        if user:  # if None, then it's an anonymous user
 | 
				
			||||||
 | 
					            client.force_login(user_factory())
 | 
				
			||||||
 | 
					        assert client.get(reverse("core:file_moderation")).status_code == status_code
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.django_db
 | 
					@pytest.mark.django_db
 | 
				
			||||||
class TestUserProfilePicture:
 | 
					class TestUserProfilePicture:
 | 
				
			||||||
    """Test interactions with user's profile picture."""
 | 
					    """Test interactions with user's profile picture."""
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -326,6 +326,14 @@ class DetailFormView(SingleObjectMixin, FormView):
 | 
				
			|||||||
        return super().get_object()
 | 
					        return super().get_object()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AllowFragment:
 | 
				
			||||||
 | 
					    """Add `is_fragment` to templates. It's only True if the request is emitted by htmx"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_context_data(self, **kwargs):
 | 
				
			||||||
 | 
					        kwargs["is_fragment"] = self.request.headers.get("HX-Request", False)
 | 
				
			||||||
 | 
					        return super().get_context_data(**kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# F403: those star-imports would be hellish to refactor
 | 
					# F403: those star-imports would be hellish to refactor
 | 
				
			||||||
# E402: putting those import at the top of the file would also be difficult
 | 
					# E402: putting those import at the top of the file would also be difficult
 | 
				
			||||||
from .files import *  # noqa: F403 E402
 | 
					from .files import *  # noqa: F403 E402
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,12 +27,13 @@ from django.shortcuts import get_object_or_404, redirect
 | 
				
			|||||||
from django.urls import reverse
 | 
					from django.urls import reverse
 | 
				
			||||||
from django.utils.http import http_date
 | 
					from django.utils.http import http_date
 | 
				
			||||||
from django.utils.translation import gettext_lazy as _
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
from django.views.generic import DetailView, ListView, TemplateView
 | 
					from django.views.generic import DetailView, ListView
 | 
				
			||||||
from django.views.generic.detail import SingleObjectMixin
 | 
					from django.views.generic.detail import SingleObjectMixin
 | 
				
			||||||
from django.views.generic.edit import DeleteView, FormMixin, UpdateView
 | 
					from django.views.generic.edit import DeleteView, FormMixin, UpdateView
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from core.models import Notification, RealGroup, SithFile
 | 
					from core.models import Notification, RealGroup, SithFile, User
 | 
				
			||||||
from core.views import (
 | 
					from core.views import (
 | 
				
			||||||
 | 
					    AllowFragment,
 | 
				
			||||||
    CanEditMixin,
 | 
					    CanEditMixin,
 | 
				
			||||||
    CanEditPropMixin,
 | 
					    CanEditPropMixin,
 | 
				
			||||||
    CanViewMixin,
 | 
					    CanViewMixin,
 | 
				
			||||||
@@ -352,7 +353,7 @@ class FileView(CanViewMixin, DetailView, FormMixin):
 | 
				
			|||||||
        return kwargs
 | 
					        return kwargs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class FileDeleteView(CanEditPropMixin, DeleteView):
 | 
					class FileDeleteView(AllowFragment, CanEditPropMixin, DeleteView):
 | 
				
			||||||
    model = SithFile
 | 
					    model = SithFile
 | 
				
			||||||
    pk_url_kwarg = "file_id"
 | 
					    pk_url_kwarg = "file_id"
 | 
				
			||||||
    template_name = "core/file_delete_confirm.jinja"
 | 
					    template_name = "core/file_delete_confirm.jinja"
 | 
				
			||||||
@@ -376,19 +377,24 @@ class FileDeleteView(CanEditPropMixin, DeleteView):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def get_context_data(self, **kwargs):
 | 
					    def get_context_data(self, **kwargs):
 | 
				
			||||||
        kwargs = super().get_context_data(**kwargs)
 | 
					        kwargs = super().get_context_data(**kwargs)
 | 
				
			||||||
        kwargs["popup"] = ""
 | 
					        kwargs["popup"] = "" if self.kwargs.get("popup") is None else "popup"
 | 
				
			||||||
        if self.kwargs.get("popup") is not None:
 | 
					        kwargs["next"] = self.request.GET.get("next", None)
 | 
				
			||||||
            kwargs["popup"] = "popup"
 | 
					        kwargs["previous"] = self.request.GET.get("previous", None)
 | 
				
			||||||
 | 
					        kwargs["current"] = self.request.path
 | 
				
			||||||
        return kwargs
 | 
					        return kwargs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class FileModerationView(TemplateView):
 | 
					class FileModerationView(AllowFragment, ListView):
 | 
				
			||||||
 | 
					    model = SithFile
 | 
				
			||||||
    template_name = "core/file_moderation.jinja"
 | 
					    template_name = "core/file_moderation.jinja"
 | 
				
			||||||
 | 
					    queryset = SithFile.objects.filter(is_moderated=False, is_in_sas=False)
 | 
				
			||||||
 | 
					    paginate_by = 100
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_context_data(self, **kwargs):
 | 
					    def dispatch(self, request: HttpRequest, *args, **kwargs):
 | 
				
			||||||
        kwargs = super().get_context_data(**kwargs)
 | 
					        user: User = request.user
 | 
				
			||||||
        kwargs["files"] = SithFile.objects.filter(is_moderated=False)[:100]
 | 
					        if user.is_root:
 | 
				
			||||||
        return kwargs
 | 
					            return super().dispatch(request, *args, **kwargs)
 | 
				
			||||||
 | 
					        raise PermissionDenied()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class FileModerateView(CanEditPropMixin, SingleObjectMixin):
 | 
					class FileModerateView(CanEditPropMixin, SingleObjectMixin):
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -200,6 +200,19 @@ Grâce à son architecture, il est extrêmement
 | 
				
			|||||||
bien adapté pour un usage dans un site multipage.
 | 
					bien adapté pour un usage dans un site multipage.
 | 
				
			||||||
C'est une technologie simple et puissante qui se veut comme le jQuery du web moderne.
 | 
					C'est une technologie simple et puissante qui se veut comme le jQuery du web moderne.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Htmx
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Site officiel](https://htmx.org/)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					En plus de AlpineJS, l’interactivité sur le site est augmentée via Htmx.
 | 
				
			||||||
 | 
					C'est une librairie js qui s'utilise également au moyen d'attributs HTML à
 | 
				
			||||||
 | 
					ajouter directement dans les templates.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Son principe est de remplacer certains éléments du html par un fragment de
 | 
				
			||||||
 | 
					HTML renvoyé par le serveur backend. Cela se marie très bien avec le
 | 
				
			||||||
 | 
					fonctionnement de django et en particulier de ses formulaires afin d'éviter
 | 
				
			||||||
 | 
					de doubler le travail pour la vérification des données.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Sass
 | 
					### Sass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[Site officiel](https://sass-lang.com/)
 | 
					[Site officiel](https://sass-lang.com/)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1 +0,0 @@
 | 
				
			|||||||
::: stock.models
 | 
					 | 
				
			||||||
@@ -1 +0,0 @@
 | 
				
			|||||||
::: stock.views
 | 
					 | 
				
			||||||
							
								
								
									
										40
									
								
								docs/tutorial/fragments.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								docs/tutorial/fragments.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					Pour utiliser HTMX, on a besoin de renvoyer des fragments depuis le backend.
 | 
				
			||||||
 | 
					Le truc, c'est que tout est optimisé pour utiliser `base.jinja` qui est assez gros.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Dans beaucoup de scénario, on veut pouvoir renvoyer soit la vue complète, soit
 | 
				
			||||||
 | 
					juste le fragment. En particulier quand on utilise l'attribut `hx-history` de htmx.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Pour remédier à cela, il existe le mixin [AllowFragment][core.views.AllowFragment].
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Une fois ajouté à une vue Django, il ajoute le boolean `is_fragment` dans les
 | 
				
			||||||
 | 
					templates jinja. Sa valeur est `True` uniquement si HTMX envoie la requête.
 | 
				
			||||||
 | 
					Il est ensuite très simple de faire un if/else pour hériter de
 | 
				
			||||||
 | 
					`core/base_fragment.jinja` au lieu de `core/base.jinja` dans cette situation.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Exemple d'utilisation d'une vue avec fragment:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```python
 | 
				
			||||||
 | 
					from django.views.generic import TemplateView
 | 
				
			||||||
 | 
					from core.views import AllowFragment
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class FragmentView(AllowFragment, TemplateView):
 | 
				
			||||||
 | 
						template_name = "my_template.jinja"
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Exemple de template (`my_template.jinja`)
 | 
				
			||||||
 | 
					```jinja
 | 
				
			||||||
 | 
					{% if is_fragment %}
 | 
				
			||||||
 | 
					  {% extends "core/base_fragment.jinja" %}
 | 
				
			||||||
 | 
					{% else %}
 | 
				
			||||||
 | 
					  {% extends "core/base.jinja" %}
 | 
				
			||||||
 | 
					{% endif %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block title %}
 | 
				
			||||||
 | 
					  {% trans %}My view with a fragment{% endtrans %}
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block content %}
 | 
				
			||||||
 | 
					  <h3>{% trans %}This will be a fragment when is_fragment is True{% endtrans %}
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
@@ -66,6 +66,7 @@ nav:
 | 
				
			|||||||
    - Structure du projet: tutorial/structure.md
 | 
					    - Structure du projet: tutorial/structure.md
 | 
				
			||||||
    - Gestion des permissions: tutorial/perms.md
 | 
					    - Gestion des permissions: tutorial/perms.md
 | 
				
			||||||
    - Gestion des groupes: tutorial/groups.md
 | 
					    - Gestion des groupes: tutorial/groups.md
 | 
				
			||||||
 | 
					    - Créer des fragments: tutorial/fragments.md
 | 
				
			||||||
    - Etransactions: tutorial/etransaction.md
 | 
					    - Etransactions: tutorial/etransaction.md
 | 
				
			||||||
  - How-to:
 | 
					  - How-to:
 | 
				
			||||||
    - L'ORM de Django: howto/querysets.md
 | 
					    - L'ORM de Django: howto/querysets.md
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										6
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -22,6 +22,7 @@
 | 
				
			|||||||
        "d3-force-3d": "^3.0.5",
 | 
					        "d3-force-3d": "^3.0.5",
 | 
				
			||||||
        "easymde": "^2.18.0",
 | 
					        "easymde": "^2.18.0",
 | 
				
			||||||
        "glob": "^11.0.0",
 | 
					        "glob": "^11.0.0",
 | 
				
			||||||
 | 
					        "htmx.org": "^2.0.3",
 | 
				
			||||||
        "jquery": "^3.7.1",
 | 
					        "jquery": "^3.7.1",
 | 
				
			||||||
        "jquery-ui": "^1.14.0",
 | 
					        "jquery-ui": "^1.14.0",
 | 
				
			||||||
        "jquery.shorten": "^1.0.0",
 | 
					        "jquery.shorten": "^1.0.0",
 | 
				
			||||||
@@ -4455,6 +4456,11 @@
 | 
				
			|||||||
      "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
 | 
					      "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
 | 
				
			||||||
      "dev": true
 | 
					      "dev": true
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/htmx.org": {
 | 
				
			||||||
 | 
					      "version": "2.0.3",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-2.0.3.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-AeoJUAjkCVVajbfKX+3sVQBTCt8Ct4lif1T+z/tptTXo8+8yyq3QIMQQe/IT+R8ssfrO1I0DeX4CAronzCL6oA=="
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/human-signals": {
 | 
					    "node_modules/human-signals": {
 | 
				
			||||||
      "version": "5.0.0",
 | 
					      "version": "5.0.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -54,6 +54,7 @@
 | 
				
			|||||||
    "d3-force-3d": "^3.0.5",
 | 
					    "d3-force-3d": "^3.0.5",
 | 
				
			||||||
    "easymde": "^2.18.0",
 | 
					    "easymde": "^2.18.0",
 | 
				
			||||||
    "glob": "^11.0.0",
 | 
					    "glob": "^11.0.0",
 | 
				
			||||||
 | 
					    "htmx.org": "^2.0.3",
 | 
				
			||||||
    "jquery": "^3.7.1",
 | 
					    "jquery": "^3.7.1",
 | 
				
			||||||
    "jquery-ui": "^1.14.0",
 | 
					    "jquery-ui": "^1.14.0",
 | 
				
			||||||
    "jquery.shorten": "^1.0.0",
 | 
					    "jquery.shorten": "^1.0.0",
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user