Compare commits
4 Commits
b76277a6c7
...
c770867c2c
Author | SHA1 | Date |
---|---|---|
|
c770867c2c | |
|
f381b4dd44 | |
|
92d4c851a3 | |
|
6e367cb9dd |
35
README.md
35
README.md
|
@ -1,2 +1,35 @@
|
|||
# kholles-web
|
||||
# Colloscope : Votre colloscope. En ligne.
|
||||
|
||||
Certifié le dernier colloscope dont vous aurez besoin. Avec ses fonctionalités de synchronisation, il reste
|
||||
toujours à jour pour vous permettre d'aborder vos colles sereinement, si vous connaissez votre cours (apprentissage
|
||||
du cours non fourni).
|
||||
|
||||
## Soyez le premier informé lors d'une modification
|
||||
|
||||
Lorsqu'une colle est modifiée, le modification se propage à l'ensemble des pages visibles par les utilisateurs.
|
||||
Aucune excuse pour manquer sa colle.
|
||||
|
||||
## Échangez vos colles en toute confianc
|
||||
|
||||
Vous ne pouvez pas venir à une colle ? Aucun problème : il vous suffit de l'échanger !
|
||||
Le *Marketplace* intégré vous donne la possibilité de récupérer des colles disponibles.
|
||||
|
||||
## Un système interopérable
|
||||
|
||||
Vous pouvez synchroniser vos colles avec votre application de calendrier favorite. Il lui suffit de supporter
|
||||
les liens iCalendar. C'est le cas de l'application Calendrier sur iOS, de OneCalendar sur Android et de
|
||||
Mozilla Thunderbird sur GNU/Linux et macOS (et Microsoft Windows).
|
||||
|
||||
|
||||
## Pensé par un nerd, pour les nerds.
|
||||
|
||||
Un système complet d'API REST vous permet d'intégrer ce colloscope à vos programmes tiers.
|
||||
|
||||
|
||||
## Libre, pour toujours.
|
||||
|
||||
Colloscope est distribué avec la licence GNU Affero GPL. Cette licence garantit vos quatre libertés, à savoir :
|
||||
0. Exécutez le code librement ;
|
||||
1. Modifiez le code librement ;
|
||||
2. Distribuez le code librement ;
|
||||
3. Distribuez librement des versions modifiées du code.
|
|
@ -383,6 +383,9 @@ class Colle(models.Model):
|
|||
# func = async_to_sync(swap.notify)
|
||||
# func()
|
||||
|
||||
def week_number(self):
|
||||
return self.slot.term.cls.week_number(self.datetime.date())
|
||||
|
||||
def __str__(self):
|
||||
return f"Colle {self.slot.subject} ({self.slot.colleur}); {self.datetime} {self.slot.time} {self.slot.room}. Groupe(s) {{{'; '.join(str(groupe) for groupe in self.groups.all())}}}"
|
||||
|
||||
|
|
|
@ -13,30 +13,33 @@
|
|||
Bienvenue {{ student }}. Votre lycée est {{ term.cls.school.description }}, et votre classe est {{ term.cls.description }}.
|
||||
</p>
|
||||
|
||||
<p>Période actuelle : {{ term }}. Votre groupe de colle est {{ group }}. <a href="table.html">Consulter le colloscope</a></p>
|
||||
<p>Période actuelle : {{ term }}. Votre groupe de colle est {{ group }}. <a href="{% url "colloscope:table" %}">Consulter le colloscope</a></p>
|
||||
|
||||
<h2>Mes colles</h2>
|
||||
|
||||
<p><a href="{{ calendar_link }}"><i class="fa-regular fa-calendar"></i> Exporter en .ics (ceci est un permalien public)</a></p>
|
||||
|
||||
<p><a href="{% url "colloscope.marketplace" %}">Accéder au marketplace</a></p>
|
||||
<p><a href="{% url "colloscope:marketplace" %}">Accéder au marketplace</a></p>
|
||||
|
||||
|
||||
{% for n, lundi, colles in colles_per_sem %}
|
||||
|
||||
{% if colles %}
|
||||
<h3 class="week">Semaine {{n}} ({{lundi}})</h3>
|
||||
<h3 class="week" id="week-no-{{ n }}">
|
||||
<a href="#week-no-{{ n }}">Semaine {{n}} ({{lundi}})</a>
|
||||
</h3>
|
||||
<div class="colle-wrapper">
|
||||
{% for colle in colles %}
|
||||
<div class="colle">
|
||||
<span class="summary">{{ colle.slot.subject }} ({{ colle.slot.colleur }})</span>
|
||||
<ul>
|
||||
<li><i class="fa-solid fa-clock"></i> Le {{ colle.datetime|date:"l" }} {{ colle.datetime|date:"DATETIME_FORMAT" }}</li>
|
||||
<li><i class="fa-solid fa-graduation-cap"></i> {{ colle.slot.subject }}</li>
|
||||
<li><i class="fa-solid fa-person-chalkboard"></i> {{ colle.slot.colleur }}</li>
|
||||
<li><i class="fa-solid fa-clock"></i> {{ colle.datetime|date:"l"|title }} {{ colle.datetime|date:"DATETIME_FORMAT" }}</li>
|
||||
<li><i class="fa-solid fa-users"></i> {{ colle.groups.all | print_manager | safe }} ({{ colle.volume }} / {{ colle.slot.capacity }})</li>
|
||||
<li><i class="fa-solid fa-earth-americas"></i> {{ colle.slot.room }}</li>
|
||||
<li><i class="fa-solid fa-circle-exclamation"></i>
|
||||
<form
|
||||
action="{% url "colloscope.withdraw" %}"
|
||||
action="{% url "colloscope:withdraw" %}"
|
||||
method="POST"
|
||||
onsubmit="return confirm('Êtes-vous sûr de vouloir vous désinscrire de la colle {{ colle }} ');">
|
||||
{% csrf_token %}
|
||||
|
|
|
@ -15,13 +15,14 @@
|
|||
<div class="colle-wrapper">
|
||||
{% for colle in colles %}
|
||||
<div class="colle">
|
||||
<span class="summary">{{ colle.slot.subject }} ({{ colle.slot.colleur }})</span>
|
||||
<ul>
|
||||
<li><i class="fa-solid fa-clock"></i> Le {{ colle.datetime|date:"l" }} {{ colle.datetime|date:"DATETIME_FORMAT" }}</li>
|
||||
<li><i class="fa-solid fa-graduation-cap"></i> {{ colle.slot.subject }}</li>
|
||||
<li><i class="fa-solid fa-person-chalkboard"></i> {{ colle.slot.colleur }}</li>
|
||||
<li><i class="fa-solid fa-clock"></i> {{ colle.datetime|date:"l"|title }} {{ colle.datetime|date:"DATETIME_FORMAT" }}</li>
|
||||
<li><i class="fa-solid fa-users"></i> {{ colle.groups.all | print_manager | safe }} ({{ colle.volume }} / {{ colle.slot.capacity }})</li>
|
||||
<li><i class="fa-solid fa-earth-americas"></i> {{ colle.slot.room }}</li>
|
||||
<li><i class="fa-solid fa-circle-exclamation"></i>
|
||||
<form action="{% url "colloscope.enroll" %}"
|
||||
<form action="{% url "colloscope:enroll" %}"
|
||||
method="POST"
|
||||
onsubmit="return confirm('Êtes-vous sûr de vouloir vous inscrire à la colle {{ colle }} ');">
|
||||
{% csrf_token %}
|
||||
|
|
|
@ -13,12 +13,12 @@
|
|||
<h1>Colloscope</h1>
|
||||
|
||||
<p>
|
||||
Lycée : {{ term.cls.school.description }}. Classe : {{ term.cls.description }}. <a href="dashboard.html">Retour au tableau de bord</a>
|
||||
Lycée : {{ term.cls.school.description }}. Classe : {{ term.cls.description }}. <a href="{% url "colloscope:table" %}">Retour au tableau de bord</a>
|
||||
</p>
|
||||
|
||||
<h2>Colloscope : {{ term.description }}</h2>
|
||||
|
||||
<form method="get" action="{% url "colloscope.table" %}">
|
||||
<form method="get" action="{% url "colloscope:table" %}">
|
||||
Changer de période :
|
||||
<select name="term" id="term">
|
||||
{% for p in term.cls.term_set.all %}
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
from django.urls import path
|
||||
from django.shortcuts import redirect
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path("", views.home_redirect, name="colloscope.home"),
|
||||
path("table.html", views.colloscope, name="colloscope.table"),
|
||||
path("dashboard.html", views.dashboard, name="colloscope.dashboard"),
|
||||
path("export.pdf", views.export, name="colloscope.export"),
|
||||
path("export/calendar/<str:key>/calendar.ics", views.icalendar, name="colloscope.calendar.ics"),
|
||||
path("select_profile", views.select_profile, name="colloscope.select_profile"),
|
||||
path("marketplace.html", views.marketplace, name="colloscope.marketplace"),
|
||||
path("action/enroll", views.enroll, name="colloscope.enroll"),
|
||||
path("action/withdraw", views.withdraw, name="colloscope.withdraw"),
|
||||
path("", lambda req: redirect("colloscope:dashboard"), name="home"),
|
||||
path("table", views.colloscope, name="table"),
|
||||
path("dashboard", views.dashboard, name="dashboard"),
|
||||
path("export.pdf", views.export, name="export"),
|
||||
path("export/calendar/<str:key>/calendar.ics", views.icalendar, name="export-ics"),
|
||||
path("select_profile", views.select_profile, name="select_profile"),
|
||||
path("marketplace", views.marketplace, name="marketplace"),
|
||||
path("action/enroll", views.enroll, name="enroll"),
|
||||
path("action/withdraw", views.withdraw, name="withdraw"),
|
||||
]
|
||||
|
|
|
@ -6,6 +6,8 @@ from django.http import HttpResponse, HttpResponseRedirect
|
|||
from django.template import loader
|
||||
from django.views.decorators.http import require_POST
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.views.generic import ListView
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from colloscope.models import *
|
||||
from colloscope.pdfexport import handle
|
||||
|
@ -13,13 +15,8 @@ from colloscope.icalexport import to_calendar
|
|||
|
||||
|
||||
def handler404(request):
|
||||
template = loader.get_template("404.html")
|
||||
context = {}
|
||||
return HttpResponse(template.render(context), status=404)
|
||||
|
||||
|
||||
def home_redirect(request):
|
||||
return redirect("/colloscope/dashboard.html")
|
||||
return render(request, '404.html', context, status=404)
|
||||
|
||||
|
||||
@login_required
|
||||
|
@ -35,10 +32,10 @@ def select_profile(request):
|
|||
|
||||
if profile.student is not None and profile.colleur is None:
|
||||
session["profile"] = "student"
|
||||
return redirect("/colloscope/")
|
||||
return redirect("colloscope:home")
|
||||
elif profile.colleur is not None and profile.student is None:
|
||||
session["profile"] = "colleur"
|
||||
return redirect("/colloscope/")
|
||||
return redirect("colloscope:home")
|
||||
else:
|
||||
if profile.student is not None:
|
||||
template = loader.get_template("select_profile.html")
|
||||
|
@ -51,16 +48,11 @@ def select_profile(request):
|
|||
return HttpResponse(template.render(context))
|
||||
|
||||
|
||||
def get_lien_calendrier(student, term):
|
||||
try:
|
||||
lien = CalendarLink.objects.get(student=student, term=term)
|
||||
except CalendarLink.DoesNotExist:
|
||||
key = uuid4().hex
|
||||
lien = CalendarLink(key=key, student=student, term=term)
|
||||
lien.save()
|
||||
|
||||
return f"calendrier.ics?key={lien.key}"
|
||||
class ColleListView(ListView):
|
||||
model = Colle
|
||||
|
||||
def get_queryset(self):
|
||||
pass
|
||||
|
||||
@login_required
|
||||
def dashboard(request):
|
||||
|
@ -72,7 +64,7 @@ def dashboard(request):
|
|||
.prefetch_related("student__cls__term_set"))
|
||||
)
|
||||
except ValueError:
|
||||
return redirect("colloscope.select_profile")
|
||||
return redirect("colloscope:select_profile")
|
||||
|
||||
if not isinstance(student, Student):
|
||||
return HttpResponse("pas encore supporté")
|
||||
|
@ -124,10 +116,10 @@ def marketplace(request):
|
|||
.prefetch_related("student__cls__term_set"))
|
||||
)
|
||||
except ValueError:
|
||||
return redirect("colloscope.select_profile")
|
||||
return redirect("colloscope:select_profile")
|
||||
|
||||
if not isinstance(student, Student):
|
||||
return HttpResponse("pas encore supporté")
|
||||
return HttpResponse(_("Not supported yet."))
|
||||
|
||||
term = student.cls.current_term()
|
||||
colles = term.query_colles_not_full_excluding_student(student)
|
||||
|
|
|
@ -52,6 +52,7 @@ INSTALLED_APPS = [
|
|||
"rest_framework",
|
||||
'rest_framework_simplejwt',
|
||||
'colloscope',
|
||||
"front",
|
||||
"drf_spectacular",
|
||||
]
|
||||
|
||||
|
@ -156,8 +157,8 @@ STATICFILES_FINDERS = [
|
|||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
|
||||
LOGIN_URL = "/accounts/login"
|
||||
LOGIN_REDIRECT_URL = "home"
|
||||
LOGOUT_REDIRECT_URL = "home"
|
||||
LOGIN_REDIRECT_URL = "colloscope:dashboard"
|
||||
LOGOUT_REDIRECT_URL = "front:index"
|
||||
|
||||
DISCORD_NOTIFY_WEBHOOK_URL = "https://discord.com/api/webhooks/YOUR_URL"
|
||||
DISCORD_NOTIFY_WEBHOOK_USERNAME = "Watchdog"
|
||||
|
|
|
@ -16,16 +16,15 @@ Including another URLconf
|
|||
"""
|
||||
from django.contrib import admin, auth
|
||||
from django.urls import include, path
|
||||
from django.shortcuts import redirect
|
||||
from django.shortcuts import redirect, render
|
||||
from django.contrib.staticfiles import views as vstatic
|
||||
from rest_framework import routers
|
||||
from rest_framework_simplejwt.views import (
|
||||
TokenObtainPairView,
|
||||
TokenRefreshView,
|
||||
)
|
||||
from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView
|
||||
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView
|
||||
|
||||
from colloscope.views import home_redirect
|
||||
from colloscope.viewsets import *
|
||||
|
||||
router = routers.SimpleRouter()
|
||||
|
@ -43,7 +42,9 @@ router.register("calendarlink", CalendarLinkViewset, basename='calendarlink')
|
|||
|
||||
|
||||
urlpatterns = [
|
||||
path('', home_redirect, name="home"),
|
||||
path('', lambda req: render(req, "index.html", {}), name="home"),
|
||||
|
||||
path("__debug__/", include("debug_toolbar.urls")),
|
||||
path('api-auth/', include('rest_framework.urls')),
|
||||
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
|
||||
path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
|
||||
|
@ -55,7 +56,7 @@ urlpatterns = [
|
|||
|
||||
path("oauth2/", include('oauth2_provider.urls', namespace='oauth2_provider')),
|
||||
path("favicon.ico", lambda req: vstatic.serve(req, "favicon.ico")),
|
||||
path('colloscope/', include('colloscope.urls')),
|
||||
path('colloscope/', include(('colloscope.urls', "colloscope"), namespace="colloscope")),
|
||||
path('admin/', admin.site.urls),
|
||||
path('accounts/', include("django.contrib.auth.urls")),
|
||||
]
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1239,3 +1239,13 @@ msgstr ""
|
|||
#: venv/lib/python3.12/site-packages/django/views/templates/default_urlconf.html:248
|
||||
msgid "Connect, get help, or contribute"
|
||||
msgstr ""
|
||||
|
||||
|
||||
#: colloscope/views.py:122
|
||||
msgid "Not supported yet."
|
||||
msgstr "Pas encore supporté."
|
||||
|
||||
|
||||
#: front/templates/index.html:7
|
||||
msgid "Your colloscope. Online."
|
||||
msgstr "Votre colloscope. En ligne."
|
|
@ -14,6 +14,7 @@ defusedxml==0.7.1
|
|||
discord.py==2.3.2
|
||||
Django==5.0.4
|
||||
django-cors-headers==4.3.1
|
||||
django-debug-toolbar==4.3.0
|
||||
django-oauth-toolkit==2.3.0
|
||||
django-smtp-ssl==1.0
|
||||
djangorestframework==3.15.1
|
||||
|
|
|
@ -7,6 +7,15 @@ body {
|
|||
margin: 0;
|
||||
}
|
||||
|
||||
a:link, a:visited {
|
||||
color: blue;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:link:active, a:visited:active {
|
||||
color: darkblue;
|
||||
}
|
||||
|
||||
header .bandeau {
|
||||
display: block;
|
||||
background-color: #333;
|
||||
|
@ -79,7 +88,7 @@ header .bandeau button:active {
|
|||
}
|
||||
|
||||
main {
|
||||
margin: 20px auto;
|
||||
margin: 0 auto;
|
||||
width: clamp(350px, 60%, 1200px);
|
||||
background-color: white;
|
||||
padding: 10px;
|
||||
|
@ -129,7 +138,11 @@ nav.semaine .select, nav.semaine .label {
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
nav.semaine .select { background-color: dodgerblue; color: white; width: 1em; }
|
||||
nav.semaine .select {
|
||||
background-color: dodgerblue;
|
||||
color: white;
|
||||
width: 1em;
|
||||
}
|
||||
nav.semaine .select:hover { background-color: #0077ea; }
|
||||
|
||||
nav.semaine .label:hover {
|
||||
|
@ -144,13 +157,26 @@ p.programme {
|
|||
|
||||
footer {
|
||||
text-align: center;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
|
||||
.week {
|
||||
background-color: dodgerblue;
|
||||
color: white;
|
||||
padding: 5px;
|
||||
padding: 5px 10px;
|
||||
border-radius: 5px;
|
||||
|
||||
transition: background-color 200ms;
|
||||
}
|
||||
|
||||
.week a, .week a:hover, .week a:active {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.week:hover {
|
||||
background-color: #0077ea;
|
||||
}
|
||||
|
||||
.week.empty {
|
||||
|
@ -159,7 +185,7 @@ footer {
|
|||
|
||||
.colle-wrapper {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 400px)
|
||||
|
@ -170,8 +196,15 @@ footer {
|
|||
}
|
||||
|
||||
.colle {
|
||||
border: 1px solid black;
|
||||
padding: 10px;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 3px 6px rgba(0,0,0,0.04),0 3px 6px rgba(0,0,0,0.0575);
|
||||
|
||||
transition: background-color 200ms;
|
||||
}
|
||||
|
||||
.colle:hover {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
.colle span {
|
||||
|
@ -180,6 +213,7 @@ footer {
|
|||
|
||||
.colle ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.colle li {
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 9e2427d5ae8e7fafef9fe001394e9566e181f217
|
||||
Subproject commit 811b224d6a8a644dffd6c34cb54acbbd1a0f4db0
|
|
@ -32,20 +32,27 @@
|
|||
|
||||
<div class="navbar">
|
||||
<div class="block">
|
||||
<a href="{% url "home" %}">
|
||||
<div class="link"><i class="fa-solid fa-home"></i> Accueil</div>
|
||||
</a>
|
||||
{% if request.user.is_authenticated %}
|
||||
<a href="{% url "colloscope.dashboard" %}">
|
||||
<a href="{% url "colloscope:dashboard" %}">
|
||||
<div class="link"><i class="fa-solid fa-rocket"></i> Tableau de bord</div>
|
||||
</a>
|
||||
<a href="{% url "colloscope.table" %}">
|
||||
<a href="{% url "colloscope:table" %}">
|
||||
<div class="link"><i class="fa-solid fa-calendar"></i> Colloscope</div>
|
||||
</a>
|
||||
<a href="{% url "colloscope.marketplace" %}">
|
||||
<a href="{% url "colloscope:marketplace" %}">
|
||||
<div class="link"><i class="fa-solid fa-shop"></i> Marketplace</div>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="block">
|
||||
{% if request.user.is_authenticated %}
|
||||
<div class="link">
|
||||
<i class="fa-solid fa-user"></i> {{ user.username }} ({{ request.session.profile }})
|
||||
</div>
|
||||
|
||||
<form action="{% url 'logout' %}" method="post">
|
||||
{% csrf_token %}
|
||||
<button class="link" type="submit" href="{% url "login" %}">
|
||||
|
@ -66,6 +73,6 @@
|
|||
{% block main %}{% endblock main %}
|
||||
</main>
|
||||
<footer>
|
||||
{% block footer %}© colles.mp2i-vms.fr 2024 - <a href="https://git.mp2i-vms.fr/mp2i-vms/kholles-web" target="_blank">Code source</a> - <a href="https://www.gnu.org/licenses/agpl-3.0.html" target="_blank">Licence GNU AGPL v3 or later</a>{% endblock footer %}
|
||||
{% block footer %}© colles.mp2i-vms.fr 2024 - <a href="https://git.mp2i-vms.fr/mp2i-vms/colles.mp2i-vms.fr" target="_blank">Code source</a> - <a href="https://www.gnu.org/licenses/agpl-3.0.html" target="_blank">Licence GNU AGPL v3 or later</a>{% endblock footer %}
|
||||
</footer>
|
||||
</body>
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}Accueil{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
<h1>{% translate "Your colloscope. Online." %}</h1>
|
||||
|
||||
<p>
|
||||
Certifié le dernier colloscope dont vous aurez besoin. Avec ses fonctionalités de synchronisation, il reste
|
||||
toujours à jour pour vous permettre d'aborder vos colles sereinement, si vous connaissez votre cours (apprentissage
|
||||
du cours non fourni).
|
||||
</p>
|
||||
|
||||
<h2>Soyez le premier informé lors d'une modification</h2>
|
||||
<p>
|
||||
Lorsqu'une colle est modifiée, le modification se propage à l'ensemble des pages visibles par les utilisateurs.
|
||||
Aucune excuse pour manquer sa colle.
|
||||
</p>
|
||||
|
||||
<h2>Échangez vos colles en toute confiance</h2>
|
||||
<p>
|
||||
Vous ne pouvez pas venir à une colle ? Aucun problème : il vous suffit de l'échanger !
|
||||
Le <em>Marketplace</em> intégré vous donne la possibilité de récupérer des colles disponibles.
|
||||
</p>
|
||||
|
||||
<h2>Un système interopérable</h2>
|
||||
<p>
|
||||
Vous pouvez synchroniser vos colles avec votre application de calendrier favorite. Il lui suffit de supporter
|
||||
les liens iCalendar. C'est le cas de l'application Calendrier sur iOS, de OneCalendar sur Android et de
|
||||
Mozilla Thunderbird sur GNU/Linux et macOS (et Microsoft Windows).
|
||||
</p>
|
||||
|
||||
<h2>Pensé par un nerd, pour les nerds.</h2>
|
||||
<p>
|
||||
Un système complet d'API REST vous permet d'intégrer ce colloscope à vos programmes tiers.
|
||||
</p>
|
||||
|
||||
<h2>Libre, pour toujours.</h2>
|
||||
<p>
|
||||
Colloscope est distribué avec la licence GNU Affero GPL. Cette licence garantit vos quatre libertés, à savoir :
|
||||
</p>
|
||||
<ol start=0>
|
||||
<li>Exécutez le code librement ;</li>
|
||||
<li>Modifiez le code librement ;</li>
|
||||
<li>Distribuez le code librement ;</li>
|
||||
<li>Distribuez librement des versions modifiées du code.</li>
|
||||
</ol>
|
||||
|
||||
{% endblock %}
|
Loading…
Reference in New Issue