mirror of
https://github.com/ae-utbm/sith.git
synced 2026-10-15 15:09:22 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a26eb4b80b |
No files matched your search
+2
-113
@@ -31,20 +31,10 @@ from django.forms.models import ModelChoiceField, ModelChoiceIterator
|
|||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from club.models import (
|
from club.models import Club, ClubLink, ClubRole, Membership
|
||||||
Club,
|
|
||||||
ClubLink,
|
|
||||||
ClubRole,
|
|
||||||
Mailing,
|
|
||||||
MailingSubscription,
|
|
||||||
Membership,
|
|
||||||
)
|
|
||||||
from core.models import User
|
from core.models import User
|
||||||
from core.views.forms import SelectDateTime
|
from core.views.forms import SelectDateTime
|
||||||
from core.views.widgets.ajax_select import (
|
from core.views.widgets.ajax_select import AutoCompleteSelectUser
|
||||||
AutoCompleteSelectMultipleUser,
|
|
||||||
AutoCompleteSelectUser,
|
|
||||||
)
|
|
||||||
from counter.models import Counter, Selling
|
from counter.models import Counter, Selling
|
||||||
from counter.schemas import SaleFilterSchema
|
from counter.schemas import SaleFilterSchema
|
||||||
|
|
||||||
@@ -131,107 +121,6 @@ class ClubAdminEditForm(ClubEditForm):
|
|||||||
fields = ["name", "parent", "is_active", *ClubEditForm.Meta.fields]
|
fields = ["name", "parent", "is_active", *ClubEditForm.Meta.fields]
|
||||||
|
|
||||||
|
|
||||||
class MailingForm(forms.Form):
|
|
||||||
"""Form handling mailing lists right."""
|
|
||||||
|
|
||||||
ACTION_NEW_MAILING = 1
|
|
||||||
ACTION_NEW_SUBSCRIPTION = 2
|
|
||||||
ACTION_REMOVE_SUBSCRIPTION = 3
|
|
||||||
|
|
||||||
subscription_users = forms.ModelMultipleChoiceField(
|
|
||||||
label=_("Users to add"),
|
|
||||||
help_text=_("Search users to add (one or more)."),
|
|
||||||
required=False,
|
|
||||||
widget=AutoCompleteSelectMultipleUser,
|
|
||||||
queryset=User.objects.all(),
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, club_id, user_id, mailings, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
self.fields["action"] = forms.TypedChoiceField(
|
|
||||||
choices=(
|
|
||||||
(self.ACTION_NEW_MAILING, _("New Mailing")),
|
|
||||||
(self.ACTION_NEW_SUBSCRIPTION, _("Subscribe")),
|
|
||||||
(self.ACTION_REMOVE_SUBSCRIPTION, _("Remove")),
|
|
||||||
),
|
|
||||||
coerce=int,
|
|
||||||
label=_("Action"),
|
|
||||||
initial=1,
|
|
||||||
required=True,
|
|
||||||
widget=forms.HiddenInput(),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Generate bulk removal forms, they are never required
|
|
||||||
for mailing in mailings:
|
|
||||||
self.fields["removal_" + str(mailing.id)] = forms.ModelMultipleChoiceField(
|
|
||||||
mailing.subscriptions.all(),
|
|
||||||
label=_("Remove"),
|
|
||||||
required=False,
|
|
||||||
widget=forms.CheckboxSelectMultiple,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Include fields for handling mailing creation
|
|
||||||
mailing_fields = ("email",)
|
|
||||||
self.fields.update(forms.fields_for_model(Mailing, fields=mailing_fields))
|
|
||||||
for field in mailing_fields:
|
|
||||||
self.fields["mailing_" + field] = self.fields.pop(field)
|
|
||||||
self.fields["mailing_" + field].required = False
|
|
||||||
|
|
||||||
# Include fields for handling subscription creation
|
|
||||||
subscription_fields = ("mailing", "email")
|
|
||||||
self.fields.update(
|
|
||||||
forms.fields_for_model(MailingSubscription, fields=subscription_fields)
|
|
||||||
)
|
|
||||||
for field in subscription_fields:
|
|
||||||
self.fields["subscription_" + field] = self.fields.pop(field)
|
|
||||||
self.fields["subscription_" + field].required = False
|
|
||||||
|
|
||||||
self.fields["subscription_mailing"].queryset = Mailing.objects.filter(
|
|
||||||
club__id=club_id, is_moderated=True
|
|
||||||
)
|
|
||||||
|
|
||||||
def check_required(self, cleaned_data, field):
|
|
||||||
"""If the given field doesn't exist or has no value, add a required error on it."""
|
|
||||||
if not cleaned_data.get(field, None):
|
|
||||||
self.add_error(field, _("This field is required"))
|
|
||||||
|
|
||||||
def clean_subscription_users(self):
|
|
||||||
"""Convert given users into real users and check their validity."""
|
|
||||||
cleaned_data = super().clean()
|
|
||||||
users = []
|
|
||||||
for user in cleaned_data["subscription_users"]:
|
|
||||||
if not user.email:
|
|
||||||
raise forms.ValidationError(
|
|
||||||
_("One of the selected users doesn't have an email address"),
|
|
||||||
code="invalid",
|
|
||||||
)
|
|
||||||
users.append(user)
|
|
||||||
return users
|
|
||||||
|
|
||||||
def clean(self):
|
|
||||||
cleaned_data = super().clean()
|
|
||||||
|
|
||||||
if "action" not in cleaned_data:
|
|
||||||
# If there is no action provided, we can stop here
|
|
||||||
raise forms.ValidationError(_("An action is required"), code="invalid")
|
|
||||||
|
|
||||||
if cleaned_data["action"] == self.ACTION_NEW_MAILING:
|
|
||||||
self.check_required(cleaned_data, "mailing_email")
|
|
||||||
|
|
||||||
if cleaned_data["action"] == self.ACTION_NEW_SUBSCRIPTION:
|
|
||||||
self.check_required(cleaned_data, "subscription_mailing")
|
|
||||||
if not cleaned_data.get(
|
|
||||||
"subscription_users", None
|
|
||||||
) and not cleaned_data.get("subscription_email", None):
|
|
||||||
raise forms.ValidationError(
|
|
||||||
_("You must specify at least an user or an email address"),
|
|
||||||
code="invalid",
|
|
||||||
)
|
|
||||||
|
|
||||||
return cleaned_data
|
|
||||||
|
|
||||||
|
|
||||||
class SellingsForm(forms.Form):
|
class SellingsForm(forms.Form):
|
||||||
begin_date = forms.DateTimeField(
|
begin_date = forms.DateTimeField(
|
||||||
label=_("Begin date"), widget=SelectDateTime, required=False
|
label=_("Begin date"), widget=SelectDateTime, required=False
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# Generated by Django 5.2.17 on 2026-10-15 13:13
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [("club", "0018_clubrole_linked_groups")]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name="mailingsubscription", unique_together=None
|
||||||
|
),
|
||||||
|
migrations.RemoveField(model_name="mailingsubscription", name="user"),
|
||||||
|
migrations.DeleteModel(name="Mailing"),
|
||||||
|
migrations.DeleteModel(name="MailingSubscription"),
|
||||||
|
]
|
||||||
+2
-166
@@ -28,8 +28,7 @@ from functools import reduce
|
|||||||
from typing import Iterable, Self
|
from typing import Iterable, Self
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.core.validators import RegexValidator, validate_email
|
|
||||||
from django.db import ProgrammingError, models, transaction
|
from django.db import ProgrammingError, models, transaction
|
||||||
from django.db.models import Exists, F, OuterRef, Q
|
from django.db.models import Exists, F, OuterRef, Q
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
@@ -41,7 +40,7 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
from ordered_model.models import OrderedModel
|
from ordered_model.models import OrderedModel
|
||||||
|
|
||||||
from core.fields import ResizedImageField
|
from core.fields import ResizedImageField
|
||||||
from core.models import Group, Notification, Page, SithFile, User
|
from core.models import Group, Page, SithFile, User
|
||||||
|
|
||||||
|
|
||||||
class ClubQuerySet(models.QuerySet):
|
class ClubQuerySet(models.QuerySet):
|
||||||
@@ -633,169 +632,6 @@ class Membership(models.Model):
|
|||||||
return User.groups.through.objects.bulk_create(groups, ignore_conflicts=True)
|
return User.groups.through.objects.bulk_create(groups, ignore_conflicts=True)
|
||||||
|
|
||||||
|
|
||||||
class Mailing(models.Model):
|
|
||||||
"""A Mailing list for a club.
|
|
||||||
|
|
||||||
Warning:
|
|
||||||
Remember that mailing lists should be validated by UTBM.
|
|
||||||
"""
|
|
||||||
|
|
||||||
club = models.ForeignKey(
|
|
||||||
Club,
|
|
||||||
verbose_name=_("Club"),
|
|
||||||
related_name="mailings",
|
|
||||||
null=False,
|
|
||||||
blank=False,
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
)
|
|
||||||
email = models.CharField(
|
|
||||||
_("Email address"),
|
|
||||||
unique=True,
|
|
||||||
null=False,
|
|
||||||
blank=False,
|
|
||||||
max_length=256,
|
|
||||||
validators=[
|
|
||||||
RegexValidator(
|
|
||||||
validate_email.user_regex,
|
|
||||||
_("Enter a valid address. Only the root of the address is needed."),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
is_moderated = models.BooleanField(_("is moderated"), default=False)
|
|
||||||
moderator = models.ForeignKey(
|
|
||||||
User,
|
|
||||||
related_name="moderated_mailings",
|
|
||||||
verbose_name=_("moderator"),
|
|
||||||
null=True,
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return "%s - %s" % (self.club, self.email_full)
|
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
|
||||||
if not self.is_moderated:
|
|
||||||
unread_notif_subquery = Notification.objects.filter(
|
|
||||||
user=OuterRef("pk"), type="MAILING_MODERATION", viewed=False
|
|
||||||
)
|
|
||||||
for user in User.objects.filter(
|
|
||||||
~Exists(unread_notif_subquery),
|
|
||||||
groups__id__in=[settings.SITH_GROUP_COM_ADMIN_ID],
|
|
||||||
):
|
|
||||||
Notification(
|
|
||||||
user=user,
|
|
||||||
url=reverse("com:mailing_admin"),
|
|
||||||
type="MAILING_MODERATION",
|
|
||||||
).save(*args, **kwargs)
|
|
||||||
super().save(*args, **kwargs)
|
|
||||||
|
|
||||||
def clean(self):
|
|
||||||
if Mailing.objects.filter(email=self.email).exists():
|
|
||||||
raise ValidationError(_("This mailing list already exists."))
|
|
||||||
if self.can_moderate(self.moderator):
|
|
||||||
self.is_moderated = True
|
|
||||||
else:
|
|
||||||
self.moderator = None
|
|
||||||
super().clean()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def email_full(self):
|
|
||||||
return self.email + "@" + settings.SITH_MAILING_DOMAIN
|
|
||||||
|
|
||||||
def can_moderate(self, user):
|
|
||||||
return user.is_root or user.is_com_admin
|
|
||||||
|
|
||||||
def is_owned_by(self, user):
|
|
||||||
if user.is_anonymous:
|
|
||||||
return False
|
|
||||||
return user.is_root or user.is_com_admin
|
|
||||||
|
|
||||||
def can_view(self, user):
|
|
||||||
return self.club.has_rights_in_club(user)
|
|
||||||
|
|
||||||
def can_be_edited_by(self, user):
|
|
||||||
return self.club.has_rights_in_club(user)
|
|
||||||
|
|
||||||
def delete(self, *args, **kwargs):
|
|
||||||
self.subscriptions.all().delete()
|
|
||||||
super().delete()
|
|
||||||
|
|
||||||
def fetch_format(self):
|
|
||||||
destination = "".join(s.fetch_format() for s in self.subscriptions.all())
|
|
||||||
return f"{self.email}: {destination}"
|
|
||||||
|
|
||||||
|
|
||||||
class MailingSubscription(models.Model):
|
|
||||||
"""Link between user and mailing list."""
|
|
||||||
|
|
||||||
mailing = models.ForeignKey(
|
|
||||||
Mailing,
|
|
||||||
verbose_name=_("Mailing"),
|
|
||||||
related_name="subscriptions",
|
|
||||||
null=False,
|
|
||||||
blank=False,
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
)
|
|
||||||
user = models.ForeignKey(
|
|
||||||
User,
|
|
||||||
verbose_name=_("User"),
|
|
||||||
related_name="mailing_subscriptions",
|
|
||||||
null=True,
|
|
||||||
blank=True,
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
)
|
|
||||||
email = models.EmailField(_("Email address"), blank=False, null=False)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
unique_together = (("user", "email", "mailing"),)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return "(%s) - %s : %s" % (self.mailing, self.get_username, self.email)
|
|
||||||
|
|
||||||
def clean(self):
|
|
||||||
if not self.user and not self.email:
|
|
||||||
raise ValidationError(_("At least user or email is required"))
|
|
||||||
try:
|
|
||||||
if self.user and not self.email:
|
|
||||||
self.email = self.user.email
|
|
||||||
if MailingSubscription.objects.filter(
|
|
||||||
mailing=self.mailing, email=self.email
|
|
||||||
).exists():
|
|
||||||
raise ValidationError(
|
|
||||||
_("This email is already suscribed in this mailing")
|
|
||||||
)
|
|
||||||
except ObjectDoesNotExist:
|
|
||||||
pass
|
|
||||||
super().clean()
|
|
||||||
|
|
||||||
def is_owned_by(self, user):
|
|
||||||
if user.is_anonymous:
|
|
||||||
return False
|
|
||||||
return (
|
|
||||||
self.mailing.club.has_rights_in_club(user)
|
|
||||||
or user.is_root
|
|
||||||
or self.user.is_com_admin
|
|
||||||
)
|
|
||||||
|
|
||||||
def can_be_edited_by(self, user):
|
|
||||||
return self.user is not None and user.id == self.user.id
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def get_email(self):
|
|
||||||
if self.user and not self.email:
|
|
||||||
return self.user.email
|
|
||||||
return self.email
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def get_username(self):
|
|
||||||
if self.user:
|
|
||||||
return str(self.user)
|
|
||||||
return _("Unregistered user")
|
|
||||||
|
|
||||||
def fetch_format(self):
|
|
||||||
return self.get_email + " "
|
|
||||||
|
|
||||||
|
|
||||||
class LinkType(models.Model):
|
class LinkType(models.Model):
|
||||||
"""A link type, in order to group links and give them icons.
|
"""A link type, in order to group links and give them icons.
|
||||||
|
|
||||||
|
|||||||
@@ -15,9 +15,6 @@
|
|||||||
{% trans %}Post in the Weekmail{% endtrans %}
|
{% trans %}Post in the Weekmail{% endtrans %}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% if object.can_roles_be_edited_by(user) %}
|
|
||||||
<li><a href="{{ url("club:club_roles", club_id=object.id) }}"></a></li>
|
|
||||||
{% endif %}
|
|
||||||
{% if object.trombi %}
|
{% if object.trombi %}
|
||||||
<li> <a href="{{ url('trombi:detail', trombi_id=object.trombi.id) }}">{% trans %}Edit Trombi{% endtrans %}</a></li>
|
<li> <a href="{{ url('trombi:detail', trombi_id=object.trombi.id) }}">{% trans %}Edit Trombi{% endtrans %}</a></li>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|||||||
@@ -1,108 +0,0 @@
|
|||||||
{% extends "core/base.jinja" %}
|
|
||||||
{% from 'core/macros.jinja' import select_all_checkbox %}
|
|
||||||
|
|
||||||
{% block title %}
|
|
||||||
{% trans %}Mailing lists{% endtrans %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<b>{% trans %}Remember : mailing lists need to be moderated, if your new created list is not shown wait until moderation takes action{% endtrans %}</b>
|
|
||||||
|
|
||||||
{% if mailings_not_moderated %}
|
|
||||||
<p>{% trans %}Mailing lists waiting for moderation{% endtrans %}</p>
|
|
||||||
<ul>
|
|
||||||
{% for mailing in mailings_not_moderated %}
|
|
||||||
<li>{{ mailing.email_full }}<a href="{{ url('club:mailing_delete', mailing_id=mailing.id) }}"> - {% trans %}Delete{% endtrans %}</a></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if mailings_moderated %}
|
|
||||||
{% for mailing in mailings_moderated %}
|
|
||||||
<h2>{% trans %}Mailing{% endtrans %} {{ mailing.email_full }}
|
|
||||||
{%- if user.is_owner(mailing) -%}
|
|
||||||
<a href="{{ url('club:mailing_delete', mailing_id=mailing.id) }}"> - {% trans %}Delete{% endtrans %}</a>
|
|
||||||
{%- endif -%}
|
|
||||||
</h2>
|
|
||||||
<form method="GET" action="{{ url('club:mailing_generate', mailing_id=mailing.id) }}" style="display:inline-block;">
|
|
||||||
<input type="submit" name="generateMalingList" value="{% trans %}Generate mailing list{% endtrans %}">
|
|
||||||
</form>
|
|
||||||
{% set form_mailing_removal = form["removal_" + mailing.id|string] %}
|
|
||||||
{% if form_mailing_removal.field.choices %}
|
|
||||||
{% set ms = dict(mailing.subscriptions.all() | groupby('id')) %}
|
|
||||||
<form action="{{ url('club:mailing', club_id=club.id) }}" id="{{ form_mailing_removal.auto_id }}" method="post" enctype="multipart/form-data">
|
|
||||||
<p style="margin-bottom: 1em;">{{ select_all_checkbox(form_mailing_removal.auto_id) }}</p>
|
|
||||||
{% csrf_token %}
|
|
||||||
<input hidden type="number" name="{{ form.action.name }}" value="{{ form_actions.REMOVE_SUBSCRIPTION }}" />
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<td>{% trans %}User{% endtrans %}</td>
|
|
||||||
<td>{% trans %}Email{% endtrans %}</td>
|
|
||||||
<td>{% trans %}Delete{% endtrans %}</td>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for widget in form_mailing_removal.subwidgets %}
|
|
||||||
{% set user = ms[widget.data.value.value][0] %}
|
|
||||||
<tr>
|
|
||||||
<td>{{ user.get_username }}</td>
|
|
||||||
<td>{{ user.get_email }}</td>
|
|
||||||
<td>{{ widget.tag() }}</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{{ form_mailing_removal.errors }}
|
|
||||||
<p><input type="submit" value="{% trans %}Remove from mailing list{% endtrans %}" /></p>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
{% else %}
|
|
||||||
<p><b>{% trans %}There is no subscriber for this mailing list{% endtrans %}</b></p>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
{% else %}
|
|
||||||
<p>{% trans %}No mailing list existing for this club{% endtrans %}</p>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<p>{{ form.non_field_errors() }}</p>
|
|
||||||
{% if mailings_moderated %}
|
|
||||||
<h2>{% trans %}New member{% endtrans %}</h2>
|
|
||||||
<form action="{{ url('club:mailing', club_id=club.id) }}" method="post" enctype="multipart/form-data">
|
|
||||||
{% csrf_token %}
|
|
||||||
<p>
|
|
||||||
{{ form.subscription_mailing.errors }}
|
|
||||||
<label for="{{ form.subscription_mailing.id_for_label }}">{{ form.subscription_mailing.label }}</label>
|
|
||||||
{{ form.subscription_mailing }}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
{{ form.subscription_users.errors }}
|
|
||||||
<label for="{{ form.subscription_users.id_for_label }}">{{ form.subscription_users.label }}</label>
|
|
||||||
{{ form.subscription_users }}
|
|
||||||
<span class="helptext">{{ form.subscription_users.help_text }}</span>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
{{ form.subscription_email.errors }}
|
|
||||||
<label for="{{ form.subscription_email.id_for_label }}">{{ form.subscription_email.label }}</label>
|
|
||||||
{{ form.subscription_email }}
|
|
||||||
</p>
|
|
||||||
<input hidden type="number" name="{{ form.action.name }}" value="{{ form_actions.NEW_SUBSCRIPTION }}" />
|
|
||||||
<p><input type="submit" value="{% trans %}Add to mailing list{% endtrans %}" /></p>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<h2>{% trans %}New mailing{% endtrans %}</h2>
|
|
||||||
<form action="{{ url('club:mailing', club_id=club.id) }}" method="post" enctype="multipart/form-data">
|
|
||||||
{% csrf_token %}
|
|
||||||
<p>
|
|
||||||
{{ form.mailing_email.errors }}
|
|
||||||
<label for="{{ form.mailing_email.id_for_label }}">{{ form.mailing_email.label }}</label>
|
|
||||||
{{ form.mailing_email }}
|
|
||||||
</p>
|
|
||||||
<input hidden type="number" name="{{ form.action.name }}" value="{{ form_actions.NEW_MALING }}" />
|
|
||||||
<p><input type="submit" value="{% trans %}Create mailing list{% endtrans %}" /></p>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
@@ -1,328 +0,0 @@
|
|||||||
from django.conf import settings
|
|
||||||
from django.test import TestCase
|
|
||||||
from django.urls import reverse
|
|
||||||
from django.utils import timezone
|
|
||||||
from django.utils.translation import gettext as _
|
|
||||||
from model_bakery import baker
|
|
||||||
|
|
||||||
from club.forms import MailingForm
|
|
||||||
from club.models import Club, ClubRole, Mailing, Membership
|
|
||||||
from core.models import User
|
|
||||||
|
|
||||||
|
|
||||||
class TestMailingForm(TestCase):
|
|
||||||
"""Perform validation tests for MailingForm."""
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setUpTestData(cls):
|
|
||||||
cls.skia = User.objects.get(username="skia")
|
|
||||||
cls.rbatsbak = User.objects.get(username="rbatsbak")
|
|
||||||
cls.krophil = User.objects.get(username="krophil")
|
|
||||||
cls.comunity = User.objects.get(username="comunity")
|
|
||||||
cls.root = User.objects.get(username="root")
|
|
||||||
cls.club = Club.objects.get(id=settings.SITH_PDF_CLUB_ID)
|
|
||||||
cls.mail_url = reverse("club:mailing", kwargs={"club_id": cls.club.id})
|
|
||||||
Membership(
|
|
||||||
user=cls.rbatsbak,
|
|
||||||
club=cls.club,
|
|
||||||
start_date=timezone.now(),
|
|
||||||
role=baker.make(ClubRole, club=cls.club, is_board=True),
|
|
||||||
).save()
|
|
||||||
|
|
||||||
def test_mailing_list_add_no_moderation(self):
|
|
||||||
# Test with Communication admin
|
|
||||||
self.client.force_login(self.comunity)
|
|
||||||
response = self.client.post(
|
|
||||||
self.mail_url,
|
|
||||||
{"action": MailingForm.ACTION_NEW_MAILING, "mailing_email": "foyer"},
|
|
||||||
)
|
|
||||||
self.assertRedirects(response, self.mail_url)
|
|
||||||
response = self.client.get(self.mail_url)
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert "Liste de diffusion foyer@utbm.fr" in response.text
|
|
||||||
|
|
||||||
# Test with Root
|
|
||||||
self.client.force_login(self.root)
|
|
||||||
self.client.post(
|
|
||||||
self.mail_url,
|
|
||||||
{"action": MailingForm.ACTION_NEW_MAILING, "mailing_email": "mde"},
|
|
||||||
)
|
|
||||||
response = self.client.get(self.mail_url)
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert "Liste de diffusion mde@utbm.fr" in response.text
|
|
||||||
|
|
||||||
def test_mailing_list_add_moderation(self):
|
|
||||||
self.client.force_login(self.rbatsbak)
|
|
||||||
self.client.post(
|
|
||||||
self.mail_url,
|
|
||||||
{"action": MailingForm.ACTION_NEW_MAILING, "mailing_email": "mde"},
|
|
||||||
)
|
|
||||||
response = self.client.get(self.mail_url)
|
|
||||||
assert response.status_code == 200
|
|
||||||
content = response.text
|
|
||||||
assert "Liste de diffusion mde@utbm.fr" not in content
|
|
||||||
assert "<p>Listes de diffusions en attente de modération</p>" in content
|
|
||||||
assert "<li>mde@utbm.fr" in content
|
|
||||||
|
|
||||||
def test_mailing_list_forbidden(self):
|
|
||||||
# With anonymous user
|
|
||||||
response = self.client.get(self.mail_url)
|
|
||||||
self.assertContains(response, "", status_code=403)
|
|
||||||
|
|
||||||
# With user not in club
|
|
||||||
self.client.force_login(self.krophil)
|
|
||||||
response = self.client.get(self.mail_url)
|
|
||||||
assert response.status_code == 403
|
|
||||||
|
|
||||||
def test_add_new_subscription_fail_not_moderated(self):
|
|
||||||
self.client.force_login(self.rbatsbak)
|
|
||||||
self.client.post(
|
|
||||||
self.mail_url,
|
|
||||||
{"action": MailingForm.ACTION_NEW_MAILING, "mailing_email": "mde"},
|
|
||||||
)
|
|
||||||
|
|
||||||
self.client.post(
|
|
||||||
self.mail_url,
|
|
||||||
{
|
|
||||||
"action": MailingForm.ACTION_NEW_SUBSCRIPTION,
|
|
||||||
"subscription_users": self.skia.id,
|
|
||||||
"subscription_mailing": Mailing.objects.get(email="mde").id,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
response = self.client.get(self.mail_url)
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert "skia@git.an" not in response.text
|
|
||||||
|
|
||||||
def test_add_new_subscription_success(self):
|
|
||||||
# Prepare mailing list
|
|
||||||
self.client.force_login(self.comunity)
|
|
||||||
self.client.post(
|
|
||||||
self.mail_url,
|
|
||||||
{"action": MailingForm.ACTION_NEW_MAILING, "mailing_email": "mde"},
|
|
||||||
)
|
|
||||||
|
|
||||||
# Add single user
|
|
||||||
self.client.post(
|
|
||||||
self.mail_url,
|
|
||||||
{
|
|
||||||
"action": MailingForm.ACTION_NEW_SUBSCRIPTION,
|
|
||||||
"subscription_users": self.skia.id,
|
|
||||||
"subscription_mailing": Mailing.objects.get(email="mde").id,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
response = self.client.get(self.mail_url)
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert "skia@git.an" in response.text
|
|
||||||
|
|
||||||
# Add multiple users
|
|
||||||
self.client.post(
|
|
||||||
self.mail_url,
|
|
||||||
{
|
|
||||||
"action": MailingForm.ACTION_NEW_SUBSCRIPTION,
|
|
||||||
"subscription_users": (self.comunity.id, self.rbatsbak.id),
|
|
||||||
"subscription_mailing": Mailing.objects.get(email="mde").id,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
response = self.client.get(self.mail_url)
|
|
||||||
assert response.status_code == 200
|
|
||||||
content = response.text
|
|
||||||
assert "richard@git.an" in content
|
|
||||||
assert "comunity@git.an" in content
|
|
||||||
assert "skia@git.an" in content
|
|
||||||
|
|
||||||
# Add arbitrary email
|
|
||||||
self.client.post(
|
|
||||||
self.mail_url,
|
|
||||||
{
|
|
||||||
"action": MailingForm.ACTION_NEW_SUBSCRIPTION,
|
|
||||||
"subscription_email": "arbitrary@git.an",
|
|
||||||
"subscription_mailing": Mailing.objects.get(email="mde").id,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
response = self.client.get(self.mail_url)
|
|
||||||
assert response.status_code == 200
|
|
||||||
content = response.text
|
|
||||||
assert "richard@git.an" in content
|
|
||||||
assert "comunity@git.an" in content
|
|
||||||
assert "skia@git.an" in content
|
|
||||||
assert "arbitrary@git.an" in content
|
|
||||||
|
|
||||||
# Add user and arbitrary email
|
|
||||||
self.client.post(
|
|
||||||
self.mail_url,
|
|
||||||
{
|
|
||||||
"action": MailingForm.ACTION_NEW_SUBSCRIPTION,
|
|
||||||
"subscription_email": "more.arbitrary@git.an",
|
|
||||||
"subscription_users": self.krophil.id,
|
|
||||||
"subscription_mailing": Mailing.objects.get(email="mde").id,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
response = self.client.get(self.mail_url)
|
|
||||||
assert response.status_code == 200
|
|
||||||
content = response.text
|
|
||||||
assert "richard@git.an" in content
|
|
||||||
assert "comunity@git.an" in content
|
|
||||||
assert "skia@git.an" in content
|
|
||||||
assert "arbitrary@git.an" in content
|
|
||||||
assert "more.arbitrary@git.an" in content
|
|
||||||
assert "krophil@git.an" in content
|
|
||||||
|
|
||||||
def test_add_new_subscription_fail_form_errors(self):
|
|
||||||
# Prepare mailing list
|
|
||||||
self.client.force_login(self.comunity)
|
|
||||||
self.client.post(
|
|
||||||
self.mail_url,
|
|
||||||
{"action": MailingForm.ACTION_NEW_MAILING, "mailing_email": "mde"},
|
|
||||||
)
|
|
||||||
|
|
||||||
# Neither email or email is specified
|
|
||||||
response = self.client.post(
|
|
||||||
self.mail_url,
|
|
||||||
{
|
|
||||||
"action": MailingForm.ACTION_NEW_SUBSCRIPTION,
|
|
||||||
"subscription_mailing": Mailing.objects.get(email="mde").id,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert response.status_code
|
|
||||||
self.assertInHTML(
|
|
||||||
_("You must specify at least an user or an email address"),
|
|
||||||
response.text,
|
|
||||||
)
|
|
||||||
|
|
||||||
# No mailing specified
|
|
||||||
response = self.client.post(
|
|
||||||
self.mail_url,
|
|
||||||
{
|
|
||||||
"action": MailingForm.ACTION_NEW_SUBSCRIPTION,
|
|
||||||
"subscription_users": self.krophil.id,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert _("This field is required") in response.text
|
|
||||||
|
|
||||||
# One of the selected users doesn't exist
|
|
||||||
response = self.client.post(
|
|
||||||
self.mail_url,
|
|
||||||
{
|
|
||||||
"action": MailingForm.ACTION_NEW_SUBSCRIPTION,
|
|
||||||
"subscription_users": [789],
|
|
||||||
"subscription_mailing": Mailing.objects.get(email="mde").id,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert response.status_code == 200
|
|
||||||
self.assertInHTML(
|
|
||||||
_("You must specify at least an user or an email address"),
|
|
||||||
response.text,
|
|
||||||
)
|
|
||||||
|
|
||||||
# An user has no email address
|
|
||||||
self.krophil.email = ""
|
|
||||||
self.krophil.save()
|
|
||||||
|
|
||||||
response = self.client.post(
|
|
||||||
self.mail_url,
|
|
||||||
{
|
|
||||||
"action": MailingForm.ACTION_NEW_SUBSCRIPTION,
|
|
||||||
"subscription_users": self.krophil.id,
|
|
||||||
"subscription_mailing": Mailing.objects.get(email="mde").id,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert response.status_code == 200
|
|
||||||
self.assertInHTML(
|
|
||||||
_("One of the selected users doesn't have an email address"),
|
|
||||||
response.text,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.krophil.email = "krophil@git.an"
|
|
||||||
self.krophil.save()
|
|
||||||
|
|
||||||
# An user is added twice
|
|
||||||
|
|
||||||
self.client.post(
|
|
||||||
self.mail_url,
|
|
||||||
{
|
|
||||||
"action": MailingForm.ACTION_NEW_SUBSCRIPTION,
|
|
||||||
"subscription_users": self.krophil.id,
|
|
||||||
"subscription_mailing": Mailing.objects.get(email="mde").id,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
response = self.client.post(
|
|
||||||
self.mail_url,
|
|
||||||
{
|
|
||||||
"action": MailingForm.ACTION_NEW_SUBSCRIPTION,
|
|
||||||
"subscription_users": self.krophil.id,
|
|
||||||
"subscription_mailing": Mailing.objects.get(email="mde").id,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert response.status_code == 200
|
|
||||||
self.assertInHTML(
|
|
||||||
_("This email is already suscribed in this mailing"),
|
|
||||||
response.text,
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_remove_subscription_success(self):
|
|
||||||
# Prepare mailing list
|
|
||||||
self.client.force_login(self.comunity)
|
|
||||||
self.client.post(
|
|
||||||
self.mail_url,
|
|
||||||
{"action": MailingForm.ACTION_NEW_MAILING, "mailing_email": "mde"},
|
|
||||||
)
|
|
||||||
mde = Mailing.objects.get(email="mde")
|
|
||||||
self.client.post(
|
|
||||||
self.mail_url,
|
|
||||||
{
|
|
||||||
"action": MailingForm.ACTION_NEW_SUBSCRIPTION,
|
|
||||||
"subscription_users": (
|
|
||||||
self.comunity.id,
|
|
||||||
self.rbatsbak.id,
|
|
||||||
self.krophil.id,
|
|
||||||
),
|
|
||||||
"subscription_mailing": mde.id,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
response = self.client.get(self.mail_url)
|
|
||||||
assert response.status_code == 200
|
|
||||||
content = response.text
|
|
||||||
|
|
||||||
assert "comunity@git.an" in content
|
|
||||||
assert "richard@git.an" in content
|
|
||||||
assert "krophil@git.an" in content
|
|
||||||
|
|
||||||
# Delete one user
|
|
||||||
self.client.post(
|
|
||||||
self.mail_url,
|
|
||||||
{
|
|
||||||
"action": MailingForm.ACTION_REMOVE_SUBSCRIPTION,
|
|
||||||
"removal_%d" % mde.id: mde.subscriptions.get(user=self.krophil).id,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
response = self.client.get(self.mail_url)
|
|
||||||
assert response.status_code == 200
|
|
||||||
content = response.text
|
|
||||||
|
|
||||||
assert "comunity@git.an" in content
|
|
||||||
assert "richard@git.an" in content
|
|
||||||
assert "krophil@git.an" not in content
|
|
||||||
|
|
||||||
# Delete multiple users
|
|
||||||
self.client.post(
|
|
||||||
self.mail_url,
|
|
||||||
{
|
|
||||||
"action": MailingForm.ACTION_REMOVE_SUBSCRIPTION,
|
|
||||||
"removal_%d" % mde.id: [
|
|
||||||
user.id
|
|
||||||
for user in mde.subscriptions.filter(
|
|
||||||
user__in=[self.rbatsbak, self.comunity]
|
|
||||||
).all()
|
|
||||||
],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
response = self.client.get(self.mail_url)
|
|
||||||
assert response.status_code == 200
|
|
||||||
content = response.text
|
|
||||||
|
|
||||||
assert "comunity@git.an" not in content
|
|
||||||
assert "richard@git.an" not in content
|
|
||||||
assert "krophil@git.an" not in content
|
|
||||||
@@ -29,7 +29,6 @@ from club.views import (
|
|||||||
ClubCreateView,
|
ClubCreateView,
|
||||||
ClubEditView,
|
ClubEditView,
|
||||||
ClubListView,
|
ClubListView,
|
||||||
ClubMailingView,
|
|
||||||
ClubMembersView,
|
ClubMembersView,
|
||||||
ClubOldMembersView,
|
ClubOldMembersView,
|
||||||
ClubPageEditView,
|
ClubPageEditView,
|
||||||
@@ -43,9 +42,6 @@ from club.views import (
|
|||||||
ClubSellingView,
|
ClubSellingView,
|
||||||
ClubToolsView,
|
ClubToolsView,
|
||||||
ClubView,
|
ClubView,
|
||||||
MailingAutoGenerationView,
|
|
||||||
MailingDeleteView,
|
|
||||||
MailingSubscriptionDeleteView,
|
|
||||||
MembershipDeleteView,
|
MembershipDeleteView,
|
||||||
MembershipSetOldView,
|
MembershipSetOldView,
|
||||||
PosterCreateView,
|
PosterCreateView,
|
||||||
@@ -96,22 +92,6 @@ urlpatterns = [
|
|||||||
"<int:club_id>/sellings/csv/", ClubSellingCSVView.as_view(), name="sellings_csv"
|
"<int:club_id>/sellings/csv/", ClubSellingCSVView.as_view(), name="sellings_csv"
|
||||||
),
|
),
|
||||||
path("<int:club_id>/tools/", ClubToolsView.as_view(), name="tools"),
|
path("<int:club_id>/tools/", ClubToolsView.as_view(), name="tools"),
|
||||||
path("<int:club_id>/mailing/", ClubMailingView.as_view(), name="mailing"),
|
|
||||||
path(
|
|
||||||
"<int:mailing_id>/mailing/generate/",
|
|
||||||
MailingAutoGenerationView.as_view(),
|
|
||||||
name="mailing_generate",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"<int:mailing_id>/mailing/delete/",
|
|
||||||
MailingDeleteView.as_view(),
|
|
||||||
name="mailing_delete",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"<int:mailing_subscription_id>/mailing/delete/subscription/",
|
|
||||||
MailingSubscriptionDeleteView.as_view(),
|
|
||||||
name="mailing_subscription_delete",
|
|
||||||
),
|
|
||||||
path(
|
path(
|
||||||
"membership/<int:membership_id>/set_old/",
|
"membership/<int:membership_id>/set_old/",
|
||||||
MembershipSetOldView.as_view(),
|
MembershipSetOldView.as_view(),
|
||||||
|
|||||||
-171
@@ -34,7 +34,6 @@ from django.contrib.auth.mixins import (
|
|||||||
UserPassesTestMixin,
|
UserPassesTestMixin,
|
||||||
)
|
)
|
||||||
from django.contrib.messages.views import SuccessMessageMixin
|
from django.contrib.messages.views import SuccessMessageMixin
|
||||||
from django.core.exceptions import NON_FIELD_ERRORS, PermissionDenied, ValidationError
|
|
||||||
from django.core.paginator import InvalidPage, Paginator
|
from django.core.paginator import InvalidPage, Paginator
|
||||||
from django.db.models import F, Prefetch, Q, Sum
|
from django.db.models import F, Prefetch, Q, Sum
|
||||||
from django.db.models.functions import Length
|
from django.db.models.functions import Length
|
||||||
@@ -59,7 +58,6 @@ from club.forms import (
|
|||||||
ClubRoleFormSet,
|
ClubRoleFormSet,
|
||||||
ClubSearchForm,
|
ClubSearchForm,
|
||||||
JoinClubForm,
|
JoinClubForm,
|
||||||
MailingForm,
|
|
||||||
SellingsForm,
|
SellingsForm,
|
||||||
)
|
)
|
||||||
from club.models import (
|
from club.models import (
|
||||||
@@ -67,8 +65,6 @@ from club.models import (
|
|||||||
ClubLink,
|
ClubLink,
|
||||||
ClubRole,
|
ClubRole,
|
||||||
LinkType,
|
LinkType,
|
||||||
Mailing,
|
|
||||||
MailingSubscription,
|
|
||||||
Membership,
|
Membership,
|
||||||
)
|
)
|
||||||
from com.models import Poster
|
from com.models import Poster
|
||||||
@@ -181,13 +177,6 @@ class ClubTabsMixin(TabedViewMixin):
|
|||||||
"slug": "sellings",
|
"slug": "sellings",
|
||||||
"name": _("Sellings"),
|
"name": _("Sellings"),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"url": reverse(
|
|
||||||
"club:mailing", kwargs={"club_id": self.object.id}
|
|
||||||
),
|
|
||||||
"slug": "mailing",
|
|
||||||
"name": _("Mailing list"),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"url": reverse(
|
"url": reverse(
|
||||||
"club:poster_list", kwargs={"club_id": self.object.id}
|
"club:poster_list", kwargs={"club_id": self.object.id}
|
||||||
@@ -742,166 +731,6 @@ class MembershipDeleteView(PermissionRequiredMixin, DeleteView):
|
|||||||
return reverse_lazy("core:user_clubs", kwargs={"user_id": self.object.user_id})
|
return reverse_lazy("core:user_clubs", kwargs={"user_id": self.object.user_id})
|
||||||
|
|
||||||
|
|
||||||
class ClubMailingView(ClubTabsMixin, CanEditMixin, DetailFormView):
|
|
||||||
"""A list of mailing for a given club."""
|
|
||||||
|
|
||||||
model = Club
|
|
||||||
form_class = MailingForm
|
|
||||||
pk_url_kwarg = "club_id"
|
|
||||||
template_name = "club/mailing.jinja"
|
|
||||||
current_tab = "mailing"
|
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
|
||||||
kwargs = super().get_form_kwargs()
|
|
||||||
kwargs["club_id"] = self.object.id
|
|
||||||
kwargs["user_id"] = self.request.user.id
|
|
||||||
kwargs["mailings"] = self.object.mailings.all()
|
|
||||||
return kwargs
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
kwargs = super().get_context_data(**kwargs)
|
|
||||||
mailings = list(self.object.mailings.all())
|
|
||||||
kwargs["club"] = self.object
|
|
||||||
kwargs["user"] = self.request.user
|
|
||||||
kwargs["mailings"] = mailings
|
|
||||||
kwargs["mailings_moderated"] = [m for m in mailings if m.is_moderated]
|
|
||||||
kwargs["mailings_not_moderated"] = [m for m in mailings if not m.is_moderated]
|
|
||||||
kwargs["form_actions"] = {
|
|
||||||
"NEW_MALING": self.form_class.ACTION_NEW_MAILING,
|
|
||||||
"NEW_SUBSCRIPTION": self.form_class.ACTION_NEW_SUBSCRIPTION,
|
|
||||||
"REMOVE_SUBSCRIPTION": self.form_class.ACTION_REMOVE_SUBSCRIPTION,
|
|
||||||
}
|
|
||||||
return kwargs
|
|
||||||
|
|
||||||
def add_new_mailing(self, cleaned_data) -> ValidationError | None:
|
|
||||||
"""Create a new mailing list from the form."""
|
|
||||||
mailing = Mailing(
|
|
||||||
club=self.object,
|
|
||||||
email=cleaned_data["mailing_email"],
|
|
||||||
moderator=self.request.user,
|
|
||||||
is_moderated=False,
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
mailing.clean()
|
|
||||||
except ValidationError as validation_error:
|
|
||||||
return validation_error
|
|
||||||
mailing.save()
|
|
||||||
return None
|
|
||||||
|
|
||||||
def add_new_subscription(self, cleaned_data) -> ValidationError | None:
|
|
||||||
"""Add mailing subscriptions for each user given and/or for the specified email in form."""
|
|
||||||
users_to_save = []
|
|
||||||
|
|
||||||
for user in cleaned_data["subscription_users"]:
|
|
||||||
sub = MailingSubscription(
|
|
||||||
mailing=cleaned_data["subscription_mailing"], user=user
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
sub.clean()
|
|
||||||
except ValidationError as validation_error:
|
|
||||||
return validation_error
|
|
||||||
|
|
||||||
sub.save()
|
|
||||||
users_to_save.append(sub)
|
|
||||||
|
|
||||||
if cleaned_data["subscription_email"]:
|
|
||||||
sub = MailingSubscription(
|
|
||||||
mailing=cleaned_data["subscription_mailing"],
|
|
||||||
email=cleaned_data["subscription_email"],
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
sub.clean()
|
|
||||||
except ValidationError as validation_error:
|
|
||||||
return validation_error
|
|
||||||
sub.save()
|
|
||||||
|
|
||||||
# Save users after we are sure there is no error
|
|
||||||
for user in users_to_save:
|
|
||||||
user.save()
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def remove_subscription(self, cleaned_data):
|
|
||||||
"""Remove specified users from a mailing list."""
|
|
||||||
fields = [
|
|
||||||
val for key, val in cleaned_data.items() if key.startswith("removal_")
|
|
||||||
]
|
|
||||||
for field in fields:
|
|
||||||
for sub in field:
|
|
||||||
sub.delete()
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
resp = super().form_valid(form)
|
|
||||||
|
|
||||||
cleaned_data = form.clean()
|
|
||||||
error = None
|
|
||||||
|
|
||||||
if cleaned_data["action"] == self.form_class.ACTION_NEW_MAILING:
|
|
||||||
error = self.add_new_mailing(cleaned_data)
|
|
||||||
|
|
||||||
if cleaned_data["action"] == self.form_class.ACTION_NEW_SUBSCRIPTION:
|
|
||||||
error = self.add_new_subscription(cleaned_data)
|
|
||||||
|
|
||||||
if cleaned_data["action"] == self.form_class.ACTION_REMOVE_SUBSCRIPTION:
|
|
||||||
self.remove_subscription(cleaned_data)
|
|
||||||
|
|
||||||
if error:
|
|
||||||
form.add_error(NON_FIELD_ERRORS, error)
|
|
||||||
return self.form_invalid(form)
|
|
||||||
|
|
||||||
return resp
|
|
||||||
|
|
||||||
def get_success_url(self, **kwargs):
|
|
||||||
return reverse("club:mailing", kwargs={"club_id": self.object.id})
|
|
||||||
|
|
||||||
|
|
||||||
class MailingDeleteView(CanEditMixin, DeleteView):
|
|
||||||
model = Mailing
|
|
||||||
template_name = "core/delete_confirm.jinja"
|
|
||||||
pk_url_kwarg = "mailing_id"
|
|
||||||
redirect_page = None
|
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
|
||||||
self.club_id = self.get_object().club.id
|
|
||||||
return super().dispatch(request, *args, **kwargs)
|
|
||||||
|
|
||||||
def get_success_url(self, **kwargs):
|
|
||||||
if self.redirect_page:
|
|
||||||
return reverse_lazy(self.redirect_page)
|
|
||||||
else:
|
|
||||||
return reverse_lazy("club:mailing", kwargs={"club_id": self.club_id})
|
|
||||||
|
|
||||||
|
|
||||||
class MailingSubscriptionDeleteView(CanEditMixin, DeleteView):
|
|
||||||
model = MailingSubscription
|
|
||||||
template_name = "core/delete_confirm.jinja"
|
|
||||||
pk_url_kwarg = "mailing_subscription_id"
|
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
|
||||||
self.club_id = self.get_object().mailing.club.id
|
|
||||||
return super().dispatch(request, *args, **kwargs)
|
|
||||||
|
|
||||||
def get_success_url(self, **kwargs):
|
|
||||||
return reverse_lazy("club:mailing", kwargs={"club_id": self.club_id})
|
|
||||||
|
|
||||||
|
|
||||||
class MailingAutoGenerationView(View):
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
|
||||||
self.mailing = get_object_or_404(Mailing, pk=kwargs["mailing_id"])
|
|
||||||
if not request.user.can_edit(self.mailing):
|
|
||||||
raise PermissionDenied
|
|
||||||
return super().dispatch(request, *args, **kwargs)
|
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
|
||||||
club = self.mailing.club
|
|
||||||
self.mailing.subscriptions.all().delete()
|
|
||||||
members = club.members.ongoing().filter(role__is_board=True)
|
|
||||||
for member in members.all():
|
|
||||||
MailingSubscription(user=member.user, mailing=self.mailing).save()
|
|
||||||
return redirect("club:mailing", club_id=club.id)
|
|
||||||
|
|
||||||
|
|
||||||
class PosterListView(
|
class PosterListView(
|
||||||
PermissionOrClubBoardRequiredMixin, ClubTabsMixin, PosterListBaseView
|
PermissionOrClubBoardRequiredMixin, ClubTabsMixin, PosterListBaseView
|
||||||
):
|
):
|
||||||
|
|||||||
+1
-4
@@ -259,10 +259,7 @@ class Weekmail(models.Model):
|
|||||||
return f"Weekmail {self.id} (sent: {self.sent}) - {self.title}"
|
return f"Weekmail {self.id} (sent: {self.sent}) - {self.title}"
|
||||||
|
|
||||||
def send(self):
|
def send(self):
|
||||||
"""Send the weekmail to all users with the receive weekmail option opt-in.
|
"""Send the weekmail to all users with the receive weekmail option opt-in."""
|
||||||
|
|
||||||
Also send the weekmail to the mailing list in settings.SITH_COM_EMAIL.
|
|
||||||
"""
|
|
||||||
dest = [
|
dest = [
|
||||||
i[0]
|
i[0]
|
||||||
for i in Preferences.objects.filter(receive_weekmail=True).values_list(
|
for i in Preferences.objects.filter(receive_weekmail=True).values_list(
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
padding: 0.4em;
|
padding: 0.4em;
|
||||||
width: 80%;
|
width: 80%;
|
||||||
|
background: $white-color;
|
||||||
|
|
||||||
h4 {
|
h4 {
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
--box-shadow: rgb(60 64 67 / 30%) 0 1px 3px 0, rgb(60 64 67 / 15%) 0 3px 7px 2px;
|
--box-shadow: rgb(60 64 67 / 30%) 0 1px 3px 0, rgb(60 64 67 / 15%) 0 3px 7px 2px;
|
||||||
background: var(--bg-yellow);
|
background: lighten($second-color, 5%);
|
||||||
box-shadow: var(--box-shadow);
|
box-shadow: var(--box-shadow);
|
||||||
padding: .75rem;
|
padding: .75rem;
|
||||||
margin: 0 0 0.5em 0;
|
margin: 0 0 0.5em 0;
|
||||||
@@ -85,7 +85,7 @@
|
|||||||
|
|
||||||
#links_content {
|
#links_content {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
box-shadow: var(--bg-darkened-off-white) 1px 1px 1px;
|
box-shadow: $shadow-color 1px 1px 1px;
|
||||||
min-height: 20em;
|
min-height: 20em;
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
|
|
||||||
@@ -111,7 +111,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#birthdays_content {
|
#birthdays_content {
|
||||||
box-shadow: var(--bg-darkened-off-white) 1px 1px 1px;
|
box-shadow: $shadow-color 1px 1px 1px;
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
|
|
||||||
ul.birthdays_year {
|
ul.birthdays_year {
|
||||||
@@ -123,7 +123,7 @@
|
|||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
|
|
||||||
&:nth-child(even) {
|
&:nth-child(even) {
|
||||||
background: var(--bg-off-white);
|
background: $secondary-neutral-light-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@
|
|||||||
|
|
||||||
/* EVENTS TODAY AND NEXT FEW DAYS */
|
/* EVENTS TODAY AND NEXT FEW DAYS */
|
||||||
.news_events_group {
|
.news_events_group {
|
||||||
box-shadow: var(--bg-darkened-off-white) 1px 1px 1px;
|
box-shadow: $shadow-color 1px 1px 1px;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
|
|
||||||
@@ -153,8 +153,8 @@
|
|||||||
display: table-cell;
|
display: table-cell;
|
||||||
padding: 0.6em;
|
padding: 0.6em;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
background: var(--deepblue);
|
background: $primary-neutral-dark-color;
|
||||||
color: var(--text-white);
|
color: $white-color;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
@@ -180,7 +180,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.news_event:nth-of-type(even) {
|
.news_event:nth-of-type(even) {
|
||||||
background: var(--bg-white);
|
background: $primary-neutral-light-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.news_event {
|
.news_event {
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
{% extends "core/base.jinja" %}
|
|
||||||
|
|
||||||
{% block title %}
|
|
||||||
{% trans %}Mailing lists administration{% endtrans %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% macro display_mailings(list) %}
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<th>{% trans %}Email{% endtrans %}</th>
|
|
||||||
<th>{% trans %}Club{%endtrans%}</th>
|
|
||||||
<th>{% trans %}Actions{% endtrans %}</th>
|
|
||||||
</tr>
|
|
||||||
{% for mailing in list %}
|
|
||||||
<tr>
|
|
||||||
<td>{{ mailing.email_full }}</td>
|
|
||||||
<td><a href="{{ url('club:mailing', club_id=mailing.club.id) }}">{{ mailing.club }}</a></td>
|
|
||||||
<td>
|
|
||||||
<a href="{{ url('com:mailing_delete', mailing_id=mailing.id) }}">{% trans %}Delete{% endtrans %}</a> - {% if not mailing.is_moderated %}<a href="{{ url('com:mailing_moderate', mailing_id=mailing.id) }}">{% trans %}Moderate{% endtrans %}</a>{% else %}{% trans user=mailing.moderator %}Moderated by {{ user }}{% endtrans %}{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
|
|
||||||
{% endmacro %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<h1>{% trans %}This page lists all mailing lists{% endtrans %}</h1>
|
|
||||||
|
|
||||||
{% if has_unmoderated %}
|
|
||||||
<h2>{% trans %}Not moderated mailing lists{% endtrans %}</h2>
|
|
||||||
{{ display_mailings(unmoderated) }}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<h2>{% trans %}Moderated mailing lists{% endtrans %}</h2>
|
|
||||||
{% if has_moderated %}
|
|
||||||
{{ display_mailings(moderated) }}
|
|
||||||
{% else %}
|
|
||||||
<p>{% trans %}No mailing list existing{% endtrans %}</p>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-14
@@ -15,12 +15,9 @@
|
|||||||
|
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
from club.views import MailingDeleteView
|
|
||||||
from com.views import (
|
from com.views import (
|
||||||
AlertMsgEditView,
|
AlertMsgEditView,
|
||||||
InfoMsgEditView,
|
InfoMsgEditView,
|
||||||
MailingListAdminView,
|
|
||||||
MailingModerateView,
|
|
||||||
NewsAdminListView,
|
NewsAdminListView,
|
||||||
NewsCreateView,
|
NewsCreateView,
|
||||||
NewsDeleteView,
|
NewsDeleteView,
|
||||||
@@ -82,17 +79,6 @@ urlpatterns = [
|
|||||||
"news/<int:news_id>/moderate/", NewsModerateView.as_view(), name="news_moderate"
|
"news/<int:news_id>/moderate/", NewsModerateView.as_view(), name="news_moderate"
|
||||||
),
|
),
|
||||||
path("news/<int:news_id>/", NewsDetailView.as_view(), name="news_detail"),
|
path("news/<int:news_id>/", NewsDetailView.as_view(), name="news_detail"),
|
||||||
path("mailings/", MailingListAdminView.as_view(), name="mailing_admin"),
|
|
||||||
path(
|
|
||||||
"mailings/<int:mailing_id>/moderate/",
|
|
||||||
MailingModerateView.as_view(),
|
|
||||||
name="mailing_moderate",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"mailings/<int:mailing_id>/delete/",
|
|
||||||
MailingDeleteView.as_view(redirect_page="com:mailing_admin"),
|
|
||||||
name="mailing_delete",
|
|
||||||
),
|
|
||||||
path("poster/", PosterListView.as_view(), name="poster_list"),
|
path("poster/", PosterListView.as_view(), name="poster_list"),
|
||||||
path("poster/create/", PosterCreateView.as_view(), name="poster_create"),
|
path("poster/create/", PosterCreateView.as_view(), name="poster_create"),
|
||||||
path("poster/<int:poster_id>/edit/", PosterEditView.as_view(), name="poster_edit"),
|
path("poster/<int:poster_id>/edit/", PosterEditView.as_view(), name="poster_edit"),
|
||||||
|
|||||||
+3
-41
@@ -30,11 +30,9 @@ from urllib.parse import urlparse
|
|||||||
from dateutil.relativedelta import relativedelta
|
from dateutil.relativedelta import relativedelta
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.mixins import (
|
from django.contrib.auth.mixins import PermissionRequiredMixin
|
||||||
PermissionRequiredMixin,
|
|
||||||
)
|
|
||||||
from django.contrib.syndication.views import Feed
|
from django.contrib.syndication.views import Feed
|
||||||
from django.core.exceptions import PermissionDenied, ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.db.models import Exists, Max, OuterRef, Value
|
from django.db.models import Exists, Max, OuterRef, Value
|
||||||
from django.forms.models import modelform_factory
|
from django.forms.models import modelform_factory
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
@@ -46,7 +44,7 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
from django.views.generic import DetailView, ListView, TemplateView, View
|
from django.views.generic import DetailView, ListView, TemplateView, View
|
||||||
from django.views.generic.edit import CreateView, DeleteView, UpdateView
|
from django.views.generic.edit import CreateView, DeleteView, UpdateView
|
||||||
|
|
||||||
from club.models import Club, Mailing, Membership
|
from club.models import Club, Membership
|
||||||
from com.forms import NewsDateForm, NewsForm, PosterForm
|
from com.forms import NewsDateForm, NewsForm, PosterForm
|
||||||
from com.ics_calendar import IcsCalendar
|
from com.ics_calendar import IcsCalendar
|
||||||
from com.models import News, NewsDate, Poster, Screen, Sith, Weekmail, WeekmailArticle
|
from com.models import News, NewsDate, Poster, Screen, Sith, Weekmail, WeekmailArticle
|
||||||
@@ -86,11 +84,6 @@ class ComTabsMixin(TabedViewMixin):
|
|||||||
"slug": "alert",
|
"slug": "alert",
|
||||||
"name": _("Alert message"),
|
"name": _("Alert message"),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"url": reverse("com:mailing_admin"),
|
|
||||||
"slug": "mailings",
|
|
||||||
"name": _("Mailing lists administration"),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"url": reverse("com:poster_list"),
|
"url": reverse("com:poster_list"),
|
||||||
"slug": "posters",
|
"slug": "posters",
|
||||||
@@ -533,37 +526,6 @@ class WeekmailArticleDeleteView(CanEditPropMixin, DeleteView):
|
|||||||
pk_url_kwarg = "article_id"
|
pk_url_kwarg = "article_id"
|
||||||
|
|
||||||
|
|
||||||
class MailingListAdminView(ComTabsMixin, ListView):
|
|
||||||
template_name = "com/mailing_admin.jinja"
|
|
||||||
model = Mailing
|
|
||||||
current_tab = "mailings"
|
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
|
||||||
if not (request.user.is_com_admin or request.user.is_root):
|
|
||||||
raise PermissionDenied
|
|
||||||
return super().dispatch(request, *args, **kwargs)
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
kwargs = super().get_context_data(**kwargs)
|
|
||||||
kwargs["moderated"] = self.get_queryset().filter(is_moderated=True).all()
|
|
||||||
kwargs["unmoderated"] = self.get_queryset().filter(is_moderated=False).all()
|
|
||||||
kwargs["has_moderated"] = len(kwargs["moderated"]) > 0
|
|
||||||
kwargs["has_unmoderated"] = len(kwargs["unmoderated"]) > 0
|
|
||||||
return kwargs
|
|
||||||
|
|
||||||
|
|
||||||
class MailingModerateView(View):
|
|
||||||
def get(self, request, *args, **kwargs):
|
|
||||||
mailing = get_object_or_404(Mailing, pk=kwargs["mailing_id"])
|
|
||||||
if mailing.can_moderate(request.user):
|
|
||||||
mailing.is_moderated = True
|
|
||||||
mailing.moderator = request.user
|
|
||||||
mailing.save()
|
|
||||||
return redirect("com:mailing_admin")
|
|
||||||
|
|
||||||
raise PermissionDenied
|
|
||||||
|
|
||||||
|
|
||||||
class PosterListBaseView(ListView):
|
class PosterListBaseView(ListView):
|
||||||
"""List communication posters."""
|
"""List communication posters."""
|
||||||
|
|
||||||
|
|||||||
-16
@@ -1,18 +1,15 @@
|
|||||||
from typing import Annotated, Literal
|
from typing import Annotated, Literal
|
||||||
|
|
||||||
from annotated_types import Ge, Le, MinLen
|
from annotated_types import Ge, Le, MinLen
|
||||||
from django.conf import settings
|
|
||||||
from django.db.models import F
|
from django.db.models import F
|
||||||
from ninja import File, Query
|
from ninja import File, Query
|
||||||
from ninja.security import SessionAuth
|
from ninja.security import SessionAuth
|
||||||
from ninja_extra import ControllerBase, api_controller, paginate, route
|
from ninja_extra import ControllerBase, api_controller, paginate, route
|
||||||
from ninja_extra.exceptions import PermissionDenied
|
|
||||||
from ninja_extra.pagination import PageNumberPaginationExtra
|
from ninja_extra.pagination import PageNumberPaginationExtra
|
||||||
from ninja_extra.schemas import PaginatedResponseSchema
|
from ninja_extra.schemas import PaginatedResponseSchema
|
||||||
|
|
||||||
from api.auth import ApiKeyAuth
|
from api.auth import ApiKeyAuth
|
||||||
from api.permissions import CanAccessLookup, CanView, HasPerm
|
from api.permissions import CanAccessLookup, CanView, HasPerm
|
||||||
from club.models import Mailing
|
|
||||||
from core.models import Group, QuickUploadImage, SithFile, User
|
from core.models import Group, QuickUploadImage, SithFile, User
|
||||||
from core.schemas import (
|
from core.schemas import (
|
||||||
FamilyGodfatherSchema,
|
FamilyGodfatherSchema,
|
||||||
@@ -48,19 +45,6 @@ class UploadController(ControllerBase):
|
|||||||
return image
|
return image
|
||||||
|
|
||||||
|
|
||||||
@api_controller("/mailings")
|
|
||||||
class MailingListController(ControllerBase):
|
|
||||||
@route.get("", response=str)
|
|
||||||
def fetch_mailing_lists(self, key: str):
|
|
||||||
if key != settings.SITH_MAILING_FETCH_KEY:
|
|
||||||
raise PermissionDenied
|
|
||||||
mailings = Mailing.objects.filter(
|
|
||||||
is_moderated=True, club__is_active=True
|
|
||||||
).prefetch_related("subscriptions")
|
|
||||||
data = "\n".join(m.fetch_format() for m in mailings)
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
@api_controller("/user")
|
@api_controller("/user")
|
||||||
class UserController(ControllerBase):
|
class UserController(ControllerBase):
|
||||||
@route.get("", response=list[UserProfileSchema])
|
@route.get("", response=list[UserProfileSchema])
|
||||||
|
|||||||
@@ -0,0 +1,123 @@
|
|||||||
|
/*--------------------------------RESET--------------------------------*/
|
||||||
|
/*--------------------------------RESET--------------------------------*/
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
div,
|
||||||
|
span,
|
||||||
|
applet,
|
||||||
|
object,
|
||||||
|
iframe,
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6,
|
||||||
|
p,
|
||||||
|
blockquote,
|
||||||
|
pre,
|
||||||
|
a,
|
||||||
|
abbr,
|
||||||
|
acronym,
|
||||||
|
address,
|
||||||
|
big,
|
||||||
|
cite,
|
||||||
|
code,
|
||||||
|
del,
|
||||||
|
dfn,
|
||||||
|
em,
|
||||||
|
img,
|
||||||
|
ins,
|
||||||
|
kbd,
|
||||||
|
q,
|
||||||
|
s,
|
||||||
|
samp,
|
||||||
|
small,
|
||||||
|
strike,
|
||||||
|
sub,
|
||||||
|
sup,
|
||||||
|
tt,
|
||||||
|
var,
|
||||||
|
b,
|
||||||
|
u,
|
||||||
|
i,
|
||||||
|
center,
|
||||||
|
dl,
|
||||||
|
dt,
|
||||||
|
dd,
|
||||||
|
ol,
|
||||||
|
ul,
|
||||||
|
li,
|
||||||
|
fieldset,
|
||||||
|
form,
|
||||||
|
label,
|
||||||
|
legend,
|
||||||
|
table,
|
||||||
|
caption,
|
||||||
|
tbody,
|
||||||
|
tfoot,
|
||||||
|
thead,
|
||||||
|
tr,
|
||||||
|
th,
|
||||||
|
td,
|
||||||
|
article,
|
||||||
|
aside,
|
||||||
|
canvas,
|
||||||
|
details,
|
||||||
|
embed,
|
||||||
|
figure,
|
||||||
|
figcaption,
|
||||||
|
footer,
|
||||||
|
header,
|
||||||
|
hgroup,
|
||||||
|
menu,
|
||||||
|
nav,
|
||||||
|
output,
|
||||||
|
ruby,
|
||||||
|
section,
|
||||||
|
summary,
|
||||||
|
time,
|
||||||
|
mark,
|
||||||
|
audio,
|
||||||
|
video {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
font-size: 100%;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
/* HTML5 display-role reset for older browsers */
|
||||||
|
article,
|
||||||
|
aside,
|
||||||
|
details,
|
||||||
|
figcaption,
|
||||||
|
figure,
|
||||||
|
footer,
|
||||||
|
header,
|
||||||
|
hgroup,
|
||||||
|
menu,
|
||||||
|
nav,
|
||||||
|
section {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
ol,
|
||||||
|
ul {
|
||||||
|
/* list-style: none;*/
|
||||||
|
}
|
||||||
|
blockquote,
|
||||||
|
q {
|
||||||
|
quotes: none;
|
||||||
|
}
|
||||||
|
blockquote:before,
|
||||||
|
blockquote:after,
|
||||||
|
q:before,
|
||||||
|
q:after {
|
||||||
|
content: none;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-spacing: 0;
|
||||||
|
}
|
||||||
@@ -1,37 +1,41 @@
|
|||||||
/**
|
$first-color: hsl(220, 100%, 50%);
|
||||||
* Sith theme variables
|
$second-color: hsl(48, 100%, 67%);
|
||||||
*/
|
$primary-color: hsl(219.9, 53.7%, 50%);
|
||||||
:root {
|
$secondary-color: hsl(204, 64%, 44%);
|
||||||
--bg-yellow: #ffe270;
|
$primary-color-text: hsl(0, 0%, 100%);
|
||||||
--bg-white: #f9fafb;
|
$secondary-color-text: hsla(0, 0%, 0%, 0.87);
|
||||||
--bg-off-white: #f0f0f0;
|
|
||||||
--bg-darkened-off-white: #e3e3e3;
|
|
||||||
--bg-lightgrey: #d4d4d4;
|
|
||||||
|
|
||||||
--text-white: #f9fafb;
|
$primary-light-color: hsl(219.8, 46.4%, 64.9%);
|
||||||
--text-black: #2b2b2b;
|
$primary-dark-color: hsl(203, 75%, 40%);
|
||||||
--text-red: #eb2f06;
|
|
||||||
|
|
||||||
--blue: #3b69c4;
|
$secondary-light-color: hsl(40, 68%, 65%);
|
||||||
--lightblue: #1a78b3;
|
$secondary-dark-color: hsl(40, 68%, 35%);
|
||||||
// darkened-deepblue is 10% darker than deepblue
|
|
||||||
// This is explicitely declared, because the `darken` scss function
|
$primary-neutral-color: hsl(219.6, 20.8%, 50%);
|
||||||
// isn't called at compile-time somehow.
|
$primary-neutral-light-color: hsl(0, 0%, 94%);
|
||||||
--deepblue: #354a5f;
|
$primary-neutral-dark-color: hsl(210, 29%, 29%);
|
||||||
--darkened-deepblue: #23313e;
|
|
||||||
}
|
$secondary-neutral-color: hsl(204, 64%, 44%);
|
||||||
|
$secondary-neutral-light-color: hsl(0, 0%, 91%);
|
||||||
|
$secondary-neutral-dark-color: hsl(40, 57.6%, 17%);
|
||||||
|
|
||||||
|
$white-color: hsl(219.6, 20.8%, 98%);
|
||||||
|
$black-color: hsl(0, 0%, 17%);
|
||||||
|
$red-text-color: #eb2f06;
|
||||||
|
$hovered-red-text-color: #ff4d4d;
|
||||||
|
|
||||||
/**
|
|
||||||
* Brand colours.
|
|
||||||
* Those are officially defined by their respective companies,
|
|
||||||
* and so are more appropriate as scss variables.
|
|
||||||
*/
|
|
||||||
$faceblue: hsl(221, 44%, 41%);
|
$faceblue: hsl(221, 44%, 41%);
|
||||||
$twitblue: hsl(206, 82%, 63%);
|
$twitblue: hsl(206, 82%, 63%);
|
||||||
$discordblurple: #7289da;
|
$discordblurple: #7289da;
|
||||||
$instagradient: radial-gradient(circle at 30% 107%, #fdf497 0%, #fdf497 5%, #fd5949 45%, #d6249f 60%, #285AEB 90%);
|
$instagradient: radial-gradient(circle at 30% 107%, #fdf497 0%, #fdf497 5%, #fd5949 45%, #d6249f 60%, #285AEB 90%);
|
||||||
$githubblack: rgb(22, 22, 20);
|
$githubblack: rgb(22, 22, 20);
|
||||||
|
|
||||||
|
$shadow-color: rgb(223, 223, 223);
|
||||||
|
|
||||||
|
$background-button-color: hsl(0, 0%, 95%);
|
||||||
|
|
||||||
|
$deepblue: #354a5f;
|
||||||
|
|
||||||
@mixin shadow {
|
@mixin shadow {
|
||||||
box-shadow: rgba(60, 64, 67, 0.3) 0 1px 3px 0,
|
box-shadow: rgba(60, 64, 67, 0.3) 0 1px 3px 0,
|
||||||
rgba(60, 64, 67, 0.15) 0 4px 8px 3px;
|
rgba(60, 64, 67, 0.15) 0 4px 8px 3px;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
background-color: var(--bg-off-white);
|
background-color: $primary-neutral-light-color;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
position: relative;
|
position: relative;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@@ -30,12 +30,12 @@
|
|||||||
gap: 20px;
|
gap: 20px;
|
||||||
|
|
||||||
&:disabled {
|
&:disabled {
|
||||||
background-color: var(--bg-darkened-off-white);
|
background-color: darken($primary-neutral-light-color, 5%);
|
||||||
opacity: 65%;
|
opacity: 65%;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.clickable:not(:disabled):hover {
|
&.clickable:not(:disabled):hover {
|
||||||
background-color: var(--bg-darkened-off-white);
|
background-color: darken($primary-neutral-light-color, 5%);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.selected {
|
&.selected {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ ui-tab-group {
|
|||||||
.tab-headers {
|
.tab-headers {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: row wrap;
|
flex-flow: row wrap;
|
||||||
background-color: var(--bg-off-white);
|
background-color: $primary-neutral-light-color;
|
||||||
padding: 3px 12px 12px;
|
padding: 3px 12px 12px;
|
||||||
column-gap: 20px;
|
column-gap: 20px;
|
||||||
border-top-left-radius: 5px;
|
border-top-left-radius: 5px;
|
||||||
@@ -25,27 +25,23 @@ ui-tab-group {
|
|||||||
background-color: unset;
|
background-color: unset;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: var(--bg-lightgrey);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-bottom: 4px solid var(--bg-lightgrey);
|
border-bottom: 4px solid darken($primary-neutral-light-color, 10%);
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
transition: all 0.2s ease-in-out;
|
transition: all 0.2s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover:after {
|
&:hover:after {
|
||||||
filter: brightness(85%);
|
border-bottom-color: darken($primary-neutral-light-color, 20%);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.active:after {
|
&.active:after {
|
||||||
border-bottom-color: var(--lightblue);
|
border-bottom-color: $primary-dark-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,75 +2,92 @@
|
|||||||
@import "devices";
|
@import "devices";
|
||||||
|
|
||||||
footer.bottom-links {
|
footer.bottom-links {
|
||||||
> section > a {
|
>section>a {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
hr {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: $small-devices) {
|
@media (max-width: $small-devices) {
|
||||||
margin-top: 0.6em;
|
margin-top: 0.6em;
|
||||||
padding: 1.25em;
|
padding: 1.25em;
|
||||||
background-color: var(--deepblue);
|
background-color: $primary-neutral-dark-color;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
gap: 1.25em;
|
gap: 1.25em;
|
||||||
|
|
||||||
> section {
|
>section {
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 0.8em;
|
gap: 0.8em;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: var(--text-white);
|
color: $white-color;
|
||||||
|
width: auto;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
text-shadow: 0.5px 0.5px 0.5px var(--bg-darkened-off-white);
|
color: $white-color;
|
||||||
|
text-shadow: 0.5px 0.5px 0.5px $shadow-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fa-github {
|
.fa-github {
|
||||||
color: var(--text-white);
|
color: $white-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
border-top: var(--bg-white) 1px solid;
|
width: 100%;
|
||||||
|
height: 0px;
|
||||||
|
border: none;
|
||||||
|
border-top: 0.5px solid $white-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@media (min-width: $small-devices) {
|
@media (min-width: $small-devices) {
|
||||||
width: 90%;
|
width: 90%;
|
||||||
margin: 2em auto;
|
margin: 2em auto;
|
||||||
|
|
||||||
font-size: 90%;
|
font-size: 90%;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
|
||||||
section:first-of-type {
|
section:first-of-type {
|
||||||
margin: 0.6em 0;
|
margin: 0.6em 0;
|
||||||
|
color: $white-color;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background-color: var(--deepblue);
|
background-color: $primary-neutral-dark-color;
|
||||||
|
box-shadow: $shadow-color 0 0 15px;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
|
color: $white-color;
|
||||||
|
width: auto;
|
||||||
padding: 0.8em;
|
padding: 0.8em;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: var(--text-white);
|
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
text-shadow: 0.5px 0.5px 0.5px var(--bg-darkened-off-white);
|
color: $white-color;
|
||||||
|
text-shadow: 0.5px 0.5px 0.5px $shadow-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fa-github {
|
||||||
|
color: $githubblack;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
border: none;
|
||||||
|
height: 5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+14
-10
@@ -16,6 +16,7 @@
|
|||||||
input[type="file"] {
|
input[type="file"] {
|
||||||
border: none;
|
border: none;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
background-color: $background-button-color;
|
||||||
padding: 0.4em;
|
padding: 0.4em;
|
||||||
margin: 0.1em;
|
margin: 0.1em;
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
@@ -23,7 +24,7 @@
|
|||||||
color: black;
|
color: black;
|
||||||
|
|
||||||
&:not(.link-like):not(:disabled):hover {
|
&:not(.link-like):not(:disabled):hover {
|
||||||
background: var(--bg-lightgrey);
|
background: hsl(0, 0%, 83%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +53,7 @@
|
|||||||
.ts-control {
|
.ts-control {
|
||||||
border: none;
|
border: none;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
//background-color: $background-button-color;
|
background-color: $background-button-color;
|
||||||
padding: 0.4em;
|
padding: 0.4em;
|
||||||
margin: 0.1em;
|
margin: 0.1em;
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
@@ -63,7 +64,7 @@
|
|||||||
textarea {
|
textarea {
|
||||||
border: none;
|
border: none;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
//background-color: $background-button-color;
|
background-color: $background-button-color;
|
||||||
padding: 7px;
|
padding: 7px;
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
@@ -74,7 +75,7 @@
|
|||||||
border: none;
|
border: none;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
//background-color: $background-button-color;
|
background-color: $background-button-color;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -82,14 +83,14 @@
|
|||||||
|
|
||||||
a:not(.button) {
|
a:not(.button) {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: var(--lightblue);
|
color: $primary-dark-color;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: var(--blue);
|
color: $primary-light-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:active {
|
&:active {
|
||||||
color: var(--blue);
|
color: $primary-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -761,13 +762,16 @@ form {
|
|||||||
// a submit input that should look like a regular <a>
|
// a submit input that should look like a regular <a>
|
||||||
input[type="submit"], button {
|
input[type="submit"], button {
|
||||||
&.link-like {
|
&.link-like {
|
||||||
color: var(--lightblue);
|
color: $primary-dark-color;
|
||||||
&:hover {
|
&:hover {
|
||||||
color: var(--blue);
|
color: $primary-light-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.link-red {
|
&.link-red {
|
||||||
color: var(--text-red);
|
color: $red-text-color;
|
||||||
|
&:hover {
|
||||||
|
color: $hovered-red-text-color;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-size: 100%;
|
font-size: 100%;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ $background-color-hovered: #283747;
|
|||||||
|
|
||||||
.header {
|
.header {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background-color: var(--deepblue);
|
background-color: $deepblue;
|
||||||
box-shadow: 3px 3px 3px 0 #dfdfdf;
|
box-shadow: 3px 3px 3px 0 #dfdfdf;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -96,7 +96,7 @@ $background-color-hovered: #283747;
|
|||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background-color: var(--deepblue);
|
background-color: $deepblue;
|
||||||
width: 45px;
|
width: 45px;
|
||||||
height: 25px;
|
height: 25px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@@ -212,7 +212,7 @@ $background-color-hovered: #283747;
|
|||||||
background-position: center center;
|
background-position: center center;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-color: var(--deepblue);
|
background-color: $deepblue;
|
||||||
}
|
}
|
||||||
|
|
||||||
>.options {
|
>.options {
|
||||||
@@ -298,7 +298,7 @@ $background-color-hovered: #283747;
|
|||||||
width: 10px;
|
width: 10px;
|
||||||
height: 10px;
|
height: 10px;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
background-color: var(--text-red);
|
background-color: $red-text-color;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -50%;
|
top: -50%;
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
.markdown {
|
.markdown {
|
||||||
|
--bg-lightgrey: rgb(240, 241, 245);
|
||||||
|
--border-lightgrey: rgb(215, 216, 220);
|
||||||
|
|
||||||
h1,
|
h1,
|
||||||
h2,
|
h2,
|
||||||
h3,
|
h3,
|
||||||
@@ -25,8 +28,8 @@
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
tab-size: 4;
|
tab-size: 4;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
border: 1px solid var(--bg-lightgrey);
|
border: 1px solid var(--border-lightgrey);
|
||||||
background-color: var(--bg-off-white);
|
background-color: var(--bg-lightgrey);
|
||||||
padding: 1px 2px;
|
padding: 1px 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
$desktop-size: 500px;
|
$desktop-size: 500px;
|
||||||
|
|
||||||
nav.navbar {
|
nav.navbar {
|
||||||
background-color: var(--lightblue);
|
background-color: $primary-dark-color;
|
||||||
margin: 1em;
|
margin: 1em;
|
||||||
color: white;
|
color: white;
|
||||||
border-radius: 0.6em;
|
border-radius: 0.6em;
|
||||||
|
|||||||
@@ -6,21 +6,27 @@
|
|||||||
margin: 30px;
|
margin: 30px;
|
||||||
|
|
||||||
button {
|
button {
|
||||||
background-color: var(--bg-off-white);
|
background-color: $secondary-neutral-light-color;
|
||||||
min-width: 37px;
|
min-width: 37px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--bg-lightgrey);
|
background-color: darken($secondary-neutral-light-color, 10%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
color: #fff;
|
||||||
|
background-color: darken($secondary-neutral-light-color, 5%);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.active,
|
&.active,
|
||||||
&.active:hover {
|
&.active:hover {
|
||||||
background-color: var(--deepblue);
|
background-color: $primary-neutral-color;
|
||||||
color: white;
|
color: white;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination-arrow {
|
&:first-of-type,
|
||||||
|
&:last-of-type {
|
||||||
// previous and next buttons
|
// previous and next buttons
|
||||||
&:disabled {
|
&:disabled {
|
||||||
// Hide the arrows when they are disabled, without
|
// Hide the arrows when they are disabled, without
|
||||||
|
|||||||
+101
-59
@@ -2,21 +2,12 @@
|
|||||||
@import "forms";
|
@import "forms";
|
||||||
@import "devices";
|
@import "devices";
|
||||||
|
|
||||||
/*----- RESET -----*/
|
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*--------------------------------GENERAL------------------------------*/
|
/*--------------------------------GENERAL------------------------------*/
|
||||||
|
|
||||||
body {
|
body {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
background-color: var(--bg-white);
|
|
||||||
line-height: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[aria-busy] {
|
[aria-busy] {
|
||||||
@@ -66,7 +57,7 @@ body {
|
|||||||
|
|
||||||
.collapse-header {
|
.collapse-header {
|
||||||
color: white;
|
color: white;
|
||||||
background-color: var(--deepblue);
|
background-color: $deepblue;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -150,7 +141,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#info_box {
|
#info_box {
|
||||||
background: var(--bg-off-white);
|
background: $primary-neutral-light-color;
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
content: "\f05a";
|
content: "\f05a";
|
||||||
@@ -159,11 +150,11 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#alert_box {
|
#alert_box {
|
||||||
background: var(--bg-yellow);
|
background: $second-color;
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
content: "\f06a";
|
content: "\f06a";
|
||||||
color: var(--text-white);
|
color: $white-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -198,10 +189,10 @@ body {
|
|||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
|
||||||
&.btn-blue {
|
&.btn-blue {
|
||||||
background-color: var(--deepblue);
|
background-color: $deepblue;
|
||||||
|
|
||||||
&:not(:disabled):hover {
|
&:not(:disabled):hover {
|
||||||
background-color: var(--darkened-deepblue);
|
background-color: darken($deepblue, 10%);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:disabled {
|
&:disabled {
|
||||||
@@ -224,7 +215,7 @@ body {
|
|||||||
&.btn-green {
|
&.btn-green {
|
||||||
$bg-color: rgba(0, 210, 83, 0.4);
|
$bg-color: rgba(0, 210, 83, 0.4);
|
||||||
background-color: $bg-color;
|
background-color: $bg-color;
|
||||||
color: var(--text-black);
|
color: $black-color;
|
||||||
|
|
||||||
&:not(:disabled):hover {
|
&:not(:disabled):hover {
|
||||||
background-color: darken($bg-color, 15%);
|
background-color: darken($bg-color, 15%);
|
||||||
@@ -281,8 +272,8 @@ body {
|
|||||||
/*--------------------------------CONTENT------------------------------*/
|
/*--------------------------------CONTENT------------------------------*/
|
||||||
#content {
|
#content {
|
||||||
padding: 1.5em 3%;
|
padding: 1.5em 3%;
|
||||||
box-shadow: var(--bg-darkened-off-white) 0 5px 10px;
|
box-shadow: $shadow-color 0 5px 10px;
|
||||||
background: var(--bg-white);
|
background: $white-color;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -352,7 +343,7 @@ body {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
float: right;
|
float: right;
|
||||||
background: var(--bg-off-white);
|
background: $primary-neutral-light-color;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -360,7 +351,7 @@ body {
|
|||||||
a {
|
a {
|
||||||
padding: 7px;
|
padding: 7px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
color: var(--text-black);
|
color: $black-color;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin: 2px;
|
margin: 2px;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
@@ -368,14 +359,14 @@ body {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
&.selected_tab {
|
&.selected_tab {
|
||||||
background: var(--blue);
|
background: $primary-color;
|
||||||
color: var(--text-white);
|
color: $white-color;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: var(--blue);
|
background: $primary-color;
|
||||||
color: var(--text-white);
|
color: $white-color;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -407,33 +398,28 @@ body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
/* Fontawesome icons */
|
||||||
|
.fa-brands, .fa-link {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
/* Fontawesome icons */
|
.fa-facebook {
|
||||||
.fa-brands, .fa-link {
|
color: $faceblue;
|
||||||
color: black;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.fa-facebook {
|
.fa-discord {
|
||||||
color: $faceblue;
|
color: $discordblurple;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fa-discord {
|
.fa-square-instagram::before, .fa-instagram::before {
|
||||||
color: $discordblurple;
|
background: $instagradient;
|
||||||
}
|
background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
.fa-square-instagram::before, .fa-instagram::before {
|
.fa-bluesky, .fa-square-bluesky {
|
||||||
background: $instagradient;
|
color: #0f73ff;
|
||||||
background-clip: text;
|
}
|
||||||
-webkit-text-fill-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fa-bluesky, .fa-square-bluesky {
|
|
||||||
color: #0f73ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fa-github {
|
|
||||||
color: $githubblack;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: $small-devices) {
|
@media screen and (max-width: $small-devices) {
|
||||||
@@ -495,6 +481,16 @@ dt {
|
|||||||
margin-top: 25px;
|
margin-top: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.edit-bar {
|
||||||
|
display: block;
|
||||||
|
margin: 4px;
|
||||||
|
|
||||||
|
a {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.important {
|
.important {
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
@@ -507,6 +503,7 @@ table {
|
|||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
border-spacing: 0;
|
border-spacing: 0;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
-moz-border-radius: 5px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-shadow: rgba(60, 64, 67, 0.3) 0 1px 3px 0,
|
box-shadow: rgba(60, 64, 67, 0.3) 0 1px 3px 0,
|
||||||
rgba(60, 64, 67, 0.15) 0 4px 8px 3px;
|
rgba(60, 64, 67, 0.15) 0 4px 8px 3px;
|
||||||
@@ -518,21 +515,25 @@ table {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
td,
|
td,
|
||||||
th {
|
th {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
|
|
||||||
> input[type="checkbox"] {
|
>input[type="checkbox"] {
|
||||||
padding: unset;
|
padding: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
> ul {
|
>ul {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
> input[type="checkbox"] {
|
>input[type="checkbox"] {
|
||||||
padding: unset;
|
padding: unset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -542,6 +543,10 @@ td {
|
|||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
|
>ul {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
th,
|
th,
|
||||||
@@ -551,23 +556,23 @@ thead td {
|
|||||||
}
|
}
|
||||||
|
|
||||||
thead {
|
thead {
|
||||||
background-color: var(--deepblue);
|
background-color: $deepblue;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
tbody > tr {
|
tbody>tr {
|
||||||
&:nth-child(even):not(.highlight) {
|
&:nth-child(even):not(.highlight) {
|
||||||
background: var(--bg-off-white);
|
background: $primary-neutral-light-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.clickable:hover {
|
&.clickable:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
background: var(--bg-lightgrey);
|
background: $secondary-neutral-light-color;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.highlight {
|
&.highlight {
|
||||||
color: var(--lightblue);
|
color: $primary-dark-color;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -593,7 +598,7 @@ em {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.highlight {
|
.highlight {
|
||||||
background: var(--bg-yellow);
|
background: $second-color;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -695,15 +700,15 @@ u,
|
|||||||
height: 18em;
|
height: 18em;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
box-shadow: var(--bg-darkened-off-white) 1px 1px 1px;
|
box-shadow: $shadow-color 1px 1px 1px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
box-shadow: 1px 1px 5px var(--bg-yellow);
|
box-shadow: 1px 1px 5px $second-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.matmat_user a {
|
.matmat_user a {
|
||||||
color: var(--deepblue);
|
color: $primary-neutral-dark-color;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@@ -718,6 +723,7 @@ u,
|
|||||||
display: block;
|
display: block;
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
background: $white-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
textarea {
|
textarea {
|
||||||
@@ -725,7 +731,43 @@ textarea {
|
|||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.ui-dialog .ui-dialog-buttonpane {
|
||||||
|
bottom: 0;
|
||||||
|
position: absolute;
|
||||||
|
width: 97%;
|
||||||
|
}
|
||||||
|
|
||||||
#cash_summary_form label,
|
#cash_summary_form label,
|
||||||
.inline {
|
.inline {
|
||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form_button {
|
||||||
|
width: 150px;
|
||||||
|
height: 120px;
|
||||||
|
padding: 2px;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 0.8em;
|
||||||
|
|
||||||
|
span {
|
||||||
|
width: 70px;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 50px;
|
||||||
|
max-height: 50px;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -71,7 +71,7 @@ main {
|
|||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background-color: var(--bg-white);
|
background-color: $primary-neutral-light-color;
|
||||||
|
|
||||||
> span {
|
> span {
|
||||||
font-size: small;
|
font-size: small;
|
||||||
|
|||||||
@@ -62,7 +62,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:enabled>.fa {
|
&:enabled>.fa {
|
||||||
background-color: var(--deepblue);
|
background-color: #354a5f;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
<meta property="og:image" content="{{ request.build_absolute_uri(static("core/img/logo_no_text.png")) }}" />
|
<meta property="og:image" content="{{ request.build_absolute_uri(static("core/img/logo_no_text.png")) }}" />
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
<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('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/tooltips.scss') }}">
|
<link rel="stylesheet" href="{{ static('core/tooltips.scss') }}">
|
||||||
<link rel="stylesheet" href="{{ static('core/markdown.scss') }}">
|
<link rel="stylesheet" href="{{ static('core/markdown.scss') }}">
|
||||||
|
|||||||
@@ -102,7 +102,6 @@
|
|||||||
@click.prevent="{{ page }}--"
|
@click.prevent="{{ page }}--"
|
||||||
:disabled="{{ page }} <= 1"
|
:disabled="{{ page }} <= 1"
|
||||||
@keyup.right.window="{{ page }} = Math.min({{ nb_pages }}, {{ page }} + 1)"
|
@keyup.right.window="{{ page }} = Math.min({{ nb_pages }}, {{ page }} + 1)"
|
||||||
class="pagination-arrow"
|
|
||||||
>
|
>
|
||||||
<i class="fa fa-caret-left"></i>
|
<i class="fa fa-caret-left"></i>
|
||||||
</button>
|
</button>
|
||||||
@@ -113,7 +112,6 @@
|
|||||||
@click.prevent="{{ page }}++"
|
@click.prevent="{{ page }}++"
|
||||||
:disabled="{{ page }} >= {{ nb_pages }}"
|
:disabled="{{ page }} >= {{ nb_pages }}"
|
||||||
@keyup.left.window="{{ page }} = Math.max(1, {{ page }} - 1)"
|
@keyup.left.window="{{ page }} = Math.max(1, {{ page }} - 1)"
|
||||||
class="pagination-arrow"
|
|
||||||
>
|
>
|
||||||
<i class="fa fa-caret-right"></i>
|
<i class="fa fa-caret-right"></i>
|
||||||
</button>
|
</button>
|
||||||
@@ -165,14 +163,12 @@
|
|||||||
href="?{{ querystring(request, page=current_page.previous_page_number()) }}"
|
href="?{{ querystring(request, page=current_page.previous_page_number()) }}"
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
>
|
>
|
||||||
<button class="pagination-arrow">
|
<button>
|
||||||
<i class="fa fa-caret-left"></i>
|
<i class="fa fa-caret-left"></i>
|
||||||
</button>
|
</button>
|
||||||
</a>
|
</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<button disabled="disabled" class="pagination-arrow">
|
<button disabled="disabled"><i class="fa fa-caret-left"></i></button>
|
||||||
<i class="fa fa-caret-left"></i>
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% for i in paginator.get_elided_page_range(current_page.number) %}
|
{% for i in paginator.get_elided_page_range(current_page.number) %}
|
||||||
{% if i == current_page.number %}
|
{% if i == current_page.number %}
|
||||||
@@ -205,14 +201,12 @@
|
|||||||
{%- else -%}
|
{%- else -%}
|
||||||
href="?{{querystring(request, page=current_page.next_page_number())}}"
|
href="?{{querystring(request, page=current_page.next_page_number())}}"
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
><button class="pagination-arrow">
|
><button>
|
||||||
<i class="fa fa-caret-right"></i>
|
<i class="fa fa-caret-right"></i>
|
||||||
</button>
|
</button>
|
||||||
</a>
|
</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<button disabled="disabled" class="pagination-arrow">
|
<button disabled="disabled"><i class="fa fa-caret-right"></i></button>
|
||||||
<i class="fa fa-caret-right"></i>
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</nav>
|
</nav>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|||||||
@@ -79,15 +79,5 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
{% if
|
|
||||||
profile.mailing_subscriptions.exists()
|
|
||||||
and (profile.id == user.id or user.is_root or user.is_com_admin)
|
|
||||||
%}
|
|
||||||
<h4>{% trans %}Subscribed mailing lists{% endtrans %}</h4>
|
|
||||||
{% for sub in profile.mailing_subscriptions.all() %}
|
|
||||||
<p>{{ sub.mailing.email }} <a href="{{ url('club:mailing_subscription_delete', mailing_subscription_id=sub.id) }}">{% trans %}Unsubscribe{% endtrans %}</a></p>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
@@ -133,7 +133,6 @@
|
|||||||
<li><a href="{{ url('com:alert_edit') }}">{% trans %}Edit alert message{% endtrans %}</a></li>
|
<li><a href="{{ url('com:alert_edit') }}">{% trans %}Edit alert message{% endtrans %}</a></li>
|
||||||
<li><a href="{{ url('com:info_edit') }}">{% trans %}Edit information message{% endtrans %}</a></li>
|
<li><a href="{{ url('com:info_edit') }}">{% trans %}Edit information message{% endtrans %}</a></li>
|
||||||
<li><a href="{{ url('core:file_moderation') }}">{% trans %}Moderate files{% endtrans %}</a></li>
|
<li><a href="{{ url('core:file_moderation') }}">{% trans %}Moderate files{% endtrans %}</a></li>
|
||||||
<li><a href="{{ url('com:mailing_admin') }}">{% trans %}Mailing lists administration{% endtrans %}</a></li>
|
|
||||||
<li><a href="{{ url('com:poster_list') }}">{% trans %}Posters{% endtrans %}</a></li>
|
<li><a href="{{ url('com:poster_list') }}">{% trans %}Posters{% endtrans %}</a></li>
|
||||||
<li><a href="{{ url('com:screen_list') }}">{% trans %}Screens{% endtrans %}</a></li>
|
<li><a href="{{ url('com:screen_list') }}">{% trans %}Screens{% endtrans %}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@
|
|||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
margin: 0.2em;
|
margin: 0.2em;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: var(--bg-off-white);
|
background: $secondary-neutral-light-color;
|
||||||
|
|
||||||
img {
|
img {
|
||||||
max-width: 70%;
|
max-width: 70%;
|
||||||
|
|||||||
@@ -67,7 +67,7 @@
|
|||||||
.fa-plus,
|
.fa-plus,
|
||||||
.fa-minus {
|
.fa-minus {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
background-color: var(--deepblue);
|
background-color: #354a5f;
|
||||||
color: white;
|
color: white;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
|
|||||||
@@ -288,7 +288,7 @@ $min_col_width: 100px;
|
|||||||
border: none;
|
border: none;
|
||||||
color: black;
|
color: black;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
background-color: var(--bg-white);
|
background-color: $primary-neutral-light-color;
|
||||||
padding: 0.4em;
|
padding: 0.4em;
|
||||||
margin: 0.1em;
|
margin: 0.1em;
|
||||||
font-size: 1.18em;
|
font-size: 1.18em;
|
||||||
|
|||||||
@@ -13,8 +13,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.topic {
|
.topic {
|
||||||
border: solid var(--darkened-deepblue) 1px;
|
border: solid $primary-neutral-color 1px;
|
||||||
border-radius: 5px;
|
|
||||||
padding: 1px;
|
padding: 1px;
|
||||||
margin: 1px;
|
margin: 1px;
|
||||||
|
|
||||||
@@ -24,7 +23,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: var(--text-black);
|
color: $black-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
a:hover {
|
a:hover {
|
||||||
@@ -57,7 +56,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.forum {
|
.forum {
|
||||||
background: var(--bg-white);
|
background: $primary-neutral-light-color;
|
||||||
padding: 1px;
|
padding: 1px;
|
||||||
margin: 1px;
|
margin: 1px;
|
||||||
|
|
||||||
@@ -67,7 +66,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: var(--text-black);
|
color: $black-color;
|
||||||
}
|
}
|
||||||
a:hover {
|
a:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
@@ -92,7 +91,7 @@
|
|||||||
|
|
||||||
.category {
|
.category {
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
background: var(--deepblue);
|
background: $secondary-color;
|
||||||
color: white;
|
color: white;
|
||||||
border-radius: 10px 10px 0 0;
|
border-radius: 10px 10px 0 0;
|
||||||
.title {
|
.title {
|
||||||
@@ -106,12 +105,12 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
background: var(--bg-off-white);
|
background: $secondary-neutral-light-color;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
border: 1px var(--bg-lightgrey) solid;
|
border: 1px darken($secondary-neutral-light-color, 10%) solid;
|
||||||
|
|
||||||
&:nth-child(odd) {
|
&:nth-child(odd) {
|
||||||
background: var(--bg-white);
|
background: $primary-neutral-light-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-header {
|
.message-header {
|
||||||
|
|||||||
+50
-207
@@ -6,7 +6,7 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2026-10-09 21:44+0200\n"
|
"POT-Creation-Date: 2026-10-15 15:20+0200\n"
|
||||||
"PO-Revision-Date: 2016-07-18\n"
|
"PO-Revision-Date: 2016-07-18\n"
|
||||||
"Last-Translator: Maréchal <thomas.girod@utbm.fr\n"
|
"Last-Translator: Maréchal <thomas.girod@utbm.fr\n"
|
||||||
"Language-Team: AE info <ae.info@utbm.fr>\n"
|
"Language-Team: AE info <ae.info@utbm.fr>\n"
|
||||||
@@ -192,46 +192,6 @@ msgstr ""
|
|||||||
"arrivée durant l'interaction avec l'application tierce. Veuillez contacter "
|
"arrivée durant l'interaction avec l'application tierce. Veuillez contacter "
|
||||||
"les responsables de cette dernière."
|
"les responsables de cette dernière."
|
||||||
|
|
||||||
#: club/forms.py
|
|
||||||
msgid "Users to add"
|
|
||||||
msgstr "Utilisateurs à ajouter"
|
|
||||||
|
|
||||||
#: club/forms.py core/views/group.py
|
|
||||||
msgid "Search users to add (one or more)."
|
|
||||||
msgstr "Recherche les utilisateurs à ajouter (un ou plus)."
|
|
||||||
|
|
||||||
#: club/forms.py
|
|
||||||
msgid "New Mailing"
|
|
||||||
msgstr "Nouvelle mailing liste"
|
|
||||||
|
|
||||||
#: club/forms.py
|
|
||||||
msgid "Subscribe"
|
|
||||||
msgstr "S'abonner"
|
|
||||||
|
|
||||||
#: club/forms.py
|
|
||||||
msgid "Remove"
|
|
||||||
msgstr "Retirer"
|
|
||||||
|
|
||||||
#: club/forms.py counter/forms.py pedagogy/templates/pedagogy/moderation.jinja
|
|
||||||
msgid "Action"
|
|
||||||
msgstr "Action"
|
|
||||||
|
|
||||||
#: club/forms.py club/tests/test_mailing.py
|
|
||||||
msgid "This field is required"
|
|
||||||
msgstr "Ce champ est obligatoire"
|
|
||||||
|
|
||||||
#: club/forms.py club/tests/test_mailing.py
|
|
||||||
msgid "One of the selected users doesn't have an email address"
|
|
||||||
msgstr "Un des utilisateurs sélectionnés n'a pas d'adresse email"
|
|
||||||
|
|
||||||
#: club/forms.py
|
|
||||||
msgid "An action is required"
|
|
||||||
msgstr "Une action est requise"
|
|
||||||
|
|
||||||
#: club/forms.py club/tests/test_mailing.py
|
|
||||||
msgid "You must specify at least an user or an email address"
|
|
||||||
msgstr "vous devez spécifier au moins un utilisateur ou une adresse email"
|
|
||||||
|
|
||||||
#: club/forms.py counter/forms.py
|
#: club/forms.py counter/forms.py
|
||||||
msgid "Begin date"
|
msgid "Begin date"
|
||||||
msgstr "Date de début"
|
msgstr "Date de début"
|
||||||
@@ -414,60 +374,6 @@ msgstr "rôle"
|
|||||||
msgid "past member"
|
msgid "past member"
|
||||||
msgstr "ancien membre"
|
msgstr "ancien membre"
|
||||||
|
|
||||||
#: club/models.py com/templates/com/mailing_admin.jinja
|
|
||||||
#: com/templates/com/news_admin_list.jinja com/templates/com/weekmail.jinja
|
|
||||||
#: core/templates/core/user_clubs.jinja
|
|
||||||
#: counter/templates/counter/invoices_call.jinja
|
|
||||||
#: trombi/templates/trombi/edit_profile.jinja
|
|
||||||
#: trombi/templates/trombi/export.jinja
|
|
||||||
#: trombi/templates/trombi/user_profile.jinja
|
|
||||||
msgid "Club"
|
|
||||||
msgstr "Club"
|
|
||||||
|
|
||||||
#: club/models.py
|
|
||||||
msgid "Email address"
|
|
||||||
msgstr "Adresse email"
|
|
||||||
|
|
||||||
#: club/models.py
|
|
||||||
msgid "Enter a valid address. Only the root of the address is needed."
|
|
||||||
msgstr ""
|
|
||||||
"Entrez une adresse valide. Seule la racine de l'adresse est nécessaire."
|
|
||||||
|
|
||||||
#: club/models.py com/models.py core/models.py
|
|
||||||
msgid "is moderated"
|
|
||||||
msgstr "est modéré"
|
|
||||||
|
|
||||||
#: club/models.py com/models.py
|
|
||||||
msgid "moderator"
|
|
||||||
msgstr "modérateur"
|
|
||||||
|
|
||||||
#: club/models.py
|
|
||||||
msgid "This mailing list already exists."
|
|
||||||
msgstr "Cette liste de diffusion existe déjà."
|
|
||||||
|
|
||||||
#: club/models.py club/templates/club/mailing.jinja
|
|
||||||
msgid "Mailing"
|
|
||||||
msgstr "Liste de diffusion"
|
|
||||||
|
|
||||||
#: club/models.py club/templates/club/club_members.jinja
|
|
||||||
#: club/templates/club/club_old_members.jinja club/templates/club/mailing.jinja
|
|
||||||
#: counter/templates/counter/cash_summary_list.jinja
|
|
||||||
#: counter/templates/counter/stats.jinja
|
|
||||||
msgid "User"
|
|
||||||
msgstr "Utilisateur"
|
|
||||||
|
|
||||||
#: club/models.py
|
|
||||||
msgid "At least user or email is required"
|
|
||||||
msgstr "Au moins un utilisateur ou un email est nécessaire"
|
|
||||||
|
|
||||||
#: club/models.py club/tests/test_mailing.py
|
|
||||||
msgid "This email is already suscribed in this mailing"
|
|
||||||
msgstr "Cet email est déjà abonné à cette mailing"
|
|
||||||
|
|
||||||
#: club/models.py
|
|
||||||
msgid "Unregistered user"
|
|
||||||
msgstr "Utilisateur non enregistré"
|
|
||||||
|
|
||||||
#: club/models.py
|
#: club/models.py
|
||||||
msgid "The base url that links with this type must respect"
|
msgid "The base url that links with this type must respect"
|
||||||
msgstr "L'url de base que tous les liens de ce type doivent respecter"
|
msgstr "L'url de base que tous les liens de ce type doivent respecter"
|
||||||
@@ -554,6 +460,13 @@ msgstr "Membres du club"
|
|||||||
msgid "Manage roles"
|
msgid "Manage roles"
|
||||||
msgstr "Gérer les rôles"
|
msgstr "Gérer les rôles"
|
||||||
|
|
||||||
|
#: club/templates/club/club_members.jinja
|
||||||
|
#: club/templates/club/club_old_members.jinja
|
||||||
|
#: counter/templates/counter/cash_summary_list.jinja
|
||||||
|
#: counter/templates/counter/stats.jinja
|
||||||
|
msgid "User"
|
||||||
|
msgstr "Utilisateur"
|
||||||
|
|
||||||
#: club/templates/club/club_members.jinja
|
#: club/templates/club/club_members.jinja
|
||||||
#: club/templates/club/club_old_members.jinja
|
#: club/templates/club/club_old_members.jinja
|
||||||
#: core/templates/core/user_clubs.jinja
|
#: core/templates/core/user_clubs.jinja
|
||||||
@@ -817,8 +730,7 @@ msgstr "Total"
|
|||||||
msgid "Payment method"
|
msgid "Payment method"
|
||||||
msgstr "Méthode de paiement"
|
msgstr "Méthode de paiement"
|
||||||
|
|
||||||
#: club/templates/club/club_sellings.jinja club/templates/club/mailing.jinja
|
#: club/templates/club/club_sellings.jinja com/templates/com/macros.jinja
|
||||||
#: com/templates/com/macros.jinja com/templates/com/mailing_admin.jinja
|
|
||||||
#: com/templates/com/news_admin_list.jinja com/templates/com/poster_edit.jinja
|
#: com/templates/com/news_admin_list.jinja com/templates/com/poster_edit.jinja
|
||||||
#: com/templates/com/screen_edit.jinja com/templates/com/weekmail.jinja
|
#: com/templates/com/screen_edit.jinja com/templates/com/weekmail.jinja
|
||||||
#: core/templates/core/file_detail.jinja
|
#: core/templates/core/file_detail.jinja
|
||||||
@@ -944,60 +856,6 @@ msgstr "Ajouter"
|
|||||||
msgid "Join"
|
msgid "Join"
|
||||||
msgstr "Rejoindre"
|
msgstr "Rejoindre"
|
||||||
|
|
||||||
#: club/templates/club/mailing.jinja
|
|
||||||
msgid "Mailing lists"
|
|
||||||
msgstr "Mailing listes"
|
|
||||||
|
|
||||||
#: club/templates/club/mailing.jinja
|
|
||||||
msgid ""
|
|
||||||
"Remember : mailing lists need to be moderated, if your new created list is "
|
|
||||||
"not shown wait until moderation takes action"
|
|
||||||
msgstr ""
|
|
||||||
"Rappelez vous : les mailing listes doivent être modérées, si votre liste "
|
|
||||||
"nouvellement créée n'est pas affichée, attendez jusqu'à ce qu'un modérateur "
|
|
||||||
"prenne une décision"
|
|
||||||
|
|
||||||
#: club/templates/club/mailing.jinja
|
|
||||||
msgid "Mailing lists waiting for moderation"
|
|
||||||
msgstr "Listes de diffusions en attente de modération"
|
|
||||||
|
|
||||||
#: club/templates/club/mailing.jinja
|
|
||||||
msgid "Generate mailing list"
|
|
||||||
msgstr "Générer la liste de diffusion"
|
|
||||||
|
|
||||||
#: club/templates/club/mailing.jinja com/templates/com/mailing_admin.jinja
|
|
||||||
msgid "Email"
|
|
||||||
msgstr "Email"
|
|
||||||
|
|
||||||
#: club/templates/club/mailing.jinja
|
|
||||||
msgid "Remove from mailing list"
|
|
||||||
msgstr "Supprimer de la liste de diffusion"
|
|
||||||
|
|
||||||
#: club/templates/club/mailing.jinja
|
|
||||||
msgid "There is no subscriber for this mailing list"
|
|
||||||
msgstr "Il n'y a pas d'abonnés dans cette liste de diffusion"
|
|
||||||
|
|
||||||
#: club/templates/club/mailing.jinja
|
|
||||||
msgid "No mailing list existing for this club"
|
|
||||||
msgstr "Aucune mailing liste n'existe pour ce club"
|
|
||||||
|
|
||||||
#: club/templates/club/mailing.jinja
|
|
||||||
#: subscription/templates/subscription/subscription.jinja
|
|
||||||
msgid "New member"
|
|
||||||
msgstr "Nouveau membre"
|
|
||||||
|
|
||||||
#: club/templates/club/mailing.jinja
|
|
||||||
msgid "Add to mailing list"
|
|
||||||
msgstr "Ajouter à la mailing liste"
|
|
||||||
|
|
||||||
#: club/templates/club/mailing.jinja
|
|
||||||
msgid "New mailing"
|
|
||||||
msgstr "Nouvelle liste de diffusion"
|
|
||||||
|
|
||||||
#: club/templates/club/mailing.jinja
|
|
||||||
msgid "Create mailing list"
|
|
||||||
msgstr "Créer une liste de diffusion"
|
|
||||||
|
|
||||||
#: club/templates/club/pagerev_edit.jinja core/templates/core/page/edit.jinja
|
#: club/templates/club/pagerev_edit.jinja core/templates/core/page/edit.jinja
|
||||||
msgid "Edit page"
|
msgid "Edit page"
|
||||||
msgstr "Éditer la page"
|
msgstr "Éditer la page"
|
||||||
@@ -1043,10 +901,6 @@ msgstr "Éditer la page de club"
|
|||||||
msgid "Sellings"
|
msgid "Sellings"
|
||||||
msgstr "Vente"
|
msgstr "Vente"
|
||||||
|
|
||||||
#: club/views.py
|
|
||||||
msgid "Mailing list"
|
|
||||||
msgstr "Listes de diffusion"
|
|
||||||
|
|
||||||
#: club/views.py
|
#: club/views.py
|
||||||
msgid "You are now a member of this club."
|
msgid "You are now a member of this club."
|
||||||
msgstr "Vous êtes maintenant membre de ce club."
|
msgstr "Vous êtes maintenant membre de ce club."
|
||||||
@@ -1177,6 +1031,10 @@ msgstr "auteur"
|
|||||||
msgid "is published"
|
msgid "is published"
|
||||||
msgstr "est publié"
|
msgstr "est publié"
|
||||||
|
|
||||||
|
#: com/models.py
|
||||||
|
msgid "moderator"
|
||||||
|
msgstr "modérateur"
|
||||||
|
|
||||||
#: com/models.py
|
#: com/models.py
|
||||||
msgid "news"
|
msgid "news"
|
||||||
msgstr "nouvelle"
|
msgstr "nouvelle"
|
||||||
@@ -1237,6 +1095,10 @@ msgstr "fichier"
|
|||||||
msgid "display time"
|
msgid "display time"
|
||||||
msgstr "temps d'affichage"
|
msgstr "temps d'affichage"
|
||||||
|
|
||||||
|
#: com/models.py core/models.py
|
||||||
|
msgid "is moderated"
|
||||||
|
msgstr "est modéré"
|
||||||
|
|
||||||
#: com/models.py
|
#: com/models.py
|
||||||
msgid "Begin date should be before end date"
|
msgid "Begin date should be before end date"
|
||||||
msgstr "La date de début doit être avant celle de fin"
|
msgstr "La date de début doit être avant celle de fin"
|
||||||
@@ -1272,46 +1134,6 @@ msgstr "Nouvelle publiée"
|
|||||||
msgid "News deleted"
|
msgid "News deleted"
|
||||||
msgstr "Nouvelle supprimée"
|
msgstr "Nouvelle supprimée"
|
||||||
|
|
||||||
#: com/templates/com/mailing_admin.jinja com/views.py
|
|
||||||
#: core/templates/core/user_tools.jinja
|
|
||||||
msgid "Mailing lists administration"
|
|
||||||
msgstr "Administration des mailing listes"
|
|
||||||
|
|
||||||
#: com/templates/com/mailing_admin.jinja
|
|
||||||
#: com/templates/com/news_admin_list.jinja com/templates/com/weekmail.jinja
|
|
||||||
#: counter/templates/counter/refilling_list.jinja
|
|
||||||
msgid "Actions"
|
|
||||||
msgstr "Actions"
|
|
||||||
|
|
||||||
#: com/templates/com/mailing_admin.jinja com/templates/com/poster_list.jinja
|
|
||||||
#: core/templates/core/file_detail.jinja
|
|
||||||
#: core/templates/core/file_moderation.jinja
|
|
||||||
#: pedagogy/templates/pedagogy/fragments/ue_detail/comments.jinja
|
|
||||||
#: sas/templates/sas/moderation.jinja sas/templates/sas/picture.jinja
|
|
||||||
msgid "Moderate"
|
|
||||||
msgstr "Modérer"
|
|
||||||
|
|
||||||
#: com/templates/com/mailing_admin.jinja
|
|
||||||
#, python-format
|
|
||||||
msgid "Moderated by %(user)s"
|
|
||||||
msgstr "Modéré par %(user)s"
|
|
||||||
|
|
||||||
#: com/templates/com/mailing_admin.jinja
|
|
||||||
msgid "This page lists all mailing lists"
|
|
||||||
msgstr "Cette page liste toutes les listes de diffusion"
|
|
||||||
|
|
||||||
#: com/templates/com/mailing_admin.jinja
|
|
||||||
msgid "Not moderated mailing lists"
|
|
||||||
msgstr "Listes de diffusion non modérées"
|
|
||||||
|
|
||||||
#: com/templates/com/mailing_admin.jinja
|
|
||||||
msgid "Moderated mailing lists"
|
|
||||||
msgstr "Modérer les listes de diffusion"
|
|
||||||
|
|
||||||
#: com/templates/com/mailing_admin.jinja
|
|
||||||
msgid "No mailing list existing"
|
|
||||||
msgstr "Aucune liste de diffusion existante"
|
|
||||||
|
|
||||||
#: com/templates/com/news_admin_list.jinja
|
#: com/templates/com/news_admin_list.jinja
|
||||||
msgid "News admin"
|
msgid "News admin"
|
||||||
msgstr "Administration des nouvelles"
|
msgstr "Administration des nouvelles"
|
||||||
@@ -1344,6 +1166,15 @@ msgstr "Titre"
|
|||||||
msgid "Summary"
|
msgid "Summary"
|
||||||
msgstr "Résumé"
|
msgstr "Résumé"
|
||||||
|
|
||||||
|
#: com/templates/com/news_admin_list.jinja com/templates/com/weekmail.jinja
|
||||||
|
#: core/templates/core/user_clubs.jinja
|
||||||
|
#: counter/templates/counter/invoices_call.jinja
|
||||||
|
#: trombi/templates/trombi/edit_profile.jinja
|
||||||
|
#: trombi/templates/trombi/export.jinja
|
||||||
|
#: trombi/templates/trombi/user_profile.jinja
|
||||||
|
msgid "Club"
|
||||||
|
msgstr "Club"
|
||||||
|
|
||||||
#: com/templates/com/news_admin_list.jinja com/templates/com/weekmail.jinja
|
#: com/templates/com/news_admin_list.jinja com/templates/com/weekmail.jinja
|
||||||
#: forum/templates/forum/forum.jinja sas/models.py
|
#: forum/templates/forum/forum.jinja sas/models.py
|
||||||
msgid "Author"
|
msgid "Author"
|
||||||
@@ -1358,6 +1189,11 @@ msgstr "Modérateur"
|
|||||||
msgid "Dates"
|
msgid "Dates"
|
||||||
msgstr "Dates"
|
msgstr "Dates"
|
||||||
|
|
||||||
|
#: com/templates/com/news_admin_list.jinja com/templates/com/weekmail.jinja
|
||||||
|
#: counter/templates/counter/refilling_list.jinja
|
||||||
|
msgid "Actions"
|
||||||
|
msgstr "Actions"
|
||||||
|
|
||||||
#: com/templates/com/news_admin_list.jinja core/templates/core/file.jinja
|
#: com/templates/com/news_admin_list.jinja core/templates/core/file.jinja
|
||||||
#: core/templates/core/page/base.jinja
|
#: core/templates/core/page/base.jinja
|
||||||
msgid "View"
|
msgid "View"
|
||||||
@@ -1518,6 +1354,13 @@ msgstr "Créer"
|
|||||||
msgid "Click to expand"
|
msgid "Click to expand"
|
||||||
msgstr "Cliquez pour agrandir"
|
msgstr "Cliquez pour agrandir"
|
||||||
|
|
||||||
|
#: com/templates/com/poster_list.jinja core/templates/core/file_detail.jinja
|
||||||
|
#: core/templates/core/file_moderation.jinja
|
||||||
|
#: pedagogy/templates/pedagogy/fragments/ue_detail/comments.jinja
|
||||||
|
#: sas/templates/sas/moderation.jinja sas/templates/sas/picture.jinja
|
||||||
|
msgid "Moderate"
|
||||||
|
msgstr "Modérer"
|
||||||
|
|
||||||
#: com/templates/com/poster_list.jinja
|
#: com/templates/com/poster_list.jinja
|
||||||
msgid "No posters"
|
msgid "No posters"
|
||||||
msgstr "Aucune affiche"
|
msgstr "Aucune affiche"
|
||||||
@@ -2828,14 +2671,6 @@ msgstr "Clubs actuels : "
|
|||||||
msgid "Old club(s) :"
|
msgid "Old club(s) :"
|
||||||
msgstr "Anciens clubs :"
|
msgstr "Anciens clubs :"
|
||||||
|
|
||||||
#: core/templates/core/user_clubs.jinja
|
|
||||||
msgid "Subscribed mailing lists"
|
|
||||||
msgstr "Mailing listes abonnées"
|
|
||||||
|
|
||||||
#: core/templates/core/user_clubs.jinja
|
|
||||||
msgid "Unsubscribe"
|
|
||||||
msgstr "Se désabonner"
|
|
||||||
|
|
||||||
#: core/templates/core/user_detail.jinja
|
#: core/templates/core/user_detail.jinja
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "%(user_name)s's profile"
|
msgid "%(user_name)s's profile"
|
||||||
@@ -3317,6 +3152,10 @@ msgstr "%s est déjà votre fillot/fillote"
|
|||||||
msgid "Users to add to group"
|
msgid "Users to add to group"
|
||||||
msgstr "Utilisateurs à ajouter au groupe"
|
msgstr "Utilisateurs à ajouter au groupe"
|
||||||
|
|
||||||
|
#: core/views/group.py
|
||||||
|
msgid "Search users to add (one or more)."
|
||||||
|
msgstr "Recherche les utilisateurs à ajouter (un ou plus)."
|
||||||
|
|
||||||
#: core/views/group.py
|
#: core/views/group.py
|
||||||
msgid "Users to remove from group"
|
msgid "Users to remove from group"
|
||||||
msgstr "Utilisateurs à retirer du groupe"
|
msgstr "Utilisateurs à retirer du groupe"
|
||||||
@@ -3416,6 +3255,10 @@ msgstr ""
|
|||||||
"temps, mais les utilisateurs suivants ont été définis comme les deux : "
|
"temps, mais les utilisateurs suivants ont été définis comme les deux : "
|
||||||
"%(users)s"
|
"%(users)s"
|
||||||
|
|
||||||
|
#: counter/forms.py pedagogy/templates/pedagogy/moderation.jinja
|
||||||
|
msgid "Action"
|
||||||
|
msgstr "Action"
|
||||||
|
|
||||||
#: counter/forms.py
|
#: counter/forms.py
|
||||||
msgid "Date and time of action"
|
msgid "Date and time of action"
|
||||||
msgstr "Date et heure de l'action"
|
msgstr "Date et heure de l'action"
|
||||||
@@ -5862,10 +5705,6 @@ msgstr "Cursus alternant (-20%)"
|
|||||||
msgid "A new poster needs to be moderated"
|
msgid "A new poster needs to be moderated"
|
||||||
msgstr "Une nouvelle affiche a besoin d'être modérée"
|
msgstr "Une nouvelle affiche a besoin d'être modérée"
|
||||||
|
|
||||||
#: sith/settings.py
|
|
||||||
msgid "A new mailing list needs to be moderated"
|
|
||||||
msgstr "Une nouvelle mailing list a besoin d'être modérée"
|
|
||||||
|
|
||||||
#: sith/settings.py
|
#: sith/settings.py
|
||||||
msgid "A new pedagogy comment has been signaled for moderation"
|
msgid "A new pedagogy comment has been signaled for moderation"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -5994,6 +5833,10 @@ msgstr "Toutes les cotisations"
|
|||||||
msgid "Existing member"
|
msgid "Existing member"
|
||||||
msgstr "Membre existant"
|
msgstr "Membre existant"
|
||||||
|
|
||||||
|
#: subscription/templates/subscription/subscription.jinja
|
||||||
|
msgid "New member"
|
||||||
|
msgstr "Nouveau membre"
|
||||||
|
|
||||||
#: subscription/views.py
|
#: subscription/views.py
|
||||||
msgid "the groups that can create subscriptions"
|
msgid "the groups that can create subscriptions"
|
||||||
msgstr "les groupes pouvant créer des cotisations"
|
msgstr "les groupes pouvant créer des cotisations"
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ $pedagogy-white-text: #f0f0f0;
|
|||||||
overflow-wrap: break-word;
|
overflow-wrap: break-word;
|
||||||
|
|
||||||
.closed td.title {
|
.closed td.title {
|
||||||
|
color: lighten($black-color, 10%);
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,6 +69,9 @@ $pedagogy-white-text: #f0f0f0;
|
|||||||
min-width: 300px;
|
min-width: 300px;
|
||||||
}
|
}
|
||||||
.info-container {
|
.info-container {
|
||||||
|
// Darken the background color just enough to make this part
|
||||||
|
// stand out a little, while not being too perceptible
|
||||||
|
background: darken($white-color, 1%);
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
@@ -154,9 +158,9 @@ $pedagogy-white-text: #f0f0f0;
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
background: var(--bg-off-white);
|
background: $primary-neutral-light-color;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
border: 1px var(--bg-lightgrey) solid;
|
border: 1px darken($secondary-neutral-light-color, 10%) solid;
|
||||||
|
|
||||||
&.reported {
|
&.reported {
|
||||||
border: #fc8181 1px solid;
|
border: #fc8181 1px solid;
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ main {
|
|||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
line-height: 1.2em;
|
line-height: 1.2em;
|
||||||
color: black;
|
color: black;
|
||||||
background-color: var(--bg-white);
|
background-color: $primary-neutral-light-color;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:disabled {
|
&:disabled {
|
||||||
background-color: var(--bg-white);
|
background-color: $primary-neutral-light-color;
|
||||||
color: #d4d4d4;
|
color: #d4d4d4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -239,7 +239,7 @@
|
|||||||
>.tools {
|
>.tools {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
.btn {
|
.btn {
|
||||||
background-color: var(--bg-white);
|
background-color: $primary-neutral-light-color;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -621,7 +621,6 @@ with open(
|
|||||||
|
|
||||||
SITH_NOTIFICATIONS = [
|
SITH_NOTIFICATIONS = [
|
||||||
("POSTER_MODERATION", _("A new poster needs to be moderated")),
|
("POSTER_MODERATION", _("A new poster needs to be moderated")),
|
||||||
("MAILING_MODERATION", _("A new mailing list needs to be moderated")),
|
|
||||||
(
|
(
|
||||||
"PEDAGOGY_MODERATION",
|
"PEDAGOGY_MODERATION",
|
||||||
_("A new pedagogy comment has been signaled for moderation"),
|
_("A new pedagogy comment has been signaled for moderation"),
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
display: block;
|
display: block;
|
||||||
margin: 2em auto;
|
margin: 2em auto;
|
||||||
.header {
|
.header {
|
||||||
background-color: var(--bg-white);
|
background-color: $white-color;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
width: calc(100% - var(--hour-side-width) - 10px);
|
width: calc(100% - var(--hour-side-width) - 10px);
|
||||||
|
|||||||
Reference in new issue
Block a user