Amélioration des pages utilisateurs pour les petits écrans (#578, #520)

- Refonte de l'organisation des pages utilisateurs (principalement du front)
  - Page des parrains/fillots
  - Page d'édition du profil
  - Page du profil
  - Page des outils
  - Page des préférences
  - Page des stats utilisateurs

- Refonte du CSS / organisation de la navbar principale (en haut de l'écran)
- Refonte du CSS de la navbar bleu clair (le menu)
- Refonte du CSS du SAS :
  - Page de photo
  - Page d'albums
This commit is contained in:
Julien Constant 2023-03-30 14:38:40 +02:00 committed by GitHub
parent 6c1fa6de0b
commit 28f397574f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 3415 additions and 1406 deletions

View File

@ -36,6 +36,7 @@ from django.core.exceptions import ValidationError
from django.utils import timezone from django.utils import timezone
from core import utils
from core.models import User, Preferences, RealGroup, Notification, SithFile from core.models import User, Preferences, RealGroup, Notification, SithFile
from club.models import Club from club.models import Club
@ -46,6 +47,7 @@ class Sith(models.Model):
alert_msg = models.TextField(_("alert message"), default="", blank=True) alert_msg = models.TextField(_("alert message"), default="", blank=True)
info_msg = models.TextField(_("info message"), default="", blank=True) info_msg = models.TextField(_("info message"), default="", blank=True)
weekmail_destinations = models.TextField(_("weekmail destinations"), default="") weekmail_destinations = models.TextField(_("weekmail destinations"), default="")
version = utils.get_git_revision_short_hash()
def is_owned_by(self, user): def is_owned_by(self, user):
return user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID) return user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)

View File

@ -13,7 +13,7 @@ class Migration(migrations.Migration):
model_name="preferences", model_name="preferences",
name="receive_weekmail", name="receive_weekmail",
field=models.BooleanField( field=models.BooleanField(
default=False, verbose_name="do you want to receive the weekmail" default=False, verbose_name="receive the weekmail"
), ),
) )
] ]

View File

@ -783,9 +783,7 @@ class Preferences(models.Model):
user = models.OneToOneField( user = models.OneToOneField(
User, related_name="_preferences", on_delete=models.CASCADE User, related_name="_preferences", on_delete=models.CASCADE
) )
receive_weekmail = models.BooleanField( receive_weekmail = models.BooleanField(_("receive the Weekmail"), default=False)
_("do you want to receive the weekmail"), default=False
)
show_my_stats = models.BooleanField(_("show your stats to others"), default=False) show_my_stats = models.BooleanField(_("show your stats to others"), default=False)
notify_on_click = models.BooleanField( notify_on_click = models.BooleanField(
_("get a notification for every click"), default=False _("get a notification for every click"), default=False

View File

@ -0,0 +1,408 @@
.header {
box-sizing: border-box;
background-color: #354a5f;
box-shadow: 3px 3px 3px 0 #dfdfdf;
border-radius: 0;
width: 100%;
display: flex;
flex-direction: row;
flex-wrap: wrap;
padding: 10px;
gap: 10px;
@media (max-width: 700px) {
height: auto;
}
@media (max-width: 580px) {
justify-content: space-between;
}
&-logo {
display: flex;
flex-direction: row;
gap: 10px;
&:hover > a {
color: #1a78b3;
}
@media (max-width: 607px) {
width: 100%;
justify-content: center;
}
&-picture {
height: 100%;
width: 65px;
display: flex;
background-position: center center;
background-size: contain;
background-repeat: no-repeat;
@media (max-width: 580px) {
height: auto;
}
}
&-text {
color: white;
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
> span:first-child {
font-size: 1.43em;
}
> span:last-child {
font-size: .7em;
}
}
}
&-lang {
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 3px;
@media (max-width: 580px) {
flex-direction: row;
}
> form {
margin: 0;
box-sizing: border-box;
position: relative;
> input[type=submit] {
border-radius: 0;
margin: 0;
box-sizing: border-box;
background-color: #354a5f;
width: 45px;
height: 25px;
padding: 0;
color: white;
font-weight: normal;
line-height: 1.3em;
&:hover {
background-color: #283747;
}
}
}
}
&-disconnected {
box-sizing: border-box;
flex: 1;
display: flex;
justify-content: flex-end;
align-items: center;
@media (max-width: 607px) {
justify-content: center;
}
> .button {
box-sizing: border-box;
height: 35px;
background-color: transparent;
font-weight: normal;
padding: 5px 20px;
display: flex;
justify-content: center;
align-items: center;
text-transform: uppercase;
text-decoration: none;
color: white;
margin: 0;
font-size: .9em;
width: 120px;
&:hover {
background-color: #283747;
}
}
}
&-disconnected ~ &-lang {
@media (max-width: 662px) {
flex-direction: row;
width: 100%;
}
}
&-connected {
box-sizing: border-box;
flex: 1;
display: flex;
flex-direction: row;
@media (min-width: 400px) and (max-width: 1200px) {
flex-direction: column;
min-width: 100%;
justify-content: center;
align-items: center;
gap: 10px;
}
@media (max-width: 400px) {
flex-direction: column;
width: 100%;
gap: 10px;
padding: 0 10px;
}
> .right,
> .left {
box-sizing: border-box;
display: flex;
flex-direction: row;
align-items: center;
@media (min-width: 400px) and (max-width: 1200px) {
width: 100%;
justify-content: space-between;
padding: 0 20px;
}
}
> .right {
flex: 1;
justify-content: flex-end;
> .user {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 15px;
@media (max-width: 1200px) {
width: 100%;
flex-direction: row-reverse;
}
> a > img {
width: 40px;
height: 40px;
border-radius: 50%;
}
> .options {
width: 100%;
display: flex;
justify-content: flex-end;
flex-direction: column;
gap: 5px;
@media (max-width: 1200px) {
justify-content: flex-start;
flex-direction: row;
gap: 15px;
}
> a {
text-align: right;
color: white;
&:hover {
color: #1a78b3;
}
&:last-child {
color: #eb2f06;
&:hover {
color: #cc2804;
}
}
}
}
}
> .notification {
height: 100%;
width: 55px;
display: flex;
justify-content: center;
align-items: center;
position: relative;
> a {
color: white;
position: relative;
font-size: 25px;
&:hover {
color: #1a78b3;
}
> span {
color: white;
font-size: 14px;
display: flex;
justify-content: center;
align-items: center;
width: 10px;
height: 10px;
padding: 5px;
background-color: #eb2f06;
border-radius: 50%;
position: absolute;
top: -50%;
right: -50%;
}
}
> #header_notif {
box-sizing: border-box;
display: none;
position: absolute;
margin: 0;
background-color: whitesmoke;
top: calc(100% + 10px);
right: calc(50% - 30px);
width: 400px;
max-width: calc(100vw - 30px);
padding: 10px;
z-index: 100;
border-radius: 10px;
box-shadow: 3px 3px 3px 0 #767676;
> ul {
list-style-type: none;
margin: 0;
display: flex;
flex-direction: column;
gap: 10px;
max-height: 120px;
overflow-y: auto;
> li {
> a {
.datetime {
display: flex;
justify-content: flex-start;
gap: 10px;
font-style: italic;
font-size: .8em;
}
}
&.empty-notification {
text-align: center;
font-style: italic;
}
}
}
.options {
width: 100%;
display: flex;
justify-content: space-between;
margin-top: 10px;
> a {
color: black;
padding: 5px;
width: 50%;
display: flex;
justify-content: center;
text-align: center;
align-items: center;
border-radius: 5px;
&:hover {
background-color: rgba(0, 0, 0, .2);
}
}
}
}
}
}
> .left {
gap: 10px;
display: flex;
@media (max-width: 1200px) {
flex-direction: row-reverse;
}
@media (max-width: 550px) {
flex-direction: column-reverse;
}
> form {
margin: 0;
width: 200px;
@media (max-width: 550px) {
width: 100%;
}
> input[type=text] {
box-sizing: border-box;
max-width: 100%;
width: 100%;
height: 35px;
border-radius: 5px;
font-size: .9em;
margin: 0;
background-color: #283747;
padding: 0 10px;
color: white;
}
}
}
}
&-connected ~ &-lang {
@media (max-width: 1200px) {
flex-direction: row;
width: 100%;
}
}
}
.bars {
list-style-type: none;
min-width: 120px;
margin: 0;
padding: 0;
@media(max-width: 1200px) {
display: flex;
flex-direction: row;
gap: 20px;
}
> li > a {
display: flex;
color: white;
&:hover {
color: #1a78b3;
}
> span {
margin-left: 10px;
}
> i {
width: 16px;
display: flex;
justify-content: center;
align-items: center;
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@ -1,5 +1,3 @@
console.log('Guy');
$( function() { $( function() {
buttons = $(".choose_file_button"); buttons = $(".choose_file_button");
popups = $(".choose_file_widget"); popups = $(".choose_file_widget");

View File

@ -0,0 +1,110 @@
nav.navbar {
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
justify-content: center;
background-color: hsl(203, 75%, 40%);
margin: 1em;
color: white;
border-radius: 0.6em;
@media (max-width: 500px) {
position: relative;
flex-direction: column;
align-items: flex-start;
gap: 0;
margin: .2em;
}
> .menu,
> .link {
box-sizing: border-box;
width: 130px;
height: 52px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
@media (max-width: 500px) {
width: 100%;
height: auto;
justify-content: flex-start;
&:first-child {
border-radius: .6em .6em 0 0;
}
&:last-child {
border-radius: 0 0 .6em .6em;
> .content {
box-shadow: 3px 3px 3px 0 #dfdfdf;
}
}
}
}
> .menu > .head,
> .link {
color: white;
padding: 10px 20px;
box-sizing: border-box;
@media (max-width: 500px) {
padding: 10px;
}
}
.link:hover,
.menu:hover {
background-color: rgba(0, 0, 0, .2);
}
> .menu:hover > .content,
> .menu > .head:hover + .content,
> .menu > .content:hover {
display: flex;
}
> .menu {
display: flex;
position: relative;
> .content {
z-index: 10;
display: none;
position: absolute;
top: 100%;
background-color: white;
margin: 0;
list-style-type: none;
width: 130px;
box-shadow: 3px 3px 3px 0 #dfdfdf;
flex-direction: column;
@media (max-width: 500px) {
position: absolute;
flex-direction: row;
flex-wrap: wrap;
width: 100%;
box-shadow: inset 3px 3px 3px 0 #dfdfdf;
}
> li > a {
display: flex;
padding: 15px 20px;
@media (max-width: 500px) {
padding: 10px;
}
&:hover {
color: hsl(203, 75%, 40%);
background-color: rgba(0, 0, 0, .05);
}
}
}
}
}

View File

@ -76,6 +76,7 @@ button:not(:disabled),
input[type="button"]:not(:disabled), input[type="button"]:not(:disabled),
input[type="submit"]:not(:disabled), input[type="submit"]:not(:disabled),
input[type="reset"]:not(:disabled), input[type="reset"]:not(:disabled),
input[type="checkbox"]:not(:disabled),
input[type="file"]:not(:disabled) { input[type="file"]:not(:disabled) {
cursor: pointer; cursor: pointer;
} }
@ -99,6 +100,7 @@ textarea {
padding: 7px; padding: 7px;
font-size: 1.2em; font-size: 1.2em;
border-radius: 5px; border-radius: 5px;
font-family: sans-serif;
} }
select { select {
border: none; border: none;
@ -181,166 +183,6 @@ a {
/*--------------------------------HEADER-------------------------------*/ /*--------------------------------HEADER-------------------------------*/
#header_language_chooser {
position: absolute;
top: 2em;
left: 0.5em;
width: 3%;
min-width: 2.2em;
text-align: center;
input {
display: block;
width: 100%;
padding: 4px;
margin: 0px;
}
form {
display: block;
margin: 0.2em 0em;
width: 100%;
}
}
header {
width: 90%;
margin: 0 auto;
display: flex;
box-shadow: $shadow-color 0 0 15px;
border-top: none;
background-color: $primary-neutral-dark-color;
border-radius: 0px 0px 10px 10px;
#header_logo {
background-color: $white-color;
padding: 0.2em;
border-radius: 0 0 0 9px;
a {
display: flex;
align-items: center;
margin: 0px;
width: 100%;
height: 100%;
img {
max-width: 70%;
max-height: 100%;
margin: auto;
display: block;
}
}
}
#header_connect_links {
margin: 0.6em 0.6em 0 auto;
padding: 0.2em;
color: $white-color;
form {
display: inline;
width: 100%;
label {
display: inline;
}
}
}
#header_bar {
display: flex;
flex: auto;
flex-wrap: wrap;
align-items: center;
width: 80%;
a {
text-decoration: none;
margin: 0 1em;
font-weight: bold;
color: $white-color;
&:hover {
color: $secondary-color;
text-decoration: underline;
}
}
#header_bars_infos {
flex: initial;
list-style-type: none;
margin: 0.2em 0.2em;
}
#header_search {
display: inline-block;
flex: auto;
margin: 0.8em 0;
input {
width: 14ch;
}
}
#header_user_links {
display: flex;
flex: initial;
flex-wrap: wrap;
text-align: right;
margin: 0;
div {
display: inline;
padding: 1.2em 0;
&:first-child {
flex: auto;
}
}
.white {
background: $white-color;
a {
color: $black-color;
}
}
#header_notif {
display: none;
position: absolute;
max-height: 20em;
width: 22em;
overflow: auto;
list-style-type: none;
box-shadow: grey 1px 1px 5px;
background: white;
text-align: left;
font-size: 80%;
margin: 1.5em 0 0em -14em;
.header_notif_date {
font-weight: bold;
}
.header_notif_time {
color: grey;
}
a {
margin: 0;
color: $black-color;
&:hover {
color: $primary-dark-color;
}
}
li {
padding: 0.2em;
&:hover {
background: hsl(180, 14%, 77%);
}
}
li:last-child {
text-align: center;
a {
color: $primary-dark-color;
&:hover {
color: $primary-light-color;
}
}
}
}
}
}
}
#popupheader { #popupheader {
width: 88%; width: 88%;
margin: 0 auto; margin: 0 auto;
@ -350,8 +192,13 @@ header {
#info_boxes { #info_boxes {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
width: 90%; margin: 1em;
margin: 1em auto;
@media (max-width: 500px) {
margin: 0;
width: 100%;
}
#alert_box, #alert_box,
#info_box { #info_box {
flex: 49%; flex: 49%;
@ -388,75 +235,6 @@ header {
width: 90%; width: 90%;
margin: 20px auto 0; margin: 20px auto 0;
/*---------------------------------NAV---------------------------------*/ /*---------------------------------NAV---------------------------------*/
nav {
display: flex;
flex-wrap: wrap;
background-color: $primary-dark-color;
color: $white-color;
border-radius: 6px 6px 0 0;
box-shadow: $shadow-color 0 0 15px;
align-items: center;
a {
flex: auto;
text-align: center;
padding: 1.5em;
color: $white-color;
font-style: normal;
font-weight: bolder;
text-decoration: none;
&:hover {
background: $secondary-neutral-color;
color: $white-color;
&:first-of-type {
border-radius: 6px 0 0 0;
}
&:last-of-type {
border-radius: 0 6px 0 0;
}
}
}
.dropdown {
flex: auto;
text-align: center;
position: relative;
}
.dropbtn {
all: unset;
padding: 20px;
font-weight: bolder;
}
.dropdown-content {
display: none;
position: absolute;
overflow: auto;
width: 100%;
background-color: #f9f9f9;
box-shadow: 3px 3px 3px 0 $shadow-color;
z-index: 1;
}
.dropdown-content a {
float: none;
color: black;
padding: 12px 16px;
display: block;
text-align: center;
&:hover {
border-radius: unset;
color: white;
background: $secondary-neutral-color;
}
}
.dropdown:hover .dropdown-content {
display: block;
}
}
.btn { .btn {
font-size: 15px; font-size: 15px;
@ -1120,32 +898,26 @@ h6 {
h1 { h1 {
font-size: 160%; font-size: 160%;
margin-left: 0;
} }
h2 { h2 {
font-size: 150%; font-size: 150%;
margin-left: 10px;
} }
h3 { h3 {
font-size: 140%; font-size: 140%;
margin-left: 20px;
} }
h4 { h4 {
font-size: 130%; font-size: 130%;
margin-left: 30px;
} }
h5 { h5 {
font-size: 120%; font-size: 120%;
margin-left: 40px;
} }
h6 { h6 {
font-size: 110%; font-size: 110%;
margin-left: 50px;
} }
p, p,
@ -1328,88 +1100,6 @@ u,
/*-----------------------------USER PROFILE----------------------------*/ /*-----------------------------USER PROFILE----------------------------*/
#user_profile_page {
#user_profile {
display: flex;
justify-content: center;
margin-top: 2em;
margin-bottom: 4em;
#user_profile_infos {
flex-basis: 30%;
border-right: solid 1px grey;
div {
margin: 0.5em;
}
#user_profile_infos_items {
margin-top: 3em;
}
.user_profile_infos_item,
.user_profile_infos_item_value {
vertical-align: top;
display: inline-block;
width: 49%;
}
.user_profile_infos_item {
color: grey;
}
#user_profile_infos_promo {
display: flex;
align-items: center;
img {
width: 5em;
margin: 0.5em;
}
}
#user_profile_infos_quote {
text-align: right;
color: grey;
font-style: italic;
&:after,
&:before {
content: "\201C";
vertical-align: middle;
}
}
}
#user_profile_pictures {
height: 20em;
flex-basis: 30%;
display: flex;
justify-content: flex-end;
#user_profile_pictures_bigone {
flex-grow: 9;
flex-basis: 20em;
display: flex;
justify-content: center;
align-items: center;
img {
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
}
#user_profile_pictures_thumbnails {
flex-grow: 1;
flex-basis: 50px;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
img {
margin: 0.1em;
width: 50px;
}
}
}
@media screen and (max-width: $small-devices) {
#user_profile_infos,
#user_profile_pictures {
flex-basis: 50%;
}
}
}
}
.user_mini_profile { .user_mini_profile {
height: 100%; height: 100%;
width: 100%; width: 100%;
@ -1691,47 +1381,6 @@ textarea {
} }
} }
/*------------------------------SAS------------------------------------*/
.album {
display: inline-block;
border: solid 1px $black-color;
text-align: center;
padding: 5px;
width: 200px;
height: 140px;
background: hsl(0, 0%, 93%);
box-shadow: black 2px 2px 10px;
margin: 10px;
vertical-align: top;
img {
max-height: 100px;
}
}
.picture {
display: inline-block;
border: solid 1px $black-color;
width: 150px;
height: 100px;
margin: 5px;
background: #eeeeee;
box-shadow: grey 2px 2px 5px;
padding: 2px;
vertical-align: middle;
img {
max-width: 100%;
max-height: 100px;
display: block;
margin: auto;
}
}
.not_moderated {
border: solid 1px red;
box-shadow: red 2px 2px 10px;
}
/*--------------------------------FOOTER-------------------------------*/ /*--------------------------------FOOTER-------------------------------*/
footer { footer {
@ -1747,6 +1396,7 @@ footer {
border-radius: 5px; border-radius: 5px;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
align-items: center;
background-color: $primary-neutral-dark-color; background-color: $primary-neutral-dark-color;
box-shadow: $shadow-color 0 0 15px; box-shadow: $shadow-color 0 0 15px;
a { a {
@ -1759,6 +1409,11 @@ footer {
} }
} }
} }
> .version {
margin-top: 3px;
color: rgba(0, 0, 0, .3)
}
} }
/*---------------------------------FORMS-------------------------------*/ /*---------------------------------FORMS-------------------------------*/

View File

@ -0,0 +1,24 @@
.activity-description {
display: flex;
flex-direction: column;
gap: 5px;
width: 100%;
margin-top: 10px;
> div {
display: flex;
flex-direction: row;
gap: 10px;
> span {
text-align: left;
}
> i {
width: 16px;
display: flex;
justify-content: center;
align-items: center;
}
}
}

249
core/static/sas/album.scss Normal file
View File

@ -0,0 +1,249 @@
main {
box-sizing: border-box;
padding: 10px;
}
.navbar {
margin-top: 10px;
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 10px;
justify-content: space-between;
}
.toolbar {
display: flex;
align-items: flex-end;
flex-wrap: wrap;
gap: 5px;
> a,
> input {
padding: 0.4em;
margin: 0.1em;
font-size: 1.2em;
line-height: 1.2em;
color: black;
background-color: #f2f2f2;
border-radius: 5px;
font-weight: bold;
&:hover {
background-color: #d4d4d4;
}
&:disabled {
background-color: #f2f2f2;
color: #d4d4d4;
}
}
}
.add-files {
display: flex;
flex-direction: column;
> .inputs {
align-items: flex-end;
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 10px;
> p {
box-sizing: border-box;
max-width: 300px;
width: 100%;
@media (max-width: 500px) {
max-width: 100%;
}
> input {
box-sizing: border-box;
max-width: 100%;
width: 100%;
height: 40px;
line-height: normal;
font-size: 16px;
}
}
> div > input,
> input {
box-sizing: border-box;
height: 40px;
width: 100%;
max-width: 300px;
@media (max-width: 500px) {
max-width: 100%;
}
}
> div {
width: 100%;
max-width: 300px;
}
> input[type=submit]:hover {
background-color: #287fb8;
color: white;
}
}
}
.clipboard {
margin-top: 10px;
padding: 10px;
background-color: rgba(0,0,0,.1);
border-radius: 10px;
}
.paginator {
display: flex;
justify-content: center;
gap: 10px;
width: -moz-fit-content;
width: fit-content;
background-color: rgba(0,0,0,.1);
border-radius: 10px;
padding: 10px;
margin: 10px 0 10px auto;
}
.photos,
.albums {
box-sizing: border-box;
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 5px;
> div {
background: rgba(0, 0, 0, .5);
cursor: not-allowed;
}
> div,
> a {
box-sizing: border-box;
position: relative;
height: 128px;
@media (max-width: 500px) {
width: calc(50% - 5px);
height: 108px;
}
@media (max-width: 300px) {
width: 100%;
}
&:hover {
background: rgba(0, 0, 0, .5);
}
> input[type=checkbox] {
position: absolute;
top: 0;
right: 0;
height: 15px;
width: 15px;
margin: 5px;
cursor: pointer;
}
> .photo,
> .album {
box-sizing: border-box;
background-size: cover;
background-repeat: no-repeat;
background-position: center center;
width: calc(16 / 9 * 128px);
height: 128px;
margin: 0;
padding: 0;
box-shadow: none;
border: 1px solid rgba(0, 0, 0, .3);
@media (max-width: 500px) {
width: 100%;
height: 100%;
}
&:hover > .text {
background-color: rgba(0, 0, 0, .5);
}
&:hover > .overlay {
-webkit-backdrop-filter: blur(2px);
backdrop-filter: blur(2px);
~ .text {
background-color: transparent;
}
}
> .text {
position: absolute;
box-sizing: border-box;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: flex-end;
align-items: flex-start;
padding: 10px;
color: white;
}
> .overlay {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
&::before {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
content: '⚠️';
color: white;
display: flex;
justify-content: center;
align-items: center;
background: rgba(0, 0, 0, .5);
-webkit-backdrop-filter: blur(5px);
backdrop-filter: blur(5px);
}
}
}
> .album > div {
background: rgba(0, 0, 0, .5);
background: linear-gradient(0deg, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, 0) 100%);
text-align: left;
word-break: break-word;
}
> .photo > .text {
align-items: center;
padding-bottom: 30px;
}
}
}

View File

@ -0,0 +1,309 @@
#content {
padding: 10px !important;
}
.title {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.container {
display: flex;
flex-direction: row;
gap: 10px;
@media (max-width: 1000px) {
flex-direction: column;
}
}
.main {
display: flex;
flex-direction: column;
width: calc(75% - 5px);
gap: 10px;
@media (max-width: 1000px) {
width: 100%;
}
> .photo {
box-sizing: border-box;
height: 500px;
display: flex;
justify-content: center;
background-color: #333333;
padding: 5px;
@media (max-width: 1000px) {
width: 100%;
height: auto;
}
> img {
height: 100%;
max-width: 100%;
object-fit: contain;
}
}
}
.subsection {
width: calc(25% - 5px);
@media (max-width: 1000px) {
width: 100%;
}
> .navigation {
display: flex;
flex-direction: row;
gap: 10px;
@media (max-width: 1000px) {
width: 100%;
}
> #prev,
> #next {
width: calc(50% - 5px);
aspect-ratio: 16/9;
background: #aaa;
> a {
display: flex;
position: relative;
width: 100%;
height: 100%;
> div {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
font-size: 30px;
color: white;
background-repeat: no-repeat;
background-position: center center;
background-size: cover;
&::before {
position: absolute;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(0, 0, 0, .3);
}
}
}
}
> #prev > a > div::before {
content: '';
}
> #next > a > div::before {
content: '';
}
}
> .tags {
@media (min-width: 1001px) {
margin-right: 5px;
}
> ul {
list-style-type: none;
margin: 0;
display: flex;
flex-direction: column;
gap: 5px;
@media (max-width: 1000px) {
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
gap: 10px;
margin-right: 5px;
}
> li {
box-sizing: border-box;
display: flex;
flex-direction: row;
align-items: center;
width: 100%;
justify-content: space-between;
@media (max-width: 1000px) {
max-width: calc(50% - 5px);
}
> a {
display: flex;
flex-direction: row;
align-items: center;
gap: 10px;
&.user {
width: 100%;
background-color: #eee;
padding: 5px 10px 5px 5px;
border-radius: 5px;
color: black;
max-width: calc(100% - 40px);
min-height: 30px;
&:hover {
background-color: #aaa;
}
> span {
width: 100%;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
}
&.delete {
margin-left: 10px;
}
> img {
width: 25px;
max-height: 25px;
object-fit: contain;
border-radius: 50%;
}
}
}
}
> form {
> p {
box-sizing: border-box;
> input {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
}
> .results_on_deck > div {
position: relative;
display: flex;
align-items: center;
word-break: break-word;
> span {
position: absolute;
top: 0;
right: 0;
}
}
> input {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
}
}
}
.general {
display: flex;
flex-direction: row;
gap: 20px;
@media (max-width: 1000px) {
flex-direction: column;
}
> .infos {
display: flex;
flex-direction: column;
> div > div {
display: flex;
flex-direction: row;
justify-content: space-between;
> *:first-child {
min-width: 150px;
@media (max-width: 1000px) {
min-width: auto;
}
}
}
}
> .tools {
display: flex;
flex-direction: column;
width: 100%;
> div {
display: flex;
flex-direction: row;
justify-content: space-between;
> div {
> a.button {
box-sizing: border-box;
background-color: #f2f2f2;
display: flex;
justify-content: center;
align-items: center;
padding: 10px;
color: black;
border-radius: 5px;
width: 40px;
height: 40px;
&:hover {
background-color: #aaa;
}
}
> a.text.danger {
color: red;
&:hover {
color: darkred;
}
}
&.buttons {
display: flex;
gap: 5px;
}
}
}
}
}
.moderation {
box-sizing: border-box;
width: 100%;
border: 2px solid coral;
border-radius: 2px;
padding: 10px;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
> div:last-child {
display: flex;
gap: 20px;
}
}

108
core/static/user/login.scss Normal file
View File

@ -0,0 +1,108 @@
html,
body {
box-sizing: border-box;
height: 100%;
}
body {
display: flex;
flex-direction: column;
}
#page {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
#content {
display: flex;
flex-direction: column;
padding: 10px;
box-shadow: none;
background-color: white;
margin: 0;
> .title {
text-align: center;
margin: 0;
}
> div,
> form {
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 10px;
width: 100%;
max-width: 500px;
margin-top: 20px;
> p,
> div {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
margin: 0;
> label {
width: 100%;
@media (min-width: 500px) {
width: 300px;
}
}
}
> input,
> p > input,
> div > input {
box-sizing: border-box;
width: 100%;
max-width: 500px;
@media (min-width: 500px) {
max-width: 300px;
}
}
> .errorlist {
color: red;
text-align: center;
margin: 10px 0 0 0;
list-style-type: none;
}
> .required > .helptext {
text-align: center;
font-style: italic;
}
> .required:last-of-type {
box-sizing: border-box;
max-width: 300px;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-between;
> label {
width: 100%;
}
> img {
width: 70px;
object-fit: contain;
}
> input {
width: 200px;
}
}
}
}
}

View File

@ -0,0 +1,200 @@
main {
box-sizing: border-box;
display: flex;
margin-bottom: 4em;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 10px;
width: 100%;
> .user_profile > .user_profile_infos {
@media (max-width: 960px) {
border-right-color: transparent;
}
}
}
.user-name {
display: flex;
flex-direction: column;
align-items: flex-start;
width: 100%;
max-width: 1080px;
margin: 0 auto;
}
.infos-and-picture {
display: flex;
flex-direction: row;
justify-content: center;
width: 100%;
max-width: 1080px;
margin: 0 auto;
@media (max-width: 960px) {
flex-direction: column-reverse;
gap: 20px;
}
> .user_profile_infos {
width: 50%;
border-right: solid 1px grey;
@media (max-width: 960px) {
width: 100%;
}
@media (min-width: 960px) {
padding-right: 20px;
}
> .user_profile_infos_promo {
display: flex;
flex-direction: row;
gap: 10px;
align-items: center;
justify-content: center;
width: 100%;
> img {
width: 5em;
margin: 0.5em;
}
}
> .user_profile_infos_items {
margin-top: 30px;
display: flex;
flex-direction: column;
gap: 5px;
> div {
box-sizing: border-box;
display: flex;
> .user_profile_infos_item,
> .user_profile_infos_item_value {
vertical-align: top;
display: block;
width: 50%;
}
> .user_profile_infos_item {
color: gray;
}
}
}
> #user_profile_infos_quote {
text-align: right;
color: grey;
font-style: italic;
@media (max-width: 960px) {
text-align: center;
}
&:after,
&:before {
vertical-align: middle;
}
&:before {
content: "\201C";
}
&:after {
content: "\201D";
}
}
}
> .user_profile_pictures {
height: 20em;
width: 50%;
display: flex;
flex-direction: row;
justify-content: flex-end;
@media (max-width: 960px) {
width: 100%;
height: 100%;
flex-direction: column;
}
@media (min-width: 960px) {
padding-left: 20px;
}
> .user_profile_pictures_bigone {
flex-grow: 9;
flex-basis: 20em;
display: flex;
justify-content: center;
align-items: center;
> img {
max-height: 100%;
max-width: 100%;
object-fit: contain;
@media (max-width: 960px) {
max-width: 300px;
width: 100%;
object-fit: contain;
}
}
}
> .user_profile_pictures_thumbnails {
padding: 20px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 20px;
flex-grow: 1;
@media (max-width: 960px) {
flex-direction: row;
height: 50%;
}
> img {
max-height: calc(100% / 3);
width: 100%;
object-fit: contain;
@media (max-width: 960px) {
max-height: 100%;
max-width: calc(100% / 3) !important;
height: auto;
}
}
}
}
}
.form-gifts {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
gap: 10px;
@media (max-width: 960px) {
flex-direction: column;
}
>select,
>input {
min-width: 300px;
max-width: 100%;
height: 40px;
@media (max-width: 960px) {
width: 100%;
}
}
}

View File

@ -0,0 +1,193 @@
@media (max-width: 750px) {
.title {
text-align: center;
}
}
.field-error {
height: auto !important;
> ul {
list-style-type: none;
margin: 0;
color: indianred;
> li {
text-align: left !important;
line-height: normal;
margin-top: 5px;
}
}
}
.profile {
&-visible {
display: flex;
justify-content: center;
align-items: center;
gap: 5px;
padding-top: 10px;
}
&-pictures {
box-sizing: border-box;
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
gap: 40px;
align-items: stretch;
@media (max-width: 750px) {
flex-direction: column;
gap: 10px
}
}
&-picture {
box-sizing: border-box;
display: flex;
justify-content: space-between;
flex-direction: column;
align-items: center;
flex-wrap: wrap;
gap: 20px;
width: 100%;
height: 100%;
max-width: 300px;
@media (max-width: 750px) {
max-width: 100%;
padding: 10px 10px 0;
}
&-display {
display: flex;
flex-direction: column;
justify-content: center;
height: 300px;
gap: 10px;
@media (max-width: 750px) {
height: auto;
}
>img {
width: 100% !important;
object-fit: contain;
height: auto;
}
>p {
text-align: left !important;
width: 100% !important;
}
}
&-edit {
display: flex;
flex-direction: column-reverse;
align-items: center;
justify-content: center;
width: 100%;
> a {
margin-bottom: 15px;
}
> input {
font-size: .8em;
font-weight: normal;
cursor: pointer;
}
> p {
margin-bottom: 0;
text-align: left !important;
min-height: 50px;
}
}
}
&-fields {
padding: 10px 10px 0;
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 10px;
justify-content: center;
}
&-field {
display: flex;
flex-direction: row;
align-items: center;
flex-wrap: wrap;
justify-content: center;
gap: 10px;
width: 100%;
max-width: 330px;
min-width: 300px;
@media (max-width: 750px) {
gap: 4px;
max-width: 100%;
}
>* {
width: 100%;
max-width: 300px;
@media (max-width: 750px) {
max-width: 100%;
}
}
&-label {
text-align: left !important;
}
&-content {
>* {
box-sizing: border-box;
text-align: left !important;
line-height: 40px;
max-width: 100%;
width: 100%;
height: 40px;
margin: 0;
>* {
text-align: left !important;
}
}
>textarea {
height: 120px;
min-height: 40px;
min-width: 300px;
max-width: 300px;
line-height: initial;
@media (max-width: 750px) {
max-width: 100%;
}
}
>input[type="file"] {
font-size: small;
line-height: 30px;
}
>input[type="checkbox"] {
width: 20px;
height: 20px;
margin: 0;
float: left;
}
}
}
}

View File

@ -0,0 +1,113 @@
.container {
display: flex;
flex-direction: column;
gap: 10px;
padding: 10px;
box-sizing: border-box;
> form {
margin: 0;
}
}
.users {
display: flex;
flex-direction: row;
flex-wrap: wrap;
list-style-type: none;
margin: 0;
gap: 10px
}
.users-card {
display: flex;
flex-direction: column;
gap: 10px;
width: 150px;
padding: 10px;
background-color: rgba(0, 0, 0, .05);
border-radius: 10px;
@media (max-width: 375px) {
width: 100%;
}
// Django moment
> div.mini_profile_link {
position: relative;
> a {
&.mini_profile_link {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 10px;
@media (max-width: 375px) {
flex-direction: row;
justify-content: flex-start;
align-items: flex-start;
max-height: 65px;
}
> span {
height: 150px;
width: 100%;
@media (max-width: 375px) {
height: 80px;
width: 80px;
}
> img {
width: 100%;
max-width: 100%;
max-height: 100%;
height: auto;
object-fit: contain;
@media (max-width: 375px) {
max-width: 100%;
max-height: 80px;
}
}
}
> em {
box-sizing: border-box;
padding: 0 5px;
text-align: center;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
@media (max-width: 375px) {
margin-top: 10px;
text-align: left;
max-width: none;
width: 100%;
}
}
}
&:last-of-type {
margin-top: 10px;
display: block;
text-align: center;
color: orangered;
@media (max-width: 375px) {
position: absolute;
bottom: 0%;
right: 0;
}
}
}
}
// Django moment
> a.mini_profile_link {
display: none;
}
}

View File

@ -0,0 +1,12 @@
#id_groups {
margin: 0;
>li {
list-style-type: none;
padding-left: 20px;
>label {
cursor: pointer;
}
}
}

View File

@ -0,0 +1,58 @@
.form {
display: flex;
flex-direction: column;
margin: 10px 0;
gap: 5px;
&-general {
> p {
display: flex;
flex-direction: row-reverse;
justify-content: left;
align-items: center;
gap: 5px;
margin: 0;
> label {
cursor: pointer;
margin: 0;
}
}
}
&-cards,
&-trombi {
>p {
display: flex;
flex-direction: column;
align-items: flex-start;
text-align: justify;
gap: 5px;
margin: 0;
>input,
>select {
min-width: 300px;
}
}
}
&-submit-btn {
margin-top: 10px !important;
max-width: 100px;
}
}
.justify {
text-align: justify;
}
.main {
padding: 10px;
}
.no-cards,
.student-cards {
margin-top: 10px;
display: block;
}

View File

@ -0,0 +1,48 @@
.container {
padding: 10px;
display: flex;
flex-direction: column;
gap: 10px;
}
.row {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-start;
align-items: center;
margin-bottom: 10px;
gap: 30px;
@media (max-width: 535px) {
gap: 20px;
>div,
>div>.flexed {
width: 100%;
align-items: stretch;
}
}
}
.flexed {
display: flex;
flex-direction: column;
gap: 2px;
align-items: self-start;
>div {
display: flex;
justify-content: space-between;
>b,
>span {
width: 120px;
&:last-child {
text-align: right;
}
}
}
}

View File

@ -0,0 +1,41 @@
main {
box-sizing: border-box;
padding: 10px;
}
.container {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
gap: 10px;
> div {
border-radius: 10px;
background-color: rgba(0, 0, 0, .05);
width: 210px;
>h4 {
text-align: center;
}
>ul {
list-style-type: none;
margin: 20px 10px;
display: flex;
flex-direction: column;
gap: 10px;
}
}
@media (max-width: 550px) {
>div {
width: 100%;
background-color: transparent;
>h4 {
text-align: left;
}
}
}
}

View File

@ -9,10 +9,14 @@
<link rel="stylesheet" href="{{ static('core/jquery.datetimepicker.min.css') }}"> <link rel="stylesheet" href="{{ static('core/jquery.datetimepicker.min.css') }}">
<link rel="stylesheet" href="{{ static('ajax_select/css/ajax_select.css') }}"> <link rel="stylesheet" href="{{ static('ajax_select/css/ajax_select.css') }}">
<link rel="stylesheet" href="{{ scss('core/style.scss') }}"> <link rel="stylesheet" href="{{ scss('core/style.scss') }}">
<link rel="stylesheet" href="{{ scss('core/header.scss') }}">
<link rel="stylesheet" href="{{ scss('core/navbar.scss') }}">
{% block jquery_css %} {% block jquery_css %}
{# Thile file is quite heavy (around 250kb), so declaring it in a block allows easy removal #} {# Thile file is quite heavy (around 250kb), so declaring it in a block allows easy removal #}
<link rel="stylesheet" href="{{ static('core/js/ui/jquery-ui.min.css') }}"> <link rel="stylesheet" href="{{ static('core/js/ui/jquery-ui.min.css') }}">
{% endblock %} {% endblock %}
<link rel="preload" as="style" href="{{ static('core/font-awesome/css/font-awesome.min.css') }}" onload="this.onload=null;this.rel='stylesheet'"> <link rel="preload" as="style" href="{{ static('core/font-awesome/css/font-awesome.min.css') }}" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="{{ static('core/font-awesome/css/font-awesome.min.css') }}"></noscript> <noscript><link rel="stylesheet" href="{{ static('core/font-awesome/css/font-awesome.min.css') }}"></noscript>
<script defer href="{{ static('core/font-awesome/js/fontawesone.min.js') }}"></script> <script defer href="{{ static('core/font-awesome/js/fontawesone.min.js') }}"></script>
@ -21,6 +25,7 @@
<script src="{{ static('core/js/jquery-3.6.2.min.js') }}"></script> <script src="{{ static('core/js/jquery-3.6.2.min.js') }}"></script>
<!-- Put here to always have acces to those functions on django widgets --> <!-- Put here to always have acces to those functions on django widgets -->
<script src="{{ static('core/js/script.js') }}"></script> <script src="{{ static('core/js/script.js') }}"></script>
{% block additional_css %}{% endblock %} {% block additional_css %}{% endblock %}
{% block additional_js %}{% endblock %} {% block additional_js %}{% endblock %}
{% endblock %} {% endblock %}
@ -34,110 +39,133 @@
<!-- BEGIN HEADER --> <!-- BEGIN HEADER -->
{% block header %} {% block header %}
{% if not popup %} {% if not popup %}
<header> <header class="header">
<div id="header_language_chooser"> <div class="header-logo">
{% for language in LANGUAGES %} <a class="header-logo-picture" href="{{ url('core:index') }}" style="background-image: url('{{ static('core/img/logo_no_text.png') }}')">
<form action="{{ url('set_language') }}" method="post">{% csrf_token %} &nbsp;
<input name="next" value="{{ request.path }}" type="hidden" /> </a>
<input name="language" value="{{ language[0] }}" type="hidden" /> <a class="header-logo-text" href="{{ url('core:index') }}">
<input type="submit" value="{{ language[0]|upper }}" /> <span>Association des Étudiants</span>
</form> <span>de l'Université de Technologie de Belfort-Montbéliard</span>
{% endfor %}
</div>
<div id="header_logo">
<a href="{{ url('core:index') }}">
<img src="{{ static('core/img/logo.png') }}" alt="AE logo">
</a> </a>
</div> </div>
{% if not user.is_authenticated %} {% if not user.is_authenticated %}
<div id="header_connect_links"> <div class="header-disconnected">
<form method="post" action="{{ url('core:login') }}"> <a class="button" href="{{ url('core:login') }}">{% trans %}Login{% endtrans %}</a>
{% csrf_token %} <a class="button" href="{{ url('core:register') }}">{% trans %}Register{% endtrans %}</a>
<label for="id_username">{% trans %}Username{% endtrans %}</label>
<input id="id_username" maxlength="254" name="username" type="text">
<label for="id_password">{% trans %}Password{% endtrans %}</label>
<input type="password" name="password" id="id_password">
<input type="submit" value="{% trans %}Login{% endtrans %}">
</form>
<a href="{{ url('core:register') }}"><button type="button">{% trans %}Register{% endtrans %}</button></a>
</div> </div>
{% else %} {% else %}
<div id="header_bar"> <div class="header-connected">
<ul id="header_bars_infos"> <div class="left">
<form class="search" action="{{ url('core:search') }}" method="GET" id="header_search">
<input class="header-input" type="text" placeholder="{% trans %}Search{% endtrans %}" name="query" id="search" />
<input type="submit" value="{% trans %}Search{% endtrans %}" style="display: none;" />
</form>
<ul class="bars">
{% cache 100 "counters_activity" %} {% cache 100 "counters_activity" %}
{% for bar in Counter.objects.annotate_has_barman(user).filter(type="BAR") %} {% for bar in Counter.objects.annotate_has_barman(user).filter(type="BAR") %}
<li> <li>
{# If the user is a barman, we redirect him directly to the barman page {# If the user is a barman, we redirect him directly to the barman page
else we redirect him to the activity page #} else we redirect him to the activity page #}
{% if bar.has_annotated_barman %} {% if bar.has_annotated_barman %}
<a href="{{ url('counter:details', counter_id=bar.id) }}" style="padding: 0"> <a href="{{ url('counter:details', counter_id=bar.id) }}">
{% else %} {% else %}
<a href="{{ url('counter:activity', counter_id=bar.id) }}" style="padding: 0"> <a href="{{ url('counter:activity', counter_id=bar.id) }}">
{% endif %} {% endif %}
{% if bar.is_inactive(): %} {% if bar.is_inactive() %}
<i class="fa fa-question" style="color: #f39c12"></i> <i class="fa fa-question" style="color: #f39c12"></i>
{% elif bar.is_open(): %} {% elif bar.is_open(): %}
<i class="fa fa-check" style="color: #2ecc71"></i> <i class="fa fa-check" style="color: #2ecc71"></i>
{% else %} {% else %}
<i class="fa fa-times" style="color: #eb2f06"></i> <i class="fa fa-times" style="color: #eb2f06"></i>
{% endif %} {% endif %}
{{ bar }} <span>{{ bar }}</span>
</a> </a>
</li> </li>
{% endfor %} {% endfor %}
</ul>
{% endcache %} {% endcache %}
<form action="{{ url('core:search') }}" method="GET" id="header_search"> </ul>
<input type="text" placeholder="{% trans %}Search{% endtrans %}" name="query" id="search" />
<input type="submit" value="{% trans %}Search{% endtrans %}" style="display: none;" />
</form>
<div id="header_user_links">
<div>
<a href="{{ url('core:user_profile', user_id=user.id) }}">{{ user.get_display_name() }}</a>
</div> </div>
<div> <div class="right">
<a href="#" onclick="display_notif()" style="white-space: nowrap;"><i class="fa fa-bell-o"></i> ({{ user.notifications.filter(viewed=False).count() }})</a> <div class="user">
<ul id="header_notif"> <div class="options">
<a href="{{ url('core:user_tools') }}">{% trans %}Tools{% endtrans %}</a>
<a href="{{ url('core:logout') }}">{% trans %}Logout{% endtrans %}</a>
</div>
<a href="{{ url('core:user_profile', user_id=user.id) }}">
{% if user.profile_pict %}
<img src="{{ user.profile_pict.get_download_url() }}" alt="{% trans %}Profile{% endtrans %}" title="{% trans %}Profile{% endtrans %}" />
{% else %}
<img src="{{ static('core/img/unknown.jpg') }}" alt="{% trans %}Profile{% endtrans %}" title="{% trans %}Profile{% endtrans %}" />
{% endif %}
</a>
</div>
<div class="notification">
<a href="#" onclick="display_notif()">
<i class="fa fa-bell-o"></i>
{% set notification_count = user.notifications.filter(viewed=False).count() %}
{% if notification_count > 0 %}
<span>
{% if notification_count < 100 %}
{{ notification_count }}
{% else %}
&nbsp;
{% endif %}
</span>
{% endif %}
</a>
<div id="header_notif">
<ul>
{% if user.notifications.filter(viewed=False).count() > 0 %}
{% for n in user.notifications.filter(viewed=False).order_by('-date') %} {% for n in user.notifications.filter(viewed=False).order_by('-date') %}
<li> <li>
<a href="{{ url("core:notification", notif_id=n.id) }}"> <a href="{{ url("core:notification", notif_id=n.id) }}">
<div class="datetime">
<span class="header_notif_date"> <span class="header_notif_date">
{{ n.date|localtime|date(DATE_FORMAT) }} {{ n.date|localtime|date(DATE_FORMAT) }}
</span> </span>
<span class="header_notif_time"> <span class="header_notif_time">
{{ n.date|localtime|time(DATETIME_FORMAT) }} {{ n.date|localtime|time(DATETIME_FORMAT) }}
</span> </span>
<br> </div>
<div class="reason">
{{ n }} {{ n }}
</div>
</a> </a>
</li> </li>
{% endfor %} {% endfor %}
<li> {% else %}
<strong> <li class="empty-notification">{% trans %}You do not have any unread notification{% endtrans %}</li>
{% endif %}
</ul>
<div class="options">
<a href="{{ url('core:notification_list') }}"> <a href="{{ url('core:notification_list') }}">
{% trans %}View more{% endtrans %} {% trans %}View more{% endtrans %}
</a> </a>
<br />
<a href="{{ url('core:notification_list') }}?see_all"> <a href="{{ url('core:notification_list') }}?see_all">
{% trans %}Mark all as read{% endtrans %} {% trans %}Mark all as read{% endtrans %}
</a> </a>
</strong>
</li>
</ul>
</div> </div>
<div>
<a href="{{ url('core:user_tools') }}">{% trans %}Tools{% endtrans %}</a>
</div> </div>
<div>
<a href="{{ url('core:logout') }}">{% trans %}Logout{% endtrans %}</a>
</div> </div>
</div> </div>
</div> </div>
{% endif %} {% endif %}
<div class="header-lang">
{% for language in LANGUAGES %}
<form action="{{ url('set_language') }}" method="post">
{% csrf_token %}
<input name="next" value="{{ request.path }}" type="hidden" />
<input name="language" value="{{ language[0] }}" type="hidden" />
<input type="submit" value="{% if language[0] == 'en' %}🇬🇧{% else %}🇫🇷{% endif %}" />
</form>
{% endfor %}
</div>
</header> </header>
<div id="info_boxes">
{% block info_boxes %} {% block info_boxes %}
<div id="info_boxes">
{% set sith = get_sith() %} {% set sith = get_sith() %}
{% if sith.alert_msg %} {% if sith.alert_msg %}
<div id="alert_box"> <div id="alert_box">
@ -149,82 +177,72 @@
{{ sith.info_msg|markdown }} {{ sith.info_msg|markdown }}
</div> </div>
{% endif %} {% endif %}
{% endblock %}
</div> </div>
{% endblock %}
{% else %}{# if not popup #} {% else %}
<div id="popupheader">{{ user.get_display_name() }}</div> <div id="popupheader">{{ user.get_display_name() }}</div>
{% endif %} {% endif %}
{% endblock %} {% endblock %}
<!-- END HEADER --> <!-- END HEADER -->
<div id="page">
{% block nav %} {% block nav %}
{% if not popup %} {% if not popup %}
<nav> <nav class="navbar">
<a href="{{ url('core:index') }}">{% trans %}Main{% endtrans %}</a> <a class="link" href="{{ url('core:index') }}">{% trans %}Main{% endtrans %}</a>
<div class="dropdown"> <div class="menu">
<button class="dropbtn">{% trans %}Associations & Clubs{% endtrans %} <span class="head">{% trans %}Associations & Clubs{% endtrans %}</span>
<i class="fa fa-caret-down"></i> <ul class="content">
</button> <li><a href="{{ url('core:page', page_name='ae') }}">{% trans %}AE{% endtrans %}</a></li>
<div class="dropdown-content"> <li><a href="{{ url('core:page', page_name='clubs') }}">{% trans %}AE's clubs{% endtrans %}</a></li>
<a href="{{ url('core:page', page_name='ae') }}">{% trans %}AE{% endtrans %}</a> <li><a href="{{ url('core:page', page_name='bdf') }}">{% trans %}BdF{% endtrans %}</a></li>
<a href="{{ url('core:page', page_name='clubs') }}">{% trans %}AE's clubs{% endtrans %}</a> <li><a href="{{ url('core:page', page_name='bds') }}">{% trans %}BDS{% endtrans %}</a></li>
<a href="{{ url('core:page', page_name='bdf') }}">{% trans %}BdF{% endtrans %}</a> <li><a href="{{ url('core:page', page_name='cetu') }}">{% trans %}CETU{% endtrans %}</a></li>
<a href="{{ url('core:page', page_name='bds') }}">{% trans %}BDS{% endtrans %}</a> <li><a href="{{ url('core:page', page_name='clubs/doceo') }}">{% trans %}Doceo{% endtrans %}</a></li>
<a href="{{ url('core:page', page_name='cetu') }}">{% trans %}CETU{% endtrans %}</a> <li><a href="{{ url('core:page', page_name='positions') }}">{% trans %}Positions{% endtrans %}</a></li>
<a href="{{ url('core:page', page_name='clubs/doceo') }}">{% trans %}Doceo{% endtrans %}</a> </ul>
<a href="{{ url('core:page', page_name='positions') }}">{% trans %}Positions{% endtrans %}</a>
</div> </div>
<div class="menu">
<span class="head">{% trans %}Events{% endtrans %}</span>
<ul class="content">
<li><a href="{{ url('election:list') }}">{% trans %}Elections{% endtrans %}</a></li>
<li><a href="{{ url('core:page', page_name='ga') }}">{% trans %}Big event{% endtrans %}</a></li>
</ul>
</div> </div>
<div class="dropdown"> <a class="link" href="{{ url('forum:main') }}">{% trans %}Forum{% endtrans %}</a>
<button class="dropbtn">{% trans %}Events{% endtrans %} <a class="link" href="{{ url('sas:main') }}">{% trans %}Gallery{% endtrans %}</a>
<i class="fa fa-caret-down"></i> <a class="link" href="{{ url('eboutic:main') }}">{% trans %}Eboutic{% endtrans %}</a>
</button> <div class="menu">
<div class="dropdown-content"> <span class="head">{% trans %}Services{% endtrans %}</span>
<a href="{{ url('election:list') }}">{% trans %}Elections{% endtrans %}</a> <ul class="content">
<a href="{{ url('core:page', page_name='ga') }}">{% trans %}Big event{% endtrans %}</a> <li><a href="{{ url('matmat:search_clear') }}">{% trans %}Matmatronch{% endtrans %}</a></li>
<li><a href="/launderette">{% trans %}Launderette{% endtrans %}</a></li>
<li><a href="{{ url('core:file_list') }}">{% trans %}Files{% endtrans %}</a></li>
<li><a href="{{ url('pedagogy:guide') }}">{% trans %}Pedagogy{% endtrans %}</a></li>
</ul>
</div> </div>
<div class="menu">
<span class="head">{% trans %}My Benefits{% endtrans %}</span>
<ul class="content">
<li><a href="{{ url('core:page', page_name='partenaires')}}">{% trans %}Sponsors{% endtrans %}</a></li>
<li><a href="{{ url('core:page', page_name='avantages') }}">{% trans %}Subscriber benefits{% endtrans %}</a></li>
</ul>
</div> </div>
<a href="{{ url('forum:main') }}">{% trans %}Forum{% endtrans %}</a> <div class="menu">
<a href="{{ url('sas:main') }}">{% trans %}Gallery{% endtrans %}</a> <span class="head">{% trans %}Help{% endtrans %}</span>
<a href="{{ url('eboutic:main') }}">{% trans %}Eboutic{% endtrans %}</a> <ul class="content">
<div class="dropdown"> <li><a href="{{ url('core:page', page_name='FAQ') }}">{% trans %}FAQ{% endtrans %}</a></li>
<button class="dropbtn">{% trans %}Services{% endtrans %} <li><a href="{{ url('core:page', 'contacts') }}">{% trans %}Contacts{% endtrans %}</a></li>
<i class="fa fa-caret-down"></i> <li><a href="{{ url('core:page', page_name='Index') }}">{% trans %}Wiki{% endtrans %}</a></li>
</button> </ul>
<div class="dropdown-content">
<a href="{{ url('matmat:search_clear') }}">{% trans %}Matmatronch{% endtrans %}</a>
<a href="/launderette">{% trans %}Launderette{% endtrans %}</a>
<a href="{{ url('core:file_list') }}">{% trans %}Files{% endtrans %}</a>
<a href="{{ url('pedagogy:guide') }}">{% trans %}Pedagogy{% endtrans %}</a>
</div>
</div>
<div class="dropdown">
<button class="dropbtn">{% trans %}My Benefits{% endtrans %}
<i class="fa fa-caret-down"></i>
</button>
<div class="dropdown-content">
<a href="{{ url('core:page', page_name='partenaires')}}">{% trans %}Sponsors{% endtrans %}</a>
<a href="{{ url('core:page', page_name='avantages') }}">{% trans %}Subscriber benefits{% endtrans %}</a>
</div>
</div>
<div class="dropdown">
<button class="dropbtn">{% trans %}Help{% endtrans %}
<i class="fa fa-caret-down"></i>
</button>
<div class="dropdown-content">
<a href="{{ url('core:page', page_name='FAQ') }}">{% trans %}FAQ{% endtrans %}</a>
<a href="{{ url('core:page', 'contacts') }}">{% trans %}Contacts{% endtrans %}</a>
<a href="{{ url('core:page', page_name="Index") }}">{% trans %}Wiki{% endtrans %}</a>
</div>
</div> </div>
</nav> </nav>
{% endif %} {% endif %}
{% endblock %} {% endblock %}
<div id="page">
<ul id="quick_notif"> <ul id="quick_notif">
{% for n in quick_notifs %} {% for n in quick_notifs %}
<li>{{ n }}</li> <li>{{ n }}</li>
@ -234,14 +252,9 @@
<div id="content"> <div id="content">
{% if list_of_tabs %} {% if list_of_tabs %}
<div class="tool_bar"> <div class="tool_bar">
<div>{{ tabs_title }}</div>
<div class="tools"> <div class="tools">
{% for t in list_of_tabs -%} {% for t in list_of_tabs -%}
<a href="{{ t.url }}" <a href="{{ t.url }}" {%- if current_tab==t.slug %} class="selected_tab" {%- endif -%}>{{ t.name }}</a>
{%- if current_tab == t.slug %}
class="selected_tab"
{%- endif -%}
>{{ t.name }}</a>
{%- endfor %} {%- endfor %}
</div> </div>
</div> </div>
@ -265,8 +278,14 @@
<a href="{{ url('core:page', 'docs') }}">{% trans %}Help & Documentation{% endtrans %}</a> <a href="{{ url('core:page', 'docs') }}">{% trans %}Help & Documentation{% endtrans %}</a>
<a href="{{ url('core:page', 'rd') }}">{% trans %}R&D{% endtrans %}</a> <a href="{{ url('core:page', 'rd') }}">{% trans %}R&D{% endtrans %}</a>
</div> </div>
{% trans %}Site made by good people{% endtrans %} <a href="https://discord.gg/XK9WfPsUFm" target="_link">
{% trans %}Site created by the IT Department of the AE{% endtrans %}
</a>
{% endblock %} {% endblock %}
<br>
<code class="version">
{% trans %}Sith version:{% endtrans %}&nbsp;{{ get_sith().version }}
</code>
</footer> </footer>
{% endif %} {% endif %}
<!-- <!--

View File

@ -1,14 +1,21 @@
{% extends "core/base.jinja" %} {% extends "core/base.jinja" %}
{%- block additional_css -%}
<link rel="stylesheet" href="{{ scss('user/login.scss') }}">
{%- endblock -%}
{% block title %} {% block title %}
{% trans %}Login{% endtrans %} {% trans %}Login{% endtrans %}
{% endblock %} {% endblock %}
{% block content %} {% block info_boxes %}
{% endblock %}
{% if form.errors %} {% block nav %}
<p>{% trans %}Your username and password didn't match. Please try again.{% endtrans %}</p> {% endblock %}
{% endif %}
{% block content %}
<h1 class="title">{% trans %}Login{% endtrans %}</h1>
{% if next %} {% if next %}
{% if user.is_authenticated %} {% if user.is_authenticated %}
@ -20,16 +27,35 @@
{% endif %} {% endif %}
<form method="post" action="{{ url('core:login') }}"> <form method="post" action="{{ url('core:login') }}">
{% if form.errors %}
<p class="alert alert-red">{% trans %}Your username and password didn't match. Please try again.{% endtrans %}</p>
<br>
{% endif %}
{% csrf_token %} {% csrf_token %}
<p>{{ form.username.errors }}<label for="{{ form.username.name }}">{{ form.username.label }}
</label><input id="id_username" maxlength="254" name="username" type="text" autofocus="autofocus" /></p> <div>
<p>{{ form.password.errors }}<label for="{{ form.password.name }}">{{ form.password.label }}</label>{{ form.password }}</p> <label for="{{ form.username.name }}">{{ form.username.label }}</label>
<input id="id_username" maxlength="254" name="username" type="text" autofocus="autofocus" />
{{ form.username.errors }}
</div>
<div>
<label for="{{ form.password.name }}">{{ form.password.label }}</label>
{{ form.password }}
{{ form.password.errors }}
</div>
<input type="hidden" name="next" value="{{ next }}"> <input type="hidden" name="next" value="{{ next }}">
<p><input type="submit" value="{% trans %}login{% endtrans %}"></p> <input type="submit" value="{% trans %}Login{% endtrans %}">
</form>
{# Assumes you setup the password_reset view in your URLconf #} {# Assumes you setup the password_reset view in your URLconf #}
<p><a href="{{ url('core:password_reset') }}">{% trans %}Lost password?{% endtrans %}</a></p> <p>
<p><a href="{{ url('core:register') }}">{% trans %}Create account{% endtrans %}</a></p> <a href="{{ url('core:password_reset') }}">{% trans %}Lost password?{% endtrans %}</a>
&nbsp;&nbsp;
<a href="{{ url('core:register') }}">{% trans %}Create account{% endtrans %}</a>
</p>
</form>
{% endblock %} {% endblock %}

View File

@ -1,20 +1,31 @@
{% extends "core/base.jinja" %} {% extends "core/base.jinja" %}
{% block title %}{% trans %}Register a user{% endtrans %}{% endblock %} {%- block additional_css -%}
<link rel="stylesheet" href="{{ scss('user/login.scss') }}">
{%- endblock -%}
{% block title %}{% trans %}Register{% endtrans %}{% endblock %}
{% block nav %}
{% endblock %}
{% block info_boxes %}
{% endblock %}
{% block content %} {% block content %}
<h1>{% trans %}Register a user{% endtrans %}</h1> <h1 class="title">{% trans %}Register{% endtrans %}</h1>
{% if user_registered %} {% if user_registered %}
{% trans user_name=user_registered.get_display_name() %}Welcome {{ user_name }}!{% endtrans %} {% trans user_name=user_registered.get_display_name() %}Welcome {{ user_name }}!{% endtrans %}<br>
{% trans %}You successfully registred and you will soon receive a confirmation mail.{% endtrans %} {% trans %}You successfully registred and you will soon receive a confirmation mail.{% endtrans %}<br>
{% trans username=user_registered.username %}Your username is {{ username }}.{% endtrans %}<br>
{% trans username=user_registered.username %}Your username is {{ username }}.{% endtrans %}
{% endif %}
{% else %}
<form action="{{ url('core:register') }}" method="post"> <form action="{{ url('core:register') }}" method="post">
{% csrf_token %} {% csrf_token %}
{{ form }} {{ form }}
<p><input type="submit" value="{% trans %}Register{% endtrans %}" /></p> <input type="submit" value="{% trans %}Register{% endtrans %}" />
</form> </form>
{% endif %}
{% endblock %} {% endblock %}

View File

@ -1,6 +1,10 @@
{% extends "core/base.jinja" %} {% extends "core/base.jinja" %}
{% from "core/macros.jinja" import show_slots, show_tokens, user_subscription %} {% from "core/macros.jinja" import show_slots, show_tokens, user_subscription %}
{%- block additional_css -%}
<link rel="stylesheet" href="{{ scss('user/user_detail.scss') }}">
{%- endblock -%}
{% block title %} {% block title %}
{% trans user_name=profile.get_display_name() %}{{ user_name }}'s profile{% endtrans %} {% trans user_name=profile.get_display_name() %}{{ user_name }}'s profile{% endtrans %}
{% endblock %} {% endblock %}
@ -10,24 +14,25 @@
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div id="user_profile_page" x-data> <div class="user_profile_page" x-data>
<div id="user_profile"> <div class="user_profile">
<!-- Profile --> <!-- Profile -->
<div id="user_profile_infos"> <div class="user-name">
<h4>{{ profile.get_full_name() }}</h4> <h3>{{ profile.get_full_name() }}</h3>
{% if profile.nick_name %} {% if profile.nick_name %}
<div id="user_profile_infos_nick">&laquo; {{ profile.nick_name }} &raquo;</div> <div class="user_profile_infos_nick">&laquo; {{ profile.nick_name }} &raquo;</div>
{% endif %} {% endif %}
</div>
<div class="infos-and-picture">
<div class="user_profile_infos">
{% if profile.quote %} {% if profile.quote %}
<div id="user_profile_infos_quote"> <div class="user_profile_infos_quote">
{{ profile.quote }} {{ profile.quote }}
</div> </div>
{% endif %} {% endif %}
<div id="user_profile_infos_items"> <div class="user_profile_infos_items">
{% if profile.pronouns %} {% if profile.pronouns %}
<div> <div>
<span class="user_profile_infos_item">{% trans %}Pronouns: {% endtrans %}</span> <span class="user_profile_infos_item">{% trans %}Pronouns: {% endtrans %}</span>
@ -44,7 +49,8 @@
{% if profile.department != "NA" %} {% if profile.department != "NA" %}
<div> <div>
<span class="user_profile_infos_item">{% trans %}Department: {% endtrans %}</span> <span class="user_profile_infos_item">{% trans %}Department: {% endtrans %}</span>
<span class="user_profile_infos_item_value">{{ profile.department }}{{ profile.semester }}</span> <span class="user_profile_infos_item_value">{{ profile.department }}{{ profile.semester
}}</span>
</div> </div>
{% endif %} {% endif %}
@ -78,15 +84,16 @@
</div> </div>
{% if profile.promo %} {% if profile.promo %}
<div id="user_profile_infos_promo"> <div class="user_profile_infos_promo">
<img src="{{ static('core/img/promo_%02d.png' % profile.promo) }}" alt="Promo {{ profile.promo }}" />
{% trans %}Promo: {% endtrans %}{{ profile.promo }} {% trans %}Promo: {% endtrans %}{{ profile.promo }}
<img src="{{ static('core/img/promo_%02d.png' % profile.promo) }}"
alt="Promo {{ profile.promo }}" />
</div> </div>
{% endif %} {% endif %}
</div> </div>
<!-- Pictures --> <!-- Pictures -->
<div id="user_profile_pictures"> <div class="user_profile_pictures">
<div id="user_profile_pictures_bigone"> <div class="user_profile_pictures_bigone">
{% if profile.profile_pict %} {% if profile.profile_pict %}
<img src="{{ profile.profile_pict.get_download_url() }}" alt="{% trans %}Profile{% endtrans %}" <img src="{{ profile.profile_pict.get_download_url() }}" alt="{% trans %}Profile{% endtrans %}"
title="{% trans %}Profile{% endtrans %}" /> title="{% trans %}Profile{% endtrans %}" />
@ -95,7 +102,7 @@
title="{% trans %}Profile{% endtrans %}" /> title="{% trans %}Profile{% endtrans %}" />
{% endif %} {% endif %}
</div> </div>
<div id="user_profile_pictures_thumbnails"> <div class="user_profile_pictures_thumbnails">
{% if profile.profile_pict %} {% if profile.profile_pict %}
<img src="{{ profile.profile_pict.get_download_url() }}" alt="{% trans %}Profile{% endtrans %}" <img src="{{ profile.profile_pict.get_download_url() }}" alt="{% trans %}Profile{% endtrans %}"
title="{% trans %}Profile{% endtrans %}" /> title="{% trans %}Profile{% endtrans %}" />
@ -122,8 +129,9 @@
</div> </div>
</div> </div>
</div> </div>
</main>
{% if user.memberships.filter(end_date=None).exists() or user.is_in_group(settings.SITH_MAIN_BOARD_GROUP) or user == profile or user.is_in_group(settings.SITH_BAR_MANAGER_BOARD_GROUP) %} {% if user.memberships.filter(end_date=None).exists() or user.is_in_group(settings.SITH_MAIN_BOARD_GROUP) or user ==
profile or user.is_in_group(settings.SITH_BAR_MANAGER_BOARD_GROUP) %}
{# if the user is member of a club, he can view the subscription state #} {# if the user is member of a club, he can view the subscription state #}
<hr> <hr>
{% if profile.is_subscribed %} {% if profile.is_subscribed %}
@ -144,7 +152,8 @@
<div> <div>
{% trans %}Not subscribed{% endtrans %} {% trans %}Not subscribed{% endtrans %}
{% if user.is_board_member %} {% if user.is_board_member %}
<a href="{{ url('subscription:subscription') }}?member={{ profile.id }}">{% trans %}New subscription{% endtrans %}</a> <a href="{{ url('subscription:subscription') }}?member={{ profile.id }}">{% trans %}New subscription{% endtrans
%}</a>
{% endif %} {% endif %}
{% endif %} {% endif %}
</div> </div>
@ -183,9 +192,9 @@
{% endif %} {% endif %}
{% if user.is_root or user.is_board_member %} {% if user.is_root or user.is_board_member %}
<div>
<hr> <hr>
<form style="margin-left: 0px;" action="{{ url('core:user_gift_create', user_id=profile.id) }}" method="post"> <div>
<form class="form-gifts" action="{{ url('core:user_gift_create', user_id=profile.id) }}" method="post">
{% csrf_token %} {% csrf_token %}
{{ gift_form.label }} {{ gift_form.label }}
{{ gift_form.user }} {{ gift_form.user }}
@ -216,7 +225,7 @@
</div> </div>
</div> </div>
{% else %} {% else %}
{% trans %}No gift given yet{% endtrans %} <em>{% trans %}No gift given yet{% endtrans %}</em>
{% endif %} {% endif %}
</div> </div>
{% endif %} {% endif %}

View File

@ -1,57 +1,159 @@
{% extends "core/base.jinja" %} {%- extends "core/base.jinja" -%}
{% block title %} {%- block title -%}
{% trans %}Edit user{% endtrans %} {%- trans -%}Edit user{%- endtrans -%}
{% endblock %} {%- endblock -%}
{% block content %} {%- block additional_css -%}
<h2>{% trans %}Edit user profile{% endtrans %}</h2> <link rel="stylesheet" href="{{ scss('user/user_edit.scss') }}">
{%- endblock -%}
{%- block content -%}
<h2 class="title">{%- trans -%}Edit user profile{%- endtrans -%}</h2>
<form action="" method="post" enctype="multipart/form-data" id="user_edit"> <form action="" method="post" enctype="multipart/form-data" id="user_edit">
{% csrf_token %}
{{ form.non_field_errors() }}
{% for field in form %}
<p>{{ field.errors }}<label for="{{ field.name }}">{{ field.label }}
{%- if field.name == "profile_pict" -%}
<br>{% trans %}Current profile: {% endtrans %}
{% if form.instance.profile_pict %}
<img src="{{ form.instance.profile_pict.get_download_url() }}" title="{% trans %}Profile{% endtrans %}" /><br>
{% if user.is_in_group(settings.SITH_MAIN_BOARD_GROUP) %}
<a href="{{ url('core:file_delete', file_id=form.instance.profile_pict.id, popup="") }}">{% trans %}Delete{% endtrans %}</a>
{% endif %}
{% else %}
<img src="{{ static('core/img/unknown.jpg') }}" title="-" crossOrigin="Anonymous" id="new_profile"/><br>
<div id="take_picture">
<div id="camera_canvas" style="width:320; height:240; margin: 0px auto;"></div>
<a href="javascript:void(take_snapshot())">{% trans %}Take picture{% endtrans %}</a>
</div>
<p>
{% endif %}<br>
{%- elif field.name == "avatar_pict" and form.instance.avatar_pict -%}
<br>{% trans %}Current avatar: {% endtrans %}
<img src="{{ form.instance.avatar_pict.get_download_url() }}" title="{% trans %}Avatar{% endtrans %}" /><br>
{%- elif field.name == "scrub_pict" and form.instance.scrub_pict -%}
<br>{% trans %}Current scrub: {% endtrans %}
<img src="{{ form.instance.scrub_pict.get_download_url() }}" title="{% trans %}Scrub{% endtrans %}" /><br>
{%- endif %}</label> {{ field }}</p>
{% endfor %}
<p><input type="submit" value="{% trans %}Update{% endtrans %}" /></p>
<p>{% trans %}Username: {% endtrans %}{{ form.instance.username }}</p>
{% if form.instance.customer %}
<p>{% trans %}Account number: {% endtrans %}{{ form.instance.customer.account_id }}</p>
{% endif %}
{% if form.instance == user %}
<p><a href="{{ url('core:password_change') }}">{% trans %}Change my password{% endtrans %}</a></p>
{% elif user.is_root %}
<p><a href="{{ url('core:password_root_change', user_id=form.instance.id) }}">{% trans %}Change user password{% endtrans %}</a></p>
{% endif %}
</form>
{% endblock %}
{% block script %} {%- csrf_token -%}
{{ form.non_field_errors() }}
{# User Pictures #}
<div class="profile-pictures">
<div class="profile-picture">
<div class="profile-picture-display">
{%- if form.instance.profile_pict -%}
<img src="{{ form.instance.profile_pict.get_download_url() }}"
alt="{%- trans -%}Profile{%- endtrans -%}" title="{%- trans -%}Profile{%- endtrans -%}" />
{%- else -%}
<img src="{{ static('core/img/unknown.jpg') }}" alt="{%- trans -%}Profile{%- endtrans -%}"
title="{%- trans -%}Profile{%- endtrans -%}" />
{%- endif -%}
</div>
<div class="profile-picture-edit">
<p>{{ form["profile_pict"].label }}</p>
{{ form["profile_pict"] }}
{%- if user.is_in_group(settings.SITH_MAIN_BOARD_GROUP) and form.instance.profile_pict.id -%}
<a href="{{ url('core:file_delete', file_id=form.instance.profile_pict.id, popup='') }}">
{%- trans -%}Delete{%- endtrans -%}
</a>
{%- endif -%}
</div>
</div>
<div class="profile-picture">
<div class="profile-picture-display">
{%- if form.instance.avatar_pict -%}
<img src="{{ form.instance.avatar_pict.get_download_url() }}" alt="{%- trans -%}Profile{%- endtrans -%}"
title="{%- trans -%}Profile{%- endtrans -%}" />
{%- else -%}
<img src="{{ static('core/img/unknown.jpg') }}" alt="{%- trans -%}Profile{%- endtrans -%}"
title="{%- trans -%}Profile{%- endtrans -%}" />
{%- endif -%}
</div>
<div class="profile-picture-edit">
<p>{{ form["avatar_pict"].label }}</p>
{{ form["avatar_pict"] }}
{%- if user.is_in_group(settings.SITH_MAIN_BOARD_GROUP) and form.instance.avatar_pict.id -%}
<a href="{{ url('core:file_delete', file_id=form.instance.avatar_pict.id, popup='') }}">
{%- trans -%}Delete{%- endtrans -%}
</a>
{%- endif -%}
</div>
</div>
<div class="profile-picture">
<div class="profile-picture-display">
{%- if form.instance.scrub_pict -%}
<img src="{{ form.instance.scrub_pict.get_download_url() }}" alt="{%- trans -%}Profile{%- endtrans -%}"
title="{%- trans -%}Profile{%- endtrans -%}" />
{%- else -%}
<img src="{{ static('core/img/unknown.jpg') }}" alt="{%- trans -%}Profile{%- endtrans -%}"
title="{%- trans -%}Profile{%- endtrans -%}" />
{%- endif -%}
</div>
<div class="profile-picture-edit">
<p>{{ form["scrub_pict"].label }}</p>
{{ form["scrub_pict"] }}
{%- if user.is_in_group(settings.SITH_MAIN_BOARD_GROUP) and form.instance.scrub_pict.id -%}
<a href="{{ url('core:file_delete', file_id=form.instance.scrub_pict.id, popup='') }}">
{%- trans -%}Delete{%-endtrans -%}
</a>
{%- endif -%}
</div>
</div>
</div>
{# All fields #}
<div class="profile-fields">
{%- for field in form -%}
{%-
if field.name in ["quote","profile_pict","avatar_pict","scrub_pict","is_subscriber_viewable","forum_signature"]
-%}
{%- continue -%}
{%- endif -%}
<div class="profile-field">
<div class="profile-field-label">{{ field.label }}</div>
<div class="profile-field-content">
{{ field }}
{%- if field.errors -%}
<div class="field-error">{{ field.errors }}</div>
{%- endif -%}
</div>
</div>
{%- endfor -%}
</div>
{# Textareas #}
<div class="profile-fields">
{%- for field in [form["quote"], form["forum_signature"]] -%}
<div class="profile-field">
<div class="profile-field-label">{{ field.label }}</div>
<div class="profile-field-content">
{{ field }}
{%- if field.errors -%}
<div class="field-error">{{ field.errors }}</div>
{%- endif -%}
</div>
</div>
{%- endfor -%}
</div>
{# Checkboxes #}
<div class="profile-visible">
{{ form["is_subscriber_viewable"] }}
{{ form["is_subscriber_viewable"].label }}
</div>
{%- if form.instance == user -%}
<p>
<a href="{{ url('core:password_change') }}">{%- trans -%}Change my password{%- endtrans -%}</a>
</p>
{%- elif user.is_root -%}
<p>
<a href="{{ url('core:password_root_change', user_id=form.instance.id) }}">{%- trans -%}Change user password{%-
endtrans -%}</a>
</p>
{%- endif -%}
<p>
<input type="submit" value="{%- trans -%}Update{%- endtrans -%}" />
</p>
</form>
<p>
<em>{%- trans -%}Username: {%- endtrans -%}&nbsp;{{ form.instance.username }}</em>
<br />
{%- if form.instance.customer -%}
<em>{%- trans -%}Account number: {%- endtrans -%}&nbsp;{{ form.instance.customer.account_id }}</em>
{%- endif -%}
</p>
{%- endblock -%}
{%- block script -%}
{{ super() }} {{ super() }}
{% if not form.instance.profile_pict %} {%- if not form.instance.profile_pict -%}
<script src="{{ static('core/js/webcam.js') }}"></script> <script src="{{ static('core/js/webcam.js') }}"></script>
<script language="JavaScript"> <script>
Webcam.on('error', function (msg) { console.log('Webcam.js error: ' + msg) }) Webcam.on('error', function (msg) { console.log('Webcam.js error: ' + msg) })
Webcam.set({ Webcam.set({
width: 320, width: 320,
@ -63,12 +165,11 @@
force_flash: false force_flash: false
}); });
Webcam.attach('#camera_canvas'); Webcam.attach('#camera_canvas');
function take_snapshot() { function take_snapshot() {
var data_uri = Webcam.snap(); const data_uri = Webcam.snap();
var url = "{{ url('core:user_profile_upload', user_id=form.instance.id) }}"; const url = "{{ url('core:user_profile_upload', user_id=form.instance.id) }}";
Webcam.upload(data_uri, url, function (code, text) { Webcam.upload(data_uri, url, function (code, text) {
if (code == 302 || code == 200) { if (code === 302 || code === 200) {
$('#new_profile').attr('src', data_uri); $('#new_profile').attr('src', data_uri);
$('#take_picture').remove(); $('#take_picture').remove();
$('#id_profile_pict').remove(); $('#id_profile_pict').remove();
@ -79,8 +180,5 @@
}, "new_profile_pict", { name: 'csrfmiddlewaretoken', value: '{{ csrf_token }}' }); }, "new_profile_pict", { name: 'csrfmiddlewaretoken', value: '{{ csrf_token }}' });
} }
</script> </script>
{% endif %} {%- endif -%}
{% endblock %} {%- endblock -%}

View File

@ -1,39 +1,60 @@
{% extends "core/base.jinja" %} {% extends "core/base.jinja" %}
{% from "core/macros.jinja" import user_link_with_pict, delete_godfather %} {% from "core/macros.jinja" import user_link_with_pict, delete_godfather %}
{%- block additional_css -%}
<link rel="stylesheet" href="{{ scss('user/user_godfathers.scss') }}">
{%- endblock -%}
{% block title %} {% block title %}
{% trans user_name=profile.get_display_name() %}{{ user_name }}'s family{% endtrans %} {% trans user_name=profile.get_display_name() %}{{ user_name }}'s family{% endtrans %}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<p><a href="{{ url("core:user_godfathers_tree_pict", user_id=profile.id) }}?family"> <div class="container">
{% trans %}Show family picture{% endtrans %}</a></p> <a href="{{ url('core:user_godfathers_tree_pict', user_id=profile.id) }}?family">
{% if profile.godfathers.exists() %} {% trans %}Show family picture{% endtrans %}
</a>
<h4>{% trans %}Godfathers / Godmothers{% endtrans %}</h4> <h4>{% trans %}Godfathers / Godmothers{% endtrans %}</h4>
<ul> {% if profile.godfathers.exists() %}
<ul class="users">
{% for u in profile.godfathers.all() %} {% for u in profile.godfathers.all() %}
<li> <a href="{{ url("core:user_godfathers", user_id=u.id) }}" class="mini_profile_link" > <li class="users-card">
{{ u.get_mini_item()|safe }} </a>{{ delete_godfather(user, profile, u, True) }}</li> <a href="{{ url('core:user_godfathers', user_id=u.id) }}" class="mini_profile_link">
{{ u.get_mini_item() | safe }}
</a>
{{ delete_godfather(user, profile, u, True) }}
</li>
{% endfor %} {% endfor %}
</ul> </ul>
<p><a href="{{ url("core:user_godfathers_tree", user_id=profile.id) }}">
{% trans %}Show ancestors tree{% endtrans %}</a></p> <a href="{{ url('core:user_godfathers_tree', user_id=profile.id) }}">
{% trans %}Show ancestors tree{% endtrans %}
</a>
{% else %} {% else %}
<p>{% trans %}No godfathers / godmothers{% endtrans %} <p>{% trans %}No godfathers / godmothers{% endtrans %}
{% endif %} {% endif %}
{% if profile.godchildren.exists() %}
<h4>{% trans %}Godchildren{% endtrans %}</h4> <h4>{% trans %}Godchildren{% endtrans %}</h4>
<ul> {% if profile.godchildren.exists() %}
<ul class="users">
{% for u in profile.godchildren.all() %} {% for u in profile.godchildren.all() %}
<li> <a href="{{ url("core:user_godfathers", user_id=u.id) }}" class="mini_profile_link" > <li class="users-card">
{{ u.get_mini_item()|safe }} </a>{{ delete_godfather(user, profile, u, False) }}</li> <a href="{{ url('core:user_godfathers', user_id=u.id) }}" class="mini_profile_link">
{{ u.get_mini_item()|safe }}
</a>
{{ delete_godfather(user, profile, u, False) }}
</li>
{% endfor %} {% endfor %}
</ul> </ul>
<p><a href="{{ url("core:user_godfathers_tree", user_id=profile.id) }}?descent">
{% trans %}Show descent tree{% endtrans %}</a></p> <a href="{{ url('core:user_godfathers_tree', user_id=profile.id) }}?descent">
{% trans %}Show descent tree{% endtrans %}
</a>
{% else %} {% else %}
<p>{% trans %}No godchildren{% endtrans %} <p>{% trans %}No godchildren{% endtrans %}
{% endif %} {% endif %}
{% if profile == user or user.is_root %} {% if profile == user or user.is_root %}
<form action="" method="post"> <form action="" method="post">
{% csrf_token %} {% csrf_token %}
@ -41,5 +62,6 @@
<p><input type="submit" value="{% trans %}Save{% endtrans %}" /></p> <p><input type="submit" value="{% trans %}Save{% endtrans %}" /></p>
</form> </form>
{% endif %} {% endif %}
</div>
{% endblock %} {% endblock %}

View File

@ -1,14 +1,16 @@
{% extends "core/base.jinja" %} {% extends "core/base.jinja" %}
{%- block additional_css -%}
<link rel="stylesheet" href="{{ scss('user/user_group.scss') }}">
{%- endblock -%}
{% block content %} {% block content %}
<main>
<h2>{% trans user_name=profile.get_full_name() %}Edit user groups for {{ user_name }}{% endtrans %}</h2> <h2>{% trans user_name=profile.get_full_name() %}Edit user groups for {{ user_name }}{% endtrans %}</h2>
<form action="" method="post"> <form action="" method="post">
{% csrf_token %} {% csrf_token %}
{{ form.as_p() }} {{ form.as_p() }}
<p><input type="submit" value="{% trans %}Update{% endtrans %}" /></p> <p><input type="submit" value="{% trans %}Update{% endtrans %}" /></p>
</form> </form>
{% endblock %} </main>
{%- endblock -%}

View File

@ -1,26 +1,48 @@
{% extends "core/base.jinja" %} {% extends "core/base.jinja" %}
{%- block additional_css -%}
<link rel="stylesheet" href="{{ scss('sas/album.scss') }}">
{%- endblock -%}
{% block title %} {% block title %}
{% trans user_name=profile.get_display_name() %}{{ user_name }}'s pictures{% endtrans %} {% trans user_name=profile.get_display_name() %}{{ user_name }}'s pictures{% endtrans %}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<main>
{% if can_edit(profile, user) %} {% if can_edit(profile, user) %}
<button id="download_all_pictures", onclick=download_pictures()>{% trans %}Download all my pictures{% endtrans %}</button> <button id="download_all_pictures", onclick=download_pictures()>{% trans %}Download all my pictures{% endtrans %}</button>
{% endif %} {% endif %}
{% for a in albums %} {% for a in albums %}
<div style="padding: 10px">
<h4>{{ a.name }}</h4> <h4>{{ a.name }}</h4>
<hr> <div class="photos">
{% for picture in pictures[a.id] %} {% for p in pictures[a.id] %}
<div class="picture"> {% if p.can_be_viewed_by(user) %}
<a href="{{ url("sas:picture", picture_id=picture.id) }}#pict"> <a href="{{ url("sas:picture", picture_id=p.id) }}#pict">
<img src="{{ picture.get_download_thumb_url() }}" alt="{{ picture.get_display_name() }}" style="max-width: 100%" loading="lazy"/> <div
class="photo{% if not p.is_moderated %} not_moderated{% endif %}"
style="background-image: url('{% if p.file %}{{ p.get_download_url() }}{% else %}{{ static('core/img/sas.jpg') }}{% endif %}');"
>
{% if not p.is_moderated %}
<div class="overlay">&nbsp;</div>
<div class="text">{% trans %}To be moderated{% endtrans %}</div>
{% else %}
<div class="text">&nbsp;</div>
{% endif %}
</div>
</a> </a>
{% else %}
<div>
<div class="photo">
<div class="text">{% trans %}Picture Unavailable{% endtrans %}</div>
</div> </div>
</div>
{% endif %}
{% endfor %} {% endfor %}
</div> </div>
<br>
{% endfor %} {% endfor %}
</main>
{% endblock %} {% endblock %}
{% block script %} {% block script %}

View File

@ -1,46 +1,69 @@
{% extends "core/base.jinja" %} {% extends "core/base.jinja" %}
{%- block additional_css -%}
<link rel="stylesheet" href="{{ scss('user/user_preferences.scss') }}">
{%- endblock -%}
{% block title %} {% block title %}
{% trans %}Preferences{% endtrans %} {% trans %}Preferences{% endtrans %}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="main">
<h2>{% trans %}Preferences{% endtrans %}</h2> <h2>{% trans %}Preferences{% endtrans %}</h2>
<form action="" method="post" enctype="multipart/form-data"> <h3>{% trans %}General{% endtrans %}</h3>
<form class="form form-general" action="" method="post" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
{{ form.as_p() }} {{ form.as_p() }}
<p><input type="submit" value="{% trans %}Save{% endtrans %}" /></p> <input class="form-submit-btn" type="submit" value="{% trans %}Save{% endtrans %}" />
</form> </form>
<h4>{% trans %}Trombi{% endtrans %}</h4>
<h3>{% trans %}Trombi{% endtrans %}</h3>
{% if trombi_form %} {% if trombi_form %}
<form action="{{ url('trombi:user_tools') }}" method="post" enctype="multipart/form-data"> <form class="form form-trombi" action="{{ url('trombi:user_tools') }}" method="post" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
{{ trombi_form.as_p() }} {{ trombi_form.as_p() }}
<p><input type="submit" value="{% trans %}Save{% endtrans %}" /></p> <input class="form-submit-btn" type="submit" value="{% trans %}Save{% endtrans %}" />
</form> </form>
{% else %} {% else %}
<p>{% trans trombi=user.trombi_user.trombi %}You already choose to be in that Trombi: {{ trombi }}.{% endtrans %} <p>{% trans trombi=user.trombi_user.trombi %}You already choose to be in that Trombi: {{ trombi }}.{% endtrans %}
<a href="{{ url('trombi:user_tools') }}">{% trans %}Go to my Trombi tools{% endtrans %}</a></p> <br />
<a href="{{ url('trombi:user_tools') }}">{% trans %}Go to my Trombi tools{% endtrans %}</a>
</p>
{% endif %} {% endif %}
{% if profile.customer %} {% if profile.customer %}
<h4>{% trans %}Student cards{% endtrans %}</h4> <h3>{% trans %}Student cards{% endtrans %}</h3>
<p>{% trans %}You can add a card by asking at a counter or add it yourself here. If you want to manually add a student card yourself, you'll need a NFC reader. We store the UID of the card which is 14 characters long.{% endtrans %}</p>
<form action="{{ url('counter:add_student_card', customer_id=profile.customer.pk) }}" method="post">
{% csrf_token %}
{{ student_card_form.as_p() }}
<p><input type="submit" value="{% trans %}Save{% endtrans %}" /></p>
</form>
{% if profile.customer.student_cards.exists() %} {% if profile.customer.student_cards.exists() %}
<ul> <ul class="student-cards">
{% for card in profile.customer.student_cards.all() %} {% for card in profile.customer.student_cards.all() %}
<li>{{ card.uid }} - <a href="{{ url('counter:delete_student_card', customer_id=profile.customer.pk, card_id=card.id) }}">{% trans %}Delete{% endtrans %}</a></li> <li>
{{ card.uid }}
&nbsp;-&nbsp;
<a href="{{ url('counter:delete_student_card', customer_id=profile.customer.pk, card_id=card.id) }}">
{% trans %}Delete{% endtrans %}
</a>
</li>
{% endfor %} {% endfor %}
</ul> </ul>
{% else %} {% else %}
<p>{% trans %}No student cards registered.{% endtrans %}</p> <em class="no-cards">{% trans %}No student card registered.{% endtrans %}</em>
<p class="justify">
{% trans %}You can add a card by asking at a counter or add it yourself here. If you want to manually
add a student card yourself, you'll need a NFC reader. We store the UID of the card which is 14 characters long.{% endtrans %}
</p>
{% endif %} {% endif %}
<form class="form form-cards" action="{{ url('counter:add_student_card', customer_id=profile.customer.pk) }}"
method="post">
{% csrf_token %}
{{ student_card_form.as_p() }}
<input class="form-submit-btn" type="submit" value="{% trans %}Save{% endtrans %}" />
</form>
{% endif %} {% endif %}
</div>
{% endblock %} {% endblock %}

View File

@ -1,25 +1,41 @@
{% extends "core/base.jinja" %} {% extends "core/base.jinja" %}
{%- block additional_css -%}
<link rel="stylesheet" href="{{ scss('user/user_stats.scss') }}">
{%- endblock -%}
{% block title %} {% block title %}
{% trans user_name=profile.get_display_name() %}{{ user_name }}'s stats{% endtrans %} {% trans user_name=profile.get_display_name() %}{{ user_name }}'s stats{% endtrans %}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="container">
<div class="row">
{% if profile.permanencies %} {% if profile.permanencies %}
<h3>{% trans %}Permanencies{% endtrans %}</h3>
<div> <div>
<p>Total: {{ total_perm_time }}</p> <h3>{% trans %}Permanencies{% endtrans %}</h3>
<p>Foyer: {{ total_foyer_time }}</p> <div class="flexed">
<p>MDE: {{ total_mde_time }}</p> <div><span>Foyer :</span><span>{{ total_foyer_time }}</span></div>
<p>La Gommette: {{ total_gommette_time }}</p> <div><span>Gommette :</span><span>{{ total_gommette_time }}</span></div>
<div><span>MDE :</span><span>{{ total_mde_time }}</span></div>
<div><b>Total :</b><b>{{ total_perm_time }}</b></div>
</div>
</div> </div>
{% endif %} {% endif %}
<h3>{% trans %}Buyings{% endtrans %}</h3>
<div> <div>
<p>Foyer: {{ total_foyer_buyings }} €</p> <h3>{% trans %}Buyings{% endtrans %}</h3>
<p>MDE: {{ total_mde_buyings }} €</p> <div class="flexed">
<p>La Gommette: {{ total_gommette_buyings }} €</p> <div><span>Foyer :</span><span>{{ total_foyer_buyings }}&nbsp;€</span></div>
<div><span>Gommette :</span><span>{{ total_gommette_buyings }}&nbsp;€</span></div>
<div><span>MDE :</span><span>{{ total_mde_buyings }}&nbsp;€</span></div>
<div><b>Total :</b><b>{{ total_foyer_buyings + total_gommette_buyings + total_mde_buyings }}&nbsp;€</b>
</div> </div>
</div>
</div>
</div>
<div>
<h3>{% trans %}Product top 10{% endtrans %}</h3> <h3>{% trans %}Product top 10{% endtrans %}</h3>
<table> <table>
<thead> <thead>
@ -37,6 +53,6 @@
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
</div>
</div>
{% endblock %} {% endblock %}

View File

@ -1,13 +1,20 @@
{% extends "core/base.jinja" %} {% extends "core/base.jinja" %}
{%- block additional_css -%}
<link rel="stylesheet" href="{{ scss('user/user_tools.scss') }}">
{%- endblock -%}
{% block title %} {% block title %}
{% trans user_name=user.get_display_name() %}{{ user_name }}'s tools{% endtrans %} {% trans user_name=user.get_display_name() %}{{ user_name }}'s tools{% endtrans %}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<main>
<h3>{% trans %}User Tools{% endtrans %}</h3> <h3>{% trans %}User Tools{% endtrans %}</h3>
<hr> <div class="container">
{% if user.can_create_subscription or user.is_root or user.is_board_member %}
<div>
<h4>{% trans %}Sith management{% endtrans %}</h4> <h4>{% trans %}Sith management{% endtrans %}</h4>
<ul> <ul>
{% if user.is_root %} {% if user.is_root %}
@ -24,8 +31,19 @@
<li><a href="{{ url('club:club_new') }}">{% trans %}New club{% endtrans %}</a></li> <li><a href="{{ url('club:club_new') }}">{% trans %}New club{% endtrans %}</a></li>
{% endif %} {% endif %}
</ul> </ul>
</div>
{% endif %}
<hr> {% set is_admin_on_a_counter = false %}
{% for b in settings.SITH_COUNTER_BARS if user.is_in_group(b[1] + " admin") %}
{% set is_admin_on_a_counter = true %}
{% endfor %}
{% if
user.is_in_group(settings.SITH_GROUP_COUNTER_ADMIN_ID) or user.is_root
or is_admin_on_a_counter
%}
<div>
<h4>{% trans %}Counters{% endtrans %}</h4> <h4>{% trans %}Counters{% endtrans %}</h4>
<ul> <ul>
{% if user.is_in_group(settings.SITH_GROUP_COUNTER_ADMIN_ID) or user.is_root %} {% if user.is_in_group(settings.SITH_GROUP_COUNTER_ADMIN_ID) or user.is_root %}
@ -53,8 +71,14 @@
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</ul> </ul>
</div>
{% endif %}
<hr> {% if
user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) or user.is_root
or user.memberships.filter(end_date=None).filter(role__gte=7).all() | length > 10
%}
<div>
<h4>{% trans %}Accounting{% endtrans %}</h4> <h4>{% trans %}Accounting{% endtrans %}</h4>
<ul> <ul>
{% if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) or user.is_root %} {% if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) or user.is_root %}
@ -62,20 +86,30 @@
<li><a href="{{ url('accounting:bank_list') }}">{% trans %}General accounting{% endtrans %}</a></li> <li><a href="{{ url('accounting:bank_list') }}">{% trans %}General accounting{% endtrans %}</a></li>
<li><a href="{{ url('accounting:co_list') }}">{% trans %}Company list{% endtrans %}</a></li> <li><a href="{{ url('accounting:co_list') }}">{% trans %}Company list{% endtrans %}</a></li>
{% endif %} {% endif %}
{% for m in user.memberships.filter(end_date=None).filter(role__gte=7).all() -%} {% for m in user.memberships.filter(end_date=None).filter(role__gte=7).all() -%}
{%- for b in m.club.bank_accounts.all() %} {%- for b in m.club.bank_accounts.all() %}
<li><strong>{% trans %}Bank account: {% endtrans %}</strong> <li>
<a href="{{ url('accounting:bank_details', b_account_id=b.id) }}">{{ b }}</a></li> <strong>{% trans %}Bank account: {% endtrans %}</strong>
<a href="{{ url('accounting:bank_details', b_account_id=b.id) }}">{{ b }}</a>
</li>
{%- endfor %} {%- endfor %}
{% if m.club.club_account.exists() -%} {% if m.club.club_account.exists() -%}
{% for ca in m.club.club_account.all() %} {% for ca in m.club.club_account.all() %}
<li><strong>{% trans %}Club account: {% endtrans %}</strong> <a href="{{ url('accounting:club_details', c_account_id=ca.id) }}">{{ ca }}</a></li> <li>
<strong>{% trans %}Club account: {% endtrans %}</strong>
<a href="{{ url('accounting:club_details', c_account_id=ca.id) }}">{{ ca }}</a>
</li>
{%- endfor %} {%- endfor %}
{%- endif -%} {%- endif -%}
{%- endfor %} {%- endfor %}
</ul> </ul>
</div>
{% endif %}
<hr> {% if user.is_in_group(settings.SITH_GROUP_SAS_ADMIN_ID) or user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID) or user.is_root %}
<div>
<h4>{% trans %}Communication{% endtrans %}</h4> <h4>{% trans %}Communication{% endtrans %}</h4>
<ul> <ul>
{% if user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID) or user.is_root %} {% if user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID) or user.is_root %}
@ -95,26 +129,31 @@
<li><a href="{{ url('sas:moderation') }}">{% trans %}Moderate pictures{% endtrans %}</a></li> <li><a href="{{ url('sas:moderation') }}">{% trans %}Moderate pictures{% endtrans %}</a></li>
{% endif %} {% endif %}
</ul> </ul>
</div>
{% endif %}
{% if user.memberships.filter(end_date=None).all().count() > 0 %}
<hr> <div>
<h4>{% trans %}Club tools{% endtrans %}</h4> <h4>{% trans %}Club tools{% endtrans %}</h4>
<ul> <ul>
{% for m in user.memberships.filter(end_date=None).all() %} {% for m in user.memberships.filter(end_date=None).all() %}
<li><a href="{{ url('club:tools', club_id=m.club.id) }}">{{ m.club }}</a></li> <li><a href="{{ url('club:tools', club_id=m.club.id) }}">{{ m.club }}</a></li>
{% endfor %} {% endfor %}
</ul> </ul>
</div>
{% endif %}
<hr> {% if user.is_in_group(settings.SITH_GROUP_PEDAGOGY_ADMIN_ID) or user.is_root %}
<div>
<h4>{% trans %}Pedagogy{% endtrans %}</h4> <h4>{% trans %}Pedagogy{% endtrans %}</h4>
<ul> <ul>
{% if user.is_in_group(settings.SITH_GROUP_PEDAGOGY_ADMIN_ID) or user.is_root %}
<li><a href="{{ url('pedagogy:uv_create') }}">{% trans %}Create UV{% endtrans %}</a></li> <li><a href="{{ url('pedagogy:uv_create') }}">{% trans %}Create UV{% endtrans %}</a></li>
<li><a href="{{ url('pedagogy:moderation') }}">{% trans %}Moderate comments{% endtrans %}</a></li> <li><a href="{{ url('pedagogy:moderation') }}">{% trans %}Moderate comments{% endtrans %}</a></li>
{% endif %}
</ul> </ul>
</div>
{% endif %}
<hr> <div>
<h4>{% trans %}Elections{% endtrans %}</h4> <h4>{% trans %}Elections{% endtrans %}</h4>
<ul> <ul>
<li><a href="{{ url('election:list') }}">{% trans %}See available elections{% endtrans %}</a></li> <li><a href="{{ url('election:list') }}">{% trans %}See available elections{% endtrans %}</a></li>
@ -123,15 +162,15 @@
<li><a href="{{ url('election:create') }}">{% trans %}Create a new election{% endtrans %}</a></li> <li><a href="{{ url('election:create') }}">{% trans %}Create a new election{% endtrans %}</a></li>
{%- endif -%} {%- endif -%}
</ul> </ul>
</div>
<hr> <div>
<h4>{% trans %}Other tools{% endtrans %}</h4> <h4>{% trans %}Other tools{% endtrans %}</h4>
<ul> <ul>
<li><a href="{{ url('core:to_markdown') }}">{% trans %}Convert dokuwiki/BBcode syntax to Markdown{% endtrans %}</a></li> <li><a href="{{ url('core:to_markdown') }}">{% trans %}Convert dokuwiki/BBcode syntax to Markdown{% endtrans %}</a></li>
<li><a href="{{ url('trombi:user_tools') }}">{% trans %}Trombi tools{% endtrans %}</a></li> <li><a href="{{ url('trombi:user_tools') }}">{% trans %}Trombi tools{% endtrans %}</a></li>
</ul> </ul>
</div>
</div>
</main>
{% endblock %} {% endblock %}

View File

@ -272,7 +272,7 @@ class UserRegistrationTest(TestCase):
) )
self.assertTrue(response.status_code == 200) self.assertTrue(response.status_code == 200)
self.assertTrue( self.assertTrue(
"""<p>Votre nom d\\'utilisateur et votre mot de passe ne correspondent pas. Merci de r\\xc3\\xa9essayer.</p>""" """<p class="alert alert-red">Votre nom d\\'utilisateur et votre mot de passe ne correspondent pas. Merci de r\\xc3\\xa9essayer.</p>"""
in str(response.content) in str(response.content)
) )

View File

@ -22,6 +22,7 @@
# #
# #
import subprocess
import re import re
# Image utils # Image utils
@ -37,6 +38,17 @@ from django.conf import settings
from django.core.files.base import ContentFile from django.core.files.base import ContentFile
def get_git_revision_short_hash() -> str:
"""
Return the short hash of the current commit
"""
return (
subprocess.check_output(["git", "rev-parse", "--short", "HEAD"])
.decode("ascii")
.strip()
)
def get_start_of_semester(d=date.today()): def get_start_of_semester(d=date.today()):
""" """
This function computes the start date of the semester with respect to the given date (default is today), This function computes the start date of the semester with respect to the given date (default is today),

View File

@ -250,6 +250,7 @@ class UserProfileForm(forms.ModelForm):
"scrub_pict": forms.ClearableFileInput, "scrub_pict": forms.ClearableFileInput,
"phone": PhoneNumberInternationalFallbackWidget, "phone": PhoneNumberInternationalFallbackWidget,
"parent_phone": PhoneNumberInternationalFallbackWidget, "parent_phone": PhoneNumberInternationalFallbackWidget,
"quote": forms.Textarea,
} }
labels = { labels = {
"profile_pict": _( "profile_pict": _(

View File

@ -5,23 +5,41 @@
{% trans counter_name=counter %}{{ counter_name }} activity{% endtrans %} {% trans counter_name=counter %}{{ counter_name }} activity{% endtrans %}
{% endblock %} {% endblock %}
{%- block additional_css -%}
<link rel="stylesheet" href="{{ scss('counter/activity.scss') }}">
{%- endblock -%}
{% block content %} {% block content %}
<h3>{% trans counter_name=counter %}{{ counter_name }} activity{% endtrans %}</h3> <h3>{% trans counter_name=counter %}{{ counter_name }} activity{% endtrans %}</h3>
{% if counter.type == 'BAR' %} {% if counter.type == 'BAR' %}
<h4>{% trans %}Barmen list{% endtrans %}</h4> <h4>{% trans %}Barmen list{% endtrans %}</h4>
<ul> <ul>
{% for b in counter.get_barmen_list() %} {% set barmans_list = counter.get_barmen_list() %}
{% if barmans_list | length > 0 %}
{% for b in barmans_list %}
<li>{{ user_profile_link(b) }}</li> <li>{{ user_profile_link(b) }}</li>
{% endfor %} {% endfor %}
{% else %}
{% trans %}There is currently no barman connected.{% endtrans %}
{% endif %}
</ul> </ul>
{% endif %} {% endif %}
<h5>{% trans %}Legend{% endtrans %}</h5> <h5>{% trans %}Legend{% endtrans %}</h5>
<span style="color: green">&#x2713;</span> : {% trans %}counter is open, there's at least one barman connected{% endtrans %} <div class="activity-description">
<br> <div>
<span style="color: orange">&#x3f;</span> : {% trans minutes=settings.SITH_COUNTER_MINUTE_INACTIVE %}counter is open but not active, the last sale was done at least {{ minutes }} minutes ago {% endtrans %} <i class="fa fa-check" style="color: #2ecc71"></i>
<br> <span>{% trans %}counter is open, there's at least one barman connected{% endtrans %}</span>
<span style="color: red">&#10007;</span> : {% trans %}counter is not open : no one is connected{% endtrans %} </div>
<div>
<i class="fa fa-question" style="color: #f39c12"></i>
<span>{% trans minutes=settings.SITH_COUNTER_MINUTE_INACTIVE %}counter is open but not active, the last sale was done at least {{ minutes }} minutes ago {% endtrans %}</span>
</div>
<div>
<i class="fa fa-times" style="color: #eb2f06"></i>
<span>{% trans %}counter is not open : no one is connected{% endtrans %}</span>
</div>
</div>
{% endblock %} {% endblock %}

View File

@ -1641,6 +1641,10 @@ msgstr "Appels affichés"
msgid "Calls to moderate" msgid "Calls to moderate"
msgstr "Appels à modérer" msgstr "Appels à modérer"
#: core/templates/core/base.jinja
msgid "Site version:"
msgstr "Version du site :"
#: com/templates/com/news_admin_list.jinja:242 #: com/templates/com/news_admin_list.jinja:242
#: core/templates/core/base.jinja:183 #: core/templates/core/base.jinja:183
msgid "Events" msgid "Events"
@ -2211,8 +2215,8 @@ msgid "Visitor"
msgstr "Visiteur" msgstr "Visiteur"
#: core/models.py:787 #: core/models.py:787
msgid "do you want to receive the weekmail" msgid "receive the Weekmail"
msgstr "voulez-vous recevoir le Weekmail" msgstr "recevoir le Weekmail"
#: core/models.py:789 #: core/models.py:789
msgid "show your stats to others" msgid "show your stats to others"
@ -2220,11 +2224,11 @@ msgstr "montrez vos statistiques aux autres"
#: core/models.py:791 #: core/models.py:791
msgid "get a notification for every click" msgid "get a notification for every click"
msgstr "recevez une notification pour chaque click" msgstr "avoir une notification pour chaque click"
#: core/models.py:794 #: core/models.py:794
msgid "get a notification for every refilling" msgid "get a notification for every refilling"
msgstr "recevez une notification pour chaque rechargement" msgstr "avoir une notification pour chaque rechargement"
#: core/models.py:817 #: core/models.py:817
msgid "file name" msgid "file name"
@ -2399,7 +2403,7 @@ msgstr "Connexion"
#: core/templates/core/base.jinja:62 core/templates/core/register.jinja:18 #: core/templates/core/base.jinja:62 core/templates/core/register.jinja:18
msgid "Register" msgid "Register"
msgstr "S'enregister" msgstr "Inscription"
#: core/templates/core/base.jinja:91 core/templates/core/base.jinja:92 #: core/templates/core/base.jinja:91 core/templates/core/base.jinja:92
#: forum/templates/forum/macros.jinja:171 #: forum/templates/forum/macros.jinja:171
@ -2559,9 +2563,9 @@ msgstr "Aide & Documentation"
msgid "R&D" msgid "R&D"
msgstr "R&D" msgstr "R&D"
#: core/templates/core/base.jinja:268 #: core/templates/core/base.jinja:262
msgid "Site made by good people" msgid "Site created by the IT Department of the AE"
msgstr "Site réalisé par des gens bons" msgstr "Site réalisé par le Pôle Informatique de l'AE"
#: core/templates/core/create.jinja:4 core/templates/core/create.jinja:8 #: core/templates/core/create.jinja:4 core/templates/core/create.jinja:8
#, python-format #, python-format
@ -2641,6 +2645,11 @@ msgstr "Coller"
msgid "Clipboard: " msgid "Clipboard: "
msgstr "Presse-papier : " msgstr "Presse-papier : "
#: sas/templates/sas/album.jinja:69
#: sas/templates/sas/album.jinja:97
msgid "To be moderated"
msgstr "A modérer"
#: core/templates/core/file_detail.jinja:53 #: core/templates/core/file_detail.jinja:53
msgid "Real name: " msgid "Real name: "
msgstr "Nom réel : " msgstr "Nom réel : "
@ -3323,6 +3332,10 @@ msgstr "Télécharger toutes mes photos"
msgid "Error downloading your pictures" msgid "Error downloading your pictures"
msgstr "Erreur de téléchargement de vos photos" msgstr "Erreur de téléchargement de vos photos"
#: core/templates/core/user_picture.jinja:
msgid "Picture Unavailable"
msgstr "Photo Indisponible"
#: core/templates/core/user_preferences.jinja:4 #: core/templates/core/user_preferences.jinja:4
#: core/templates/core/user_preferences.jinja:8 core/views/user.py:234 #: core/templates/core/user_preferences.jinja:8 core/views/user.py:234
msgid "Preferences" msgid "Preferences"
@ -3357,8 +3370,8 @@ msgstr ""
"14 caractères de long." "14 caractères de long."
#: core/templates/core/user_preferences.jinja:40 #: core/templates/core/user_preferences.jinja:40
msgid "No student cards registered." msgid "No student card registered."
msgstr "Aucune cartes étudiante enregistré." msgstr "Aucune carte étudiante enregistrée."
#: core/templates/core/user_stats.jinja:4 #: core/templates/core/user_stats.jinja:4
#, python-format #, python-format
@ -3981,6 +3994,10 @@ msgstr "Activité sur %(counter_name)s"
msgid "Barmen list" msgid "Barmen list"
msgstr "Barmans" msgstr "Barmans"
#: counter/templates/counter/activity.jinja:23
msgid "There is currently no barman connected."
msgstr "Il n'y a actuellement aucun barman connecté."
#: counter/templates/counter/activity.jinja:19 #: counter/templates/counter/activity.jinja:19
msgid "Legend" msgid "Legend"
msgstr "Légende" msgstr "Légende"
@ -5328,12 +5345,20 @@ msgstr "photo"
msgid "SAS" msgid "SAS"
msgstr "SAS" msgstr "SAS"
#: sas/templates/sas/album.jinja:102
msgid "This album does not contain any photos."
msgstr "Cet album ne contient aucune photo."
#: sas/templates/sas/album.jinja:53 sas/templates/sas/album.jinja:55 #: sas/templates/sas/album.jinja:53 sas/templates/sas/album.jinja:55
#: sas/templates/sas/main.jinja:13 sas/templates/sas/main.jinja:15 #: sas/templates/sas/main.jinja:13 sas/templates/sas/main.jinja:15
#: sas/templates/sas/main.jinja:17 #: sas/templates/sas/main.jinja:17
msgid "preview" msgid "preview"
msgstr "miniature" msgstr "miniature"
#: sas/templates/sas/main.jinja:42
msgid "You must be logged in to see the SAS."
msgstr "Vous devez être connecté pour voir les photos."
#: sas/templates/sas/album.jinja:89 #: sas/templates/sas/album.jinja:89
msgid "Upload" msgid "Upload"
msgstr "Envoyer" msgstr "Envoyer"
@ -5752,6 +5777,12 @@ msgstr "Vous avez acheté %s"
msgid "You have a notification" msgid "You have a notification"
msgstr "Vous avez une notification" msgstr "Vous avez une notification"
#: core/templates/core/base.jinja
msgid "You do not have any unread notification"
msgstr "Vous n'avez aucune notification non lue"
#: sith/settings.py:624
#: sith/settings.py:648
#: sith/settings.py:650 #: sith/settings.py:650
msgid "Success!" msgid "Success!"
msgstr "Succès !" msgstr "Succès !"

View File

@ -69,7 +69,6 @@ class Picture(SithFile):
im = Image.open(BytesIO(f.read())) im = Image.open(BytesIO(f.read()))
(w, h) = im.size (w, h) = im.size
return (w / h) < 1 return (w / h) < 1
return False
def can_be_edited_by(self, user): def can_be_edited_by(self, user):
return user.is_in_group(settings.SITH_GROUP_SAS_ADMIN_ID) return user.is_in_group(settings.SITH_GROUP_SAS_ADMIN_ID)
@ -79,11 +78,20 @@ class Picture(SithFile):
# Result is cached 4s for this user # Result is cached 4s for this user
if user.is_anonymous: if user.is_anonymous:
return False return False
perm = cache.get("%d_can_view_pictures" % (user.id), False) perm = cache.get("%d_can_view_pictures" % (user.id), False)
if perm:
# use cache only when user is in SAS Admins or when picture is moderated
if perm and (self.is_moderated or self.can_be_edited_by(user)):
return perm return perm
perm = self.is_in_sas and self.is_moderated and user.was_subscribed
perm = (
self.is_in_sas
and (self.is_moderated or self.can_be_edited_by(user))
and user.was_subscribed
)
cache.set("%d_can_view_pictures" % (user.id), perm, timeout=4) cache.set("%d_can_view_pictures" % (user.id), perm, timeout=4)
return perm return perm
def get_download_url(self): def get_download_url(self):

View File

@ -1,6 +1,10 @@
{% extends "core/base.jinja" %} {% extends "core/base.jinja" %}
{% from "core/macros.jinja" import paginate %} {% from "core/macros.jinja" import paginate %}
{%- block additional_css -%}
<link rel="stylesheet" href="{{ scss('sas/album.scss') }}">
{%- endblock -%}
{% block title %} {% block title %}
{% trans %}SAS{% endtrans %} {% trans %}SAS{% endtrans %}
{% endblock %} {% endblock %}
@ -8,86 +12,126 @@
{% macro print_path(file) %} {% macro print_path(file) %}
{% if file and file.parent %} {% if file and file.parent %}
{{ print_path(file.parent) }} {{ print_path(file.parent) }}
<a href="{{ url('sas:album', album_id=file.id) }}">{{ file.get_display_name() }}</a> > <a href="{{ url('sas:album', album_id=file.id) }}">{{ file.get_display_name() }}</a> /
{% endif %} {% endif %}
{% endmacro %} {% endmacro %}
{% block content %} {% block content %}
<a href="{{ url('sas:main') }}">SAS</a> > {{ print_path(album.parent) }} {{ album.get_display_name() }} <code>
<h3>{{ album.get_display_name() }}</h3> <a href="{{ url('sas:main') }}">SAS</a> / {{ print_path(album.parent) }} {{ album.get_display_name() }}
<a href="{{ url('sas:album_edit', album_id=album.id) }}">{% trans %}Edit{% endtrans %}</a><br> </code>
{% set start = timezone.now() %}
<hr>
{% set edit_mode = user.can_edit(album) %} {% set edit_mode = user.can_edit(album) %}
{% set start = timezone.now() %}
{% if edit_mode %} {% if edit_mode %}
<form action="" method="post" enctype="multipart/form-data" style="width: 100%;"> <form action="" method="post" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
<p>
<input name="delete" type="submit" value="{% trans %}Delete{% endtrans %}"> | <div class="navbar">
<input name="clear" type="submit" value="{% trans %}Clear clipboard{% endtrans %}"> | <h3>{{ album.get_display_name() }}</h3>
<input name="cut" type="submit" value="{% trans %}Cut{% endtrans %}"> |
<input name="paste" type="submit" value="{% trans %}Paste{% endtrans %}"> <div class="toolbar">
</p> <a href="{{ url('sas:album_edit', album_id=album.id) }}">{% trans %}Edit{% endtrans %}</a>
<input name="delete" type="submit" value="{% trans %}Delete{% endtrans %}">
<input name="cut" type="submit" value="{% trans %}Cut{% endtrans %}">
<input {% if not clipboard %}disabled{% endif %} name="paste" type="submit" value="{% trans %}Paste{% endtrans %}">
</div>
</div>
{% if clipboard %} {% if clipboard %}
<p>{% trans %}Clipboard: {% endtrans %} <div class="clipboard">
{% trans %}Clipboard: {% endtrans %}
<ul> <ul>
{% for f in clipboard %} {% for f in clipboard %}
<li>{{ f.get_full_path() }}</li> <li>{{ f.get_full_path() }}</li>
{% endfor %} {% endfor %}
</ul> </ul>
</p> <input name="clear" type="submit" value="{% trans %}Clear clipboard{% endtrans %}">
</div>
{% endif %} {% endif %}
{% endif %} {% endif %}
<div>
{% if album.children_albums.count() > 0 %}
<h4>{% trans %}Albums{% endtrans %}</h4>
<div class="albums">
{% for a in album.children_albums.order_by('-date') %} {% for a in album.children_albums.order_by('-date') %}
<div style="display: inline-block;"> {% if a.can_be_viewed_by(user) %}
<a href="{{ url('sas:album', album_id=a.id) }}">
<div
class="album{% if not a.is_moderated %} not_moderated{% endif %}"
style="background-image: url('{% if a.file %}{{ a.get_download_url() }}{% else %}{{ static('core/img/sas.jpg') }}{% endif %}');"
>
{% if not a.is_moderated %}
<div class="overlay">&nbsp;</div>
<div class="text">{% trans %}To be moderated{% endtrans %}</div>
{% else %}
<div class="text">{{ a.name }}</div>
{% endif %}
</div>
{% if edit_mode %} {% if edit_mode %}
<input type="checkbox" name="file_list" value="{{ a.id }}"> <input type="checkbox" name="file_list" value="{{ a.id }}">
{% endif %} {% endif %}
{% if user.can_view(a) %}
<a href="{{ url("sas:album", album_id=a.id) }}" style="display: inline-block">
<div class="album{% if not a.is_moderated %} not_moderated{% endif %}">
<div>
{% if a.file %}
<img src="{{ a.get_download_url() }}" alt="{% trans %}preview{% endtrans %}">
{% else %}
<img src="{{ static('core/img/sas.jpg') }}" alt="{% trans %}preview{% endtrans %}">
{% endif %}
</div>
{{ a.name }}
</div>
</a> </a>
{% endif %} {% endif %}
</div>
{% endfor %} {% endfor %}
</div> </div>
<div>
<br>
{% endif %}
<h4>{% trans %}Pictures{% endtrans %}</h4>
{% if pictures | length != 0 %}
<div class="photos">
{% for p in pictures %} {% for p in pictures %}
<div style="display: inline-block;"> {% if p.can_be_viewed_by(user) %}
<a href="{{ url('sas:picture', picture_id=p.id) }}#pict">
<div
class="photo"
style="background-image: url('{{ p.get_download_thumb_url() }}')"
>
{% if not p.is_moderated %}
<div class="overlay">&nbsp;</div>
<div class="text">{% trans %}To be moderated{% endtrans %}</div>
{% else %}
<div class="text">&nbsp;</div>
{% endif %}
</div>
{% if edit_mode %} {% if edit_mode %}
<input type="checkbox" name="file_list" value="{{ p.id }}"> <input type="checkbox" name="file_list" value="{{ p.id }}">
{% endif %} {% endif %}
{% if user.can_view(p) %}
<div class="picture{% if not p.is_moderated %} not_moderated{% endif %}">
<a href="{{ url("sas:picture", picture_id=p.id) }}#pict">
<img src="{{ p.get_download_thumb_url() }}" alt="{{ p.get_display_name() }}" />
</a> </a>
</div>
{% endif %} {% endif %}
</div>
{% endfor %} {% endfor %}
</div> </div>
<br> {% else %}
{% trans %}This album does not contain any photos.{% endtrans %}
{% endif %}
{% if pictures.has_previous() or pictures.has_next() %}
<div class="paginator">
{{ paginate(pictures, paginator) }} {{ paginate(pictures, paginator) }}
</div>
{% endif %}
{% if edit_mode %} {% if edit_mode %}
</form> </form>
{% endif %} {% endif %}
<form id="upload_form" action="" method="post" enctype="multipart/form-data">
{% if user.is_in_group(settings.SITH_GROUP_SAS_ADMIN_ID) %}
<form class="add-files" id="upload_form" action="" method="post" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
<div class="inputs">
{{ form.as_p() }} {{ form.as_p() }}
<p><input type="submit" value="{% trans %}Upload{% endtrans %}" /></p>
<input type="submit" value="{% trans %}Upload{% endtrans %}" />
</div>
</form> </form>
{% endif %}
<hr>
<p style="font-size: small; color: #444;">{% trans %}Template generation time: {% endtrans %} <p style="font-size: small; color: #444;">{% trans %}Template generation time: {% endtrans %}
{{ timezone.now() - start }} {{ timezone.now() - start }}
</p> </p>
@ -97,7 +141,7 @@
{{ super() }} {{ super() }}
<script> <script>
$("form#upload_form").submit(function (event) { $("form#upload_form").submit(function (event) {
var formData = new FormData($(this)[0]); let formData = new FormData($(this)[0]);
if(!formData.get('album_name') && !formData.get('images').name) if(!formData.get('album_name') && !formData.get('images').name)
return false; return false;
@ -108,26 +152,26 @@ $("form#upload_form").submit(function (event) {
event.preventDefault(); event.preventDefault();
var errorlist; let errorList;
if((errorlist = this.querySelector('#upload_form ul.errorlist.nonfield')) === null) { if((errorList = this.querySelector('#upload_form ul.errorlist.nonfield')) === null) {
errorlist = document.createElement('ul'); errorList = document.createElement('ul');
errorlist.classList.add('errorlist', 'nonfield'); errorList.classList.add('errorlist', 'nonfield');
this.insertBefore(errorlist, this.firstElementChild); this.insertBefore(errorList, this.firstElementChild);
} }
while(errorlist.childElementCount > 0) while(errorList.childElementCount > 0)
errorlist.removeChild(errorlist.firstElementChild); errorList.removeChild(errorList.firstElementChild);
var progress; let progress;
if((progress = this.querySelector('progress')) === null) { if((progress = this.querySelector('progress')) === null) {
progress = document.createElement('progress'); progress = document.createElement('progress');
progress.value = 0; progress.value = 0;
var p = document.createElement('p'); let p = document.createElement('p');
p.appendChild(progress); p.appendChild(progress);
this.insertBefore(p, this.lastElementChild); this.insertBefore(p, this.lastElementChild);
} }
var dataHolder; let dataHolder;
if(formData.get('album_name')) { if(formData.get('album_name')) {
dataHolder = new FormData(); dataHolder = new FormData();
@ -143,15 +187,15 @@ $("form#upload_form").submit(function (event) {
}); });
} }
var images = formData.getAll('images'); let images = formData.getAll('images');
var imagesCount = images.length; let imagesCount = images.length;
var completeCount = 0; let completeCount = 0;
var poolSize = 1; let poolSize = 1;
var imagePool = []; let imagePool = [];
while(images.length > 0 && imagePool.length < poolSize) { while(images.length > 0 && imagePool.length < poolSize) {
var image = images.shift(); let image = images.shift();
imagePool.push(image); imagePool.push(image);
sendImage(image); sendImage(image);
} }
@ -173,29 +217,31 @@ $("form#upload_form").submit(function (event) {
.always(next.bind(undefined, image)); .always(next.bind(undefined, image));
} }
function next(image, status, jqXHR) { function next(image, _, __) {
var index; let index = imagePool.indexOf(image);
if(index = imagePool.indexOf(image) !== -1) { let nextImage = images.shift();
imagePool.splice(index, 1);
}
var nextImage;
if(nextImage = images.shift()) {
imagePool.push(nextImage);
if(index !== -1)
imagePool.splice(index, 1);
if(nextImage) {
imagePool.push(nextImage);
sendImage(nextImage); sendImage(nextImage);
} }
} }
function onSuccess(image, data, status, jqXHR) { function onSuccess(image, data, _, __) {
let errors = [];
if ($(data.responseText).find('.errorlist.nonfield')[0]) if ($(data.responseText).find('.errorlist.nonfield')[0])
var errors = Array.from($(data.responseText).find('.errorlist.nonfield')[0].children); errors = Array.from($(data.responseText).find('.errorlist.nonfield')[0].children);
else
var errors = []
while(errors.length > 0) while(errors.length > 0)
errorlist.appendChild(errors.shift()); errorList.appendChild(errors.shift());
progress.value = ++completeCount / imagesCount; progress.value = ++completeCount / imagesCount;
if(progress.value === 1 && errorlist.children.length === 0) if(progress.value === 1 && errorList.children.length === 0)
document.location.reload(true) document.location.reload()
} }
}); });
</script> </script>

View File

@ -1,56 +1,110 @@
{% extends "core/base.jinja" %} {% extends "core/base.jinja" %}
{%- block additional_css -%}
<link rel="stylesheet" href="{{ scss('sas/album.scss') }}">
{%- endblock -%}
{% block title %} {% block title %}
{% trans %}SAS{% endtrans %} {% trans %}SAS{% endtrans %}
{% endblock %} {% endblock %}
{% macro display_album(a) %} {% set edit_mode = user.is_in_group(settings.SITH_GROUP_SAS_ADMIN_ID) %}
{% if a.is_moderated %}
<a href="{{ url("sas:album", album_id=a.id) }}"> {% macro display_album(a, checkbox) %}
<div class="album"> <a href="{{ url('sas:album', album_id=a.id) }}">
<div>
{% if a.file %} {% if a.file %}
<img src="{{ a.get_download_url() }}" alt="{% trans %}preview{% endtrans %}"> {% set img = a.get_download_url() %}
{% elif a.children.filter(is_folder=False, is_moderated=True).exists() %} {% elif a.children.filter(is_folder=False, is_moderated=True).exists() %}
<img src="{{ a.children.filter(is_folder=False).first().as_picture.get_download_thumb_url() }}" alt="{% trans %}preview{% endtrans %}"> {% set img = a.children.filter(is_folder=False).first().as_picture.get_download_thumb_url() %}
{% else %} {% else %}
<img src="{{ static('core/img/sas.jpg') }}" alt="{% trans %}preview{% endtrans %}"> {% set img = static('core/img/sas.jpg') %}
{% endif %} {% endif %}
</div>
<div
class="album"
style="background-image: url('{{ img }}');"
>
<div class="text">
{{ a.name }} {{ a.name }}
</div> </div>
</a>
{% elif user.is_in_group(settings.SITH_GROUP_SAS_ADMIN_ID) %}
<div style="display: inline-block; border: solid 1px red; text-align: center">
<p><a href="{{ url('core:file_moderate', file_id=a.id) }}?next={{ url('sas:moderation') }}">Moderate</a> or <a href="">Delete</a></p>
<a href="{{ url("sas:album", album_id=a.id) }}">{{ a.name }}</a>
</div> </div>
{% endif %} {# {% if edit_mode and checkbox %}
<input type="checkbox" name="file_list" value="{{ a.id }}">
{% endif %} #}
</a>
{% endmacro %} {% endmacro %}
{% block content %} {% block content %}
<main>
<h3>{% trans %}SAS{% endtrans %}</h3> <h3>{% trans %}SAS{% endtrans %}</h3>
<hr>
{% if not user.is_authenticated %}
<p>{% trans %}You must be logged in to see the SAS.{% endtrans %}</p>
{% else %}
<br>
<h4>{% trans %}Latest albums{% endtrans %}</h4> <h4>{% trans %}Latest albums{% endtrans %}</h4>
<div>
<div class="albums">
{% for a in latest %} {% for a in latest %}
{{ display_album(a) }} {{ display_album(a) }}
{% endfor %} {% endfor %}
</div> </div>
<hr>
<h4>{% trans %}All categories{% endtrans %}</h4> <br>
<div>
{% for a in categories %} {% if edit_mode %}
{{ display_album(a) }}
{% endfor %}
</div>
{% if user.is_in_group(settings.SITH_GROUP_SAS_ADMIN_ID) %}
<form action="" method="post" enctype="multipart/form-data"> <form action="" method="post" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
<div class="navbar">
<h4>{% trans %}All categories{% endtrans %}</h4>
{# <div class="toolbar">
<input name="delete" type="submit" value="{% trans %}Delete{% endtrans %}">
</div> #}
</div>
{% if clipboard %}
<div class="clipboard">
{% trans %}Clipboard: {% endtrans %}
<ul>
{% for f in clipboard %}
<li>{{ f.get_full_path() }}</li>
{% endfor %}
</ul>
<input name="clear" type="submit" value="{% trans %}Clear clipboard{% endtrans %}">
</div>
{% endif %}
{% else %}
<h4>{% trans %}All categories{% endtrans %}</h4>
{% endif %}
<div class="albums">
{% for a in categories %}
{{ display_album(a, true) }}
{% endfor %}
</div>
{% if edit_mode %}
</form>
<br>
<form class="add-files" action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="inputs">
<div>
<label for="{{ form.album_name.name }}">{{ form.album_name.label }}</label>
{{ form.album_name }}
</div>
<input type="submit" value="{% trans %}Create{% endtrans %}" />
</div>
{{ form.non_field_errors() }} {{ form.non_field_errors() }}
<p>{{ form.album_name.errors }}<label for="{{ form.album_name.name }}">{{ form.album_name.label }}</label> {{ form.album_name.errors }}
{{ form.album_name }}</p>
<p><input type="submit" value="{% trans %}Create{% endtrans %}" /></p>
</form> </form>
{% endif %} {% endif %}
{% endif %}
</main>
{% endblock %} {% endblock %}

View File

@ -1,25 +1,11 @@
{% extends "core/base.jinja" %} {% extends "core/base.jinja" %}
{%- block additional_css -%}
<link rel="stylesheet" href="{{ scss('sas/picture.scss') }}">
{%- endblock -%}
{% block head %} {% block head %}
{{ super() }} {{ super() }}
<style>
#prev, #next {
display: inline-block;
width: 42%;
height: 100px;
margin: 0.5%;
border: solid 1px grey;
overflow: hidden;
background: #aaa;
text-align: center;
}
#prev img, #next img {
display: block;
margin: auto;
max-height: 80%;
max-width: 100%;
}
</style>
{% if picture.get_previous() %} {% if picture.get_previous() %}
<link rel="preload" as="image" href="{{ url("sas:download_compressed", picture_id=picture.get_previous().id) }}"> <link rel="preload" as="image" href="{{ url("sas:download_compressed", picture_id=picture.get_previous().id) }}">
@ -37,111 +23,151 @@
{% macro print_path(file) %} {% macro print_path(file) %}
{% if file and file.parent %} {% if file and file.parent %}
{{ print_path(file.parent) }} {{ print_path(file.parent) }}
<a href="{{ url('sas:album', album_id=file.id) }}">{{ file.get_display_name() }}</a> > <a href="{{ url('sas:album', album_id=file.id) }}">{{ file.get_display_name() }}</a> /
{% endif %} {% endif %}
{% endmacro %} {% endmacro %}
{% block content %} {% block content %}
<a href="{{ url('sas:main') }}">SAS</a> > {{ print_path(picture.parent) }} {{ picture.get_display_name() }} <code>
({{ picture.parent.children.filter(id__lte=picture.id).count() }} / {{ picture.parent.children.count() }}) <a href="{{ url('sas:main') }}">SAS</a> / {{ print_path(picture.parent) }} {{ picture.get_display_name() }}
</code>
<br>
<div class="title">
<h3>{{ picture.get_display_name() }}</h3> <h3>{{ picture.get_display_name() }}</h3>
<div style="display: inline-block; width: 19%; vertical-align: top; overflow: hidden; float: right"> <h4>{{ picture.parent.children.filter(id__lte=picture.id).count() }} / {{ picture.parent.children.count() }}</h4>
<div>
<div id="prev">
{% if picture.get_previous() %}
<a href="{{ url("sas:picture", picture_id=picture.get_previous().id) }}#pict">
&larr;
<img src="{{ picture.get_previous().as_picture.get_download_thumb_url() }}" alt="{{ picture.get_previous().get_display_name() }}" />
</a>
{% endif %}
</div> </div>
<div id="next">
{% if picture.get_next() %}
<a href="{{ url("sas:picture", picture_id=picture.get_next().id) }}#pict"> {% if not picture.is_moderated %}
&rarr;
<img src="{{ picture.get_next().as_picture.get_download_thumb_url() }}" alt="{{ picture.get_next().get_display_name() }}" />
</a>
{% endif %}
</div>
</div>
<div>
<h5>{% trans %}People{% endtrans %}</h5>
<ul>
{% for r in picture.people.all() %}
<li>
<a href="{{ r.user.get_absolute_url() }}">{{ r.user.get_short_name() }}</a>
{% if user == r.user or user.can_edit(picture) %}
<a href="?remove_user={{ r.user.id }}">{% trans %}Delete{% endtrans %}</a>
{% endif %}
</li>
{% endfor %}
</ul>
<form action="" method="post" enctype="multipart/form-data" style="margin: 0px;">
{% csrf_token %}
{{ form.as_p() }}
<p><input type="submit" value="{% trans %}Go{% endtrans %}" /></p>
</form>
</div>
<div>
<h5>{% trans %}Infos{% endtrans %}</h5>
<p>{% trans %}Date: {% endtrans %}{{ picture.date|date(DATETIME_FORMAT) }}</p>
<p>{% trans %}Owner: {% endtrans %}<a href="{{ picture.owner.get_absolute_url() }}">{{ picture.owner.get_short_name() }}</a></p>
{% if picture.moderator %}
<p>{% trans %}Moderator: {% endtrans %}<a href="{{ picture.moderator.get_absolute_url() }}">{{ picture.moderator.get_short_name() }}</a></p>
{% endif %}
<p>{{ picture.parent.children.filter(id__lte=picture.id).count() }} / {{ picture.parent.children.count() }}</p>
</div>
<div>
<h5>{% trans %}Tools{% endtrans %}</h5>
<p>
<a href="{{ picture.get_download_url() }}">{% trans %}HD version{% endtrans %}</a>
</p>
<p style="font-size: smaller;">
<a href="{{ url('sas:picture_edit', picture_id=picture.id) }}">{% trans %}Edit{% endtrans %}</a><br>
<a href="?rotate_left">{% trans %}Rotate left{% endtrans %}</a><br>
<a href="?rotate_right">{% trans %}Rotate right{% endtrans %}</a><br>
<a href="?ask_removal">{% trans %}Ask for removal{% endtrans %}</a><br>
</p>
</div>
</div>
{% if picture.is_moderated %}
<div id="pict">
{% else %}
<div id="pict" style="border: solid #f00 2px; box-shadow: red 0px 0px 5px">
{% set next = picture.get_next() %} {% set next = picture.get_next() %}
{% if not next %} {% if not next %}
{% set next = url('sas:moderation') %} {% set next = url('sas:moderation') %}
{% else %} {% else %}
{% set next = next.get_absolute_url() + "#pict" %} {% set next = next.get_absolute_url() + "#pict" %}
{% endif %} {% endif %}
<div style="background: lightgrey; padding: 2px;">
<div class="moderation">
<div>
{% if picture.asked_for_removal %} {% if picture.asked_for_removal %}
<span class="important">{% trans %}Asked for removal{% endtrans %}</span> <span class="important">{% trans %}Asked for removal{% endtrans %}</span>
{% else %}
&nbsp;
{% endif %} {% endif %}
</div>
<div>
<a href="{{ url('core:file_moderate', file_id=picture.id) }}?next={{ next }}"> <a href="{{ url('core:file_moderate', file_id=picture.id) }}?next={{ next }}">
{% trans %}Moderate{% endtrans %}</a> | {% trans %}Moderate{% endtrans %}
</a>
<a href="{{ url('core:file_delete', file_id=picture.id) }}?next={{ next }}"> <a href="{{ url('core:file_delete', file_id=picture.id) }}?next={{ next }}">
{% trans %}Delete{% endtrans %}</a> {% trans %}Delete{% endtrans %}
</a>
</div>
</div> </div>
{% endif %} {% endif %}
{% if picture.is_vertical %}
<img src="{{ picture.get_download_compressed_url() }}" alt="{{ picture.get_display_name() }}" style="width: 60%; display: block; margin: auto"/> <div class="container">
{% else %} <div class="main">
<img src="{{ picture.get_download_compressed_url() }}" alt="{{ picture.get_display_name() }}" style="width: 100%; display: block; margin: auto"/>
<div class="photo">
<img src="{{ picture.get_download_compressed_url() }}" alt="{{ picture.get_display_name() }}"/>
</div>
<div class="general">
<div class="infos">
<h5>{% trans %}Infos{% endtrans %}</h5>
<div>
<div>
<span>{% trans %}Date: {% endtrans %}</span>
<span>{{ picture.date|date(DATETIME_FORMAT) }}</span>
</div>
<div>
<span>{% trans %}Owner: {% endtrans %}</span>
<a href="{{ picture.owner.get_absolute_url() }}">{{ picture.owner.get_short_name() }}</a>
</div>
{% if picture.moderator %}
<div>
<span>{% trans %}Moderator: {% endtrans %}</span>
<a href="{{ picture.moderator.get_absolute_url() }}">{{ picture.moderator.get_short_name() }}</a>
</div>
{% endif %} {% endif %}
</div> </div>
</div>
<div class="tools">
<h5>{% trans %}Tools{% endtrans %}</h5>
<div>
<div>
<a class="text" href="{{ picture.get_download_url() }}">{% trans %}HD version{% endtrans %}</a>
<br>
<a class="text danger" href="?ask_removal">{% trans %}Ask for removal{% endtrans %}</a>
</div>
<div class="buttons">
<a class="button" href="{{ url('sas:picture_edit', picture_id=picture.id) }}">✏️</a>
<a class="button" href="?rotate_left">↺</a>
<a class="button" href="?rotate_right">↻</a>
</div>
</div>
</div>
</div>
</div>
<div class="subsection">
<div class="navigation">
<div id="prev">
{% if picture.get_previous() %}
<a href="{{ url( 'sas:picture', picture_id=picture.get_previous().id) }}#pict">
<div style="background-image: url('{{ picture.get_previous().as_picture.get_download_thumb_url() }}');"></div>
</a>
{% endif %}
</div>
<div id="next">
{% if picture.get_next() %}
<a href="{{ url( 'sas:picture', picture_id=picture.get_next().id) }}#pict">
<div style="background-image: url('{{ picture.get_next().as_picture.get_download_thumb_url() }}');"></div>
</a>
{% endif %}
</div>
</div>
<div class="tags">
<h5>{% trans %}People{% endtrans %}</h5>
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p() }}
<input type="submit" value="{% trans %}Go{% endtrans %}" />
</form>
<ul>
{% for r in picture.people.all() %}
<li>
<a class="user" href="{{ r.user.get_absolute_url() }}">
{% if r.user.profile_pict %}
<img src="{{ r.user.profile_pict.get_download_url() }}">
{% endif %}
<span>{{ r.user.get_short_name() }}</span>
</a>
{% if user == r.user or user.can_edit(picture) %}
<a class="delete" href="?remove_user={{ r.user.id }}">❌</a>
{% endif %}
</li>
{% endfor %}
</ul>
</div>
</div>
</div>
{% endblock %} {% endblock %}
{% block script %} {% block script %}
{{ super() }} {{ super() }}
<script> <script>
$( function() { $(() => {
$(document).keydown(function (e) { $(document).keydown((e) => {
if (e.keyCode == 37) { switch (e.keyCode) {
console.log("prev"); case 37:
$('#prev a')[0].click(); $('#prev a')[0].click();
} else if (e.keyCode == 39) { case 39:
console.log("next");
$('#next a')[0].click(); $('#next a')[0].click();
} }
}); });

View File

@ -357,7 +357,7 @@ SITH_FORUM_PAGE_LENGTH = 30
# SAS variables # SAS variables
SITH_SAS_ROOT_DIR_ID = 4 SITH_SAS_ROOT_DIR_ID = 4
SITH_SAS_IMAGES_PER_PAGE = 30 SITH_SAS_IMAGES_PER_PAGE = 60
SITH_BOARD_SUFFIX = "-bureau" SITH_BOARD_SUFFIX = "-bureau"
SITH_MEMBER_SUFFIX = "-membres" SITH_MEMBER_SUFFIX = "-membres"